concurrent-ruby 0.7.0-java

Sign up to get free protection for your applications and to get access to all the features.
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