concurrent-ruby 1.1.5

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 (143) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +478 -0
  3. data/Gemfile +41 -0
  4. data/LICENSE.md +23 -0
  5. data/README.md +381 -0
  6. data/Rakefile +327 -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 +159 -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.rb +1 -0
  23. data/lib/concurrent.rb +134 -0
  24. data/lib/concurrent/agent.rb +587 -0
  25. data/lib/concurrent/array.rb +66 -0
  26. data/lib/concurrent/async.rb +459 -0
  27. data/lib/concurrent/atom.rb +222 -0
  28. data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  29. data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
  30. data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
  31. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  32. data/lib/concurrent/atomic/atomic_reference.rb +204 -0
  33. data/lib/concurrent/atomic/count_down_latch.rb +100 -0
  34. data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
  35. data/lib/concurrent/atomic/event.rb +109 -0
  36. data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
  37. data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
  38. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  39. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  40. data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  41. data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
  42. data/lib/concurrent/atomic/read_write_lock.rb +254 -0
  43. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
  44. data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
  45. data/lib/concurrent/atomic/semaphore.rb +145 -0
  46. data/lib/concurrent/atomic/thread_local_var.rb +104 -0
  47. data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  48. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  49. data/lib/concurrent/atomics.rb +10 -0
  50. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  51. data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  52. data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  53. data/lib/concurrent/collection/lock_free_stack.rb +158 -0
  54. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  55. data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
  56. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  57. data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  58. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  59. data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
  60. data/lib/concurrent/concern/deprecation.rb +34 -0
  61. data/lib/concurrent/concern/dereferenceable.rb +73 -0
  62. data/lib/concurrent/concern/logging.rb +32 -0
  63. data/lib/concurrent/concern/obligation.rb +220 -0
  64. data/lib/concurrent/concern/observable.rb +110 -0
  65. data/lib/concurrent/concurrent_ruby.jar +0 -0
  66. data/lib/concurrent/configuration.rb +184 -0
  67. data/lib/concurrent/constants.rb +8 -0
  68. data/lib/concurrent/dataflow.rb +81 -0
  69. data/lib/concurrent/delay.rb +199 -0
  70. data/lib/concurrent/errors.rb +69 -0
  71. data/lib/concurrent/exchanger.rb +352 -0
  72. data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
  73. data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
  74. data/lib/concurrent/executor/executor_service.rb +185 -0
  75. data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
  76. data/lib/concurrent/executor/immediate_executor.rb +66 -0
  77. data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
  78. data/lib/concurrent/executor/java_executor_service.rb +91 -0
  79. data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
  80. data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
  81. data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
  82. data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
  83. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
  84. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  85. data/lib/concurrent/executor/serial_executor_service.rb +34 -0
  86. data/lib/concurrent/executor/serialized_execution.rb +107 -0
  87. data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
  88. data/lib/concurrent/executor/simple_executor_service.rb +100 -0
  89. data/lib/concurrent/executor/single_thread_executor.rb +56 -0
  90. data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
  91. data/lib/concurrent/executor/timer_set.rb +173 -0
  92. data/lib/concurrent/executors.rb +20 -0
  93. data/lib/concurrent/future.rb +141 -0
  94. data/lib/concurrent/hash.rb +59 -0
  95. data/lib/concurrent/immutable_struct.rb +93 -0
  96. data/lib/concurrent/ivar.rb +207 -0
  97. data/lib/concurrent/map.rb +337 -0
  98. data/lib/concurrent/maybe.rb +229 -0
  99. data/lib/concurrent/mutable_struct.rb +229 -0
  100. data/lib/concurrent/mvar.rb +242 -0
  101. data/lib/concurrent/options.rb +42 -0
  102. data/lib/concurrent/promise.rb +579 -0
  103. data/lib/concurrent/promises.rb +2167 -0
  104. data/lib/concurrent/re_include.rb +58 -0
  105. data/lib/concurrent/scheduled_task.rb +318 -0
  106. data/lib/concurrent/set.rb +66 -0
  107. data/lib/concurrent/settable_struct.rb +129 -0
  108. data/lib/concurrent/synchronization.rb +30 -0
  109. data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  110. data/lib/concurrent/synchronization/abstract_object.rb +24 -0
  111. data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
  112. data/lib/concurrent/synchronization/condition.rb +60 -0
  113. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  114. data/lib/concurrent/synchronization/jruby_object.rb +45 -0
  115. data/lib/concurrent/synchronization/lock.rb +36 -0
  116. data/lib/concurrent/synchronization/lockable_object.rb +74 -0
  117. data/lib/concurrent/synchronization/mri_object.rb +44 -0
  118. data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
  119. data/lib/concurrent/synchronization/object.rb +183 -0
  120. data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
  121. data/lib/concurrent/synchronization/rbx_object.rb +49 -0
  122. data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
  123. data/lib/concurrent/synchronization/volatile.rb +36 -0
  124. data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  125. data/lib/concurrent/thread_safe/util.rb +16 -0
  126. data/lib/concurrent/thread_safe/util/adder.rb +74 -0
  127. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  128. data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
  129. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  130. data/lib/concurrent/thread_safe/util/striped64.rb +246 -0
  131. data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
  132. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  133. data/lib/concurrent/timer_task.rb +334 -0
  134. data/lib/concurrent/tuple.rb +86 -0
  135. data/lib/concurrent/tvar.rb +258 -0
  136. data/lib/concurrent/utility/at_exit.rb +97 -0
  137. data/lib/concurrent/utility/engine.rb +56 -0
  138. data/lib/concurrent/utility/monotonic_time.rb +58 -0
  139. data/lib/concurrent/utility/native_extension_loader.rb +79 -0
  140. data/lib/concurrent/utility/native_integer.rb +53 -0
  141. data/lib/concurrent/utility/processor_counter.rb +158 -0
  142. data/lib/concurrent/version.rb +3 -0
  143. metadata +193 -0
