concurrent-ruby 1.1.9 → 1.2.0

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/Gemfile +2 -8
  4. data/README.md +34 -37
  5. data/Rakefile +48 -69
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  9. data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
  10. data/lib/concurrent-ruby/concurrent/array.rb +0 -10
  11. data/lib/concurrent-ruby/concurrent/async.rb +1 -0
  12. data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
  13. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
  14. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
  15. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
  16. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +82 -151
  17. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
  18. data/lib/concurrent-ruby/concurrent/atomic/event.rb +3 -3
  19. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  20. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
  21. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +188 -0
  22. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
  23. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
  24. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
  25. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  26. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +19 -3
  27. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
  28. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +9 -9
  29. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +32 -14
  30. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
  31. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  32. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +15 -4
  33. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
  34. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
  35. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
  36. data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
  37. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  38. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  39. data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
  40. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  41. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  42. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +17 -14
  43. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
  44. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +3 -3
  45. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
  46. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
  47. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
  48. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +6 -6
  49. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  50. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  51. data/lib/concurrent-ruby/concurrent/hash.rb +0 -9
  52. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  53. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  54. data/lib/concurrent-ruby/concurrent/map.rb +9 -9
  55. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  56. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  57. data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
  58. data/lib/concurrent-ruby/concurrent/promise.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/promises.rb +7 -6
  60. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  61. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +30 -17
  62. data/lib/concurrent-ruby/concurrent/set.rb +0 -10
  63. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  64. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  65. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  66. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  67. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  68. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  69. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  70. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +6 -5
  71. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
  72. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
  73. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  74. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  75. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  76. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  77. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +2 -39
  78. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
  79. data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
  80. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  81. data/lib/concurrent-ruby/concurrent/tvar.rb +22 -61
  82. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  83. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +7 -46
  84. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  85. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  86. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +36 -89
  87. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  88. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
  89. metadata +10 -12
  90. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  91. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  92. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  93. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  94. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  95. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
  96. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  97. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
@@ -3,6 +3,7 @@ require 'rbconfig'
3
3
  require 'concurrent/delay'
4
4
 
5
5
  module Concurrent
6
+ # @!visibility private
6
7
  module Utility
7
8
 
8
9
  # @!visibility private
@@ -12,64 +13,10 @@ module Concurrent
12
13
  @physical_processor_count = Delay.new { compute_physical_processor_count }
13
14
  end
14
15
 
15
- # Number of processors seen by the OS and used for process scheduling. For
16
- # performance reasons the calculated value will be memoized on the first
17
- # call.
18
- #
19
- # When running under JRuby the Java runtime call
20
- # `java.lang.Runtime.getRuntime.availableProcessors` will be used. According
21
- # to the Java documentation this "value may change during a particular
22
- # invocation of the virtual machine... [applications] should therefore
23
- # occasionally poll this property." Subsequently the result will NOT be
24
- # memoized under JRuby.
25
- #
26
- # Ruby's Etc.nprocessors will be used if available (MRI 2.2+).
27
- #
28
- # On Windows the Win32 API will be queried for the
29
- # `NumberOfLogicalProcessors from Win32_Processor`. This will return the
30
- # total number "logical processors for the current instance of the
31
- # processor", which taked into account hyperthreading.
32
- #
33
- # * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
34
- # * Alpha: /usr/bin/nproc (/proc/cpuinfo exists but cannot be used)
35
- # * BSD: /sbin/sysctl
36
- # * Cygwin: /proc/cpuinfo
37
- # * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
38
- # * HP-UX: /usr/sbin/ioscan
39
- # * IRIX: /usr/sbin/sysconf
40
- # * Linux: /proc/cpuinfo
41
- # * Minix 3+: /proc/cpuinfo
42
- # * Solaris: /usr/sbin/psrinfo
43
- # * Tru64 UNIX: /usr/sbin/psrinfo
44
- # * UnixWare: /usr/sbin/psrinfo
45
- #
46
- # @return [Integer] number of processors seen by the OS or Java runtime
47
- #
48
- # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
49
- #
50
- # @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors()
51
- # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
52
16
  def processor_count
