concurrent-ruby 0.2.1 → 0.2.2

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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -21
  3. data/README.md +276 -275
  4. data/lib/concurrent.rb +28 -28
  5. data/lib/concurrent/agent.rb +114 -114
  6. data/lib/concurrent/cached_thread_pool.rb +131 -131
  7. data/lib/concurrent/defer.rb +65 -65
  8. data/lib/concurrent/event.rb +60 -60
  9. data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
  10. data/lib/concurrent/executor.rb +96 -96
  11. data/lib/concurrent/fixed_thread_pool.rb +99 -99
  12. data/lib/concurrent/functions.rb +120 -120
  13. data/lib/concurrent/future.rb +42 -42
  14. data/lib/concurrent/global_thread_pool.rb +24 -16
  15. data/lib/concurrent/goroutine.rb +29 -29
  16. data/lib/concurrent/null_thread_pool.rb +22 -22
  17. data/lib/concurrent/obligation.rb +67 -67
  18. data/lib/concurrent/promise.rb +174 -174
  19. data/lib/concurrent/reactor.rb +166 -166
  20. data/lib/concurrent/reactor/drb_async_demux.rb +83 -83
  21. data/lib/concurrent/reactor/tcp_sync_demux.rb +131 -131
  22. data/lib/concurrent/supervisor.rb +105 -105
  23. data/lib/concurrent/thread_pool.rb +76 -76
  24. data/lib/concurrent/utilities.rb +32 -32
  25. data/lib/concurrent/version.rb +3 -3
  26. data/lib/concurrent_ruby.rb +1 -1
  27. data/md/agent.md +123 -123
  28. data/md/defer.md +174 -174
  29. data/md/event.md +32 -32
  30. data/md/executor.md +187 -187
  31. data/md/future.md +83 -83
  32. data/md/goroutine.md +52 -52
  33. data/md/obligation.md +32 -32
  34. data/md/promise.md +227 -227
  35. data/md/thread_pool.md +224 -224
  36. data/spec/concurrent/agent_spec.rb +390 -386
  37. data/spec/concurrent/cached_thread_pool_spec.rb +125 -125
  38. data/spec/concurrent/defer_spec.rb +199 -195
  39. data/spec/concurrent/event_machine_defer_proxy_spec.rb +256 -256
  40. data/spec/concurrent/event_spec.rb +134 -134
  41. data/spec/concurrent/executor_spec.rb +200 -200
  42. data/spec/concurrent/fixed_thread_pool_spec.rb +83 -83
  43. data/spec/concurrent/functions_spec.rb +217 -217
  44. data/spec/concurrent/future_spec.rb +112 -108
  45. data/spec/concurrent/global_thread_pool_spec.rb +11 -38
  46. data/spec/concurrent/goroutine_spec.rb +67 -67
  47. data/spec/concurrent/null_thread_pool_spec.rb +57 -57
  48. data/spec/concurrent/obligation_shared.rb +132 -132
  49. data/spec/concurrent/promise_spec.rb +316 -312
  50. data/spec/concurrent/reactor/drb_async_demux_spec.rb +196 -196
  51. data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +410 -410
  52. data/spec/concurrent/reactor_spec.rb +364 -364
  53. data/spec/concurrent/supervisor_spec.rb +269 -269
  54. data/spec/concurrent/thread_pool_shared.rb +204 -204
  55. data/spec/concurrent/uses_global_thread_pool_shared.rb +64 -0
  56. data/spec/concurrent/utilities_spec.rb +74 -74
  57. data/spec/spec_helper.rb +32 -32
  58. metadata +17 -19
