concurrent-ruby 0.8.0.pre2-java → 0.9.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +114 -3
  3. data/README.md +111 -55
  4. data/lib/concurrent.rb +90 -14
  5. data/lib/concurrent/async.rb +143 -51
  6. data/lib/concurrent/atom.rb +131 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +34 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +27 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +234 -109
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +19 -7
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  46. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  47. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  48. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  49. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  50. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  51. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  52. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  53. data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
  54. data/lib/concurrent/executor/timer_set.rb +96 -84
  55. data/lib/concurrent/executors.rb +1 -1
  56. data/lib/concurrent/future.rb +71 -38
  57. data/lib/concurrent/immutable_struct.rb +89 -0
  58. data/lib/concurrent/ivar.rb +152 -60
  59. data/lib/concurrent/lazy_register.rb +40 -20
  60. data/lib/concurrent/maybe.rb +226 -0
  61. data/lib/concurrent/mutable_struct.rb +227 -0
  62. data/lib/concurrent/mvar.rb +44 -43
  63. data/lib/concurrent/promise.rb +229 -136
  64. data/lib/concurrent/scheduled_task.rb +341 -43
  65. data/lib/concurrent/settable_struct.rb +127 -0
  66. data/lib/concurrent/synchronization.rb +17 -0
  67. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  68. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  69. data/lib/concurrent/synchronization/condition.rb +53 -0
  70. data/lib/concurrent/synchronization/java_object.rb +34 -0
  71. data/lib/concurrent/synchronization/lock.rb +32 -0
  72. data/lib/concurrent/synchronization/monitor_object.rb +26 -0
  73. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  74. data/lib/concurrent/synchronization/object.rb +78 -0
  75. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  76. data/lib/concurrent/timer_task.rb +92 -103
  77. data/lib/concurrent/tvar.rb +42 -38
  78. data/lib/concurrent/utilities.rb +3 -1
  79. data/lib/concurrent/utility/at_exit.rb +97 -0
  80. data/lib/concurrent/utility/engine.rb +44 -0
  81. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  82. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  83. data/lib/concurrent/utility/processor_counter.rb +156 -0
  84. data/lib/concurrent/utility/timeout.rb +18 -14
  85. data/lib/concurrent/utility/timer.rb +11 -6
  86. data/lib/concurrent/version.rb +2 -1
  87. data/lib/concurrent_ruby.rb +1 -0
  88. data/lib/concurrent_ruby_ext.jar +0 -0
  89. metadata +46 -66
  90. data/lib/concurrent/actor.rb +0 -103
  91. data/lib/concurrent/actor/behaviour.rb +0 -70
  92. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  93. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  94. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  95. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  96. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  97. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  98. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  99. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  100. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  101. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  102. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  103. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  104. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  105. data/lib/concurrent/actor/context.rb +0 -154
  106. data/lib/concurrent/actor/core.rb +0 -217
  107. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  108. data/lib/concurrent/actor/envelope.rb +0 -41
  109. data/lib/concurrent/actor/errors.rb +0 -27
  110. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  111. data/lib/concurrent/actor/public_delegations.rb +0 -40
  112. data/lib/concurrent/actor/reference.rb +0 -81
  113. data/lib/concurrent/actor/root.rb +0 -37
  114. data/lib/concurrent/actor/type_check.rb +0 -48
  115. data/lib/concurrent/actor/utils.rb +0 -10
  116. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  117. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  118. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  119. data/lib/concurrent/actor/utils/pool.rb +0 -59
  120. data/lib/concurrent/actress.rb +0 -3
  121. data/lib/concurrent/agent.rb +0 -209
  122. data/lib/concurrent/atomic.rb +0 -92
  123. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  124. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  125. data/lib/concurrent/atomic/synchronization.rb +0 -51
  126. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  127. data/lib/concurrent/channel/channel.rb +0 -41
  128. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  129. data/lib/concurrent/channel/waitable_list.rb +0 -40
  130. data/lib/concurrent/channels.rb +0 -5
  131. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  132. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  133. data/lib/concurrent/collections.rb +0 -3
  134. data/lib/concurrent/dereferenceable.rb +0 -108
  135. data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
  136. data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
  137. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
  138. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
  139. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  140. data/lib/concurrent/logging.rb +0 -20
  141. data/lib/concurrent/obligation.rb +0 -171
  142. data/lib/concurrent/observable.rb +0 -73
  143. data/lib/concurrent/options_parser.rb +0 -48
  144. data/lib/concurrent/utility/processor_count.rb +0 -152
  145. data/lib/extension_helper.rb +0 -37