53
17
  @processor_count.value
54
18
  end
55
19
 
56
- # Number of physical processor cores on the current system. For performance
57
- # reasons the calculated value will be memoized on the first call.
58
- #
59
- # On Windows the Win32 API will be queried for the `NumberOfCores from
60
- # Win32_Processor`. This will return the total number "of cores for the
61
- # current instance of the processor." On Unix-like operating systems either
62
- # the `hwprefs` or `sysctl` utility will be called in a subshell and the
63
- # returned value will be used. In the rare case where none of these methods
64
- # work or an exception is raised the function will simply return 1.
65
- #
66
- # @return [Integer] number physical processor cores on the current system
67
- #
68
- # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
69
- #
70
- # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
71
- # @see http://www.unix.com/man-page/osx/1/HWPREFS/
72
- # @see http://linux.die.net/man/8/sysctl
73
20
  def physical_processor_count
74
21
  @physical_processor_count.value
75
22
  end
@@ -79,47 +26,14 @@ module Concurrent
79
26
  def compute_processor_count
80
27
  if Concurrent.on_jruby?
81
28
  java.lang.Runtime.getRuntime.availableProcessors
82
- elsif Etc.respond_to?(:nprocessors) && (nprocessor = Etc.nprocessors rescue nil)
83
- nprocessor
84
29
  else
85
- os_name = RbConfig::CONFIG["target_os"]
86
- if os_name =~ /mingw|mswin/
87
- require 'win32ole'
88
- result = WIN32OLE.connect("winmgmts://").ExecQuery(
89
- "select NumberOfLogicalProcessors from Win32_Processor")
90
- result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
91
- elsif File.readable?("/proc/cpuinfo") && (cpuinfo_count = IO.read("/proc/cpuinfo").scan(/^processor/).size) > 0
92
- cpuinfo_count
93
- elsif File.executable?("/usr/bin/nproc")
94
- IO.popen("/usr/bin/nproc --all", &:read).to_i
95
- elsif File.executable?("/usr/bin/hwprefs")
96
- IO.popen("/usr/bin/hwprefs thread_count", &:read).to_i
97
- elsif File.executable?("/usr/sbin/psrinfo")
98
- IO.popen("/usr/sbin/psrinfo", &:read).scan(/^.*on-*line/).size
99
- elsif File.executable?("/usr/sbin/ioscan")
100
- IO.popen("/usr/sbin/ioscan -kC processor", &:read).scan(/^.*processor/).size
101
- elsif File.executable?("/usr/sbin/pmcycles")
102
- IO.popen("/usr/sbin/pmcycles -m", &:read).count("\n")
103
- elsif File.executable?("/usr/sbin/lsdev")
104
- IO.popen("/usr/sbin/lsdev -Cc processor -S 1", &:read).count("\n")
105
- elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i
106
- IO.popen("/usr/sbin/sysconf NPROC_ONLN", &:read).to_i
107
- elsif File.executable?("/usr/sbin/sysctl")
108
- IO.popen("/usr/sbin/sysctl -n hw.ncpu", &:read).to_i
109
- elsif File.executable?("/sbin/sysctl")
110
- IO.popen("/sbin/sysctl -n hw.ncpu", &:read).to_i
111
- else
112
- # TODO (pitr-ch 05-Nov-2016): warn about failures
113
- 1
114
- end
30
+ Etc.nprocessors
115
31
  end
116
- rescue
117
- return 1
118
32
  end
119
33
 
120
34
  def compute_physical_processor_count
121
35
  ppc = case RbConfig::CONFIG["target_os"]
122
- when /darwin1/
36
+ when /darwin\d\d/
123
37
  IO.popen("/usr/sbin/sysctl -n hw.physicalcpu", &:read).to_i
124
38
  when /linux/
