concurrent-ruby 0.7.0-java

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 (112) hide show
  1. data/LICENSE.txt +21 -0
  2. data/README.md +217 -0
  3. data/lib/concurrent.rb +45 -0
  4. data/lib/concurrent/actor.rb +104 -0
  5. data/lib/concurrent/actor/behaviour.rb +70 -0
  6. data/lib/concurrent/actor/behaviour/abstract.rb +48 -0
  7. data/lib/concurrent/actor/behaviour/awaits.rb +21 -0
  8. data/lib/concurrent/actor/behaviour/buffer.rb +54 -0
  9. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +12 -0
  10. data/lib/concurrent/actor/behaviour/executes_context.rb +18 -0
  11. data/lib/concurrent/actor/behaviour/linking.rb +42 -0
  12. data/lib/concurrent/actor/behaviour/pausing.rb +77 -0
  13. data/lib/concurrent/actor/behaviour/removes_child.rb +16 -0
  14. data/lib/concurrent/actor/behaviour/sets_results.rb +36 -0
  15. data/lib/concurrent/actor/behaviour/supervised.rb +58 -0
  16. data/lib/concurrent/actor/behaviour/supervising.rb +34 -0
  17. data/lib/concurrent/actor/behaviour/terminates_children.rb +13 -0
  18. data/lib/concurrent/actor/behaviour/termination.rb +54 -0
  19. data/lib/concurrent/actor/context.rb +153 -0
  20. data/lib/concurrent/actor/core.rb +213 -0
  21. data/lib/concurrent/actor/default_dead_letter_handler.rb +9 -0
  22. data/lib/concurrent/actor/envelope.rb +41 -0
  23. data/lib/concurrent/actor/errors.rb +27 -0
  24. data/lib/concurrent/actor/internal_delegations.rb +49 -0
  25. data/lib/concurrent/actor/public_delegations.rb +40 -0
  26. data/lib/concurrent/actor/reference.rb +81 -0
  27. data/lib/concurrent/actor/root.rb +37 -0
  28. data/lib/concurrent/actor/type_check.rb +48 -0
  29. data/lib/concurrent/actor/utils.rb +10 -0
  30. data/lib/concurrent/actor/utils/ad_hoc.rb +21 -0
  31. data/lib/concurrent/actor/utils/balancer.rb +40 -0
  32. data/lib/concurrent/actor/utils/broadcast.rb +52 -0
  33. data/lib/concurrent/actor/utils/pool.rb +59 -0
  34. data/lib/concurrent/actress.rb +3 -0
  35. data/lib/concurrent/agent.rb +230 -0
  36. data/lib/concurrent/async.rb +284 -0
  37. data/lib/concurrent/atomic.rb +91 -0
  38. data/lib/concurrent/atomic/atomic_boolean.rb +202 -0
  39. data/lib/concurrent/atomic/atomic_fixnum.rb +203 -0
  40. data/lib/concurrent/atomic/condition.rb +67 -0
  41. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +118 -0
  42. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +117 -0
  43. data/lib/concurrent/atomic/count_down_latch.rb +116 -0
  44. data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
  45. data/lib/concurrent/atomic/event.rb +98 -0
  46. data/lib/concurrent/atomic/synchronization.rb +51 -0
  47. data/lib/concurrent/atomic/thread_local_var.rb +82 -0
  48. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +8 -0
  49. data/lib/concurrent/atomic_reference/direct_update.rb +50 -0
  50. data/lib/concurrent/atomic_reference/jruby.rb +14 -0
  51. data/lib/concurrent/atomic_reference/mutex_atomic.rb +77 -0
  52. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +25 -0
  53. data/lib/concurrent/atomic_reference/rbx.rb +19 -0
  54. data/lib/concurrent/atomic_reference/ruby.rb +37 -0
  55. data/lib/concurrent/atomics.rb +11 -0
  56. data/lib/concurrent/channel/buffered_channel.rb +85 -0
  57. data/lib/concurrent/channel/channel.rb +41 -0
  58. data/lib/concurrent/channel/unbuffered_channel.rb +35 -0
  59. data/lib/concurrent/channel/waitable_list.rb +40 -0
  60. data/lib/concurrent/channels.rb +5 -0
  61. data/lib/concurrent/collection/blocking_ring_buffer.rb +71 -0
  62. data/lib/concurrent/collection/priority_queue.rb +305 -0
  63. data/lib/concurrent/collection/ring_buffer.rb +59 -0
  64. data/lib/concurrent/collections.rb +3 -0
  65. data/lib/concurrent/configuration.rb +161 -0
  66. data/lib/concurrent/dataflow.rb +108 -0
  67. data/lib/concurrent/delay.rb +104 -0
  68. data/lib/concurrent/dereferenceable.rb +101 -0
  69. data/lib/concurrent/errors.rb +30 -0
  70. data/lib/concurrent/exchanger.rb +34 -0
  71. data/lib/concurrent/executor/cached_thread_pool.rb +44 -0
  72. data/lib/concurrent/executor/executor.rb +282 -0
  73. data/lib/concurrent/executor/fixed_thread_pool.rb +33 -0
  74. data/lib/concurrent/executor/immediate_executor.rb +65 -0
  75. data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
  76. data/lib/concurrent/executor/java_fixed_thread_pool.rb +41 -0
  77. data/lib/concurrent/executor/java_single_thread_executor.rb +22 -0
  78. data/lib/concurrent/executor/java_thread_pool_executor.rb +180 -0
  79. data/lib/concurrent/executor/per_thread_executor.rb +100 -0
  80. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
  81. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +32 -0
  82. data/lib/concurrent/executor/ruby_single_thread_executor.rb +74 -0
  83. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +288 -0
  84. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +72 -0
  85. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  86. data/lib/concurrent/executor/serialized_execution.rb +126 -0
  87. data/lib/concurrent/executor/single_thread_executor.rb +35 -0
  88. data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
  89. data/lib/concurrent/executor/timer_set.rb +143 -0
  90. data/lib/concurrent/executors.rb +9 -0
  91. data/lib/concurrent/future.rb +125 -0
  92. data/lib/concurrent/ivar.rb +111 -0
  93. data/lib/concurrent/lazy_register.rb +58 -0
  94. data/lib/concurrent/logging.rb +17 -0
  95. data/lib/concurrent/mvar.rb +200 -0
  96. data/lib/concurrent/obligation.rb +171 -0
  97. data/lib/concurrent/observable.rb +40 -0
  98. data/lib/concurrent/options_parser.rb +48 -0
  99. data/lib/concurrent/promise.rb +170 -0
  100. data/lib/concurrent/scheduled_task.rb +79 -0
  101. data/lib/concurrent/timer_task.rb +341 -0
  102. data/lib/concurrent/tvar.rb +248 -0
  103. data/lib/concurrent/utilities.rb +3 -0
  104. data/lib/concurrent/utility/processor_count.rb +152 -0
  105. data/lib/concurrent/utility/timeout.rb +35 -0
  106. data/lib/concurrent/utility/timer.rb +21 -0
  107. data/lib/concurrent/version.rb +3 -0
  108. data/lib/concurrent_ruby.rb +1 -0
  109. data/lib/concurrent_ruby_ext.jar +0 -0
  110. data/lib/concurrent_ruby_ext.so +0 -0
  111. data/lib/extension_helper.rb +28 -0
  112. metadata +163 -0
