concurrent-ruby 0.2.0 → 0.2.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 (57) hide show
  1. data/LICENSE +21 -21
  2. data/README.md +275 -275
  3. data/lib/concurrent.rb +28 -28
  4. data/lib/concurrent/agent.rb +114 -114
  5. data/lib/concurrent/cached_thread_pool.rb +131 -129
  6. data/lib/concurrent/defer.rb +65 -65
  7. data/lib/concurrent/event.rb +60 -60
  8. data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
  9. data/lib/concurrent/executor.rb +96 -95
  10. data/lib/concurrent/fixed_thread_pool.rb +99 -95
  11. data/lib/concurrent/functions.rb +120 -120
  12. data/lib/concurrent/future.rb +42 -42
  13. data/lib/concurrent/global_thread_pool.rb +16 -16
  14. data/lib/concurrent/goroutine.rb +29 -29
  15. data/lib/concurrent/null_thread_pool.rb +22 -22
  16. data/lib/concurrent/obligation.rb +67 -67
  17. data/lib/concurrent/promise.rb +174 -174
  18. data/lib/concurrent/reactor.rb +166 -166
  19. data/lib/concurrent/reactor/drb_async_demux.rb +83 -83
  20. data/lib/concurrent/reactor/tcp_sync_demux.rb +131 -131
  21. data/lib/concurrent/supervisor.rb +105 -100
  22. data/lib/concurrent/thread_pool.rb +76 -76
  23. data/lib/concurrent/utilities.rb +32 -32
  24. data/lib/concurrent/version.rb +3 -3
  25. data/lib/concurrent_ruby.rb +1 -1
  26. data/md/agent.md +123 -123
  27. data/md/defer.md +174 -174
  28. data/md/event.md +32 -32
  29. data/md/executor.md +187 -187
  30. data/md/future.md +83 -83
  31. data/md/goroutine.md +52 -52
  32. data/md/obligation.md +32 -32
  33. data/md/promise.md +227 -227
  34. data/md/thread_pool.md +224 -224
  35. data/spec/concurrent/agent_spec.rb +386 -386
  36. data/spec/concurrent/cached_thread_pool_spec.rb +125 -125
  37. data/spec/concurrent/defer_spec.rb +195 -195
  38. data/spec/concurrent/event_machine_defer_proxy_spec.rb +256 -256
  39. data/spec/concurrent/event_spec.rb +134 -134
  40. data/spec/concurrent/executor_spec.rb +200 -200
  41. data/spec/concurrent/fixed_thread_pool_spec.rb +83 -83
  42. data/spec/concurrent/functions_spec.rb +217 -217
  43. data/spec/concurrent/future_spec.rb +108 -108
  44. data/spec/concurrent/global_thread_pool_spec.rb +38 -38
  45. data/spec/concurrent/goroutine_spec.rb +67 -67
  46. data/spec/concurrent/null_thread_pool_spec.rb +57 -54
  47. data/spec/concurrent/obligation_shared.rb +132 -132
  48. data/spec/concurrent/promise_spec.rb +312 -312
  49. data/spec/concurrent/reactor/drb_async_demux_spec.rb +196 -196
  50. data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +410 -410
  51. data/spec/concurrent/reactor_spec.rb +364 -364
  52. data/spec/concurrent/supervisor_spec.rb +269 -258
  53. data/spec/concurrent/thread_pool_shared.rb +204 -204
  54. data/spec/concurrent/utilities_spec.rb +74 -74
  55. data/spec/spec_helper.rb +32 -32
  56. metadata +20 -16
  57. checksums.yaml +0 -7
@@ -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,129 +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
- @pool << worker
110
- end
111
-
112
- # @private
113
- def collect_garbage # :nodoc:
114
- @collector = Thread.new do
115
- loop do
116
- sleep(@gc_interval)
117
- mutex.synchronize do
118
- @pool.reject! do |worker|
119
- worker.thread.status.nil? ||
120
- (worker.status == :idle && @thread_idletime >= delta(worker.idletime, timestamp))
121
- end
122
- end
123
- @working = @pool.count{|worker| worker.status == :working}
124
- break if @pool.empty?
125
- end
126
- end
127
- end
128
- end
129
- 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