125
39
  cores = {} # unique physical ID / core ID combinations
@@ -153,10 +67,43 @@ module Concurrent
153
67
  @processor_counter = Utility::ProcessorCounter.new
154
68
  singleton_class.send :attr_reader, :processor_counter
155
69
 
70
+ # Number of processors seen by the OS and used for process scheduling. For
71
+ # performance reasons the calculated value will be memoized on the first
72
+ # call.
73
+ #
74
+ # When running under JRuby the Java runtime call
75
+ # `java.lang.Runtime.getRuntime.availableProcessors` will be used. According
76
+ # to the Java documentation this "value may change during a particular
77
+ # invocation of the virtual machine... [applications] should therefore
78
+ # occasionally poll this property." Subsequently the result will NOT be
79
+ # memoized under JRuby.
80
+ #
81
+ # Otherwise Ruby's Etc.nprocessors will be used.
82
+ #
83
+ # @return [Integer] number of processors seen by the OS or Java runtime
84
+ #
85
+ # @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors()
156
86
  def self.processor_count
157
87
  processor_counter.processor_count
158
88
  end
159
89
 
90
+ # Number of physical processor cores on the current system. For performance
91
+ # reasons the calculated value will be memoized on the first call.
92
+ #
93
+ # On Windows the Win32 API will be queried for the `NumberOfCores from
94
+ # Win32_Processor`. This will return the total number "of cores for the
95
+ # current instance of the processor." On Unix-like operating systems either
96
+ # the `hwprefs` or `sysctl` utility will be called in a subshell and the
97
+ # returned value will be used. In the rare case where none of these methods
98
+ # work or an exception is raised the function will simply return 1.
99
+ #
100
+ # @return [Integer] number physical processor cores on the current system
101
+ #
102
+ # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
103
+ #
104
+ # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
105
+ # @see http://www.unix.com/man-page/osx/1/HWPREFS/
106
+ # @see http://linux.die.net/man/8/sysctl
160
107
  def self.physical_processor_count
161
108
  processor_counter.physical_processor_count
162
109
  end
@@ -1,3 +1,3 @@
1
1
  module Concurrent
2
- VERSION = '1.1.9'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -1 +1,5 @@
1
- require_relative "./concurrent"
1
+ # This file is here so that there is a file with the same name as the gem that
2
+ # can be required by Bundler.require. Applications should normally
3
+ # require 'concurrent'.
4
+
5
+ require_relative "concurrent"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: concurrent-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.9
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jerry D'Antonio
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-06-05 00:00:00.000000000 Z
13
+ date: 2023-01-23 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: |
16
16
  Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.
@@ -49,7 +49,6 @@ files:
49
49
  - lib/concurrent-ruby/concurrent/array.rb
50
50
  - lib/concurrent-ruby/concurrent/async.rb
51
51
  - lib/concurrent-ruby/concurrent/atom.rb
52
- - lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb
53
52
  - lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb
54
53
  - lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb
55
54
  - lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb
@@ -57,17 +56,19 @@ files:
57
56
  - lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb
58
57
  - lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb
59
58
  - lib/concurrent-ruby/concurrent/atomic/event.rb
59
+ - lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb
60
60
  - lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb
61
- - lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb
61
+ - lib/concurrent-ruby/concurrent/atomic/locals.rb
62
+ - lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb
62
63
  - lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb
63
64
  - lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb
64
65
  - lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb
65
66
  - lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb
66
67
  - lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb
67
68
  - lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb
68
- - lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb
69
69
  - lib/concurrent-ruby/concurrent/atomic/semaphore.rb
70
70
  - lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb
71
+ - lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb
71
72
  - lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb
72
73
  - lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb
73
74
  - lib/concurrent-ruby/concurrent/atomics.rb
@@ -135,16 +136,13 @@ files:
135
136
  - lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb
136
137
  - lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb
137
138
  - lib/concurrent-ruby/concurrent/synchronization/condition.rb
139
+ - lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb
138
140
  - lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb
