concurrent-ruby 0.1.0 → 0.1.1.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/LICENSE +21 -21
  2. data/README.md +279 -224
  3. data/lib/concurrent.rb +27 -20
  4. data/lib/concurrent/agent.rb +106 -130
  5. data/lib/concurrent/cached_thread_pool.rb +130 -122
  6. data/lib/concurrent/defer.rb +67 -69
  7. data/lib/concurrent/drb_async_demux.rb +72 -0
  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 +87 -0
  11. data/lib/concurrent/fixed_thread_pool.rb +89 -89
  12. data/lib/concurrent/functions.rb +120 -0
  13. data/lib/concurrent/future.rb +52 -42
  14. data/lib/concurrent/global_thread_pool.rb +3 -3
  15. data/lib/concurrent/goroutine.rb +29 -25
  16. data/lib/concurrent/obligation.rb +67 -121
  17. data/lib/concurrent/promise.rb +172 -194
  18. data/lib/concurrent/reactor.rb +162 -0
  19. data/lib/concurrent/smart_mutex.rb +66 -0
  20. data/lib/concurrent/tcp_sync_demux.rb +96 -0
  21. data/lib/concurrent/thread_pool.rb +65 -61
  22. data/lib/concurrent/utilities.rb +34 -0
  23. data/lib/concurrent/version.rb +3 -3
  24. data/lib/concurrent_ruby.rb +1 -1
  25. data/md/agent.md +123 -123
  26. data/md/defer.md +174 -174
  27. data/md/event.md +32 -32
  28. data/md/executor.md +176 -0
  29. data/md/future.md +83 -83
  30. data/md/goroutine.md +52 -52
  31. data/md/obligation.md +32 -32
  32. data/md/promise.md +225 -225
  33. data/md/thread_pool.md +197 -197
  34. data/spec/concurrent/agent_spec.rb +376 -405
  35. data/spec/concurrent/cached_thread_pool_spec.rb +112 -112
  36. data/spec/concurrent/defer_spec.rb +209 -199
  37. data/spec/concurrent/event_machine_defer_proxy_spec.rb +250 -246
  38. data/spec/concurrent/event_spec.rb +134 -134
  39. data/spec/concurrent/executor_spec.rb +146 -0
  40. data/spec/concurrent/fixed_thread_pool_spec.rb +84 -84
  41. data/spec/concurrent/functions_spec.rb +57 -0
  42. data/spec/concurrent/future_spec.rb +125 -115
  43. data/spec/concurrent/goroutine_spec.rb +67 -52
  44. data/spec/concurrent/obligation_shared.rb +121 -121
  45. data/spec/concurrent/promise_spec.rb +299 -310
  46. data/spec/concurrent/smart_mutex_spec.rb +234 -0
  47. data/spec/concurrent/thread_pool_shared.rb +209 -209
  48. data/spec/concurrent/utilities_spec.rb +74 -0
  49. data/spec/spec_helper.rb +21 -19
  50. metadata +38 -14
  51. checksums.yaml +0 -7
