concurrent-ruby 0.1.1.pre.3 → 0.1.1.pre.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -21
  3. data/README.md +275 -279
  4. data/lib/concurrent.rb +27 -28
  5. data/lib/concurrent/agent.rb +114 -108
  6. data/lib/concurrent/cached_thread_pool.rb +129 -130
  7. data/lib/concurrent/defer.rb +65 -67
  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 +93 -95
  11. data/lib/concurrent/fixed_thread_pool.rb +95 -89
  12. data/lib/concurrent/functions.rb +120 -120
  13. data/lib/concurrent/future.rb +42 -47
  14. data/lib/concurrent/global_thread_pool.rb +16 -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 -166
  19. data/lib/concurrent/reactor.rb +161 -162
  20. data/lib/concurrent/reactor/drb_async_demux.rb +74 -74
  21. data/lib/concurrent/reactor/tcp_sync_demux.rb +98 -98
  22. data/lib/concurrent/thread_pool.rb +76 -69
  23. data/lib/concurrent/utilities.rb +32 -34
  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 +176 -176
  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 -380
  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 -253
  39. data/spec/concurrent/event_spec.rb +134 -134
  40. data/spec/concurrent/executor_spec.rb +184 -184
  41. data/spec/concurrent/fixed_thread_pool_spec.rb +83 -84
  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 +54 -54
  47. data/spec/concurrent/obligation_shared.rb +135 -121
  48. data/spec/concurrent/promise_spec.rb +312 -305
  49. data/spec/concurrent/reactor/drb_async_demux_spec.rb +12 -12
  50. data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +12 -12
  51. data/spec/concurrent/reactor_spec.rb +351 -10
  52. data/spec/concurrent/thread_pool_shared.rb +209 -210
  53. data/spec/concurrent/utilities_spec.rb +74 -74
  54. data/spec/spec_helper.rb +44 -30
  55. metadata +11 -22
  56. data/lib/concurrent/smart_mutex.rb +0 -66
  57. data/spec/concurrent/smart_mutex_spec.rb +0 -234
data/lib/concurrent.rb CHANGED
@@ -1,28 +1,27 @@
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/promise'
13
- require 'concurrent/obligation'
14
- require 'concurrent/smart_mutex'
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/promise'
13
+ require 'concurrent/obligation'
14
+ require 'concurrent/utilities'
15
+
16
+ require 'concurrent/reactor'
17
+ require 'concurrent/reactor/drb_async_demux'
18
+ require 'concurrent/reactor/tcp_sync_demux'
19
+
20
+ require 'concurrent/thread_pool'
21
+ require 'concurrent/cached_thread_pool'
22
+ require 'concurrent/fixed_thread_pool'
23
+ require 'concurrent/null_thread_pool'
24
+
25
+ require 'concurrent/global_thread_pool'
26
+
27
+ require 'concurrent/event_machine_defer_proxy' if defined?(EventMachine)
@@ -1,108 +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
-
31
- Agent.thread_pool.post{ work }
32
- end
33
-
34
- def value(timeout = 0) return @value; end
35
- alias_method :deref, :value
36
-
37
- def rescue(clazz = Exception, &block)
38
- @rescuers << Rescuer.new(clazz, block) if block_given?
39
- return self
40
- end
41
- alias_method :catch, :rescue
42
- alias_method :on_error, :rescue
43
-
44
- def validate(&block)
45
- @validator = block if block_given?
46
- return self
47
- end
48
- alias_method :validates, :validate
49
- alias_method :validate_with, :validate
50
- alias_method :validates_with, :validate
51
-
52
- def post(&block)
53
- return @queue.length unless block_given?
54
- return atomic {
55
- @queue << block
56
- @queue.length
57
- }
58
- end
59
-
60
- def <<(block)
61
- self.post(&block)
62
- return self
63
- end
64
-
65
- def length
66
- @queue.length
67
- end
68
- alias_method :size, :length
69
- alias_method :count, :length
70
-
71
- alias_method :add_watch, :add_observer
72
-
73
- private
74
-
75
- # @private
76
- Rescuer = Struct.new(:clazz, :block)
77
-
78
- # @private
79
- def try_rescue(ex) # :nodoc:
80
- rescuer = @rescuers.find{|r| ex.is_a?(r.clazz) }
81
- rescuer.block.call(ex) if rescuer
82
- rescue Exception => e
83
- # supress
84
- end
85
-
86
- # @private
87
- def work # :nodoc:
88
- loop do
89
- Thread.pass
90
- handler = @queue.pop
91
- begin
92
- result = Timeout.timeout(@timeout){
93
- handler.call(@value)
94
- }
95
- if @validator.nil? || @validator.call(result)
96
- atomic {
97
- @value = result
98
- changed
99
- }
100
- notify_observers(Time.now, @value)
101
- end
102
- rescue Exception => ex
103
- try_rescue(ex)
104
- end
105
- end
106
- end
107
- end
108
- 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,130 +1,129 @@
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
- @mutex = Mutex.new
28
- end
29
-
30
- def kill
31
- @status = :killed
32
- @mutex.synchronize do
33
- @pool.each{|t| Thread.kill(t.thread) }
34
- end
35
- end
36
-
37
- def size
38
- return @pool.length
39
- end
40
-
41
- def post(*args, &block)
42
- raise ArgumentError.new('no block given') unless block_given?
43
- if running?
44
- collect_garbage if @pool.empty?
45
- @mutex.synchronize do
46
- if @working >= @pool.length
47
- create_worker_thread
48
- end
49
- @queue << [args, block]
50
- end
51
- return true
52
- else
53
- return false
54
- end
55
- end
56
-
57
- # @private
58
- def status # :nodoc:
59
- @mutex.synchronize do
60
- @pool.collect do |worker|
61
- [
62
- worker.status,
63
- worker.status == :idle ? delta(worker.idletime, timestamp) : nil,
64
- worker.thread.status
65
- ]
66
- end
67
- end
68
- end
69
-
70
- private
71
-
72
- Worker = Struct.new(:status, :idletime, :thread)
73
-
74
- # @private
75
- def create_worker_thread # :nodoc:
76
- worker = Worker.new(:idle, timestamp, nil)
77
-
78
- worker.thread = Thread.new(worker) do |me|
79
-
80
- loop do
81
- task = @queue.pop
82
-
83
- atomic {
84
- @working += 1
85
- me.status = :working
86
- }
87
-
88
- if task == :stop
89
- me.status = :stopping
90
- break
91
- else
92
- task.last.call(*task.first)
93
- atomic {
94
- @working -= 1
95
- me.status = :idle
96
- me.idletime = timestamp
97
- }
98
- end
99
- end
100
-
101
- atomic {
102
- @pool.delete(me)
103
- if @pool.empty?
104
- @termination.set
105
- @status = :shutdown unless killed?
106
- end
107
- }
108
- end
109
-
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
- end
129
- end
130
- 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
+ @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