@@ -1,28 +1,28 @@
1
- require 'thread'
2
-
3
- require 'concurrent/version'
4
-
5
- require 'concurrent/event'
6
-
7
- require 'concurrent/agent'
8
- require 'concurrent/defer'
9
- require 'concurrent/executor'
10
- require 'concurrent/future'
11
- require 'concurrent/goroutine'
12
- require 'concurrent/obligation'
13
- require 'concurrent/promise'
14
- require 'concurrent/supervisor'
15
- require 'concurrent/utilities'
16
-
17
- require 'concurrent/reactor'
18
- require 'concurrent/reactor/drb_async_demux'
19
- require 'concurrent/reactor/tcp_sync_demux'
20
-
21
- require 'concurrent/thread_pool'
22
- require 'concurrent/cached_thread_pool'
23
- require 'concurrent/fixed_thread_pool'
24
- require 'concurrent/null_thread_pool'
25
-
26
- require 'concurrent/global_thread_pool'
27
-
28
- require 'concurrent/event_machine_defer_proxy' if defined?(EventMachine)
1
+ require 'thread'
2
+
3
+ require 'concurrent/version'
4
+
5
+ require 'concurrent/event'
6
+
7
+ require 'concurrent/agent'
8
+ require 'concurrent/defer'
9
+ require 'concurrent/executor'
10
+ require 'concurrent/future'
11
+ require 'concurrent/goroutine'
12
+ require 'concurrent/obligation'
13
+ require 'concurrent/promise'
14
+ require 'concurrent/supervisor'
15
+ require 'concurrent/utilities'
16
+
17
+ require 'concurrent/reactor'
18
+ require 'concurrent/reactor/drb_async_demux'
19
+ require 'concurrent/reactor/tcp_sync_demux'
20
+
21
+ require 'concurrent/thread_pool'
22
+ require 'concurrent/cached_thread_pool'
23
+ require 'concurrent/fixed_thread_pool'
24
+ require 'concurrent/null_thread_pool'
25
+
26
+ require 'concurrent/global_thread_pool'
27
+
28
+ require 'concurrent/event_machine_defer_proxy' if defined?(EventMachine)
@@ -1,114 +1,114 @@
1
- require 'observer'
2
- require 'thread'
3
-
4
- require 'concurrent/global_thread_pool'
5
- require 'concurrent/utilities'
6
-
7
- module Concurrent
8
-
9
- # An agent is a single atomic value that represents an identity. The current value
10
- # of the agent can be requested at any time (#deref). Each agent has a work queue and operates on
11
- # the global thread pool. Consumers can #post code blocks to the agent. The code block (function)
12
- # will receive the current value of the agent as its sole parameter. The return value of the block
13
- # will become the new value of the agent. Agents support two error handling modes: fail and continue.
14
- # A good example of an agent is a shared incrementing counter, such as the score in a video game.
15
- class Agent
16
- include Observable
17
- include UsesGlobalThreadPool
18
-
19
- TIMEOUT = 5
20
-
21
- attr_reader :initial
22
- attr_reader :timeout
23
-
24
- def initialize(initial, timeout = TIMEOUT)
25
- @value = initial
26
- @timeout = timeout
27
- @rescuers = []
28
- @validator = nil
29
- @queue = Queue.new
30
- @mutex = Mutex.new
31
-
32
- Agent.thread_pool.post{ work }
33
- end
34
-
35
- def value(timeout = 0) return @value; end
36
- alias_method :deref, :value
37
-
38
- def rescue(clazz = Exception, &block)
39
- if block_given?
40
- @mutex.synchronize do
41
- @rescuers << Rescuer.new(clazz, block)
42
- end
43
- end
44
- return self
45
- end
46
- alias_method :catch, :rescue
47
- alias_method :on_error, :rescue
48
-
49
- def validate(&block)
50
- @validator = block if block_given?
51
- return self
52
- end
53
- alias_method :validates, :validate
54
- alias_method :validate_with, :validate
55
- alias_method :validates_with, :validate
56
-
57
- def post(&block)
58
- return @queue.length unless block_given?
59
- return @mutex.synchronize do
60
- @queue << block
61
- @queue.length
62
- end
63
- end
64
-
65
- def <<(block)
66
- self.post(&block)
67
- return self
68
- end
69
-
70
- def length
71
- return @queue.length
72
- end
73
- alias_method :size, :length
74
- alias_method :count, :length
75
-
76
- alias_method :add_watch, :add_observer
77
-
78
- private
79
-
80
- # @private
81
- Rescuer = Struct.new(:clazz, :block)
82
-
83
- # @private
84
- def try_rescue(ex) # :nodoc:
85
- rescuer = @mutex.synchronize do
86
- @rescuers.find{|r| ex.is_a?(r.clazz) }
87
- end
88
- rescuer.block.call(ex) if rescuer
89
- rescue Exception => e
90
- # supress
91
- end
92
-
93
- # @private
94
- def work # :nodoc:
95
- loop do
96
- handler = @queue.pop
97
- begin
98
- result = Timeout.timeout(@timeout) do
99
- handler.call(@value)
100
- end
101
- if @validator.nil? || @validator.call(result)
102
- @mutex.synchronize do
103
- @value = result
104
- changed
105
- notify_observers(Time.now, @value)
106
- end
107
- end
108
- rescue Exception => ex
109
- try_rescue(ex)
110
- end
111
- end
112
- end
113
- end
114
- end
1
+ require 'observer'
2
+ require 'thread'
3
+
4
+ require 'concurrent/global_thread_pool'
5
+ require 'concurrent/utilities'
6
+
7
+ module Concurrent
8
+
9
+ # An agent is a single atomic value that represents an identity. The current value
10
+ # of the agent can be requested at any time (#deref). Each agent has a work queue and operates on
11
+ # the global thread pool. Consumers can #post code blocks to the agent. The code block (function)
12
+ # will receive the current value of the agent as its sole parameter. The return value of the block
13
+ # will become the new value of the agent. Agents support two error handling modes: fail and continue.
14
+ # A good example of an agent is a shared incrementing counter, such as the score in a video game.
15
+ class Agent
16
+ include Observable
17
+ include UsesGlobalThreadPool
18
+
19
+ TIMEOUT = 5
20
+
21
+ attr_reader :initial
22
+ attr_reader :timeout
23
+
24
+ def initialize(initial, timeout = TIMEOUT)
25
+ @value = initial
26
+ @timeout = timeout
27
+ @rescuers = []
28
+ @validator = nil
29
+ @queue = Queue.new
30
+ @mutex = Mutex.new
31
+
32
+ Agent.thread_pool.post{ work }
33
+ end
34
+
35
+ def value(timeout = 0) return @value; end
36
+ alias_method :deref, :value
37
+
38
+ def rescue(clazz = Exception, &block)
39
+ if block_given?
40
+ @mutex.synchronize do
41
+ @rescuers << Rescuer.new(clazz, block)
42
+ end
43
+ end
44
+ return self
45
+ end
46
+ alias_method :catch, :rescue
47
+ alias_method :on_error, :rescue
48
+
49
+ def validate(&block)
50
+ @validator = block if block_given?
51
+ return self
52
+ end
53
+ alias_method :validates, :validate
54
+ alias_method :validate_with, :validate
55
+ alias_method :validates_with, :validate
56
+
57
+ def post(&block)
58
+ return @queue.length unless block_given?
59
+ return @mutex.synchronize do
60
+ @queue << block
61
+ @queue.length
62
+ end
63
+ end
64
+
65
+ def <<(block)
66
+ self.post(&block)
67
+ return self
68
+ end
69
+
70
+ def length
71
+ return @queue.length
72
+ end
73
+ alias_method :size, :length
74
+ alias_method :count, :length
75
+
76
+ alias_method :add_watch, :add_observer
77
+
78
+ private
79
+
80
+ # @private
81
+ Rescuer = Struct.new(:clazz, :block)
82
+
83
+ # @private
84
+ def try_rescue(ex) # :nodoc:
85
+ rescuer = @mutex.synchronize do
86
+ @rescuers.find{|r| ex.is_a?(r.clazz) }
87
+ end
88
+ rescuer.block.call(ex) if rescuer
89
+ rescue Exception => e
90
+ # supress
91
+ end
92
+
93
+ # @private
94
+ def work # :nodoc:
95
+ loop do
96
+ handler = @queue.pop
97
+ begin
98
+ result = Timeout.timeout(@timeout) do
99
+ handler.call(@value)
100
+ end
101
+ if @validator.nil? || @validator.call(result)
102
+ @mutex.synchronize do
103
+ @value = result
104
+ changed
105
+ notify_observers(Time.now, @value)
106
+ end
107
+ end
108
+ rescue Exception => ex
109
+ try_rescue(ex)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -1,131 +1,131 @@
1
- require 'thread'
2
-
3
- require 'concurrent/thread_pool'
4
- require 'concurrent/utilities'
5
-
6
- require 'functional/utilities'
7
-
8
- module Concurrent
9
-
10
- def self.new_cached_thread_pool
11
- return CachedThreadPool.new
12
- end
13
-
14
- class CachedThreadPool < ThreadPool
15
- behavior(:thread_pool)
16
-
17
- DEFAULT_GC_INTERVAL = 60
18
- DEFAULT_THREAD_IDLETIME = 60
19
-
20
- attr_reader :working
21
-
22
- def initialize(opts = {})
23
- @gc_interval = (opts[:gc_interval] || DEFAULT_GC_INTERVAL).freeze
24
- @thread_idletime = (opts[:thread_idletime] || DEFAULT_THREAD_IDLETIME).freeze
25
- super()
26
- @working = 0
27
- end
28
-
29
- def kill
30
- @status = :killed
31
- mutex.synchronize do
32
- @pool.each{|t| Thread.kill(t.thread) }
33
- end
34
- end
35
-
36
- def size
37
- return @pool.length
38
- end
39
-
40
- def post(*args, &block)
41
- raise ArgumentError.new('no block given') unless block_given?
42
- if running?
43
- collect_garbage if @pool.empty?
44
- mutex.synchronize do
45
- if @working >= @pool.length
46
- create_worker_thread
47
- end
48
- @queue << [args, block]
49
- end
50
- return true
51
- else
52
- return false
53
- end
54
- end
55
-
56
- # @private
57
- def status # :nodoc:
58
- mutex.synchronize do
59
- @pool.collect do |worker|
60
- [
61
- worker.status,
62
- worker.status == :idle ? delta(worker.idletime, timestamp) : nil,
63
- worker.thread.status
64
- ]
65
- end
66
- end
67
- end
68
-
69
- private
70
-
71
- Worker = Struct.new(:status, :idletime, :thread)
72
-
73
- # @private
74
- def create_worker_thread # :nodoc:
75
- worker = Worker.new(:idle, timestamp, nil)
76
-
77
- worker.thread = Thread.new(worker) do |me|
78
-
79
- loop do
80
- task = @queue.pop
81
-
82
- mutex.synchronize do
83
- @working += 1
84
- me.status = :working
85
- end
86
-
87
- if task == :stop
88
- me.status = :stopping
89
- break
90
- else
91
- task.last.call(*task.first)
92
- mutex.synchronize do
93
- @working -= 1
94
- me.status = :idle
95
- me.idletime = timestamp
96
- end
97
- end
98
- end
99
-
100
- mutex.synchronize do
101
- @pool.delete(me)
102
- if @pool.empty?
103
- @termination.set
104
- @status = :shutdown unless killed?
105
- end
106
- end
107
- end
108
-
109
- worker.thread.abort_on_exception = false
110
- @pool << worker
111
- end
112
-
113
- # @private
114
- def collect_garbage # :nodoc:
115
- @collector = Thread.new do
116
- loop do
117
- sleep(@gc_interval)
118
- mutex.synchronize do
119
- @pool.reject! do |worker|
120
- worker.thread.status.nil? ||
121
- (worker.status == :idle && @thread_idletime >= delta(worker.idletime, timestamp))
122
- end
123
- end
124
- @working = @pool.count{|worker| worker.status == :working}
125
- break if @pool.empty?
126
- end
127
- end
128
- @collector.abort_on_exception = false
129
- end
130
- end
131
- end
1
+ require 'thread'
2
+
3
+ require 'concurrent/thread_pool'
4
+ require 'concurrent/utilities'
5
+
6
+ require 'functional/utilities'
7
+
8
+ module Concurrent
9
+
10
+ def self.new_cached_thread_pool
11
+ return CachedThreadPool.new
12
+ end
13
+
14
+ class CachedThreadPool < ThreadPool
15
+ behavior(:thread_pool)
16
+
17
+ DEFAULT_GC_INTERVAL = 60
18
+ DEFAULT_THREAD_IDLETIME = 60
19
+
20
+ attr_reader :working
21
+
22
+ def initialize(opts = {})
23
+ @gc_interval = (opts[:gc_interval] || DEFAULT_GC_INTERVAL).freeze
24
+ @thread_idletime = (opts[:thread_idletime] || DEFAULT_THREAD_IDLETIME).freeze
25
+ super()
26
+ @working = 0
27
+ end
28
+
29
+ def kill
30
+ @status = :killed
31
+ mutex.synchronize do
32
+ @pool.each{|t| Thread.kill(t.thread) }
33
+ end
34
+ end
35
+
36
+ def size
37
+ return @pool.length
38
+ end
39
+
40
+ def post(*args, &block)
41
+ raise ArgumentError.new('no block given') unless block_given?
42
+ if running?
43
+ collect_garbage if @pool.empty?
44
+ mutex.synchronize do
45
+ if @working >= @pool.length
46
+ create_worker_thread
47
+ end
48
+ @queue << [args, block]
49
+ end
50
+ return true
51
+ else
52
+ return false
53
+ end
54
+ end
55
+
56
+ # @private
57
+ def status # :nodoc:
58
+ mutex.synchronize do
59
+ @pool.collect do |worker|
60
+ [
61
+ worker.status,
62
+ worker.status == :idle ? delta(worker.idletime, timestamp) : nil,
63
+ worker.thread.status
64
+ ]
65
+ end
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ Worker = Struct.new(:status, :idletime, :thread)
72
+
73
+ # @private
74
+ def create_worker_thread # :nodoc:
75
+ worker = Worker.new(:idle, timestamp, nil)
76
+
77
+ worker.thread = Thread.new(worker) do |me|
78
+
79
+ loop do
80
+ task = @queue.pop
81
+
82
+ mutex.synchronize do
83
+ @working += 1
84
+ me.status = :working
85
+ end
86
+
87
+ if task == :stop
88
+ me.status = :stopping
89
+ break
90
+ else
91
+ task.last.call(*task.first)
92
+ mutex.synchronize do
93
+ @working -= 1
94
+ me.status = :idle
95
+ me.idletime = timestamp
96
+ end
97
+ end
98
+ end
99
+
100
+ mutex.synchronize do
101
+ @pool.delete(me)
102
+ if @pool.empty?
103
+ @termination.set
104
+ @status = :shutdown unless killed?
105
+ end
106
+ end
107
+ end
108
+
109
+ worker.thread.abort_on_exception = false
110
+ @pool << worker
111
+ end
112
+
113
+ # @private
114
+ def collect_garbage # :nodoc:
115
+ @collector = Thread.new do
116
+ loop do
117
+ sleep(@gc_interval)
118
+ mutex.synchronize do
119
+ @pool.reject! do |worker|
120
+ worker.thread.status.nil? ||
121
+ (worker.status == :idle && @thread_idletime >= delta(worker.idletime, timestamp))
122
+ end
123
+ end
124
+ @working = @pool.count{|worker| worker.status == :working}
125
+ break if @pool.empty?
126
+ end
127
+ end
128
+ @collector.abort_on_exception = false
129
+ end
130
+ end
131
+ end