concurrent-ruby 0.7.0.rc0-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +166 -0
  4. data/ext/concurrent_ruby_ext/atomic_reference.c +78 -0
  5. data/ext/concurrent_ruby_ext/atomic_reference.h +12 -0
  6. data/ext/concurrent_ruby_ext/extconf.rb +59 -0
  7. data/ext/concurrent_ruby_ext/rb_concurrent.c +28 -0
  8. data/lib/2.0/concurrent_ruby_ext.so +0 -0
  9. data/lib/concurrent.rb +45 -0
  10. data/lib/concurrent/actress.rb +221 -0
  11. data/lib/concurrent/actress/ad_hoc.rb +20 -0
  12. data/lib/concurrent/actress/context.rb +98 -0
  13. data/lib/concurrent/actress/core.rb +228 -0
  14. data/lib/concurrent/actress/core_delegations.rb +42 -0
  15. data/lib/concurrent/actress/envelope.rb +41 -0
  16. data/lib/concurrent/actress/errors.rb +14 -0
  17. data/lib/concurrent/actress/reference.rb +64 -0
  18. data/lib/concurrent/actress/type_check.rb +48 -0
  19. data/lib/concurrent/agent.rb +232 -0
  20. data/lib/concurrent/async.rb +319 -0
  21. data/lib/concurrent/atomic.rb +46 -0
  22. data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
  23. data/lib/concurrent/atomic/atomic_fixnum.rb +162 -0
  24. data/lib/concurrent/atomic/condition.rb +67 -0
  25. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +118 -0
  26. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +117 -0
  27. data/lib/concurrent/atomic/count_down_latch.rb +116 -0
  28. data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
  29. data/lib/concurrent/atomic/event.rb +98 -0
  30. data/lib/concurrent/atomic/thread_local_var.rb +117 -0
  31. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
  32. data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
  33. data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
  34. data/lib/concurrent/atomic_reference/jruby.rb +8 -0
  35. data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
  36. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
  37. data/lib/concurrent/atomic_reference/rbx.rb +16 -0
  38. data/lib/concurrent/atomic_reference/ruby.rb +16 -0
  39. data/lib/concurrent/atomics.rb +10 -0
  40. data/lib/concurrent/channel/buffered_channel.rb +85 -0
  41. data/lib/concurrent/channel/channel.rb +41 -0
  42. data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
  43. data/lib/concurrent/channel/waitable_list.rb +40 -0
  44. data/lib/concurrent/channels.rb +5 -0
  45. data/lib/concurrent/collection/blocking_ring_buffer.rb +71 -0
  46. data/lib/concurrent/collection/priority_queue.rb +305 -0
  47. data/lib/concurrent/collection/ring_buffer.rb +59 -0
  48. data/lib/concurrent/collections.rb +3 -0
  49. data/lib/concurrent/configuration.rb +158 -0
  50. data/lib/concurrent/dataflow.rb +91 -0
  51. data/lib/concurrent/delay.rb +112 -0
  52. data/lib/concurrent/dereferenceable.rb +101 -0
  53. data/lib/concurrent/errors.rb +30 -0
  54. data/lib/concurrent/exchanger.rb +34 -0
  55. data/lib/concurrent/executor/cached_thread_pool.rb +44 -0
  56. data/lib/concurrent/executor/executor.rb +229 -0
  57. data/lib/concurrent/executor/fixed_thread_pool.rb +33 -0
  58. data/lib/concurrent/executor/immediate_executor.rb +16 -0
  59. data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
  60. data/lib/concurrent/executor/java_fixed_thread_pool.rb +33 -0
  61. data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
  62. data/lib/concurrent/executor/java_thread_pool_executor.rb +187 -0
  63. data/lib/concurrent/executor/per_thread_executor.rb +24 -0
  64. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
  65. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +32 -0
  66. data/lib/concurrent/executor/ruby_single_thread_executor.rb +73 -0
  67. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +286 -0
  68. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +72 -0
  69. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  70. data/lib/concurrent/executor/serialized_execution.rb +90 -0
  71. data/lib/concurrent/executor/single_thread_executor.rb +35 -0
  72. data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
  73. data/lib/concurrent/executor/timer_set.rb +143 -0
  74. data/lib/concurrent/executors.rb +9 -0
  75. data/lib/concurrent/future.rb +124 -0
  76. data/lib/concurrent/ivar.rb +111 -0
  77. data/lib/concurrent/logging.rb +17 -0
  78. data/lib/concurrent/mvar.rb +200 -0
  79. data/lib/concurrent/obligation.rb +171 -0
  80. data/lib/concurrent/observable.rb +40 -0
  81. data/lib/concurrent/options_parser.rb +46 -0
  82. data/lib/concurrent/promise.rb +169 -0
  83. data/lib/concurrent/scheduled_task.rb +78 -0
  84. data/lib/concurrent/supervisor.rb +343 -0
  85. data/lib/concurrent/timer_task.rb +341 -0
  86. data/lib/concurrent/tvar.rb +252 -0
  87. data/lib/concurrent/utilities.rb +3 -0
  88. data/lib/concurrent/utility/processor_count.rb +150 -0
  89. data/lib/concurrent/utility/timeout.rb +35 -0
  90. data/lib/concurrent/utility/timer.rb +21 -0
  91. data/lib/concurrent/version.rb +3 -0
  92. data/lib/concurrent_ruby.rb +1 -0
  93. data/lib/concurrent_ruby_ext.so +0 -0
  94. data/lib/extension_helper.rb +9 -0
  95. metadata +141 -0