@@ -1,89 +1,89 @@
1
- require 'thread'
2
-
3
- require 'concurrent/thread_pool'
4
- require 'concurrent/event'
5
-
6
- module Concurrent
7
-
8
- def self.new_fixed_thread_pool(size)
9
- return FixedThreadPool.new(size)
10
- end
11
-
12
- class FixedThreadPool < ThreadPool
13
- behavior(:thread_pool)
14
-
15
- MIN_POOL_SIZE = 1
16
- MAX_POOL_SIZE = 1024
17
-
18
- def initialize(size)
19
- super()
20
- if size < MIN_POOL_SIZE || size > MAX_POOL_SIZE
21
- raise ArgumentError.new("size must be between #{MIN_POOL_SIZE} and #{MAX_POOL_SIZE}")
22
- end
23
-
24
- @pool = size.times.collect{ create_worker_thread }
25
- collect_garbage
26
- end
27
-
28
- def kill
29
- @status = :killed
30
- @pool.each{|t| Thread.kill(t) }
31
- end
32
-
33
- def size
34
- if running?
35
- return @pool.length
36
- else
37
- return 0
38
- end
39
- end
40
-
41
- def post(*args, &block)
42
- raise ArgumentError.new('no block given') unless block_given?
43
- if running?
44
- @queue << [args, block]
45
- return true
46
- else
47
- return false
48
- end
49
- end
50
-
51
- # @private
52
- def status # :nodoc:
53
- @pool.collect{|t| t.status }
54
- end
55
-
56
- private
57
-
58
- # @private
59
- def create_worker_thread # :nodoc:
60
- Thread.new do
61
- loop do
62
- task = @queue.pop
63
- if task == :stop
64
- break
65
- else
66
- task.last.call(*task.first)
67
- end
68
- end
69
- @pool.delete(Thread.current)
70
- if @pool.empty?
71
- @termination.set
72
- @status = :shutdown unless killed?
73
- end
74
- end
75
- end
76
-
77
- # @private
78
- def collect_garbage # :nodoc:
79
- @collector = Thread.new do
80
- sleep(1)
81
- @pool.size.times do |i|
82
- if @pool[i].status.nil?
83
- @pool[i] = create_worker_thread
84
- end
85
- end
86
- end
87
- end
88
- end
89
- end
1
+ require 'thread'
2
+
3
+ require 'concurrent/thread_pool'
4
+ require 'concurrent/event'
5
+
6
+ module Concurrent
7
+
8
+ def self.new_fixed_thread_pool(size)
9
+ return FixedThreadPool.new(size)
10
+ end
11
+
12
+ class FixedThreadPool < ThreadPool
13
+ behavior(:thread_pool)
14
+
15
+ MIN_POOL_SIZE = 1
16
+ MAX_POOL_SIZE = 1024
17
+
18
+ def initialize(size)
19
+ super()
20
+ if size < MIN_POOL_SIZE || size > MAX_POOL_SIZE
21
+ raise ArgumentError.new("size must be between #{MIN_POOL_SIZE} and #{MAX_POOL_SIZE}")
22
+ end
23
+
24
+ @pool = size.times.collect{ create_worker_thread }
25
+ collect_garbage
26
+ end
27
+
28
+ def kill
29
+ @status = :killed
30
+ @pool.each{|t| Thread.kill(t) }
31
+ end
32
+
33
+ def size
34
+ if running?
35
+ return @pool.length
36
+ else
37
+ return 0
38
+ end
39
+ end
40
+
41
+ def post(*args, &block)
42
+ raise ArgumentError.new('no block given') unless block_given?
43
+ if running?
44
+ @queue << [args, block]
45
+ return true
46
+ else
47
+ return false
48
+ end
49
+ end
50
+
51
+ # @private
52
+ def status # :nodoc:
53
+ @pool.collect{|t| t.status }
54
+ end
55
+
56
+ private
57
+
58
+ # @private
59
+ def create_worker_thread # :nodoc:
60
+ Thread.new do
61
+ loop do
62
+ task = @queue.pop
63
+ if task == :stop
64
+ break
65
+ else
66
+ task.last.call(*task.first)
67
+ end
68
+ end
69
+ @pool.delete(Thread.current)
70
+ if @pool.empty?
71
+ @termination.set
72
+ @status = :shutdown unless killed?
73
+ end
74
+ end
75
+ end
76
+
77
+ # @private
78
+ def collect_garbage # :nodoc:
79
+ @collector = Thread.new do
80
+ sleep(1)
81
+ @pool.size.times do |i|
82
+ if @pool[i].status.nil?
83
+ @pool[i] = create_worker_thread
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,120 @@
1
+ require 'concurrent/agent'
2
+ require 'concurrent/defer'
3
+ require 'concurrent/future'
4
+ require 'concurrent/promise'
5
+
6
+ module Kernel
7
+
8
+ ## agent
9
+
10
+ def agent(initial, timeout = Concurrent::Agent::TIMEOUT)
11
+ return Concurrent::Agent.new(initial, timeout)
12
+ end
13
+ module_function :agent
14
+
15
+ def post(agent, &block)
16
+ if agent.respond_to?(:post)
17
+ return agent.post(&block)
18
+ else
19
+ return nil
20
+ end
21
+ end
22
+ module_function :post
23
+
24
+ ## defer
25
+
26
+ def defer(*args, &block)
27
+ return Concurrent::Defer.new(*args, &block)
28
+ end
29
+ module_function :defer
30
+
31
+ ## executor
32
+
33
+ def executor(*args, &block)
34
+ return Concurrent::Executor.run(*args, &block)
35
+ end
36
+ module_function :executor
37
+
38
+ ## future
39
+
40
+ def future(*args, &block)
41
+ return Concurrent::Future.new(*args, &block)
42
+ end
43
+ module_function :future
44
+
45
+ ## obligation
46
+
47
+ def deref(obligation, timeout = nil)
48
+ if obligation.respond_to?(:deref)
49
+ return obligation.deref(timeout)
50
+ elsif obligation.respond_to?(:value)
51
+ return obligation.deref(timeout)
52
+ else
53
+ return nil
54
+ end
55
+ end
56
+ module_function :deref
57
+
58
+ def pending?(obligation)
59
+ if obligation.respond_to?(:pending?)
60
+ return obligation.pending?
61
+ else
62
+ return false
63
+ end
64
+ end
65
+ module_function :pending?
66
+
67
+ def fulfilled?(obligation)
68
+ if obligation.respond_to?(:fulfilled?)
69
+ return obligation.fulfilled?
70
+ elsif obligation.respond_to?(:realized?)
71
+ return obligation.realized?
72
+ else
73
+ return false
74
+ end
75
+ end
76
+ module_function :fulfilled?
77
+
78
+ def realized?(obligation)
79
+ if obligation.respond_to?(:realized?)
80
+ return obligation.realized?
81
+ elsif obligation.respond_to?(:fulfilled?)
82
+ return obligation.fulfilled?
83
+ else
84
+ return false
85
+ end
86
+ end
87
+ module_function :realized?
88
+
89
+ def rejected?(obligation)
90
+ if obligation.respond_to?(:rejected?)
91
+ return obligation.rejected?
92
+ else
93
+ return false
94
+ end
95
+ end
96
+ module_function :rejected?
97
+
98
+ ## promise
99
+
100
+ # Creates a new promise object. "A promise represents the eventual
101
+ # value returned from the single completion of an operation."
102
+ # Promises can be chained in a tree structure where each promise
103
+ # has zero or more children. Promises are resolved asynchronously
104
+ # in the order they are added to the tree. Parents are guaranteed
105
+ # to be resolved before their children. The result of each promise
106
+ # is passes to each of its children when the child resolves. When
107
+ # a promise is rejected all its children will be summarily rejected.
108
+ # A promise added to a rejected promise will immediately be rejected.
109
+ # A promise that is neither resolved or rejected is pending.
110
+ #
111
+ # @param args [Array] zero or more arguments for the block
112
+ # @param block [Proc] the block to call when attempting fulfillment
113
+ #
114
+ # @see Promise
115
+ # @see http://wiki.commonjs.org/wiki/Promises/A
116
+ def promise(*args, &block)
117
+ return Concurrent::Promise.new(*args, &block)
118
+ end
119
+ module_function :promise
120
+ end
@@ -1,42 +1,52 @@
1
- require 'thread'
2
-
3
- require 'concurrent/obligation'
4
- require 'concurrent/global_thread_pool'
5
-
6
- module Concurrent
7
-
8
- class Future
9
- include Obligation
10
- behavior(:future)
11
-
12
- def initialize(*args)
13
-
14
- unless block_given?
15
- @state = :fulfilled
16
- else
17
- @value = nil
18
- @state = :pending
19
- $GLOBAL_THREAD_POOL.post do
20
- semaphore.synchronize do
21
- Thread.pass
22
- begin
23
- @value = yield(*args)
24
- @state = :fulfilled
25
- rescue Exception => ex
26
- @state = :rejected
27
- @reason = ex
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
34
- end
35
-
36
- module Kernel
37
-
38
- def future(*args, &block)
39
- return Concurrent::Future.new(*args, &block)
40
- end
41
- module_function :future
42
- end
1
+ require 'thread'
2
+
3
+ require 'concurrent/global_thread_pool'
4
+ require 'concurrent/obligation'
5
+ require 'concurrent/utilities'
6
+
7
+ module Concurrent
8
+
9
+ class Future
10
+ include Obligation
11
+ behavior(:future)
12
+
13
+ def initialize(*args, &block)
14
+ if args.first.behaves_as?(:global_thread_pool)
15
+ thread_pool = args.first
16
+ args = args.slice(1, args.length)
17
+ else
18
+ thread_pool = $GLOBAL_THREAD_POOL
19
+ end
20
+
21
+ unless block_given?
22
+ @state = :fulfilled
23
+ else
24
+ @value = nil
25
+ @state = :pending
26
+ thread_pool.post do
27
+ Thread.pass
28
+ work(*args, &block)
29
+ end
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ # @private
36
+ def work(*args) # :nodoc:
37
+ semaphore.synchronize do
38
+ begin
39
+ atomic {
40
+ @value = yield(*args)
41
+ @state = :fulfilled
42
+ }
43
+ rescue Exception => ex
44
+ atomic {
45
+ @state = :rejected
46
+ @reason = ex
47
+ }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,3 +1,3 @@
1
- require 'concurrent/cached_thread_pool'
2
-
3
- $GLOBAL_THREAD_POOL ||= Concurrent::CachedThreadPool.new
1
+ require 'concurrent/cached_thread_pool'
2
+
3
+ $GLOBAL_THREAD_POOL ||= Concurrent::CachedThreadPool.new
@@ -1,25 +1,29 @@
1
- require 'thread'
2
-
3
- require 'concurrent/global_thread_pool'
4
-
5
- module Kernel
6
-
7
- # Post the given agruments and block to the Global Thread Pool.
8
- #
9
- # @param args [Array] zero or more arguments for the block
10
- # @param block [Proc] operation to be performed concurrently
11
- #
12
- # @return [true,false] success/failre of thread creation
13
- #
14
- # @note Althought based on Go's goroutines and Erlang's spawn/1,
15
- # Ruby has a vastly different runtime. Threads aren't nearly as
16
- # efficient in Ruby. Use this function appropriately.
17
- #
18
- # @see http://golang.org/doc/effective_go.html#goroutines
19
- # @see https://gobyexample.com/goroutines
20
- def go(*args, &block)
21
- return false unless block_given?
22
- $GLOBAL_THREAD_POOL.post(*args, &block)
23
- end
24
- module_function :go
25
- end
1
+ require 'thread'
2
+
3
+ require 'concurrent/global_thread_pool'
4
+
5
+ module Kernel
6
+
7
+ # Post the given agruments and block to the Global Thread Pool.
8
+ #
9
+ # @param args [Array] zero or more arguments for the block
10
+ # @param block [Proc] operation to be performed concurrently
11
+ #
12
+ # @return [true,false] success/failre of thread creation
13
+ #
14
+ # @note Althought based on Go's goroutines and Erlang's spawn/1,
15
+ # Ruby has a vastly different runtime. Threads aren't nearly as
16
+ # efficient in Ruby. Use this function appropriately.
17
+ #
18
+ # @see http://golang.org/doc/effective_go.html#goroutines
19
+ # @see https://gobyexample.com/goroutines
20
+ def go(*args, &block)
21
+ return false unless block_given?
22
+ if args.first.behaves_as?(:global_thread_pool)
23
+ args.first.post(*args.slice(1, args.length), &block)
24
+ else
25
+ $GLOBAL_THREAD_POOL.post(*args, &block)
26
+ end
27
+ end
28
+ module_function :go
29
+ end