@@ -57,28 +57,31 @@ module Concurrent
57
57
  end
58
58
 
59
59
  # Run a block that reads and writes `TVar`s as a single atomic transaction.
60
- # With respect to the value of `TVar` objects, the transaction is atomic,
61
- # in that it either happens or it does not, consistent, in that the `TVar`
60
+ # With respect to the value of `TVar` objects, the transaction is atomic, in
61
+ # that it either happens or it does not, consistent, in that the `TVar`
62
62
  # objects involved will never enter an illegal state, and isolated, in that
63
63
  # transactions never interfere with each other. You may recognise these
64
64
  # properties from database transactions.
65
- #
65
+ #
66
66
  # There are some very important and unusual semantics that you must be aware of:
67
- #
68
- # * 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.
69
- #
70
- # * If an exception escapes an atomically block it will abort the transaction.
71
- #
72
- # * It is undefined behaviour to use callcc or Fiber with atomically.
73
- #
74
- # * 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.
75
- #
67
+ #
68
+ # * Most importantly, the block that you pass to atomically may be executed
69
+ # more than once. In most cases your code should be free of
70
+ # side-effects, except for via TVar.
71
+ #
72
+ # * If an exception escapes an atomically block it will abort the transaction.
73
+ #
74
+ # * It is undefined behaviour to use callcc or Fiber with atomically.
75
+ #
76
+ # * If you create a new thread within an atomically, it will not be part of
77
+ # the transaction. Creating a thread counts as a side-effect.
78
+ #
76
79
  # Transactions within transactions are flattened to a single transaction.
77
- #
80
+ #
78
81
  # @example
79
82
  # a = new TVar(100_000)
80
83
  # b = new TVar(100)
81
- #
84
+ #
82
85
  # Concurrent::atomically do
83
86
  # a.value -= 10
84
87
  # b.value += 10
@@ -112,6 +115,9 @@ module Concurrent
112
115
  rescue Transaction::AbortError => e
113
116
  transaction.abort
114
117
  result = Transaction::ABORTED
118
+ rescue Transaction::LeaveError => e
119
+ transaction.abort
120
+ break result
115
121
  rescue => e
116
122
  transaction.abort
117
123
  raise e
@@ -141,7 +147,12 @@ module Concurrent
141
147
  raise Transaction::AbortError.new
142
148
  end
143
149
 
144
- module_function :atomically, :abort_transaction
150
+ # Leave a transaction without commiting or aborting - see `Concurrent::atomically`.
151
+ def leave_transaction
152
+ raise Transaction::LeaveError.new
153
+ end
154
+
155
+ module_function :atomically, :abort_transaction, :leave_transaction
145
156
 
146
157
  private
147
158
 
@@ -150,26 +161,30 @@ module Concurrent
150
161
  ABORTED = Object.new
151
162
 
152
163
  ReadLogEntry = Struct.new(:tvar, :version)
153
- UndoLogEntry = Struct.new(:tvar, :value)
154
164
 
155
165
  AbortError = Class.new(StandardError)
166
+ LeaveError = Class.new(StandardError)
156
167
 
157
168
  def initialize
158
- @write_set = Set.new
159
169
  @read_log = []
160
- @undo_log = []
170
+ @write_log = {}
161
171
  end
162
172
 
163
173
  def read(tvar)
164
174
  Concurrent::abort_transaction unless valid?
165
- @read_log.push(ReadLogEntry.new(tvar, tvar.unsafe_version))
166
- tvar.unsafe_value
175
+
176
+ if @write_log.has_key? tvar
177
+ @write_log[tvar]
178
+ else
179
+ @read_log.push(ReadLogEntry.new(tvar, tvar.unsafe_version))
180
+ tvar.unsafe_value
181
+ end
167
182
  end
168
183
 
169
184
  def write(tvar, value)
170
185
  # Have we already written to this TVar?
171
186
 