@@ -0,0 +1,305 @@
1
+ module Concurrent
2
+
3
+ # @!macro [attach] priority_queue
4
+ #
5
+ # A queue collection in which the elements are sorted based on their
6
+ # comparison (spaceship) operator `<=>`. Items are added to the queue
7
+ # at a position relative to their priority. On removal the element
8
+ # with the "highest" priority is removed. By default the sort order is
9
+ # from highest to lowest, but a lowest-to-highest sort order can be
10
+ # set on construction.
11
+ #
12
+ # The API is based on the `Queue` class from the Ruby standard library.
13
+ #
14
+ # The pure Ruby implementation, `MutexPriorityQueue` uses a heap algorithm
15
+ # stored in an array. The algorithm is based on the work of Robert Sedgewick
16
+ # and Kevin Wayne.
17
+ #
18
+ # The JRuby native implementation is a thin wrapper around the standard
19
+ # library `java.util.PriorityQueue`.
20
+ #
21
+ # When running under JRuby the class `PriorityQueue` extends `JavaPriorityQueue`.
22
+ # When running under all other interpreters it extends `MutexPriorityQueue`.
23
+ #
24
+ # @note This implementation is *not* thread safe and performs no blocking.
25
+ #
26
+ # @see http://en.wikipedia.org/wiki/Priority_queue
27
+ # @see http://ruby-doc.org/stdlib-2.0.0/libdoc/thread/rdoc/Queue.html
28
+ #
29
+ # @see http://algs4.cs.princeton.edu/24pq/index.php#2.6
30
+ # @see http://algs4.cs.princeton.edu/24pq/MaxPQ.java.html
31
+ #
32
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
33
+ class MutexPriorityQueue
34
+
35
+ # @!macro [attach] priority_queue_method_initialize
36
+ #
37
+ # Create a new priority queue with no items.
38
+ #
39
+ # @param [Hash] opts the options for creating the queue
40
+ # @option opts [Symbol] :order (:max) dictates the order in which items are
41
+ # stored: from highest to lowest when `:max` or `:high`; from lowest to
42
+ # highest when `:min` or `:low`
43
+ def initialize(opts = {})
44
+ order = opts.fetch(:order, :max)
45
+ @comparator = [:min, :low].include?(order) ? -1 : 1
46
+ clear
47
+ end
48
+
49
+ # @!macro [attach] priority_queue_method_clear
50
+ #
51
+ # Removes all of the elements from this priority queue.
52
+ def clear
53
+ @queue = [nil]
54
+ @length = 0
55
+ true
56
+ end
57
+
58
+ # @!macro [attach] priority_queue_method_delete
59
+ #
60
+ # Deletes all items from `self` that are equal to `item`.
61
+ #
62
+ # @param [Object] item the item to be removed from the queue
63
+ # @return [Object] true if the item is found else false
64
+ def delete(item)
65
+ original_length = @length
66
+ k = 1
67
+ while k <= @length
68
+ if @queue[k] == item
69
+ swap(k, @length)
70
+ @length -= 1
71
+ sink(k)
72
+ @queue.pop
73
+ else
74
+ k += 1
75
+ end
76
+ end
77
+ @length != original_length
78
+ end
79
+
80
+ # @!macro [attach] priority_queue_method_empty
81
+ #
82
+ # Returns `true` if `self` contains no elements.
83
+ #
84
+ # @return [Boolean] true if there are no items in the queue else false
85
+ def empty?
86
+ size == 0
87
+ end
88
+
89
+ # @!macro [attach] priority_queue_method_include
90
+ #
91
+ # Returns `true` if the given item is present in `self` (that is, if any
92
+ # element == `item`), otherwise returns false.
93
+ #
94
+ # @param [Object] item the item to search for
95
+ #
96
+ # @return [Boolean] true if the item is found else false
97
+ def include?(item)
98
+ @queue.include?(item)
99
+ end
100
+ alias_method :has_priority?, :include?
101
+
102
+ # @!macro [attach] priority_queue_method_length
103
+ #
104
+ # The current length of the queue.
105
+ #
106
+ # @return [Fixnum] the number of items in the queue
107
+ def length
108
+ @length
109
+ end
110
+ alias_method :size, :length
111
+
112
+ # @!macro [attach] priority_queue_method_peek
113
+ #
114
+ # Retrieves, but does not remove, the head of this queue, or returns `nil`
115
+ # if this queue is empty.
116
+ #
117
+ # @return [Object] the head of the queue or `nil` when empty
118
+ def peek
119
+ @queue[1]
120
+ end
121
+
122
+ # @!macro [attach] priority_queue_method_pop
123
+ #
124
+ # Retrieves and removes the head of this queue, or returns `nil` if this
125
+ # queue is empty.
126
+ #
127
+ # @return [Object] the head of the queue or `nil` when empty
128
+ def pop
129
+ max = @queue[1]
130
+ swap(1, @length)
131
+ @length -= 1
132
+ sink(1)
133
+ @queue.pop
134
+ max
135
+ end
136
+ alias_method :deq, :pop
137
+ alias_method :shift, :pop
138
+
139
+ # @!macro [attach] priority_queue_method_push
140
+ #
141
+ # Inserts the specified element into this priority queue.
142
+ #
143
+ # @param [Object] item the item to insert onto the queue
144
+ def push(item)
145
+ @length += 1
146
+ @queue << item
147
+ swim(@length)
148
+ true
149
+ end
150
+ alias_method :<<, :push
151
+ alias_method :enq, :push
152
+
153
+ # @!macro [attach] priority_queue_method_from_list
154
+ #
155
+ # Create a new priority queue from the given list.
156
+ #
157
+ # @param [Enumerable] list the list to build the queue from
158
+ # @param [Hash] opts the options for creating the queue
159
+ #
160
+ # @return [PriorityQueue] the newly created and populated queue
161
+ def self.from_list(list, opts = {})
162
+ queue = new(opts)
163
+ list.each{|item| queue << item }
164
+ queue
165
+ end
166
+
167
+ protected
168
+
169
+ # Exchange the values at the given indexes within the internal array.
170
+ #
171
+ # @param [Integer] x the first index to swap
172
+ # @param [Integer] y the second index to swap
173
+ #
174
+ # @!visibility private
175
+ def swap(x, y)
176
+ temp = @queue[x]
177
+ @queue[x] = @queue[y]
178
+ @queue[y] = temp
179
+ end
180
+
181
+ # Are the items at the given indexes ordered based on the priority
182
+ # order specified at construction?
183
+ #
184
+ # @param [Integer] x the first index from which to retrieve a comparable value
185
+ # @param [Integer] y the second index from which to retrieve a comparable value
186
+ #
187
+ # @return [Boolean] true if the two elements are in the correct priority order
188
+ # else false
189
+ #
190
+ # @!visibility private
191
+ def ordered?(x, y)
192
+ (@queue[x] <=> @queue[y]) == @comparator
193
+ end
194
+
195
+ # Percolate down to maintain heap invariant.
196
+ #
197
+ # @param [Integer] k the index at which to start the percolation
198
+ #
199
+ # @!visibility private
200
+ def sink(k)
201
+ while (j = (2 * k)) <= @length do
202
+ j += 1 if j < @length && ! ordered?(j, j+1)
203
+ break if ordered?(k, j)
204
+ swap(k, j)
205
+ k = j
206
+ end
207
+ end
208
+
209
+ # Percolate up to maintain heap invariant.
210
+ #
211
+ # @param [Integer] k the index at which to start the percolation
212
+ #
213
+ # @!visibility private
214
+ def swim(k)
215
+ while k > 1 && ! ordered?(k/2, k) do
216
+ swap(k, k/2)
217
+ k = k/2
218
+ end
219
+ end
220
+ end
221
+
222
+ if RUBY_PLATFORM == 'java'
223
+
224
+ # @!macro priority_queue
225
+ class JavaPriorityQueue
226
+
227
+ # @!macro priority_queue_method_initialize
228
+ def initialize(opts = {})
229
+ order = opts.fetch(:order, :max)
230
+ if [:min, :low].include?(order)
231
+ @queue = java.util.PriorityQueue.new(11) # 11 is the default initial capacity
232
+ else
233
+ @queue = java.util.PriorityQueue.new(11, java.util.Collections.reverseOrder())
234
+ end
235
+ end
236
+
237
+ # @!macro priority_queue_method_clear
238
+ def clear
239
+ @queue.clear
240
+ true
241
+ end
242
+
243
+ # @!macro priority_queue_method_delete
244
+ def delete(item)
245
+ found = false
246
+ while @queue.remove(item) do
247
+ found = true
248
+ end
249
+ found
250
+ end
251
+
252
+ # @!macro priority_queue_method_empty
253
+ def empty?
254
+ @queue.size == 0
255
+ end
256
+
257
+ # @!macro priority_queue_method_include
258
+ def include?(item)
259
+ @queue.contains(item)
260
+ end
261
+ alias_method :has_priority?, :include?
262
+
263
+ # @!macro priority_queue_method_length
264
+ def length
265
+ @queue.size
266
+ end
267
+ alias_method :size, :length
268
+
269
+ # @!macro priority_queue_method_peek
270
+ def peek
271
+ @queue.peek
272
+ end
273
+
274
+ # @!macro priority_queue_method_pop
275
+ def pop
276
+ @queue.poll
277
+ end
278
+ alias_method :deq, :pop
279
+ alias_method :shift, :pop
280
+
281
+ # @!macro priority_queue_method_push
282
+ def push(item)
283
+ @queue.add(item)
284
+ end
285
+ alias_method :<<, :push
286
+ alias_method :enq, :push
287
+
288
+ # @!macro priority_queue_method_from_list
289
+ def self.from_list(list, opts = {})
290
+ queue = new(opts)
291
+ list.each{|item| queue << item }
292
+ queue
293
+ end
294
+ end
295
+
296
+ # @!macro priority_queue
297
+ class PriorityQueue < JavaPriorityQueue
298
+ end
299
+ else
300
+
301
+ # @!macro priority_queue
302
+ class PriorityQueue < MutexPriorityQueue
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,59 @@
1
+ module Concurrent
2
+
3
+ # non-thread safe buffer
4
+ class RingBuffer
5
+
6
+ def initialize(capacity)
7
+ @buffer = Array.new(capacity)
8
+ @first = @last = 0
9
+ @count = 0
10
+ end
11
+
12
+
13
+ # @return [Integer] the capacity of the buffer
14
+ def capacity
15
+ @buffer.size
16
+ end
17
+
18
+ # @return [Integer] the number of elements currently in the buffer
19
+ def count
20
+ @count
21
+ end
22
+
23
+ # @return [Boolean] true if buffer is empty, false otherwise
24
+ def empty?
25
+ @count == 0
26
+ end
27
+
28
+ # @return [Boolean] true if buffer is full, false otherwise
29
+ def full?
30
+ @count == capacity
31
+ end
32
+
33
+ # @param [Object] value
34
+ # @return [Boolean] true if value has been inserted, false otherwise
35
+ def offer(value)
36
+ return false if full?
37
+
38
+ @buffer[@last] = value
39
+ @last = (@last + 1) % @buffer.size
40
+ @count += 1
41
+ true
42
+ end
43
+
44
+ # @return [Object] the first available value and removes it from the buffer. If buffer is empty returns nil
45
+ def poll
46
+ result = @buffer[@first]
47
+ @buffer[@first] = nil
48
+ @first = (@first + 1) % @buffer.size
49
+ @count -= 1
50
+ result
51
+ end
52
+
53
+ # @return [Object] the first available value and without removing it from the buffer. If buffer is empty returns nil
54
+ def peek
55
+ @buffer[@first]
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ require 'concurrent/collection/priority_queue'
2
+ require 'concurrent/collection/ring_buffer'
3
+ require 'concurrent/collection/blocking_ring_buffer'
@@ -0,0 +1,158 @@
1
+ require 'thread'
2
+ require 'concurrent/delay'
3
+ require 'concurrent/errors'
4
+ require 'concurrent/atomic'
5
+ require 'concurrent/executor/thread_pool_executor'
6
+ require 'concurrent/executor/timer_set'
7
+ require 'concurrent/utility/processor_count'
8
+
9
+ module Concurrent
10
+ extend Logging
11
+
12
+ # A gem-level configuration object.
13
+ class Configuration
14
+
15
+ # a proc defining how to log messages, its interface has to be:
16
+ # lambda { |level, progname, message = nil, &block| _ }
17
+ attr_accessor :logger
18
+
19
+ # Create a new configuration object.
20
+ def initialize
21
+ @global_task_pool = Delay.new { new_task_pool }
22
+ @global_operation_pool = Delay.new { new_operation_pool }
23
+ @global_timer_set = Delay.new { Concurrent::TimerSet.new }
24
+ @logger = no_logger
25
+ end
26
+
27
+ # if assigned to {#logger}, it will log nothing.
28
+ def no_logger
29
+ lambda { |level, progname, message = nil, &block| }
30
+ end
31
+
32
+ # Global thread pool optimized for short *tasks*.
33
+ #
34
+ # @return [ThreadPoolExecutor] the thread pool
35
+ def global_task_pool
36
+ @global_task_pool.value
37
+ end
38
+
39
+ # Global thread pool optimized for long *operations*.
40
+ #
41
+ # @return [ThreadPoolExecutor] the thread pool
42
+ def global_operation_pool
43
+ @global_operation_pool.value
44
+ end
45
+
46
+ # Global thread pool optimized for *timers*
47
+ #
48
+ # @return [ThreadPoolExecutor] the thread pool
49
+ #
50
+ # @see Concurrent::timer
51
+ def global_timer_set
52
+ @global_timer_set.value
53
+ end
54
+
55
+ # Global thread pool optimized for short *tasks*.
56
+ #
57
+ # A global thread pool must be set as soon as the gem is loaded. Setting a new
58
+ # thread pool once tasks and operations have been post can lead to unpredictable
59
+ # results. The first time a task/operation is post a new thread pool will be
60
+ # created using the default configuration. Once set the thread pool cannot be
61
+ # changed. Thus, explicitly setting the thread pool must occur *before* any
62
+ # tasks/operations are post else an exception will be raised.
63
+ #
64
+ # @param [Executor] executor the executor to be used for this thread pool
65
+ #
66
+ # @return [ThreadPoolExecutor] the new thread pool
67
+ #
68
+ # @raise [Concurrent::ConfigurationError] if this thread pool has already been set
69
+ def global_task_pool=(executor)
70
+ @global_task_pool.reconfigure { executor } or
71
+ raise ConfigurationError.new('global task pool was already set')
72
+ end
73
+
74
+ # Global thread pool optimized for long *operations*.
75
+ #
76
+ # A global thread pool must be set as soon as the gem is loaded. Setting a new
77
+ # thread pool once tasks and operations have been post can lead to unpredictable
78
+ # results. The first time a task/operation is post a new thread pool will be
79
+ # created using the default configuration. Once set the thread pool cannot be
80
+ # changed. Thus, explicitly setting the thread pool must occur *before* any
81
+ # tasks/operations are post else an exception will be raised.
82
+ #
83
+ # @param [Executor] executor the executor to be used for this thread pool
84
+ #
85
+ # @return [ThreadPoolExecutor] the new thread pool
86
+ #
87
+ # @raise [Concurrent::ConfigurationError] if this thread pool has already been set
88
+ def global_operation_pool=(executor)
89
+ @global_operation_pool.reconfigure { executor } or
90
+ raise ConfigurationError.new('global operation pool was already set')
91
+ end
92
+
93
+ def new_task_pool
94
+ Concurrent::ThreadPoolExecutor.new(
95
+ min_threads: [2, Concurrent.processor_count].max,
96
+ max_threads: [20, Concurrent.processor_count * 15].max,
97
+ idletime: 2 * 60, # 2 minutes
98
+ max_queue: 0, # unlimited
99
+ overflow_policy: :abort # raise an exception
100
+ )
101
+ end
102
+
103
+ def new_operation_pool
104
+ Concurrent::ThreadPoolExecutor.new(
105
+ min_threads: [2, Concurrent.processor_count].max,
106
+ max_threads: [2, Concurrent.processor_count].max,
107
+ idletime: 10 * 60, # 10 minutes
108
+ max_queue: [20, Concurrent.processor_count * 15].max,
109
+ overflow_policy: :abort # raise an exception
110
+ )
111
+ end
112
+ end
113
+
114
+ # create the default configuration on load
115
+ @configuration = Atomic.new Configuration.new
116
+
117
+ # @return [Configuration]
118
+ def self.configuration
119
+ @configuration.value
120
+ end
121
+
122
+ # Perform gem-level configuration.
123
+ #
124
+ # @yield the configuration commands
125
+ # @yieldparam [Configuration] the current configuration object
126
+ def self.configure
127
+ yield(configuration)
128
+ end
129
+
130
+ private
131
+
132
+ # Attempt to properly shutdown the given executor using the `shutdown` or
133
+ # `kill` method when available.
134
+ #
135
+ # @param [Executor] executor the executor to shutdown
136
+ #
137
+ # @return [Boolean] `true` if the executor is successfully shut down or `nil`, else `false`
138
+ def self.finalize_executor(executor)
139
+ return true if executor.nil?
140
+ if executor.respond_to?(:shutdown)
141
+ executor.shutdown
142
+ elsif executor.respond_to?(:kill)
143
+ executor.kill
144
+ end
145
+ true
146
+ rescue => ex
147
+ log DEBUG, ex
148
+ false
149
+ end
150
+
151
+
152
+ # set exit hook to shutdown global thread pools
153
+ at_exit do
154
+ self.finalize_executor(self.configuration.global_timer_set)
155
+ self.finalize_executor(self.configuration.global_task_pool)
156
+ self.finalize_executor(self.configuration.global_operation_pool)
157
+ end
158
+ end