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

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