172
- unless @write_set.include? tvar
187
+ unless @write_log.has_key? tvar
173
188
  # Try to lock the TVar
174
189
 
175
190
  unless tvar.unsafe_lock.try_lock
@@ -177,10 +192,6 @@ module Concurrent
177
192
  Concurrent::abort_transaction
178
193
  end
179
194
 
180
- # We've locked it - add it to the write set
181
-
182
- @write_set.add(tvar)
183
-
184
195
  # If we previously wrote to it, check the version hasn't changed
185
196
 
186
197
  @read_log.each do |log_entry|
@@ -190,27 +201,20 @@ module Concurrent
190
201
  end
191
202
  end
192
203
 
193
- # Record the current value of the TVar so we can undo it later
204
+ # Record the value written
194
205
 
195
- @undo_log.push(UndoLogEntry.new(tvar, tvar.unsafe_value))
196
-
197
- # Write the new value to the TVar
198
-
199
- tvar.unsafe_value = value
206
+ @write_log[tvar] = value
200
207
  end
201
208
 
202
209
  def abort
203
- @undo_log.each do |entry|
204
- entry.tvar.unsafe_value = entry.value
205
- end
206
-
207
210
  unlock
208
211
  end
209
212
 
210
213
  def commit
211
214
  return false unless valid?
212
215
 
213
- @write_set.each do |tvar|
216
+ @write_log.each_pair do |tvar, value|
217
+ tvar.unsafe_value = value
214
218
  tvar.unsafe_increment_version
215
219
  end
216
220
 
@@ -221,7 +225,7 @@ module Concurrent
221
225
 
222
226
  def valid?
223
227
  @read_log.each do |log_entry|
224
- unless @write_set.include? log_entry.tvar
228
+ unless @write_log.has_key? log_entry.tvar
225
229
  if log_entry.tvar.unsafe_version > log_entry.version
226
230
  return false
227
231
  end
@@ -232,7 +236,7 @@ module Concurrent
232
236
  end
233
237
 
234
238
  def unlock
235
- @write_set.each do |tvar|
239
+ @write_log.each_key do |tvar|
236
240
  tvar.unsafe_lock.unlock
237
241
  end
238
242
  end
@@ -1,3 +1,5 @@
1
- require 'concurrent/utility/processor_count'
1
+ # DEPRECATED Remove this file at v1.0
2
+ require 'concurrent/utility/monotonic_time'
3
+ require 'concurrent/utility/processor_counter'
2
4
  require 'concurrent/utility/timeout'
3
5
  require 'concurrent/utility/timer'