139
- - lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb
140
141
  - lib/concurrent-ruby/concurrent/synchronization/lock.rb
141
142
  - lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb
142
- - lib/concurrent-ruby/concurrent/synchronization/mri_object.rb
143
143
  - lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb
144
144
  - lib/concurrent-ruby/concurrent/synchronization/object.rb
145
- - lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb
146
- - lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb
147
- - lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb
145
+ - lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb
148
146
  - lib/concurrent-ruby/concurrent/synchronization/volatile.rb
149
147
  - lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb
150
148
  - lib/concurrent-ruby/concurrent/thread_safe/util.rb
@@ -178,14 +176,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
178
176
  requirements:
179
177
  - - ">="
180
178
  - !ruby/object:Gem::Version
181
- version: 1.9.3
179
+ version: '2.3'
182
180
  required_rubygems_version: !ruby/object:Gem::Requirement
183
181
  requirements:
184
182
  - - ">="
185
183
  - !ruby/object:Gem::Version
186
184
  version: '0'
187
185
  requirements: []
188
- rubygems_version: 3.2.3
186
+ rubygems_version: 3.3.26
189
187
  signing_key:
190
188
  specification_version: 4
191
189
  summary: Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
@@ -1,66 +0,0 @@
1
- require 'concurrent/constants'
2
-
3
- module Concurrent
4
-
5
- # @!macro thread_local_var
6
- # @!macro internal_implementation_note
7
- # @!visibility private
8
- class AbstractThreadLocalVar
9
-
10
- # @!macro thread_local_var_method_initialize
11
- def initialize(default = nil, &default_block)
12
- if default && block_given?
13
- raise ArgumentError, "Cannot use both value and block as default value"
14
- end
15
-
16
- if block_given?
17
- @default_block = default_block
18
- @default = nil
19
- else
20
- @default_block = nil
21
- @default = default
22
- end
23
-
24
- allocate_storage
25
- end
26
-
27
- # @!macro thread_local_var_method_get
28
- def value
29
- raise NotImplementedError
30
- end
31
-
32
- # @!macro thread_local_var_method_set
33
- def value=(value)
34
- raise NotImplementedError
35
- end
36
-
37
- # @!macro thread_local_var_method_bind
38
- def bind(value, &block)
39
- if block_given?
40
- old_value = self.value
41
- begin
42
- self.value = value
43
- yield
44
- ensure
45
- self.value = old_value
46
- end
47
- end
48
- end
49
-
50
- protected
51
-
52
- # @!visibility private
53
- def allocate_storage
54
- raise NotImplementedError
55
- end
56
-
57
- # @!visibility private
58
- def default
59
- if @default_block
60
- self.value = @default_block.call
61
- else
62
- @default
63
- end
64
- end
65
- end
66
- end
@@ -1,37 +0,0 @@
1
- require 'concurrent/atomic/abstract_thread_local_var'
2
-
3
- if Concurrent.on_jruby?
4
-
5
- module Concurrent
6
-
7
- # @!visibility private
8
- # @!macro internal_implementation_note
9
- class JavaThreadLocalVar < AbstractThreadLocalVar
10
-
11
- # @!macro thread_local_var_method_get
12
- def value
13
- value = @var.get
14
-
15
- if value.nil?
16
- default
17
- elsif value == NULL
18
- nil
19
- else
20
- value
21
- end
22
- end
23
-
24
- # @!macro thread_local_var_method_set
25
- def value=(value)
26
- @var.set(value)
27
- end
28
-
29
- protected
30
-
31
- # @!visibility private
32
- def allocate_storage
33
- @var = java.lang.ThreadLocal.new
34
- end
35
- end
36
- end
37
- end
@@ -1,181 +0,0 @@
1
- require 'thread'
2
- require 'concurrent/atomic/abstract_thread_local_var'
3
-
4
- module Concurrent
5
-
6
- # @!visibility private
7
- # @!macro internal_implementation_note
8
- class RubyThreadLocalVar < AbstractThreadLocalVar
9
-
10
- # Each thread has a (lazily initialized) array of thread-local variable values
11
- # Each time a new thread-local var is created, we allocate an "index" for it
12
- # For example, if the allocated index is 1, that means slot #1 in EVERY
13
- # thread's thread-local array will be used for the value of that TLV
14
- #
15
- # The good thing about using a per-THREAD structure to hold values, rather
16
- # than a per-TLV structure, is that no synchronization is needed when
17
- # reading and writing those values (since the structure is only ever
18
- # accessed by a single thread)
19
- #
20
- # Of course, when a TLV is GC'd, 1) we need to recover its index for use
21
- # by other new TLVs (otherwise the thread-local arrays could get bigger
22
- # and bigger with time), and 2) we need to null out all the references
23
- # held in the now-unused slots (both to avoid blocking GC of those objects,
24
- # and also to prevent "stale" values from being passed on to a new TLV
25
- # when the index is reused)
26
- # Because we need to null out freed slots, we need to keep references to
27
- # ALL the thread-local arrays -- ARRAYS is for that
28
- # But when a Thread is GC'd, we need to drop the reference to its thread-local
29
- # array, so we don't leak memory
30
-
31
- FREE = []
32
- LOCK = Mutex.new
33
- THREAD_LOCAL_ARRAYS = {} # used as a hash set
34
-
35
- # synchronize when not on MRI
36
- # on MRI using lock in finalizer leads to "can't be called from trap context" error
37
- # so the code is carefully written to be tread-safe on MRI relying on GIL
38
-
39
- if Concurrent.on_cruby?
40
- # @!visibility private
41
- def self.semi_sync(&block)
42
- block.call
43
- end
44
- else
45
- # @!visibility private
46
- def self.semi_sync(&block)
47
- LOCK.synchronize(&block)
48
- end
49
- end
50
-
51
- private_constant :FREE, :LOCK, :THREAD_LOCAL_ARRAYS
52
-
53
- # @!macro thread_local_var_method_get
54
- def value
55
- if (array = get_threadlocal_array)
56
- value = array[@index]
57
- if value.nil?
58
- default
59
- elsif value.equal?(NULL)
60
- nil
61
- else
62
- value
63
- end
64
- else
65
- default
66
- end
67
- end
68
-
69
- # @!macro thread_local_var_method_set
70
- def value=(value)
71
- me = Thread.current
72
- # We could keep the thread-local arrays in a hash, keyed by Thread
73
- # But why? That would require locking
74
- # Using Ruby's built-in thread-local storage is faster
75
- unless (array = get_threadlocal_array(me))
76
- array = set_threadlocal_array([], me)
77
- self.class.semi_sync { THREAD_LOCAL_ARRAYS[array.object_id] = array }
78
- ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array.object_id))
79
- end
80
- array[@index] = (value.nil? ? NULL : value)
81
- value
82
- end
83
-
84
- protected
85
-
86
- # @!visibility private
87
- def allocate_storage
88
- @index = FREE.pop || next_index
89
-
90
- ObjectSpace.define_finalizer(self, self.class.thread_local_finalizer(@index))
91
- end
92
-
93
- # @!visibility private
94
- def self.thread_local_finalizer(index)
95
- proc do
96
- semi_sync do
97
- # The cost of GC'ing a TLV is linear in the number of threads using TLVs
98
- # But that is natural! More threads means more storage is used per TLV
99
- # So naturally more CPU time is required to free more storage
100
- #
101
- # DO NOT use each_value which might conflict with new pair assignment
102
- # into the hash in #value= method
103
- THREAD_LOCAL_ARRAYS.values.each { |array| array[index] = nil }
104
- # free index has to be published after the arrays are cleared
105
- FREE.push(index)
106
- end
107
- end
108
- end
109
-
110
- # @!visibility private
111
- def self.thread_finalizer(id)
112
- proc do
113
- semi_sync do
114
- # The thread which used this thread-local array is now gone
115
- # So don't hold onto a reference to the array (thus blocking GC)
116
- THREAD_LOCAL_ARRAYS.delete(id)
117
- end
118
- end
119
- end
120
-
121
- private
122
-
123
- # noinspection RubyClassVariableUsageInspection
124
- @@next = 0
125
- # noinspection RubyClassVariableUsageInspection
126
- def next_index
127
- LOCK.synchronize do
128
- result = @@next
129
- @@next += 1
130
- result
131
- end
132
- end
133
-
134
- if Thread.instance_methods.include?(:thread_variable_get)
135
-
136
- def get_threadlocal_array(thread = Thread.current)
137
- thread.thread_variable_get(:__threadlocal_array__)
138
- end
139
-
140
- def set_threadlocal_array(array, thread = Thread.current)
141
- thread.thread_variable_set(:__threadlocal_array__, array)
142
- end
143
-
144
- else
145
-
146
- def get_threadlocal_array(thread = Thread.current)
147
- thread[:__threadlocal_array__]
148
- end
149
-
150
- def set_threadlocal_array(array, thread = Thread.current)
151
- thread[:__threadlocal_array__] = array
152
- end
153
- end
154
-
155
- # This exists only for use in testing
156
- # @!visibility private
157
- def value_for(thread)
158
- if (array = get_threadlocal_array(thread))
159
- value = array[@index]
160
- if value.nil?
161
- get_default
162
- elsif value.equal?(NULL)
163
- nil
164
- else
165
- value
166
- end
167
- else
168
- get_default
169
- end
170
- end
171
-
172
- # @!visibility private
173
- def get_default
174
- if @default_block
175
- raise "Cannot use default_for with default block"
176
- else
177
- @default
178
- end
179
- end
180
- end
181
- end
@@ -1,45 +0,0 @@
1
- module Concurrent
2
- module Synchronization
3
-
4
- if Concurrent.on_jruby? && Concurrent.java_extensions_loaded?
5
-
6
- # @!visibility private
7
- module JRubyAttrVolatile
8
- def self.included(base)
9
- base.extend(ClassMethods)
10
- end
11
-
12
- module ClassMethods
13
- def attr_volatile(*names)
14
- names.each do |name|
15
-
16
- ivar = :"@volatile_#{name}"
17
-
18
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
19
- def #{name}
20
- instance_variable_get_volatile(:#{ivar})
21
- end
22
-
23
- def #{name}=(value)
24
- instance_variable_set_volatile(:#{ivar}, value)
25
- end
26
- RUBY
27
-
28
- end
29
- names.map { |n| [n, :"#{n}="] }.flatten
30
- end
31
- end
32
- end
33
-
34
- # @!visibility private
35
- # @!macro internal_implementation_note
36
- class JRubyObject < AbstractObject
37
- include JRubyAttrVolatile
38
-
39
- def initialize
40
- # nothing to do
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,44 +0,0 @@
1
- module Concurrent
2
- module Synchronization
3
-
4
- # @!visibility private
5
- module MriAttrVolatile
6
- def self.included(base)
7
- base.extend(ClassMethods)
8
- end
9
-
10
- module ClassMethods
11
- def attr_volatile(*names)
12
- names.each do |name|
13
- ivar = :"@volatile_#{name}"
14
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
15
- def #{name}
16
- #{ivar}
17
- end
18
-
19
- def #{name}=(value)
20
- #{ivar} = value
21
- end
22
- RUBY
23
- end
24
- names.map { |n| [n, :"#{n}="] }.flatten
25
- end
26
- end
27
-
28
- def full_memory_barrier
29
- # relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
30
- # https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
31
- end
32
- end
33
-
34
- # @!visibility private
35
- # @!macro internal_implementation_note
36
- class MriObject < AbstractObject
37
- include MriAttrVolatile
38
-
39
- def initialize
40
- # nothing to do
41
- end
42
- end
43
- end
44
- end