o-concurrent-ruby 1.1.11

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 (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +542 -0
  3. data/Gemfile +37 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +404 -0
  6. data/Rakefile +307 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +189 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby/concurrent/agent.rb +587 -0
  23. data/lib/concurrent-ruby/concurrent/array.rb +66 -0
  24. data/lib/concurrent-ruby/concurrent/async.rb +449 -0
  25. data/lib/concurrent-ruby/concurrent/atom.rb +222 -0
  26. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  27. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +126 -0
  28. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +143 -0
  29. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
  30. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
  31. data/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb +100 -0
  32. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +128 -0
  33. data/lib/concurrent-ruby/concurrent/atomic/event.rb +109 -0
  34. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +42 -0
  35. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +37 -0
  36. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  37. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  38. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  39. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +131 -0
  40. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +254 -0
  41. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +377 -0
  42. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +181 -0
  43. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +166 -0
  44. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +104 -0
  45. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  46. data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  47. data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
  48. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  49. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  50. data/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  51. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
  52. data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  53. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +66 -0
  54. data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  55. data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  56. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  57. data/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  58. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +160 -0
  59. data/lib/concurrent-ruby/concurrent/concern/deprecation.rb +34 -0
  60. data/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb +73 -0
  61. data/lib/concurrent-ruby/concurrent/concern/logging.rb +32 -0
  62. data/lib/concurrent-ruby/concurrent/concern/obligation.rb +220 -0
  63. data/lib/concurrent-ruby/concurrent/concern/observable.rb +110 -0
  64. data/lib/concurrent-ruby/concurrent/configuration.rb +188 -0
  65. data/lib/concurrent-ruby/concurrent/constants.rb +8 -0
  66. data/lib/concurrent-ruby/concurrent/dataflow.rb +81 -0
  67. data/lib/concurrent-ruby/concurrent/delay.rb +199 -0
  68. data/lib/concurrent-ruby/concurrent/errors.rb +69 -0
  69. data/lib/concurrent-ruby/concurrent/exchanger.rb +352 -0
  70. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +131 -0
  71. data/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb +62 -0
  72. data/lib/concurrent-ruby/concurrent/executor/executor_service.rb +185 -0
  73. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +220 -0
  74. data/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb +66 -0
  75. data/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb +44 -0
  76. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +103 -0
  77. data/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb +30 -0
  78. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +140 -0
  79. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +82 -0
  80. data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +21 -0
  81. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +368 -0
  82. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +35 -0
  83. data/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb +34 -0
  84. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +107 -0
  85. data/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb +28 -0
  86. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +100 -0
  87. data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +57 -0
  88. data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +88 -0
  89. data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +172 -0
  90. data/lib/concurrent-ruby/concurrent/executors.rb +20 -0
  91. data/lib/concurrent-ruby/concurrent/future.rb +141 -0
  92. data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
  93. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +101 -0
  94. data/lib/concurrent-ruby/concurrent/ivar.rb +207 -0
  95. data/lib/concurrent-ruby/concurrent/map.rb +346 -0
  96. data/lib/concurrent-ruby/concurrent/maybe.rb +229 -0
  97. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +239 -0
  98. data/lib/concurrent-ruby/concurrent/mvar.rb +242 -0
  99. data/lib/concurrent-ruby/concurrent/options.rb +42 -0
  100. data/lib/concurrent-ruby/concurrent/promise.rb +580 -0
  101. data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
  102. data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
  103. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +331 -0
  104. data/lib/concurrent-ruby/concurrent/set.rb +74 -0
  105. data/lib/concurrent-ruby/concurrent/settable_struct.rb +139 -0
  106. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  107. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +24 -0
  108. data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +171 -0
  109. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +60 -0
  110. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  111. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +45 -0
  112. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +36 -0
  113. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +72 -0
  114. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +44 -0
  115. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
  116. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +183 -0
  117. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +71 -0
  118. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +49 -0
  119. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
  120. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +36 -0
  121. data/lib/concurrent-ruby/concurrent/synchronization.rb +30 -0
  122. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  123. data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +74 -0
  124. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  125. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
  126. data/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  127. data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +246 -0
  128. data/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb +75 -0
  129. data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  130. data/lib/concurrent-ruby/concurrent/thread_safe/util.rb +16 -0
  131. data/lib/concurrent-ruby/concurrent/timer_task.rb +311 -0
  132. data/lib/concurrent-ruby/concurrent/tuple.rb +86 -0
  133. data/lib/concurrent-ruby/concurrent/tvar.rb +221 -0
  134. data/lib/concurrent-ruby/concurrent/utility/engine.rb +56 -0
  135. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
  136. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
  137. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +53 -0
  138. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +130 -0
  139. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  140. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
  141. data/lib/concurrent-ruby/concurrent.rb +134 -0
  142. metadata +192 -0
@@ -0,0 +1,57 @@
1
+ require 'concurrent/utility/engine'
2
+ require 'concurrent/executor/ruby_single_thread_executor'
3
+
4
+ module Concurrent
5
+
6
+ if Concurrent.on_jruby?
7
+ require 'concurrent/executor/java_single_thread_executor'
8
+ end
9
+
10
+ SingleThreadExecutorImplementation = case
11
+ when Concurrent.on_jruby?
12
+ JavaSingleThreadExecutor
13
+ else
14
+ RubySingleThreadExecutor
15
+ end
16
+ private_constant :SingleThreadExecutorImplementation
17
+
18
+ # @!macro single_thread_executor
19
+ #
20
+ # A thread pool with a single thread an unlimited queue. Should the thread
21
+ # die for any reason it will be removed and replaced, thus ensuring that
22
+ # the executor will always remain viable and available to process jobs.
23
+ #
24
+ # A common pattern for background processing is to create a single thread
25
+ # on which an infinite loop is run. The thread's loop blocks on an input
26
+ # source (perhaps blocking I/O or a queue) and processes each input as it
27
+ # is received. This pattern has several issues. The thread itself is highly
28
+ # susceptible to errors during processing. Also, the thread itself must be
29
+ # constantly monitored and restarted should it die. `SingleThreadExecutor`
30
+ # encapsulates all these bahaviors. The task processor is highly resilient
31
+ # to errors from within tasks. Also, should the thread die it will
32
+ # automatically be restarted.
33
+ #
34
+ # The API and behavior of this class are based on Java's `SingleThreadExecutor`.
35
+ #
36
+ # @!macro abstract_executor_service_public_api
37
+ class SingleThreadExecutor < SingleThreadExecutorImplementation
38
+
39
+ # @!macro single_thread_executor_method_initialize
40
+ #
41
+ # Create a new thread pool.
42
+ #
43
+ # @option opts [Symbol] :fallback_policy (:discard) the policy for handling new
44
+ # tasks that are received when the queue size has reached
45
+ # `max_queue` or the executor has shut down
46
+ #
47
+ # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
48
+ # in `FALLBACK_POLICIES`
49
+ #
50
+ # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
51
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
52
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
53
+
54
+ # @!method initialize(opts = {})
55
+ # @!macro single_thread_executor_method_initialize
56
+ end
57
+ end
@@ -0,0 +1,88 @@
1
+ require 'concurrent/utility/engine'
2
+ require 'concurrent/executor/ruby_thread_pool_executor'
3
+
4
+ module Concurrent
5
+
6
+ if Concurrent.on_jruby?
7
+ require 'concurrent/executor/java_thread_pool_executor'
8
+ end
9
+
10
+ ThreadPoolExecutorImplementation = case
11
+ when Concurrent.on_jruby?
12
+ JavaThreadPoolExecutor
13
+ else
14
+ RubyThreadPoolExecutor
15
+ end
16
+ private_constant :ThreadPoolExecutorImplementation
17
+
18
+ # @!macro thread_pool_executor
19
+ #
20
+ # An abstraction composed of one or more threads and a task queue. Tasks
21
+ # (blocks or `proc` objects) are submitted to the pool and added to the queue.
22
+ # The threads in the pool remove the tasks and execute them in the order
23
+ # they were received.
24
+ #
25
+ # A `ThreadPoolExecutor` will automatically adjust the pool size according
26
+ # to the bounds set by `min-threads` and `max-threads`. When a new task is
27
+ # submitted and fewer than `min-threads` threads are running, a new thread
28
+ # is created to handle the request, even if other worker threads are idle.
29
+ # If there are more than `min-threads` but less than `max-threads` threads
30
+ # running, a new thread will be created only if the queue is full.
31
+ #
32
+ # Threads that are idle for too long will be garbage collected, down to the
33
+ # configured minimum options. Should a thread crash it, too, will be garbage collected.
34
+ #
35
+ # `ThreadPoolExecutor` is based on the Java class of the same name. From
36
+ # the official Java documentation;
37
+ #
38
+ # > Thread pools address two different problems: they usually provide
39
+ # > improved performance when executing large numbers of asynchronous tasks,
40
+ # > due to reduced per-task invocation overhead, and they provide a means
41
+ # > of bounding and managing the resources, including threads, consumed
42
+ # > when executing a collection of tasks. Each ThreadPoolExecutor also
43
+ # > maintains some basic statistics, such as the number of completed tasks.
44
+ # >
45
+ # > To be useful across a wide range of contexts, this class provides many
46
+ # > adjustable parameters and extensibility hooks. However, programmers are
47
+ # > urged to use the more convenient Executors factory methods
48
+ # > [CachedThreadPool] (unbounded thread pool, with automatic thread reclamation),
49
+ # > [FixedThreadPool] (fixed size thread pool) and [SingleThreadExecutor] (single
50
+ # > background thread), that preconfigure settings for the most common usage
51
+ # > scenarios.
52
+ #
53
+ # @!macro thread_pool_options
54
+ #
55
+ # @!macro thread_pool_executor_public_api
56
+ class ThreadPoolExecutor < ThreadPoolExecutorImplementation
57
+
58
+ # @!macro thread_pool_executor_method_initialize
59
+ #
60
+ # Create a new thread pool.
61
+ #
62
+ # @param [Hash] opts the options which configure the thread pool.
63
+ #
64
+ # @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum
65
+ # number of threads to be created
66
+ # @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) When a new task is submitted
67
+ # and fewer than `min_threads` are running, a new thread is created
68
+ # @option opts [Integer] :idletime (DEFAULT_THREAD_IDLETIMEOUT) the maximum
69
+ # number of seconds a thread may be idle before being reclaimed
70
+ # @option opts [Integer] :max_queue (DEFAULT_MAX_QUEUE_SIZE) the maximum
71
+ # number of tasks allowed in the work queue at any one time; a value of
72
+ # zero means the queue may grow without bound
73
+ # @option opts [Symbol] :fallback_policy (:abort) the policy for handling new
74
+ # tasks that are received when the queue size has reached
75
+ # `max_queue` or the executor has shut down
76
+ # @option opts [Boolean] :synchronous (DEFAULT_SYNCHRONOUS) whether or not a value of 0
77
+ # for :max_queue means the queue must perform direct hand-off rather than unbounded.
78
+ # @raise [ArgumentError] if `:max_threads` is less than one
79
+ # @raise [ArgumentError] if `:min_threads` is less than zero
80
+ # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
81
+ # in `FALLBACK_POLICIES`
82
+ #
83
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
84
+
85
+ # @!method initialize(opts = {})
86
+ # @!macro thread_pool_executor_method_initialize
87
+ end
88
+ end
@@ -0,0 +1,172 @@
1
+ require 'concurrent/scheduled_task'
2
+ require 'concurrent/atomic/event'
3
+ require 'concurrent/collection/non_concurrent_priority_queue'
4
+ require 'concurrent/executor/executor_service'
5
+ require 'concurrent/executor/single_thread_executor'
6
+
7
+ require 'concurrent/options'
8
+
9
+ module Concurrent
10
+
11
+ # Executes a collection of tasks, each after a given delay. A master task
12
+ # monitors the set and schedules each task for execution at the appropriate
13
+ # time. Tasks are run on the global thread pool or on the supplied executor.
14
+ # Each task is represented as a `ScheduledTask`.
15
+ #
16
+ # @see Concurrent::ScheduledTask
17
+ #
18
+ # @!macro monotonic_clock_warning
19
+ class TimerSet < RubyExecutorService
20
+
21
+ # Create a new set of timed tasks.
22
+ #
23
+ # @!macro executor_options
24
+ #
25
+ # @param [Hash] opts the options used to specify the executor on which to perform actions
26
+ # @option opts [Executor] :executor when set use the given `Executor` instance.
27
+ # Three special values are also supported: `:task` returns the global task pool,
28
+ # `:operation` returns the global operation pool, and `:immediate` returns a new
29
+ # `ImmediateExecutor` object.
30
+ def initialize(opts = {})
31
+ super(opts)
32
+ end
33
+
34
+ # Post a task to be execute run after a given delay (in seconds). If the
35
+ # delay is less than 1/100th of a second the task will be immediately post
36
+ # to the executor.
37
+ #
38
+ # @param [Float] delay the number of seconds to wait for before executing the task.
39
+ # @param [Array<Object>] args the arguments passed to the task on execution.
40
+ #
41
+ # @yield the task to be performed.
42
+ #
43
+ # @return [Concurrent::ScheduledTask, false] IVar representing the task if the post
44
+ # is successful; false after shutdown.
45
+ #
46
+ # @raise [ArgumentError] if the intended execution time is not in the future.
47
+ # @raise [ArgumentError] if no block is given.
48
+ def post(delay, *args, &task)
49
+ raise ArgumentError.new('no block given') unless block_given?
50
+ return false unless running?
51
+ opts = { executor: @task_executor,
52
+ args: args,
53
+ timer_set: self }
54
+ task = ScheduledTask.execute(delay, opts, &task) # may raise exception
55
+ task.unscheduled? ? false : task
56
+ end
57
+
58
+ # Begin an immediate shutdown. In-progress tasks will be allowed to
59
+ # complete but enqueued tasks will be dismissed and no new tasks
60
+ # will be accepted. Has no additional effect if the thread pool is
61
+ # not running.
62
+ def kill
63
+ shutdown
64
+ end
65
+
66
+ private :<<
67
+
68
+ private
69
+
70
+ # Initialize the object.
71
+ #
72
+ # @param [Hash] opts the options to create the object with.
73
+ # @!visibility private
74
+ def ns_initialize(opts)
75
+ @queue = Collection::NonConcurrentPriorityQueue.new(order: :min)
76
+ @task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
77
+ @timer_executor = SingleThreadExecutor.new
78
+ @condition = Event.new
79
+ @ruby_pid = $$ # detects if Ruby has forked
80
+ end
81
+
82
+ # Post the task to the internal queue.
83
+ #
84
+ # @note This is intended as a callback method from ScheduledTask
85
+ # only. It is not intended to be used directly. Post a task
86
+ # by using the `SchedulesTask#execute` method.
87
+ #
88
+ # @!visibility private
89
+ def post_task(task)
90
+ synchronize { ns_post_task(task) }
91
+ end
92
+
93
+ # @!visibility private
94
+ def ns_post_task(task)
95
+ return false unless ns_running?
96
+ ns_reset_if_forked
97
+ if (task.initial_delay) <= 0.01
98
+ task.executor.post { task.process_task }
99
+ else
100
+ @queue.push(task)
101
+ # only post the process method when the queue is empty
102
+ @timer_executor.post(&method(:process_tasks)) if @queue.length == 1
103
+ @condition.set
104
+ end
105
+ true
106
+ end
107
+
108
+ # Remove the given task from the queue.
109
+ #
110
+ # @note This is intended as a callback method from `ScheduledTask`
111
+ # only. It is not intended to be used directly. Cancel a task
112
+ # by using the `ScheduledTask#cancel` method.
113
+ #
114
+ # @!visibility private
115
+ def remove_task(task)
116
+ synchronize { @queue.delete(task) }
117
+ end
118
+
119
+ # `ExecutorService` callback called during shutdown.
120
+ #
121
+ # @!visibility private
122
+ def ns_shutdown_execution
123
+ ns_reset_if_forked
124
+ @queue.clear
125
+ @timer_executor.kill
126
+ stopped_event.set
127
+ end
128
+
129
+ def ns_reset_if_forked
130
+ if $$ != @ruby_pid
131
+ @queue.clear
132
+ @condition.reset
133
+ @ruby_pid = $$
134
+ end
135
+ end
136
+
137
+ # Run a loop and execute tasks in the scheduled order and at the approximate
138
+ # scheduled time. If no tasks remain the thread will exit gracefully so that
139
+ # garbage collection can occur. If there are no ready tasks it will sleep
140
+ # for up to 60 seconds waiting for the next scheduled task.
141
+ #
142
+ # @!visibility private
143
+ def process_tasks
144
+ loop do
145
+ task = synchronize { @condition.reset; @queue.peek }
146
+ break unless task
147
+
148
+ now = Concurrent.monotonic_time
149
+ diff = task.schedule_time - now
150
+
151
+ if diff <= 0
152
+ # We need to remove the task from the queue before passing
153
+ # it to the executor, to avoid race conditions where we pass
154
+ # the peek'ed task to the executor and then pop a different
155
+ # one that's been added in the meantime.
156
+ #
157
+ # Note that there's no race condition between the peek and
158
+ # this pop - this pop could retrieve a different task from
159
+ # the peek, but that task would be due to fire now anyway
160
+ # (because @queue is a priority queue, and this thread is
161
+ # the only reader, so whatever timer is at the head of the
162
+ # queue now must have the same pop time, or a closer one, as
163
+ # when we peeked).
164
+ task = synchronize { @queue.pop }
165
+ task.executor.post { task.process_task }
166
+ else
167
+ @condition.wait([diff, 60].min)
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,20 @@
1
+ require 'concurrent/executor/abstract_executor_service'
2
+ require 'concurrent/executor/cached_thread_pool'
3
+ require 'concurrent/executor/executor_service'
4
+ require 'concurrent/executor/fixed_thread_pool'
5
+ require 'concurrent/executor/immediate_executor'
6
+ require 'concurrent/executor/indirect_immediate_executor'
7
+ require 'concurrent/executor/java_executor_service'
8
+ require 'concurrent/executor/java_single_thread_executor'
9
+ require 'concurrent/executor/java_thread_pool_executor'
10
+ require 'concurrent/executor/ruby_executor_service'
11
+ require 'concurrent/executor/ruby_single_thread_executor'
12
+ require 'concurrent/executor/ruby_thread_pool_executor'
13
+ require 'concurrent/executor/cached_thread_pool'
14
+ require 'concurrent/executor/safe_task_executor'
15
+ require 'concurrent/executor/serial_executor_service'
16
+ require 'concurrent/executor/serialized_execution'
17
+ require 'concurrent/executor/serialized_execution_delegator'
18
+ require 'concurrent/executor/single_thread_executor'
19
+ require 'concurrent/executor/thread_pool_executor'
20
+ require 'concurrent/executor/timer_set'
@@ -0,0 +1,141 @@
1
+ require 'thread'
2
+ require 'concurrent/constants'
3
+ require 'concurrent/errors'
4
+ require 'concurrent/ivar'
5
+ require 'concurrent/executor/safe_task_executor'
6
+
7
+ require 'concurrent/options'
8
+
9
+ # TODO (pitr-ch 14-Mar-2017): deprecate, Future, Promise, etc.
10
+
11
+
12
+ module Concurrent
13
+
14
+ # {include:file:docs-source/future.md}
15
+ #
16
+ # @!macro copy_options
17
+ #
18
+ # @see http://ruby-doc.org/stdlib-2.1.1/libdoc/observer/rdoc/Observable.html Ruby Observable module
19
+ # @see http://clojuredocs.org/clojure_core/clojure.core/future Clojure's future function
20
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html java.util.concurrent.Future
21
+ class Future < IVar
22
+
23
+ # Create a new `Future` in the `:unscheduled` state.
24
+ #
25
+ # @yield the asynchronous operation to perform
26
+ #
27
+ # @!macro executor_and_deref_options
28
+ #
29
+ # @option opts [object, Array] :args zero or more arguments to be passed the task
30
+ # block on execution
31
+ #
32
+ # @raise [ArgumentError] if no block is given
33
+ def initialize(opts = {}, &block)
34
+ raise ArgumentError.new('no block given') unless block_given?
35
+ super(NULL, opts.merge(__task_from_block__: block), &nil)
36
+ end
37
+
38
+ # Execute an `:unscheduled` `Future`. Immediately sets the state to `:pending` and
39
+ # passes the block to a new thread/thread pool for eventual execution.
40
+ # Does nothing if the `Future` is in any state other than `:unscheduled`.
41
+ #
42
+ # @return [Future] a reference to `self`
43
+ #
44
+ # @example Instance and execute in separate steps
45
+ # future = Concurrent::Future.new{ sleep(1); 42 }
46
+ # future.state #=> :unscheduled
47
+ # future.execute
48
+ # future.state #=> :pending
49
+ #
50
+ # @example Instance and execute in one line
51
+ # future = Concurrent::Future.new{ sleep(1); 42 }.execute
52
+ # future.state #=> :pending
53
+ def execute
54
+ if compare_and_set_state(:pending, :unscheduled)
55
+ @executor.post{ safe_execute(@task, @args) }
56
+ self
57
+ end
58
+ end
59
+
60
+ # Create a new `Future` object with the given block, execute it, and return the
61
+ # `:pending` object.
62
+ #
63
+ # @yield the asynchronous operation to perform
64
+ #
65
+ # @!macro executor_and_deref_options
66
+ #
67
+ # @option opts [object, Array] :args zero or more arguments to be passed the task
68
+ # block on execution
69
+ #
70
+ # @raise [ArgumentError] if no block is given
71
+ #
72
+ # @return [Future] the newly created `Future` in the `:pending` state
73
+ #
74
+ # @example
75
+ # future = Concurrent::Future.execute{ sleep(1); 42 }
76
+ # future.state #=> :pending
77
+ def self.execute(opts = {}, &block)
78
+ Future.new(opts, &block).execute
79
+ end
80
+
81
+ # @!macro ivar_set_method
82
+ def set(value = NULL, &block)
83
+ check_for_block_or_value!(block_given?, value)
84
+ synchronize do
85
+ if @state != :unscheduled
86
+ raise MultipleAssignmentError
87
+ else
88
+ @task = block || Proc.new { value }
89
+ end
90
+ end
91
+ execute
92
+ end
93
+
94
+ # Attempt to cancel the operation if it has not already processed.
95
+ # The operation can only be cancelled while still `pending`. It cannot
96
+ # be cancelled once it has begun processing or has completed.
97
+ #
98
+ # @return [Boolean] was the operation successfully cancelled.
99
+ def cancel
100
+ if compare_and_set_state(:cancelled, :pending)
101
+ complete(false, nil, CancelledOperationError.new)
102
+ true
103
+ else
104
+ false
105
+ end
106
+ end
107
+
108
+ # Has the operation been successfully cancelled?
109
+ #
110
+ # @return [Boolean]
111
+ def cancelled?
112
+ state == :cancelled
113
+ end
114
+
115
+ # Wait the given number of seconds for the operation to complete.
116
+ # On timeout attempt to cancel the operation.
117
+ #
118
+ # @param [Numeric] timeout the maximum time in seconds to wait.
119
+ # @return [Boolean] true if the operation completed before the timeout
120
+ # else false
121
+ def wait_or_cancel(timeout)
122
+ wait(timeout)
123
+ if complete?
124
+ true
125
+ else
126
+ cancel
127
+ false
128
+ end
129
+ end
130
+
131
+ protected
132
+
133
+ def ns_initialize(value, opts)
134
+ super
135
+ @state = :unscheduled
136
+ @task = opts[:__task_from_block__]
137
+ @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
138
+ @args = get_arguments_from(opts)
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,59 @@
1
+ require 'concurrent/utility/engine'
2
+ require 'concurrent/thread_safe/util'
3
+
4
+ module Concurrent
5
+
6
+ # @!macro concurrent_hash
7
+ #
8
+ # A thread-safe subclass of Hash. This version locks against the object
9
+ # itself for every method call, ensuring only one thread can be reading
10
+ # or writing at a time. This includes iteration methods like `#each`,
11
+ # which takes the lock repeatedly when reading an item.
12
+ #
13
+ # @see http://ruby-doc.org/core/Hash.html Ruby standard library `Hash`
14
+
15
+ # @!macro internal_implementation_note
16
+ HashImplementation = case
17
+ when Concurrent.on_cruby?
18
+ # Hash is thread-safe in practice because CRuby runs
19
+ # threads one at a time and does not do context
20
+ # switching during the execution of C functions.
21
+ ::Hash
22
+
23
+ when Concurrent.on_jruby?
24
+ require 'jruby/synchronized'
25
+
26
+ class JRubyHash < ::Hash
27
+ include JRuby::Synchronized
28
+ end
29
+ JRubyHash
30
+
31
+ when Concurrent.on_rbx?
32
+ require 'monitor'
33
+ require 'concurrent/thread_safe/util/data_structures'
34
+
35
+ class RbxHash < ::Hash
36
+ end
37
+ ThreadSafe::Util.make_synchronized_on_rbx RbxHash
38
+ RbxHash
39
+
40
+ when Concurrent.on_truffleruby?
41
+ require 'concurrent/thread_safe/util/data_structures'
42
+
43
+ class TruffleRubyHash < ::Hash
44
+ end
45
+
46
+ ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubyHash
47
+ TruffleRubyHash
48
+
49
+ else
50
+ warn 'Possibly unsupported Ruby implementation'
51
+ ::Hash
52
+ end
53
+ private_constant :HashImplementation
54
+
55
+ # @!macro concurrent_hash
56
+ class Hash < HashImplementation
57
+ end
58
+
59
+ end
@@ -0,0 +1,101 @@
1
+ require 'concurrent/synchronization/abstract_struct'
2
+ require 'concurrent/synchronization'
3
+
4
+ module Concurrent
5
+
6
+ # A thread-safe, immutable variation of Ruby's standard `Struct`.
7
+ #
8
+ # @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
9
+ module ImmutableStruct
10
+ include Synchronization::AbstractStruct
11
+
12
+ def self.included(base)
13
+ base.safe_initialization!
14
+ end
15
+
16
+ # @!macro struct_values
17
+ def values
18
+ ns_values
19
+ end
20
+
21
+ alias_method :to_a, :values
22
+
23
+ # @!macro struct_values_at
24
+ def values_at(*indexes)
25
+ ns_values_at(indexes)
26
+ end
27
+
28
+ # @!macro struct_inspect
29
+ def inspect
30
+ ns_inspect
31
+ end
32
+
33
+ alias_method :to_s, :inspect
34
+
35
+ # @!macro struct_merge
36
+ def merge(other, &block)
37
+ ns_merge(other, &block)
38
+ end
39
+
40
+ # @!macro struct_to_h
41
+ def to_h
42
+ ns_to_h
43
+ end
44
+
45
+ # @!macro struct_get
46
+ def [](member)
47
+ ns_get(member)
48
+ end
49
+
50
+ # @!macro struct_equality
51
+ def ==(other)
52
+ ns_equality(other)
53
+ end
54
+
55
+ # @!macro struct_each
56
+ def each(&block)
57
+ return enum_for(:each) unless block_given?
58
+ ns_each(&block)
59
+ end
60
+
61
+ # @!macro struct_each_pair
62
+ def each_pair(&block)
63
+ return enum_for(:each_pair) unless block_given?
64
+ ns_each_pair(&block)
65
+ end
66
+
67
+ # @!macro struct_select
68
+ def select(&block)
69
+ return enum_for(:select) unless block_given?
70
+ ns_select(&block)
71
+ end
72
+
73
+ private
74
+
75
+ # @!visibility private
76
+ def initialize_copy(original)
77
+ super(original)
78
+ ns_initialize_copy
79
+ end
80
+
81
+ # @!macro struct_new
82
+ def self.new(*args, &block)
83
+ clazz_name = nil
84
+ if args.length == 0
85
+ raise ArgumentError.new('wrong number of arguments (0 for 1+)')
86
+ elsif args.length > 0 && args.first.is_a?(String)
87
+ clazz_name = args.shift
88
+ end
89
+ FACTORY.define_struct(clazz_name, args, &block)
90
+ end
91
+
92
+ FACTORY = Class.new(Synchronization::LockableObject) do
93
+ def define_struct(name, members, &block)
94
+ synchronize do
95
+ Synchronization::AbstractStruct.define_struct_class(ImmutableStruct, Synchronization::Object, name, members, &block)
96
+ end
97
+ end
98
+ end.new
99
+ private_constant :FACTORY
100
+ end
101
+ end