concurrent-ruby 0.1.1.pre.5 → 0.1.1

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.
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