concurrent-ruby 0.1.1.pre.5 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -48
  3. data/lib/concurrent.rb +0 -6
  4. data/lib/concurrent/agent.rb +40 -19
  5. data/lib/concurrent/cached_thread_pool.rb +11 -10
  6. data/lib/concurrent/defer.rb +12 -8
  7. data/lib/concurrent/fixed_thread_pool.rb +6 -12
  8. data/lib/concurrent/future.rb +20 -8
  9. data/lib/concurrent/global_thread_pool.rb +0 -13
  10. data/lib/concurrent/goroutine.rb +1 -5
  11. data/lib/concurrent/obligation.rb +64 -10
  12. data/lib/concurrent/promise.rb +60 -38
  13. data/lib/concurrent/thread_pool.rb +5 -16
  14. data/lib/concurrent/utilities.rb +0 -8
  15. data/lib/concurrent/version.rb +1 -1
  16. data/md/defer.md +4 -4
  17. data/md/promise.md +0 -2
  18. data/md/thread_pool.md +0 -27
  19. data/spec/concurrent/agent_spec.rb +27 -8
  20. data/spec/concurrent/cached_thread_pool_spec.rb +1 -14
  21. data/spec/concurrent/defer_spec.rb +21 -17
  22. data/spec/concurrent/event_machine_defer_proxy_spec.rb +149 -159
  23. data/spec/concurrent/fixed_thread_pool_spec.rb +3 -2
  24. data/spec/concurrent/future_spec.rb +10 -3
  25. data/spec/concurrent/goroutine_spec.rb +0 -15
  26. data/spec/concurrent/obligation_shared.rb +2 -16
  27. data/spec/concurrent/promise_spec.rb +13 -15
  28. data/spec/concurrent/thread_pool_shared.rb +5 -5
  29. data/spec/concurrent/utilities_spec.rb +1 -30
  30. data/spec/spec_helper.rb +0 -25
  31. metadata +7 -28
  32. data/lib/concurrent/executor.rb +0 -95
  33. data/lib/concurrent/functions.rb +0 -120
  34. data/lib/concurrent/null_thread_pool.rb +0 -22
  35. data/lib/concurrent/reactor.rb +0 -161
  36. data/lib/concurrent/reactor/drb_async_demux.rb +0 -74
  37. data/lib/concurrent/reactor/tcp_sync_demux.rb +0 -98
  38. data/md/executor.md +0 -176
  39. data/spec/concurrent/executor_spec.rb +0 -200
  40. data/spec/concurrent/functions_spec.rb +0 -217
  41. data/spec/concurrent/global_thread_pool_spec.rb +0 -38
  42. data/spec/concurrent/null_thread_pool_spec.rb +0 -54
  43. data/spec/concurrent/reactor/drb_async_demux_spec.rb +0 -12
  44. data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +0 -12
  45. data/spec/concurrent/reactor_spec.rb +0 -351
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d466fea03aa4bbc40ea569433ab79bccd946e175
4
- data.tar.gz: f2e0bab0869b00f0da3ca079eea4055bd45412a7
3
+ metadata.gz: ace46fc451714785036604e4174b57f81ff2f3c6
4
+ data.tar.gz: f5278235efdd0cc59ae6645aace41c35ef61c8ef
5
5
  SHA512:
