concurrent-ruby 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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