@@ -0,0 +1,28 @@
1
+ require 'delegate'
2
+ require 'concurrent/executor/serial_executor_service'
3
+ require 'concurrent/executor/serialized_execution'
4
+
5
+ module Concurrent
6
+
7
+ # A wrapper/delegator for any `ExecutorService` that
8
+ # guarantees serialized execution of tasks.
9
+ #
10
+ # @see [SimpleDelegator](http://www.ruby-doc.org/stdlib-2.1.2/libdoc/delegate/rdoc/SimpleDelegator.html)
11
+ # @see Concurrent::SerializedExecution
12
+ class SerializedExecutionDelegator < SimpleDelegator
13
+ include SerialExecutorService
14
+
15
+ def initialize(executor)
16
+ @executor = executor
17
+ @serializer = SerializedExecution.new
18
+ super(executor)
19
+ end
20
+
21
+ # @!macro executor_service_method_post
22
+ def post(*args, &task)
23
+ raise ArgumentError.new('no block given') unless block_given?
24
+ return false unless running?
25
+ @serializer.post(@executor, *args, &task)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,100 @@
1
+ require 'concurrent/atomics'
2
+ require 'concurrent/executor/executor_service'
3
+
4
+ module Concurrent
5
+
6
+ # An executor service in which every operation spawns a new,
7
+ # independently operating thread.
8
+ #
9
+ # This is perhaps the most inefficient executor service in this
10
+ # library. It exists mainly for testing an debugging. Thread creation
11
+ # and management is expensive in Ruby and this executor performs no
12
+ # resource pooling. This can be very beneficial during testing and
13
+ # debugging because it decouples the using code from the underlying
14
+ # executor implementation. In production this executor will likely
15
+ # lead to suboptimal performance.
16
+ #
17
+ # @note Intended for use primarily in testing and debugging.
18
+ class SimpleExecutorService < RubyExecutorService
19
+
20
+ # @!macro executor_service_method_post
21
+ def self.post(*args)
22
+ raise ArgumentError.new('no block given') unless block_given?
23
+ Thread.new(*args) do
24
+ Thread.current.abort_on_exception = false
25
+ yield(*args)
26
+ end
27
+ true
28
+ end
29
+
30
+ # @!macro executor_service_method_left_shift
31
+ def self.<<(task)
32
+ post(&task)
33
+ self
34
+ end
35
+
36
+ # @!macro executor_service_method_post
37
+ def post(*args, &task)
38
+ raise ArgumentError.new('no block given') unless block_given?
39
+ return false unless running?
40
+ @count.increment
41
+ Thread.new(*args) do
42
+ Thread.current.abort_on_exception = false
43
+ begin
44
+ yield(*args)
45
+ ensure
46
+ @count.decrement
47
+ @stopped.set if @running.false? && @count.value == 0
48
+ end
49
+ end
50
+ end
51
+
52
+ # @!macro executor_service_method_left_shift
53
+ def <<(task)
54
+ post(&task)
55
+ self
56
+ end
57
+
58
+ # @!macro executor_service_method_running_question
59
+ def running?
60
+ @running.true?
61
+ end
62
+
63
+ # @!macro executor_service_method_shuttingdown_question
64
+ def shuttingdown?
65
+ @running.false? && ! @stopped.set?
66
+ end
67
+
68
+ # @!macro executor_service_method_shutdown_question
69
+ def shutdown?
70
+ @stopped.set?
71
+ end
72
+
73
+ # @!macro executor_service_method_shutdown
74
+ def shutdown
75
+ @running.make_false
76
+ @stopped.set if @count.value == 0
77
+ true
78
+ end
79
+
80
+ # @!macro executor_service_method_kill
81
+ def kill
82
+ @running.make_false
83
+ @stopped.set
84
+ true
85
+ end
86
+
87
+ # @!macro executor_service_method_wait_for_termination
88
+ def wait_for_termination(timeout = nil)
89
+ @stopped.wait(timeout)
90
+ end
91
+
92
+ private
93
+
94
+ def ns_initialize
95
+ @running = Concurrent::AtomicBoolean.new(true)
96
+ @stopped = Concurrent::Event.new
97
+ @count = Concurrent::AtomicFixnum.new(0)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,56 @@
1
+ require 'concurrent/executor/ruby_single_thread_executor'
2
+
3
+ module Concurrent
4
+
5
+ if Concurrent.on_jruby?
6
+ require 'concurrent/executor/java_single_thread_executor'
7
+ end
8
+
9
+ SingleThreadExecutorImplementation = case
10
+ when Concurrent.on_jruby?
11
+ JavaSingleThreadExecutor
12
+ else
13
+ RubySingleThreadExecutor
14
+ end
15
+ private_constant :SingleThreadExecutorImplementation
16
+
17
+ # @!macro single_thread_executor
18
+ #
19
+ # A thread pool with a single thread an unlimited queue. Should the thread
20
+ # die for any reason it will be removed and replaced, thus ensuring that
21
+ # the executor will always remain viable and available to process jobs.
22
+ #
23
+ # A common pattern for background processing is to create a single thread
24
+ # on which an infinite loop is run. The thread's loop blocks on an input
25
+ # source (perhaps blocking I/O or a queue) and processes each input as it
26
+ # is received. This pattern has several issues. The thread itself is highly
27
+ # susceptible to errors during processing. Also, the thread itself must be
28
+ # constantly monitored and restarted should it die. `SingleThreadExecutor`
29
+ # encapsulates all these bahaviors. The task processor is highly resilient
30
+ # to errors from within tasks. Also, should the thread die it will
31
+ # automatically be restarted.
32
+ #
33
+ # The API and behavior of this class are based on Java's `SingleThreadExecutor`.
34
+ #
35
+ # @!macro abstract_executor_service_public_api
36
+ class SingleThreadExecutor < SingleThreadExecutorImplementation
37
+
38
+ # @!macro single_thread_executor_method_initialize
39
+ #
40
+ # Create a new thread pool.
41
+ #
42
+ # @option opts [Symbol] :fallback_policy (:discard) the policy for handling new
43
+ # tasks that are received when the queue size has reached
44
+ # `max_queue` or the executor has shut down
45
+ #
46
+ # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
47
+ # in `FALLBACK_POLICIES`
48
+ #
49
+ # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
50
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
51
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
52
+
53
+ # @!method initialize(opts = {})
54
+ # @!macro single_thread_executor_method_initialize
55
+ end
56
+ end
@@ -0,0 +1,87 @@
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
+ #
77
+ # @raise [ArgumentError] if `:max_threads` is less than one
78
+ # @raise [ArgumentError] if `:min_threads` is less than zero
79
+ # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
80
+ # in `FALLBACK_POLICIES`
81
+ #
82
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
83
+
84
+ # @!method initialize(opts = {})
85
+ # @!macro thread_pool_executor_method_initialize
86
+ end
87
+ end
@@ -0,0 +1,173 @@
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
+ self.auto_terminate = opts.fetch(:auto_terminate, true)
81
+ end
82
+
83
+ # Post the task to the internal queue.
84
+ #
85
+ # @note This is intended as a callback method from ScheduledTask
86
+ # only. It is not intended to be used directly. Post a task
87
+ # by using the `SchedulesTask#execute` method.
88
+ #
89
+ # @!visibility private
90
+ def post_task(task)
91
+ synchronize { ns_post_task(task) }
92
+ end
93
+
94
+ # @!visibility private
95
+ def ns_post_task(task)
96
+ return false unless ns_running?
97
+ ns_reset_if_forked
98
+ if (task.initial_delay) <= 0.01
99
+ task.executor.post { task.process_task }
100
+ else
101
+ @queue.push(task)
102
+ # only post the process method when the queue is empty
103
+ @timer_executor.post(&method(:process_tasks)) if @queue.length == 1
104
+ @condition.set
105
+ end
106
+ true
107
+ end
108
+
109
+ # Remove the given task from the queue.
110
+ #
111
+ # @note This is intended as a callback method from `ScheduledTask`
112
+ # only. It is not intended to be used directly. Cancel a task
113
+ # by using the `ScheduledTask#cancel` method.
114
+ #
115
+ # @!visibility private
116
+ def remove_task(task)
117
+ synchronize { @queue.delete(task) }
118
+ end
119
+
120
+ # `ExecutorService` callback called during shutdown.
121
+ #
122
+ # @!visibility private
123
+ def ns_shutdown_execution
124
+ ns_reset_if_forked
125
+ @queue.clear
126
+ @timer_executor.kill
127
+ stopped_event.set
128
+ end
129
+
130
+ def ns_reset_if_forked
131
+ if $$ != @ruby_pid
132
+ @queue.clear
133
+ @condition.reset
134
+ @ruby_pid = $$
135
+ end
136
+ end
137
+
138
+ # Run a loop and execute tasks in the scheduled order and at the approximate
139
+ # scheduled time. If no tasks remain the thread will exit gracefully so that
140
+ # garbage collection can occur. If there are no ready tasks it will sleep
141
+ # for up to 60 seconds waiting for the next scheduled task.
142
+ #
143
+ # @!visibility private
144
+ def process_tasks
145
+ loop do
146
+ task = synchronize { @condition.reset; @queue.peek }
147
+ break unless task
148
+
149
+ now = Concurrent.monotonic_time
150
+ diff = task.schedule_time - now
151
+
152
+ if diff <= 0
153
+ # We need to remove the task from the queue before passing
154
+ # it to the executor, to avoid race conditions where we pass
155
+ # the peek'ed task to the executor and then pop a different
156
+ # one that's been added in the meantime.
157
+ #
158
+ # Note that there's no race condition between the peek and
159
+ # this pop - this pop could retrieve a different task from
160
+ # the peek, but that task would be due to fire now anyway
161
+ # (because @queue is a priority queue, and this thread is
162
+ # the only reader, so whatever timer is at the head of the
163
+ # queue now must have the same pop time, or a closer one, as
164
+ # when we peeked).
165
+ task = synchronize { @queue.pop }
166
+ task.executor.post { task.process_task }
167
+ else
168
+ @condition.wait([diff, 60].min)
169
+ end
170
+ end
171
+ end
172
+ end
173
+ 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'