@@ -0,0 +1,248 @@
1
+ require 'set'
2
+
3
+ module Concurrent
4
+
5
+ # A `TVar` is a transactional variable - a single-element container that
6
+ # is used as part of a transaction - see `Concurrent::atomically`.
7
+ class TVar
8
+
9
+ # Create a new `TVar` with an initial value.
10
+ def initialize(value)
11
+ @value = value
12
+ @version = 0
13
+ @lock = Mutex.new
14
+ end
15
+
16
+ # Get the value of a `TVar`.
17
+ def value
18
+ Concurrent::atomically do
19
+ Transaction::current.read(self)
20
+ end
21
+ end
22
+
23
+ # Set the value of a `TVar`.
24
+ def value=(value)
25
+ Concurrent::atomically do
26
+ Transaction::current.write(self, value)
27
+ end
28
+ end
29
+
30
+ # @!visibility private
31
+ def unsafe_value # :nodoc:
32
+ @value
33
+ end
34
+
35
+ # @!visibility private
36
+ def unsafe_value=(value) # :nodoc:
37
+ @value = value
38
+ end
39
+
40
+ # @!visibility private
41
+ def unsafe_version # :nodoc:
42
+ @version
43
+ end
44
+
45
+ # @!visibility private
46
+ def unsafe_increment_version # :nodoc:
47
+ @version += 1
48
+ end
49
+
50
+ # @!visibility private
51
+ def unsafe_lock # :nodoc:
52
+ @lock
53
+ end
54
+
55
+ end
56
+
57
+ # Run a block that reads and writes `TVar`s as a single atomic transaction.
58
+ # With respect to the value of `TVar` objects, the transaction is atomic,
59
+ # in that it either happens or it does not, consistent, in that the `TVar`
60
+ # objects involved will never enter an illegal state, and isolated, in that
61
+ # transactions never interfere with each other. You may recognise these
62
+ # properties from database transactions.
63
+ #
64
+ # There are some very important and unusual semantics that you must be aware of:
65
+ #
66
+ # * Most importantly, the block that you pass to atomically may be executed more than once. In most cases your code should be free of side-effects, except for via TVar.
67
+ #
68
+ # * If an exception escapes an atomically block it will abort the transaction.
69
+ #
70
+ # * It is undefined behaviour to use callcc or Fiber with atomically.
71
+ #
72
+ # * If you create a new thread within an atomically, it will not be part of the transaction. Creating a thread counts as a side-effect.
73
+ #
74
+ # Transactions within transactions are flattened to a single transaction.
75
+ #
76
+ # @example
77
+ # a = new TVar(100_000)
78
+ # b = new TVar(100)
79
+ #
80
+ # Concurrent::atomically do
81
+ # a.value -= 10
82
+ # b.value += 10
83
+ # end
84
+ def atomically
85
+ raise ArgumentError.new('no block given') unless block_given?
86
+
87
+ # Get the current transaction
88
+
89
+ transaction = Transaction::current
90
+
91
+ # Are we not already in a transaction (not nested)?
92
+
93
+ if transaction.nil?
94
+ # New transaction
95
+
96
+ begin
97
+ # Retry loop
98
+
99
+ loop do
100
+
101
+ # Create a new transaction
102
+
103
+ transaction = Transaction.new
104
+ Transaction::current = transaction
105
+
106
+ # Run the block, aborting on exceptions
107
+
108
+ begin
109
+ result = yield
110
+ rescue Transaction::AbortError => e
111
+ transaction.abort
112
+ result = Transaction::ABORTED
113
+ rescue => e
114
+ transaction.abort
115
+ raise e
116
+ end
117
+ # If we can commit, break out of the loop
118
+
119
+ if result != Transaction::ABORTED
120
+ if transaction.commit
121
+ break result
122
+ end
123
+ end
124
+ end
125
+ ensure
126
+ # Clear the current transaction
127
+
128
+ Transaction::current = nil
129
+ end
130
+ else
131
+ # Nested transaction - flatten it and just run the block
132
+
133
+ yield
134
+ end
135
+ end
136
+
137
+ # Abort a currently running transaction - see `Concurrent::atomically`.
138
+ def abort_transaction
139
+ raise Transaction::AbortError.new
140
+ end
141
+
142
+ module_function :atomically, :abort_transaction
143
+
144
+ private
145
+
146
+ class Transaction
147
+
148
+ ABORTED = Object.new
149
+
150
+ ReadLogEntry = Struct.new(:tvar, :version)
151
+ UndoLogEntry = Struct.new(:tvar, :value)
152
+
153
+ AbortError = Class.new(StandardError)
154
+
155
+ def initialize
156
+ @write_set = Set.new
157
+ @read_log = []
158
+ @undo_log = []
159
+ end
160
+
161
+ def read(tvar)
162
+ Concurrent::abort_transaction unless valid?
163
+ @read_log.push(ReadLogEntry.new(tvar, tvar.unsafe_version))
164
+ tvar.unsafe_value
165
+ end
166
+
167
+ def write(tvar, value)
168
+ # Have we already written to this TVar?
169
+
170
+ unless @write_set.include? tvar
171
+ # Try to lock the TVar
172
+
173
+ unless tvar.unsafe_lock.try_lock
174
+ # Someone else is writing to this TVar - abort
175
+ Concurrent::abort_transaction
176
+ end
177
+
178
+ # We've locked it - add it to the write set
179
+
180
+ @write_set.add(tvar)
181
+
182
+ # If we previously wrote to it, check the version hasn't changed
183
+
184
+ @read_log.each do |log_entry|
185
+ if log_entry.tvar == tvar and tvar.unsafe_version > log_entry.version
186
+ Concurrent::abort_transaction
187
+ end
188
+ end
189
+ end
190
+
191
+ # Record the current value of the TVar so we can undo it later
192
+
193
+ @undo_log.push(UndoLogEntry.new(tvar, tvar.unsafe_value))
194
+
195
+ # Write the new value to the TVar
196
+
197
+ tvar.unsafe_value = value
198
+ end
199
+
200
+ def abort
201
+ @undo_log.each do |entry|
202
+ entry.tvar.unsafe_value = entry.value
203
+ end
204
+
205
+ unlock
206
+ end
207
+
208
+ def commit
209
+ return false unless valid?
210
+
211
+ @write_set.each do |tvar|
212
+ tvar.unsafe_increment_version
213
+ end
214
+
215
+ unlock
216
+
217
+ true
218
+ end
219
+
220
+ def valid?
221
+ @read_log.each do |log_entry|
222
+ unless @write_set.include? log_entry.tvar
223
+ if log_entry.tvar.unsafe_version > log_entry.version
224
+ return false
225
+ end
226
+ end
227
+ end
228
+
229
+ true
230
+ end
231
+
232
+ def unlock
233
+ @write_set.each do |tvar|
234
+ tvar.unsafe_lock.unlock
235
+ end
236
+ end
237
+
238
+ def self.current
239
+ Thread.current[:current_tvar_transaction]
240
+ end
241
+
242
+ def self.current=(transaction)
243
+ Thread.current[:current_tvar_transaction] = transaction
244
+ end
245
+
246
+ end
247
+
248
+ end
@@ -0,0 +1,3 @@
1
+ require 'concurrent/utility/processor_count'
2
+ require 'concurrent/utility/timeout'
3
+ require 'concurrent/utility/timer'
@@ -0,0 +1,152 @@
1
+ require 'rbconfig'
2
+ require 'concurrent/delay'
3
+ require 'concurrent/executor/immediate_executor'
4
+
5
+ module Concurrent
6
+
7
+ class ProcessorCounter
8
+ def initialize
9
+ immediate_executor = ImmediateExecutor.new
10
+ @processor_count = Delay.new(executor: immediate_executor) { compute_processor_count }
11
+ @physical_processor_count = Delay.new(executor: immediate_executor) { compute_physical_processor_count }
12
+ end
13
+
14
+ # Number of processors seen by the OS and used for process scheduling. For performance
15
+ # reasons the calculated value will be memoized on the first call.
16
+ #
17
+ # When running under JRuby the Java runtime call `java.lang.Runtime.getRuntime.availableProcessors`
18
+ # will be used. According to the Java documentation this "value may change
19
+ # during a particular invocation of the virtual machine... [applications]
20
+ # should therefore occasionally poll this property." Subsequently the result
21
+ # will NOT be memoized under JRuby.
22
+ #
23
+ # On Windows the Win32 API will be queried for the `NumberOfLogicalProcessors from Win32_Processor`.
24
+ # This will return the total number "logical processors for the current instance of the processor",
25
+ # which taked into account hyperthreading.
26
+ #
27
+ # * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
28
+ # * BSD: /sbin/sysctl
29
+ # * Cygwin: /proc/cpuinfo
30
+ # * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
31
+ # * HP-UX: /usr/sbin/ioscan
32
+ # * IRIX: /usr/sbin/sysconf
33
+ # * Linux: /proc/cpuinfo
34
+ # * Minix 3+: /proc/cpuinfo
35
+ # * Solaris: /usr/sbin/psrinfo
36
+ # * Tru64 UNIX: /usr/sbin/psrinfo
37
+ # * UnixWare: /usr/sbin/psrinfo
38
+ #
39
+ # @return [Integer] number of processors seen by the OS or Java runtime
40
+ #
41
+ # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
42
+ #
43
+ # @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors()
44
+ # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
45
+ def processor_count
46
+ @processor_count.value
47
+ end
48
+
49
+ # Number of physical processor cores on the current system. For performance reasons
50
+ # the calculated value will be memoized on the first call.
51
+ #
52
+ # On Windows the Win32 API will be queried for the `NumberOfCores from Win32_Processor`.
53
+ # This will return the total number "of cores for the current instance of the processor."
54
+ # On Unix-like operating systems either the `hwprefs` or `sysctl` utility will be called
55
+ # in a subshell and the returned value will be used. In the rare case where none of these
56
+ # methods work or an exception is raised the function will simply return 1.
57
+ #
58
+ # @return [Integer] number physical processor cores on the current system
59
+ #
60
+ # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
61
+ #
62
+ # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
63
+ # @see http://www.unix.com/man-page/osx/1/HWPREFS/
64
+ # @see http://linux.die.net/man/8/sysctl
65
+ def physical_processor_count
66
+ @physical_processor_count.value
67
+ end
68
+
69
+ private
70
+
71
+ def compute_processor_count
72
+ if RUBY_PLATFORM == 'java'
73
+ java.lang.Runtime.getRuntime.availableProcessors
74
+ else
75
+ os_name = RbConfig::CONFIG["target_os"]
76
+ if os_name =~ /mingw|mswin/
77
+ require 'win32ole'
78
+ result = WIN32OLE.connect("winmgmts://").ExecQuery(
79
+ "select NumberOfLogicalProcessors from Win32_Processor")
80
+ result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
81
+ elsif File.readable?("/proc/cpuinfo")
82
+ IO.read("/proc/cpuinfo").scan(/^processor/).size
83
+ elsif File.executable?("/usr/bin/hwprefs")
84
+ IO.popen("/usr/bin/hwprefs thread_count").read.to_i
85
+ elsif File.executable?("/usr/sbin/psrinfo")
86
+ IO.popen("/usr/sbin/psrinfo").read.scan(/^.*on-*line/).size
87
+ elsif File.executable?("/usr/sbin/ioscan")
88
+ IO.popen("/usr/sbin/ioscan -kC processor") do |out|
89
+ out.read.scan(/^.*processor/).size
90
+ end
91
+ elsif File.executable?("/usr/sbin/pmcycles")
92
+ IO.popen("/usr/sbin/pmcycles -m").read.count("\n")
93
+ elsif File.executable?("/usr/sbin/lsdev")
94
+ IO.popen("/usr/sbin/lsdev -Cc processor -S 1").read.count("\n")
95
+ elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i
96
+ IO.popen("/usr/sbin/sysconf NPROC_ONLN").read.to_i
97
+ elsif File.executable?("/usr/sbin/sysctl")
98
+ IO.popen("/usr/sbin/sysctl -n hw.ncpu").read.to_i
99
+ elsif File.executable?("/sbin/sysctl")
100
+ IO.popen("/sbin/sysctl -n hw.ncpu").read.to_i
101
+ else
102
+ 1
103
+ end
104
+ end
105
+ rescue
106
+ return 1
107
+ end
108
+
109
+ def compute_physical_processor_count
110
+ ppc = case RbConfig::CONFIG["target_os"]
111
+ when /darwin1/
112
+ IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
113
+ when /linux/
114
+ cores = {} # unique physical ID / core ID combinations
115
+ phy = 0
116
+ IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
117
+ if ln.start_with?("physical")
118
+ phy = ln[/\d+/]
119
+ elsif ln.start_with?("core")
120
+ cid = phy + ":" + ln[/\d+/]
121
+ cores[cid] = true if not cores[cid]
122
+ end
123
+ end
124
+ cores.count
125
+ when /mswin|mingw/
126
+ require 'win32ole'
127
+ result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
128
+ "select NumberOfCores from Win32_Processor")
129
+ result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
130
+ else
131
+ processor_count
132
+ end
133
+ # fall back to logical count if physical info is invalid
134
+ ppc > 0 ? ppc : processor_count
135
+ rescue
136
+ return 1
137
+ end
138
+ end
139
+
140
+ # create the default ProcessorCounter on load
141
+ @processor_counter = ProcessorCounter.new
142
+ singleton_class.send :attr_reader, :processor_counter
143
+
144
+ def self.processor_count
145
+ processor_counter.processor_count
146
+ end
147
+
148
+ def self.physical_processor_count
149
+ processor_counter.physical_processor_count
150
+ end
151
+
152
+ end
@@ -0,0 +1,35 @@
1
+ require 'rbconfig'
2
+ require 'thread'
3
+
4
+ require 'concurrent/errors'
5
+
6
+ module Concurrent
7
+
8
+ # Wait the given number of seconds for the block operation to complete.
9
+ #
10
+ # @param [Integer] seconds The number of seconds to wait
11
+ #
12
+ # @return [Object] The result of the block operation
13
+ #
14
+ # @raise [Concurrent::TimeoutError] when the block operation does not complete
15
+ # in the allotted number of seconds.
16
+ #
17
+ # @note This method is intended to be a simpler and more reliable replacement
18
+ # to the Ruby standard library `Timeout::timeout` method.
19
+ def timeout(seconds)
20
+
21
+ thread = Thread.new do
22
+ Thread.current[:result] = yield
23
+ end
24
+ success = thread.join(seconds)
25
+
26
+ if success
27
+ return thread[:result]
28
+ else
29
+ raise TimeoutError
30
+ end
31
+ ensure
32
+ Thread.kill(thread) unless thread.nil?
33
+ end
34
+ module_function :timeout
35
+ end
@@ -0,0 +1,21 @@
1
+ require 'concurrent/configuration'
2
+ require 'thread'
3
+
4
+ module Concurrent
5
+
6
+ # Perform the given operation asynchronously after the given number of seconds.
7
+ #
8
+ # @param [Fixnum] seconds the interval in seconds to wait before executing the task
9
+ #
10
+ # @yield the task to execute
11
+ #
12
+ # @return [Boolean] true
13
+ def timer(seconds, *args, &block)
14
+ raise ArgumentError.new('no block given') unless block_given?
15
+ raise ArgumentError.new('interval must be greater than or equal to zero') if seconds < 0
16
+
17
+ Concurrent.configuration.global_timer_set.post(seconds, *args, &block)
18
+ true
19
+ end
20
+ module_function :timer
21
+ end