concurrent-ruby 0.1.0 → 0.1.1.pre.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 (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