@@ -0,0 +1,97 @@
1
+ require 'logger'
2
+ require 'concurrent/synchronization'
3
+
4
+ module Concurrent
5
+
6
+ # Provides ability to add and remove handlers to be run at `Kernel#at_exit`, order is undefined.
7
+ # Each handler is executed at most once.
8
+ #
9
+ # @!visibility private
10
+ class AtExitImplementation < Synchronization::Object
11
+ include Logger::Severity
12
+
13
+ def initialize(*args)
14
+ super()
15
+ synchronize { ns_initialize *args }
16
+ end
17
+
18
+ # Add a handler to be run at `Kernel#at_exit`
19
+ # @param [Object] handler_id optionally provide an id, if allready present, handler is replaced
20
+ # @yield the handler
21
+ # @return id of the handler
22
+ def add(handler_id = nil, &handler)
23
+ id = handler_id || handler.object_id
24
+ synchronize { @handlers[id] = handler }
25
+ id
26
+ end
27
+
28
+ # Delete a handler by handler_id
29
+ # @return [true, false]
30
+ def delete(handler_id)
31
+ !!synchronize { @handlers.delete handler_id }
32
+ end
33
+
34
+ # Is handler with handler_id rpesent?
35
+ # @return [true, false]
36
+ def handler?(handler_id)
37
+ synchronize { @handlers.key? handler_id }
38
+ end
39
+
40
+ # @return copy of the handlers
41
+ def handlers
42
+ synchronize { @handlers }.clone
43
+ end
44
+
45
+ # install `Kernel#at_exit` callback to execute added handlers
46
+ def install
47
+ synchronize do
48
+ @installed ||= begin
49
+ at_exit { runner }
50
+ true
51
+ end
52
+ self
53
+ end
54
+ end
55
+
56
+ # Will it run during `Kernel#at_exit`
57
+ def enabled?
58
+ synchronize { @enabled }
59
+ end
60
+
61
+ # Configure if it runs during `Kernel#at_exit`
62
+ def enabled=(value)
63
+ synchronize { @enabled = value }
64
+ end
65
+
66
+ # run the handlers manually
67
+ # @return ids of the handlers
68
+ def run
69
+ handlers, _ = synchronize { handlers, @handlers = @handlers, {} }
70
+ handlers.each do |_, handler|
71
+ begin
72
+ handler.call
73
+ rescue => error
74
+ Concurrent.global_logger.call(ERROR, error)
75
+ end
76
+ end
77
+ handlers.keys
78
+ end
79
+
80
+ private
81
+
82
+ def ns_initialize(enabled = true)
83
+ @handlers = {}
84
+ @enabled = enabled
85
+ end
86
+
87
+ def runner
88
+ run if synchronize { @enabled }
89
+ end
90
+ end
91
+
92
+ private_constant :AtExitImplementation
93
+
94
+ # @see AtExitImplementation
95
+ # @!visibility private
96
+ AtExit = AtExitImplementation.new.install
97
+ end
@@ -0,0 +1,44 @@
1
+ module Concurrent
2
+ module Utility
3
+
4
+ # @!visibility private
5
+ module EngineDetector
6
+ def on_jruby?
7
+ ruby_engine == 'jruby'
8
+ end
9
+
10
+ def on_jruby_9000?
11
+ on_jruby? && 0 == (JRUBY_VERSION =~ /^9\.0\.0\.0/)
12
+ end
13
+
14
+ def on_cruby?
15
+ ruby_engine == 'ruby'
16
+ end
17
+
18
+ def on_rbx?
19
+ ruby_engine == 'rbx'
20
+ end
21
+
22
+ def on_windows?
23
+ !(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/).nil?
24
+ end
25
+
26
+ def ruby_engine
27
+ defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
28
+ end
29
+
30
+ def ruby_version(comparison, major, minor = 0, patch = 0)
31
+ result = (RUBY_VERSION.split('.').map(&:to_i) <=> [major, minor, patch])
32
+ comparisons = { :== => [0],
33
+ :>= => [1, 0],
34
+ :<= => [-1, 0],
35
+ :> => [1],
36
+ :< => [-1] }
37
+ comparisons.fetch(comparison).include? result
38
+ end
39
+ end
40
+ end
41
+
42
+ # @!visibility private
43
+ extend Utility::EngineDetector
44
+ end
@@ -0,0 +1,59 @@
1
+ require 'concurrent/synchronization'
2
+
3
+ module Concurrent
4
+
5
+ class_definition = Class.new(Synchronization::Object) do
6
+ def initialize
7
+ super()
8
+ @last_time = Time.now.to_f
9
+ ensure_ivar_visibility!
10
+ end
11
+
12
+ if defined?(Process::CLOCK_MONOTONIC)
13
+ # @!visibility private
14
+ def get_time
15
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
16
+ end
17
+ elsif Concurrent.on_jruby?
18
+ # @!visibility private
19
+ def get_time
20
+ java.lang.System.nanoTime() / 1_000_000_000.0
21
+ end
22
+ else
23
+
24
+ # @!visibility private
25
+ def get_time
26
+ synchronize do
27
+ now = Time.now.to_f
28
+ if @last_time < now
29
+ @last_time = now
30
+ else # clock has moved back in time
31
+ @last_time += 0.000_001
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+
39
+ # Clock that cannot be set and represents monotonic time since
40
+ # some unspecified starting point.
41
+ #
42
+ # @!visibility private
43
+ GLOBAL_MONOTONIC_CLOCK = class_definition.new
44
+ private_constant :GLOBAL_MONOTONIC_CLOCK
45
+
46
+ # @!macro [attach] monotonic_get_time
47
+ #
48
+ # Returns the current time a tracked by the application monotonic clock.
49
+ #
50
+ # @return [Float] The current monotonic time when `since` not given else
51
+ # the elapsed monotonic time between `since` and the current time
52
+ #
53
+ # @!macro monotonic_clock_warning
54
+ def monotonic_time
55
+ GLOBAL_MONOTONIC_CLOCK.get_time
56
+ end
57
+
58
+ module_function :monotonic_time
59
+ end
@@ -0,0 +1,56 @@
1
+ require 'concurrent/synchronization/abstract_object' # for JRuby
2
+ require 'concurrent/utility/engine'
3
+
4
+ module Concurrent
5
+ module Utility
6
+
7
+ # @!visibility private
8
+ module NativeExtensionLoader
9
+
10
+ @c_ext_loaded ||= false
11
+ @java_ext_loaded ||= false
12
+
13
+ # @!visibility private
14
+ def allow_c_extensions?
15
+ Concurrent.on_cruby?
16
+ end
17
+
18
+ if Concurrent.on_cruby? && !@c_ext_loaded
19
+ tries = [
20
+ lambda do
21
+ require 'concurrent/extension'
22
+ @c_ext_loaded = true
23
+ end,
24
+ lambda do
25
+ # may be a Windows cross-compiled native gem
26
+ require "concurrent/#{RUBY_VERSION[0..2]}/extension"
27
+ @c_ext_loaded = true
28
+ end,
29
+ lambda do
30
+ warn 'Performance on MRI may be improved with the concurrent-ruby-ext gem. Please see http://concurrent-ruby.com'
31
+ end]
32
+
33
+ tries.each do |try|
34
+ begin
35
+ try.call
36
+ break
37
+ rescue LoadError
38
+ next
39
+ end
40
+ end
41
+ end
42
+
43
+ if Concurrent.on_jruby? && !@java_ext_loaded
44
+ begin
45
+ require 'concurrent_ruby_ext'
46
+ @java_ext_loaded = true
47
+ rescue LoadError
48
+ warn 'Performance on JRuby may be improved by installing the pre-compiled Java extensions. Please see http://concurrent-ruby.com'
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ # @!visibility private
55
+ extend Utility::NativeExtensionLoader
56
+ end
@@ -0,0 +1,156 @@
1
+ require 'rbconfig'
2
+ require 'concurrent/delay'
3
+
4
+ module Concurrent
5
+ module Utility
6
+
7
+ # @!visibility private
8
+ class ProcessorCounter
9
+ def initialize
10
+ @processor_count = Delay.new { compute_processor_count }
11
+ @physical_processor_count = Delay.new { compute_physical_processor_count }
12
+ end
13
+
14
+ # Number of processors seen by the OS and used for process scheduling. For
15
+ # performance reasons the calculated value will be memoized on the first
16
+ # call.
17
+ #
18
+ # When running under JRuby the Java runtime call
19
+ # `java.lang.Runtime.getRuntime.availableProcessors` will be used. According
20
+ # to the Java documentation this "value may change during a particular
21
+ # invocation of the virtual machine... [applications] should therefore
22
+ # occasionally poll this property." Subsequently the result will NOT be
23
+ # memoized under JRuby.
24
+ #
25
+ # On Windows the Win32 API will be queried for the
26
+ # `NumberOfLogicalProcessors from Win32_Processor`. This will return the
27
+ # total number "logical processors for the current instance of the
28
+ # processor", which taked into account hyperthreading.
29
+ #
30
+ # * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
31
+ # * BSD: /sbin/sysctl
32
+ # * Cygwin: /proc/cpuinfo
33
+ # * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
34
+ # * HP-UX: /usr/sbin/ioscan
35
+ # * IRIX: /usr/sbin/sysconf
36
+ # * Linux: /proc/cpuinfo
37
+ # * Minix 3+: /proc/cpuinfo
38
+ # * Solaris: /usr/sbin/psrinfo
39
+ # * Tru64 UNIX: /usr/sbin/psrinfo
40
+ # * UnixWare: /usr/sbin/psrinfo
41
+ #
42
+ # @return [Integer] number of processors seen by the OS or Java runtime
43
+ #
44
+ # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
45
+ #
46
+ # @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors()
47
+ # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
48
+ def processor_count
49
+ @processor_count.value
50
+ end
51
+
52
+ # Number of physical processor cores on the current system. For performance
53
+ # reasons the calculated value will be memoized on the first call.
54
+ #
55
+ # On Windows the Win32 API will be queried for the `NumberOfCores from
56
+ # Win32_Processor`. This will return the total number "of cores for the
57
+ # current instance of the processor." On Unix-like operating systems either
58
+ # the `hwprefs` or `sysctl` utility will be called in a subshell and the
59
+ # returned value will be used. In the rare case where none of these methods
60
+ # work or an exception is raised the function will simply return 1.
61
+ #
62
+ # @return [Integer] number physical processor cores on the current system
63
+ #
64
+ # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
65
+ #
66
+ # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
67
+ # @see http://www.unix.com/man-page/osx/1/HWPREFS/
68
+ # @see http://linux.die.net/man/8/sysctl
69
+ def physical_processor_count
70
+ @physical_processor_count.value
71
+ end
72
+
73
+ private
74
+
75
+ def compute_processor_count
76
+ if Concurrent.on_jruby?
77
+ java.lang.Runtime.getRuntime.availableProcessors
78
+ else
79
+ os_name = RbConfig::CONFIG["target_os"]
80
+ if os_name =~ /mingw|mswin/
81
+ require 'win32ole'
82
+ result = WIN32OLE.connect("winmgmts://").ExecQuery(
83
+ "select NumberOfLogicalProcessors from Win32_Processor")
84
+ result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
85
+ elsif File.readable?("/proc/cpuinfo")
86
+ IO.read("/proc/cpuinfo").scan(/^processor/).size
87
+ elsif File.executable?("/usr/bin/hwprefs")
88
+ IO.popen("/usr/bin/hwprefs thread_count").read.to_i
89
+ elsif File.executable?("/usr/sbin/psrinfo")
90
+ IO.popen("/usr/sbin/psrinfo").read.scan(/^.*on-*line/).size
91
+ elsif File.executable?("/usr/sbin/ioscan")
92
+ IO.popen("/usr/sbin/ioscan -kC processor") do |out|
93
+ out.read.scan(/^.*processor/).size
94
+ end
95
+ elsif File.executable?("/usr/sbin/pmcycles")
96
+ IO.popen("/usr/sbin/pmcycles -m").read.count("\n")
97
+ elsif File.executable?("/usr/sbin/lsdev")
98
+ IO.popen("/usr/sbin/lsdev -Cc processor -S 1").read.count("\n")
99
+ elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i
100
+ IO.popen("/usr/sbin/sysconf NPROC_ONLN").read.to_i
101
+ elsif File.executable?("/usr/sbin/sysctl")
102
+ IO.popen("/usr/sbin/sysctl -n hw.ncpu").read.to_i
103
+ elsif File.executable?("/sbin/sysctl")
104
+ IO.popen("/sbin/sysctl -n hw.ncpu").read.to_i
105
+ else
106
+ 1
107
+ end
108
+ end
109
+ rescue
110
+ return 1
111
+ end
112
+
113
+ def compute_physical_processor_count
114
+ ppc = case RbConfig::CONFIG["target_os"]
115
+ when /darwin1/
116
+ IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
117
+ when /linux/
118
+ cores = {} # unique physical ID / core ID combinations
119
+ phy = 0
120
+ IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
121
+ if ln.start_with?("physical")
122
+ phy = ln[/\d+/]
123
+ elsif ln.start_with?("core")
124
+ cid = phy + ":" + ln[/\d+/]
125
+ cores[cid] = true if not cores[cid]
126
+ end
127
+ end
128
+ cores.count
129
+ when /mswin|mingw/
130
+ require 'win32ole'
131
+ result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
132
+ "select NumberOfCores from Win32_Processor")
133
+ result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
134
+ else
135
+ processor_count
136
+ end
137
+ # fall back to logical count if physical info is invalid
138
+ ppc > 0 ? ppc : processor_count
139
+ rescue
140
+ return 1
141
+ end
142
+ end
143
+ end
144
+
145
+ # create the default ProcessorCounter on load
146
+ @processor_counter = Utility::ProcessorCounter.new
147
+ singleton_class.send :attr_reader, :processor_counter
148
+
149
+ def self.processor_count
150
+ processor_counter.processor_count
151
+ end
152
+
153
+ def self.physical_processor_count
154
+ processor_counter.physical_processor_count
155
+ end
156
+ end