6
- metadata.gz: 51b483f1cdafc31484283d9e294b4a927170b781b289a02b0ffca6aeb0d92ffebe34ea3e391c0a8bb948aa886d68dea61f030d00651fd4e56c54611bb0eaaf72
7
- data.tar.gz: 4c5b7114bade4f3627a735d4ed8e1fbd83636609f67805c23de691167f0dfc9658114d1c3380daa348ed5eb6c848b87605174bd324f4cb0492c161c6288db034
6
+ metadata.gz: 48569321a92ec1304c60d425af98412f56dad24ce4fa6c17b149fa18cf40221df320a95ed806196a017a8f521c3d935433b02b626901ec28de5bac5ddcf92332
7
+ data.tar.gz: f5731e01e2a559c25d92d11f9c9693fafd8219aa681711962dc8d777a46198fb3e3c0d97e26461d4e4d05e3aad8c8cf8b6aee17fbb31441c83fb36bcc91da920
data/README.md CHANGED
@@ -55,7 +55,6 @@ Several features from Erlang, Go, Clojure, Java, and JavaScript have been implem
55
55
  * Go inspired [Goroutine](https://github.com/jdantonio/concurrent-ruby/blob/master/md/goroutine.md)
56
56
  * JavaScript inspired [Promise](https://github.com/jdantonio/concurrent-ruby/blob/master/md/promise.md)
57
57
  * Java inspired [Thread Pools](https://github.com/jdantonio/concurrent-ruby/blob/master/md/thread_pool.md)
58
- * Scheduled task execution with the [Executor](https://github.com/jdantonio/concurrent-ruby/blob/master/md/executor.md) service
59
58
 
60
59
  ### Is it any good?
61
60
 
@@ -63,11 +62,7 @@ Several features from Erlang, Go, Clojure, Java, and JavaScript have been implem
63
62
 
64
63
  ### Supported Ruby versions
65
64
 
66
- MRI 1.9.2, 1.9.3, and 2.0. This library is pure Ruby and has minimal gem dependencies. It should be
67
- fully compatible with any Ruby interpreter that is 1.9.x compliant. I simply don't know enough
68
- about JRuby, Rubinius, or the others to fully support them. I can promise good karma and
69
- attribution on this page to anyone wishing to take responsibility for verifying compaitibility
70
- with any Ruby other than MRI.
65
+ Optimized for and tested under MRI Ruby 1.9.x and 2.0.
71
66
 
72
67
  ### Install
73
68
 
@@ -89,25 +84,6 @@ Once you've installed the gem you must `require` it in your project:
89
84
  require 'concurrent'
90
85
  ```
91
86
 
92
- ### Kernel Methods
93
-
94
- Many Ruby developers consider it bad form to add function to the global (Kernel) namespace.
95
- I don't necessarily agree. If the function acts like a low-level feature of the language
96
- I think it is OK to add the method to the `Kernel` module. To support my personal programming
97
- style I have chosen to implement `Kernel` methods to instance many of the objects in this
98
- library. Out of respect for the larger Ruby community I have made these methods optional.
99
- They are not imported with the normal `require 'concurrent'` directive. To import these
100
- functions you must import the `concurrent/functions` library.
101
-
102
- ```ruby
103
- require 'concurrent'
104
- score = agent(10) #=> NoMethodError: undefined method `agent' for main:Object
105
-
106
- require 'concurrent/functions'
107
- score = agent(10) #=> #<Concurrent::Agent:0x35b2b28 ...
108
- score.value #=> 10
109
- ```
110
-
111
87
  ### Examples
112
88
 
113
89
  For complete examples, see the specific documentation linked above. Below are a few examples to whet your appetite.
@@ -127,7 +103,6 @@ sleep(0.1)
127
103
 
128
104
  ```ruby
129
105
  require 'concurrent'
130
- require 'concurrent/functions'
131
106
 
132
107
  score = agent(10)
133
108
  score.value #=> 10
@@ -149,7 +124,6 @@ score.value #=> 170
149
124
 
150
125
  ```ruby
151
126
  require 'concurrent'
152
- require 'concurrent/functions'
153
127
 
154
128
  Concurrent::Defer.new{ "Jerry D'Antonio" }.
155
129
  then{|result| puts "Hello, #{result}!" }.
@@ -171,7 +145,6 @@ sleep(0.1)
171
145
 
172
146
  ```ruby
173
147
  require 'concurrent'
174
- require 'concurrent/functions'
175
148
 
176
149
  count = future{ sleep(1); 10 }
177
150
  count.state #=> :pending
@@ -184,7 +157,6 @@ deref count #=> 10
184
157
 
185
158
  ```ruby
186
159
  require 'concurrent'
187
- require 'concurrent/functions'
188
160
 
189
161
  p = promise("Jerry", "D'Antonio"){|a, b| "#{a} #{b}" }.
190
162
  then{|result| "Hello #{result}." }.
@@ -218,25 +190,6 @@ sleep(1)
218
190
  @expected #=> 30
219
191
  ```
220
192
 
221
- #### Executor
222
-
223
- ```ruby
224
- require 'concurrent'
225
-
226
- ec = Concurrent::Executor.run('Foo'){ puts 'Boom!' }
227
-
228
- ec.name #=> "Foo"
229
- ec.execution_interval #=> 60 == Concurrent::Executor::EXECUTION_INTERVAL
230
- ec.timeout_interval #=> 30 == Concurrent::Executor::TIMEOUT_INTERVAL
231
- ec.status #=> "sleep"
232
-
233
- # wait 60 seconds...
234
- #=> 'Boom!'
235
- #=> ' INFO (2013-08-02 23:20:15) Foo: execution completed successfully'
236
-
237
- ec.kill #=> true
238
- ```
239
-
240
193
  ## Contributing
241
194
 
242
195
  1. Fork it
@@ -6,21 +6,15 @@ require 'concurrent/event'
6
6
 
7
7
  require 'concurrent/agent'
8
8
  require 'concurrent/defer'
9
- require 'concurrent/executor'
10
9
  require 'concurrent/future'
11
10
  require 'concurrent/goroutine'
12
11
  require 'concurrent/promise'
13
12
  require 'concurrent/obligation'
14
13
  require 'concurrent/utilities'
15
14
 
16
- require 'concurrent/reactor'
17
- require 'concurrent/reactor/drb_async_demux'
18
- require 'concurrent/reactor/tcp_sync_demux'
19
-
20
15
  require 'concurrent/thread_pool'
21
16
  require 'concurrent/cached_thread_pool'
22
17
  require 'concurrent/fixed_thread_pool'
23
- require 'concurrent/null_thread_pool'
24
18
 
25
19
  require 'concurrent/global_thread_pool'
26
20
 
@@ -14,7 +14,6 @@ module Concurrent
14
14
  # A good example of an agent is a shared incrementing counter, such as the score in a video game.
15
15
  class Agent
16
16
  include Observable
17
- include UsesGlobalThreadPool
18
17
 
19
18
  TIMEOUT = 5
20
19
 
@@ -27,20 +26,15 @@ module Concurrent
27
26
  @rescuers = []
28
27
  @validator = nil
29
28
  @queue = Queue.new
30
- @mutex = Mutex.new
31
29
 
32
- Agent.thread_pool.post{ work }
30
+ $GLOBAL_THREAD_POOL << proc{ work }
33
31
  end
34
32
 
35
33
  def value(timeout = 0) return @value; end
36
34
  alias_method :deref, :value
37
35
 
38
36
  def rescue(clazz = Exception, &block)
39
- if block_given?
40
- @mutex.synchronize do
41
- @rescuers << Rescuer.new(clazz, block)
42
- end
43
- end
37
+ @rescuers << Rescuer.new(clazz, block) if block_given?
44
38
  return self
45
39
  end
46
40
  alias_method :catch, :rescue
@@ -56,10 +50,10 @@ module Concurrent
56
50
 
57
51
  def post(&block)
58
52
  return @queue.length unless block_given?
59
- return @mutex.synchronize do
53
+ return atomic {
60
54
  @queue << block
61
55
  @queue.length
62
- end
56
+ }
63
57
  end
64
58
 
65
59
  def <<(block)
@@ -68,7 +62,7 @@ module Concurrent
68
62
  end
69
63
 
70
64
  def length
71
- return @queue.length
65
+ @queue.length
72
66
  end
73
67
  alias_method :size, :length
74
68
  alias_method :count, :length
@@ -82,9 +76,7 @@ module Concurrent
82
76
 
83
77
  # @private
84
78
  def try_rescue(ex) # :nodoc:
85
- rescuer = @mutex.synchronize do
86
- @rescuers.find{|r| ex.is_a?(r.clazz) }
87
- end
79
+ rescuer = @rescuers.find{|r| ex.is_a?(r.clazz) }
88
80
  rescuer.block.call(ex) if rescuer
89
81
  rescue Exception => e
90
82
  # supress
@@ -93,17 +85,18 @@ module Concurrent
93
85
  # @private
94
86
  def work # :nodoc:
95
87
  loop do
88
+ Thread.pass
96
89
  handler = @queue.pop
97
90
  begin
98
- result = Timeout.timeout(@timeout) do
91
+ result = Timeout.timeout(@timeout){
99
92
  handler.call(@value)
100
- end
93
+ }
101
94
  if @validator.nil? || @validator.call(result)
102
- @mutex.synchronize do
95
+ atomic {
103
96
  @value = result
104
97
  changed
105
- notify_observers(Time.now, @value)
106
- end
98
+ }
99
+ notify_observers(Time.now, @value)
107
100
  end
108
101
  rescue Exception => ex
109
102
  try_rescue(ex)
@@ -112,3 +105,31 @@ module Concurrent
112
105
  end
113
106
  end
114
107
  end
108
+
109
+ module Kernel
110
+
111
+ def agent(initial, timeout = Concurrent::Agent::TIMEOUT)
112
+ return Concurrent::Agent.new(initial, timeout)
113
+ end
114
+ module_function :agent
115
+
116
+ def deref(agent, timeout = nil)
117
+ if agent.respond_to?(:deref)
118
+ return agent.deref(timeout)
119
+ elsif agent.respond_to?(:value)
120
+ return agent.deref(timeout)
121
+ else
122
+ return nil
123
+ end
124
+ end
125
+ module_function :deref
126
+
127
+ def post(agent, &block)
128
+ if agent.respond_to?(:post)
129
+ return agent.post(&block)
130
+ else
131
+ return nil
132
+ end
133
+ end
134
+ module_function :deref
135
+ end
@@ -24,11 +24,12 @@ module Concurrent
24
24
  @thread_idletime = (opts[:thread_idletime] || DEFAULT_THREAD_IDLETIME).freeze
25
25
  super()
26
26
  @working = 0
27
+ @mutex = Mutex.new
27
28
  end
28
29
 
29
30
  def kill
30
31
  @status = :killed
31
- mutex.synchronize do
32
+ @mutex.synchronize do
32
33
  @pool.each{|t| Thread.kill(t.thread) }
33
34
  end
34
35
  end
@@ -41,7 +42,7 @@ module Concurrent
41
42
  raise ArgumentError.new('no block given') unless block_given?
42
43
  if running?
43
44
  collect_garbage if @pool.empty?
44
- mutex.synchronize do
45
+ @mutex.synchronize do
45
46
  if @working >= @pool.length
46
47
  create_worker_thread
47
48
  end
@@ -55,7 +56,7 @@ module Concurrent
55
56
 
56
57
  # @private
57
58
  def status # :nodoc:
58
- mutex.synchronize do
59
+ @mutex.synchronize do
59
60
  @pool.collect do |worker|
60
61
  [
61
62
  worker.status,
@@ -79,31 +80,31 @@ module Concurrent
79
80
  loop do
80
81
  task = @queue.pop
81
82
 
82
- mutex.synchronize do
83
+ atomic {
83
84
  @working += 1
84
85
  me.status = :working
85
- end
86
+ }
86
87
 
87
88
  if task == :stop
88
89
  me.status = :stopping
89
90
  break
90
91
  else
91
92
  task.last.call(*task.first)
92
- mutex.synchronize do
93
+ atomic {
93
94
  @working -= 1
94
95
  me.status = :idle
95
96
  me.idletime = timestamp
96
- end
97
+ }
97
98
  end
98
99
  end
99
100
 
100
- mutex.synchronize do
101
+ atomic {
101
102
  @pool.delete(me)
102
103
  if @pool.empty?
103
104
  @termination.set
104
105
  @status = :shutdown unless killed?
105
106
  end
106
- end
107
+ }
107
108
  end
108
109
 
109
110
  @pool << worker
@@ -114,7 +115,7 @@ module Concurrent
114
115
  @collector = Thread.new do
115
116
  loop do
116
117
  sleep(@gc_interval)
117
- mutex.synchronize do
118
+ @mutex.synchronize do
118
119
  @pool.reject! do |worker|
119
120
  worker.thread.status.nil? ||
120
121
  (worker.status == :idle && @thread_idletime >= delta(worker.idletime, timestamp))
@@ -7,18 +7,14 @@ module Concurrent
7
7
  IllegalMethodCallError = Class.new(StandardError)
8
8
 
9
9
  class Defer
10
- include UsesGlobalThreadPool
11
-
12
- def initialize(opts = {}, &block)
13
- operation = opts[:op] || opts[:operation]
14
- @callback = opts[:cback] || opts[:callback]
15
- @errorback = opts[:eback] || opts[:error] || opts[:errorback]
16
- thread_pool = opts[:pool] || opts[:thread_pool]
17
10
 
11
+ def initialize(operation = nil, callback = nil, errorback = nil, &block)
18
12
  raise ArgumentError.new('no operation given') if operation.nil? && ! block_given?
19
13
  raise ArgumentError.new('two operations given') if ! operation.nil? && block_given?
20
14
 
21
15
  @operation = operation || block
16
+ @callback = callback
17
+ @errorback = errorback
22
18
 
23
19
  if operation.nil?
24
20
  @running = false
@@ -48,7 +44,7 @@ module Concurrent
48
44
  def go
49
45
  return nil if @running
50
46
  @running = true
51
- Defer.thread_pool.post { fulfill }
47
+ $GLOBAL_THREAD_POOL.post { Thread.pass; fulfill }
52
48
  return nil
53
49
  end
54
50
 
@@ -63,3 +59,11 @@ module Concurrent
63
59
  end
64
60
  end
65
61
  end
62
+
63
+ module Kernel
64
+
65
+ def defer(*args, &block)
66
+ return Concurrent::Defer.new(*args, &block)
67
+ end
68
+ module_function :defer
69
+ end
@@ -26,10 +26,8 @@ module Concurrent
26
26
  end
27
27
 
28
28
  def kill
29
- mutex.synchronize do
30
- @status = :killed
31
- @pool.each{|t| Thread.kill(t) }
32
- end
29
+ @status = :killed
30
+ @pool.each{|t| Thread.kill(t) }
33
31
  end
34
32
 
35
33
  def size
@@ -52,9 +50,7 @@ module Concurrent
52
50
 
53
51
  # @private
54
52
  def status # :nodoc:
55
- mutex.synchronize do
56
- @pool.collect{|t| t.status }
57
- end
53
+ @pool.collect{|t| t.status }
58
54
  end
59
55
 
60
56
  private
@@ -82,11 +78,9 @@ module Concurrent
82
78
  def collect_garbage # :nodoc:
83
79
  @collector = Thread.new do
84
80
  sleep(1)
85
- mutex.synchronize do
86
- @pool.size.times do |i|
87
- if @pool[i].status.nil?
88
- @pool[i] = create_worker_thread
89
- end
81
+ @pool.size.times do |i|
82
+ if @pool[i].status.nil?
83
+ @pool[i] = create_worker_thread
90
84
  end
91
85
  end
92
86
  end
@@ -8,17 +8,17 @@ module Concurrent
8
8
 
9
9
  class Future
10
10
  include Obligation
11
- include UsesGlobalThreadPool
12
-
13
11
  behavior(:future)
14
12
 
15
13
  def initialize(*args, &block)
14
+
16
15
  unless block_given?
17
16
  @state = :fulfilled
18
17
  else
19
18
  @value = nil
20
19
  @state = :pending
21
- Future.thread_pool.post(*args) do
20
+ $GLOBAL_THREAD_POOL.post do
21
+ Thread.pass
22
22
  work(*args, &block)
23
23
  end
24
24
  end
@@ -28,15 +28,27 @@ module Concurrent
28
28
 
29
29
  # @private
30
30
  def work(*args) # :nodoc:
31
- mutex.synchronize do
31
+ semaphore.synchronize do
32
32
  begin
33
- @value = yield(*args)
34
- @state = :fulfilled
33
+ atomic {
34
+ @value = yield(*args)
35
+ @state = :fulfilled
36
+ }
35
37
  rescue Exception => ex
36
- @state = :rejected
37
- @reason = ex
38
+ atomic {
39
+ @state = :rejected
40
+ @reason = ex
41
+ }
38
42
  end
39
43
  end
40
44
  end
41
45
  end
42
46
  end
47
+
48
+ module Kernel
49
+
50
+ def future(*args, &block)
51
+ return Concurrent::Future.new(*args, &block)
52
+ end
53
+ module_function :future
54
+ end