vcap-concurrency 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/Gemfile +3 -0
- data/LICENSE +7136 -0
- data/README.md +3 -0
- data/Rakefile +15 -0
- data/lib/vcap/concurrency.rb +4 -0
- data/lib/vcap/concurrency/atomic_var.rb +68 -0
- data/lib/vcap/concurrency/errors.rb +7 -0
- data/lib/vcap/concurrency/promise.rb +97 -0
- data/lib/vcap/concurrency/proxy.rb +18 -0
- data/lib/vcap/concurrency/thread_pool.rb +152 -0
- data/lib/vcap/concurrency/version.rb +5 -0
- data/spec/atomic_var_spec.rb +56 -0
- data/spec/promise_spec.rb +83 -0
- data/spec/proxy_spec.rb +13 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/thread_pool_spec.rb +175 -0
- data/vcap-concurrency.gemspec +22 -0
- metadata +116 -0
data/spec/proxy_spec.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe VCAP::Concurrency::Proxy do
|
4
|
+
describe "#method_missing" do
|
5
|
+
it "should proxy method calls to the underlying object" do
|
6
|
+
proxied_object = ["hi"]
|
7
|
+
proxy = VCAP::Concurrency::Proxy.new(proxied_object)
|
8
|
+
proxy << "there"
|
9
|
+
|
10
|
+
proxied_object.should == ["hi", "there"]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
$stdout.sync = true
|
6
|
+
|
7
|
+
describe VCAP::Concurrency::ThreadPool do
|
8
|
+
describe "#start" do
|
9
|
+
it "should raise an error if it has already been started or stopped" do
|
10
|
+
tp = VCAP::Concurrency::ThreadPool.new(1)
|
11
|
+
tp.start
|
12
|
+
|
13
|
+
# Started
|
14
|
+
expect do
|
15
|
+
tp.start
|
16
|
+
end.to raise_error(/invalid state/i)
|
17
|
+
|
18
|
+
# Stopped
|
19
|
+
tp.stop
|
20
|
+
expect do
|
21
|
+
tp.start
|
22
|
+
end.to raise_error(/invalid state/i)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should start the requested number of worker threads" do
|
26
|
+
waiting = VCAP::Concurrency::AtomicVar.new(0)
|
27
|
+
barrier = Mutex.new
|
28
|
+
num_threads = 5
|
29
|
+
promises = []
|
30
|
+
tp = VCAP::Concurrency::ThreadPool.new(num_threads)
|
31
|
+
|
32
|
+
# Each worker notifies the main thread that it is ready. When all workers
|
33
|
+
# have checked in, the main thread unblocks them all.
|
34
|
+
num_threads.times do |ii|
|
35
|
+
promises << tp.enqueue do
|
36
|
+
waiting.mutate { |v| v + 1 }
|
37
|
+
|
38
|
+
barrier.synchronize {}
|
39
|
+
|
40
|
+
Thread.current
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
barrier.lock
|
45
|
+
|
46
|
+
tp.start
|
47
|
+
|
48
|
+
# Wait until all threads have checked in
|
49
|
+
wait_threads_active(waiting, num_threads)
|
50
|
+
|
51
|
+
# Let all threads proceed
|
52
|
+
barrier.unlock
|
53
|
+
|
54
|
+
# Wait for the threads to finish and collect our result
|
55
|
+
results = Set.new(promises.map { |p| p.resolve })
|
56
|
+
|
57
|
+
tp.shutdown
|
58
|
+
|
59
|
+
results.length.should == num_threads
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#enqueue" do
|
64
|
+
it "should raise an error if the pool is stopped" do
|
65
|
+
tp = VCAP::Concurrency::ThreadPool.new(1)
|
66
|
+
|
67
|
+
tp.stop
|
68
|
+
expect do
|
69
|
+
tp.enqueue { true }
|
70
|
+
end.to raise_error(/invalid state/i)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should be responsible for executing the supplied bock in a worker" do
|
74
|
+
tp = VCAP::Concurrency::ThreadPool.new(1)
|
75
|
+
expected_result = fib(5)
|
76
|
+
promise = tp.enqueue { fib(5) }
|
77
|
+
|
78
|
+
tp.start
|
79
|
+
|
80
|
+
result = promise.resolve
|
81
|
+
|
82
|
+
tp.shutdown
|
83
|
+
|
84
|
+
result.should == expected_result
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should propagate exceptions thrown in the block" do
|
88
|
+
tp = VCAP::Concurrency::ThreadPool.new(1)
|
89
|
+
e = RuntimeError.new("test")
|
90
|
+
promise = tp.enqueue { raise e }
|
91
|
+
|
92
|
+
tp.start
|
93
|
+
|
94
|
+
expect do
|
95
|
+
promise.resolve
|
96
|
+
end.to raise_error(e)
|
97
|
+
|
98
|
+
tp.shutdown
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#stop" do
|
103
|
+
it "should allow existing work to be processed" do
|
104
|
+
waiting = VCAP::Concurrency::AtomicVar.new(0)
|
105
|
+
barrier = Mutex.new
|
106
|
+
num_threads = 5
|
107
|
+
promises = []
|
108
|
+
tp = VCAP::Concurrency::ThreadPool.new(num_threads)
|
109
|
+
|
110
|
+
# Each worker notifies the main thread that it is ready. When all workers
|
111
|
+
# have checked in, the main thread unblocks them all.
|
112
|
+
num_threads.times do |ii|
|
113
|
+
promises << tp.enqueue do
|
114
|
+
waiting.mutate { |v| v + 1 }
|
115
|
+
|
116
|
+
barrier.synchronize {}
|
117
|
+
|
118
|
+
ii
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
barrier.lock
|
123
|
+
|
124
|
+
tp.start
|
125
|
+
|
126
|
+
# Wait until all threads have checked in
|
127
|
+
wait_threads_active(waiting, num_threads)
|
128
|
+
|
129
|
+
# All threads are active here, but cannot proceed until we unlock the
|
130
|
+
# barrier. Thus, any work added here must live in the queue.
|
131
|
+
num_threads.times do |ii|
|
132
|
+
promises << tp.enqueue { ii + num_threads }
|
133
|
+
end
|
134
|
+
|
135
|
+
tp.stop
|
136
|
+
|
137
|
+
# Let all threads proceed
|
138
|
+
barrier.unlock
|
139
|
+
|
140
|
+
# Wait for the threads to finish and collect our results
|
141
|
+
results = promises.map { |p| p.resolve }
|
142
|
+
|
143
|
+
tp.join
|
144
|
+
|
145
|
+
results.length.should == 2 * num_threads
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "#join" do
|
150
|
+
it "should raise an error unless the pool has been started or stopped" do
|
151
|
+
tp = VCAP::Concurrency::ThreadPool.new(5)
|
152
|
+
expect do
|
153
|
+
tp.join
|
154
|
+
end.to raise_error(/invalid state/i)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def wait_threads_active(counter, expected)
|
159
|
+
cur = 0
|
160
|
+
done = false
|
161
|
+
while (cur = counter.wait_value_changed(cur)) != expected
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def fib(ii)
|
166
|
+
case ii
|
167
|
+
when 1
|
168
|
+
1
|
169
|
+
when 0
|
170
|
+
1
|
171
|
+
else
|
172
|
+
fib(ii - 1) + fib(ii - 2)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path("../lib/vcap/concurrency/version", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["VMware"]
|
6
|
+
gem.email = ["support@vmware.com"]
|
7
|
+
gem.description = "Provides utility classes to support common patterns" \
|
8
|
+
+ " in concurrent programming."
|
9
|
+
gem.summary = %q{Concurrency related utility classes}
|
10
|
+
gem.homepage = "http://www.cloudfoundry.org"
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split($\)
|
13
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
14
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
15
|
+
gem.name = "vcap-concurrency"
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.version = VCAP::Concurrency::VERSION
|
18
|
+
|
19
|
+
gem.add_development_dependency("ci_reporter")
|
20
|
+
gem.add_development_dependency("rake")
|
21
|
+
gem.add_development_dependency("rspec")
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vcap-concurrency
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- VMware
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ci_reporter
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Provides utility classes to support common patterns in concurrent programming.
|
63
|
+
email:
|
64
|
+
- support@vmware.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/vcap/concurrency.rb
|
75
|
+
- lib/vcap/concurrency/atomic_var.rb
|
76
|
+
- lib/vcap/concurrency/errors.rb
|
77
|
+
- lib/vcap/concurrency/promise.rb
|
78
|
+
- lib/vcap/concurrency/proxy.rb
|
79
|
+
- lib/vcap/concurrency/thread_pool.rb
|
80
|
+
- lib/vcap/concurrency/version.rb
|
81
|
+
- spec/atomic_var_spec.rb
|
82
|
+
- spec/promise_spec.rb
|
83
|
+
- spec/proxy_spec.rb
|
84
|
+
- spec/spec_helper.rb
|
85
|
+
- spec/thread_pool_spec.rb
|
86
|
+
- vcap-concurrency.gemspec
|
87
|
+
homepage: http://www.cloudfoundry.org
|
88
|
+
licenses: []
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.8.23
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Concurrency related utility classes
|
111
|
+
test_files:
|
112
|
+
- spec/atomic_var_spec.rb
|
113
|
+
- spec/promise_spec.rb
|
114
|
+
- spec/proxy_spec.rb
|
115
|
+
- spec/spec_helper.rb
|
116
|
+
- spec/thread_pool_spec.rb
|