concurrent-ruby 1.0.5 → 1.1.0.pre2

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 (107) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +44 -0
  3. data/Gemfile +39 -0
  4. data/{LICENSE.txt → LICENSE.md} +2 -0
  5. data/README.md +203 -105
  6. data/Rakefile +279 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +304 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby.rb +1 -0
  23. data/lib/concurrent.rb +24 -20
  24. data/lib/concurrent/agent.rb +7 -7
  25. data/lib/concurrent/array.rb +59 -32
  26. data/lib/concurrent/async.rb +4 -4
  27. data/lib/concurrent/atom.rb +9 -9
  28. data/lib/concurrent/atomic/atomic_boolean.rb +24 -20
  29. data/lib/concurrent/atomic/atomic_fixnum.rb +27 -23
  30. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  31. data/lib/concurrent/atomic/atomic_reference.rb +176 -33
  32. data/lib/concurrent/atomic/count_down_latch.rb +6 -6
  33. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  34. data/lib/concurrent/atomic/event.rb +1 -1
  35. data/lib/concurrent/atomic/java_count_down_latch.rb +6 -5
  36. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -0
  37. data/lib/concurrent/atomic/read_write_lock.rb +2 -1
  38. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  39. data/lib/concurrent/atomic/semaphore.rb +8 -8
  40. data/lib/concurrent/atomic/thread_local_var.rb +7 -7
  41. data/lib/concurrent/atomic_reference/mutex_atomic.rb +3 -8
  42. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +1 -1
  43. data/lib/concurrent/atomics.rb +0 -43
  44. data/lib/concurrent/collection/lock_free_stack.rb +158 -0
  45. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +3 -3
  46. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +1 -2
  47. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +29 -29
  48. data/lib/concurrent/concern/dereferenceable.rb +1 -1
  49. data/lib/concurrent/concern/logging.rb +6 -1
  50. data/lib/concurrent/concern/observable.rb +7 -7
  51. data/lib/concurrent/concurrent_ruby.jar +0 -0
  52. data/lib/concurrent/configuration.rb +1 -6
  53. data/lib/concurrent/constants.rb +1 -1
  54. data/lib/concurrent/dataflow.rb +2 -1
  55. data/lib/concurrent/delay.rb +9 -7
  56. data/lib/concurrent/exchanger.rb +13 -21
  57. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  58. data/lib/concurrent/executor/cached_thread_pool.rb +1 -1
  59. data/lib/concurrent/executor/executor_service.rb +15 -15
  60. data/lib/concurrent/executor/fixed_thread_pool.rb +18 -18
  61. data/lib/concurrent/executor/java_thread_pool_executor.rb +10 -7
  62. data/lib/concurrent/executor/single_thread_executor.rb +2 -2
  63. data/lib/concurrent/executor/thread_pool_executor.rb +6 -6
  64. data/lib/concurrent/executor/timer_set.rb +1 -1
  65. data/lib/concurrent/future.rb +4 -1
  66. data/lib/concurrent/hash.rb +53 -30
  67. data/lib/concurrent/ivar.rb +5 -6
  68. data/lib/concurrent/map.rb +20 -25
  69. data/lib/concurrent/maybe.rb +1 -1
  70. data/lib/concurrent/mutable_struct.rb +15 -14
  71. data/lib/concurrent/mvar.rb +2 -2
  72. data/lib/concurrent/promise.rb +53 -21
  73. data/lib/concurrent/promises.rb +1936 -0
  74. data/lib/concurrent/re_include.rb +58 -0
  75. data/lib/concurrent/set.rb +66 -0
  76. data/lib/concurrent/settable_struct.rb +1 -0
  77. data/lib/concurrent/synchronization.rb +4 -5
  78. data/lib/concurrent/synchronization/abstract_lockable_object.rb +5 -5
  79. data/lib/concurrent/synchronization/abstract_struct.rb +6 -4
  80. data/lib/concurrent/synchronization/lockable_object.rb +6 -6
  81. data/lib/concurrent/synchronization/{mri_lockable_object.rb → mutex_lockable_object.rb} +19 -14
  82. data/lib/concurrent/synchronization/object.rb +8 -4
  83. data/lib/concurrent/synchronization/truffleruby_object.rb +46 -0
  84. data/lib/concurrent/synchronization/volatile.rb +11 -9
  85. data/lib/concurrent/thread_safe/util/data_structures.rb +55 -0
  86. data/lib/concurrent/thread_safe/util/striped64.rb +9 -4
  87. data/lib/concurrent/timer_task.rb +5 -2
  88. data/lib/concurrent/tuple.rb +1 -1
  89. data/lib/concurrent/tvar.rb +2 -2
  90. data/lib/concurrent/utility/at_exit.rb +1 -1
  91. data/lib/concurrent/utility/engine.rb +2 -2
  92. data/lib/concurrent/utility/monotonic_time.rb +3 -3
  93. data/lib/concurrent/utility/native_extension_loader.rb +31 -33
  94. data/lib/concurrent/utility/processor_counter.rb +0 -2
  95. data/lib/concurrent/version.rb +2 -2
  96. metadata +35 -21
  97. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
  98. data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
  99. data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
  100. data/lib/concurrent/atomic_reference/jruby.rb +0 -16
  101. data/lib/concurrent/atomic_reference/rbx.rb +0 -22
  102. data/lib/concurrent/atomic_reference/ruby.rb +0 -32
  103. data/lib/concurrent/edge.rb +0 -26
  104. data/lib/concurrent/lazy_register.rb +0 -81
  105. data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
  106. data/lib/concurrent/synchronization/truffle_object.rb +0 -31
  107. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require_relative 'lib/concurrent/version'
4
+ require_relative 'lib/concurrent/utility/engine'
5
+
6
+ core_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby.gemspec')
7
+ ext_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-ext.gemspec')
8
+ edge_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-edge.gemspec')
9
+
10
+ require 'rake/javaextensiontask'
11
+
12
+ Rake::JavaExtensionTask.new('concurrent_ruby', core_gemspec) do |ext|
13
+ ext.ext_dir = 'ext/concurrent-ruby'
14
+ ext.lib_dir = 'lib/concurrent'
15
+ end
16
+
17
+ unless Concurrent.on_jruby?
18
+ require 'rake/extensiontask'
19
+
20
+ Rake::ExtensionTask.new('concurrent_ruby_ext', ext_gemspec) do |ext|
21
+ ext.ext_dir = 'ext/concurrent-ruby-ext'
22
+ ext.lib_dir = 'lib/concurrent'
23
+ ext.source_pattern = '*.{c,h}'
24
+
25
+ ext.cross_compile = true
26
+ ext.cross_platform = ['x86-mingw32', 'x64-mingw32']
27
+ end
28
+ end
29
+
30
+ require 'rake_compiler_dock'
31
+ namespace :repackage do
32
+ desc '* with Windows fat distributions'
33
+ task :all do
34
+ Dir.chdir(__dir__) do
35
+ sh 'bundle package'
36
+ # needed only if the jar is built outside of docker
37
+ Rake::Task['lib/concurrent/concurrent_ruby.jar'].invoke
38
+ RakeCompilerDock.exec 'support/cross_building.sh'
39
+ end
40
+ end
41
+ end
42
+
43
+ require 'rubygems'
44
+ require 'rubygems/package_task'
45
+
46
+ Gem::PackageTask.new(core_gemspec) {} if core_gemspec
47
+ Gem::PackageTask.new(ext_gemspec) {} if ext_gemspec && !Concurrent.on_jruby?
48
+ Gem::PackageTask.new(edge_gemspec) {} if edge_gemspec
49
+
50
+ CLEAN.include('lib/concurrent/2.*', 'lib/concurrent/*.jar')
51
+
52
+ begin
53
+ require 'rspec'
54
+ require 'rspec/core/rake_task'
55
+
56
+ RSpec::Core::RakeTask.new(:spec)
57
+
58
+ options = %w[ --color
59
+ --backtrace
60
+ --seed 1
61
+ --format documentation
62
+ --tag ~notravis ]
63
+
64
+ namespace :spec do
65
+ desc '* Configured for ci'
66
+ RSpec::Core::RakeTask.new(:ci) do |t|
67
+ t.rspec_opts = [*options].join(' ')
68
+ end
69
+
70
+ desc '* test packaged and installed gems instead of local files'
71
+ task :installed do
72
+ Dir.chdir(__dir__) do
73
+ sh 'gem install pkg/concurrent-ruby-1.1.0.pre1.gem'
74
+ sh 'gem install pkg/concurrent-ruby-ext-1.1.0.pre1.gem' if Concurrent.on_cruby?
75
+ sh 'gem install pkg/concurrent-ruby-edge-0.4.0.pre1.gem'
76
+ ENV['NO_PATH'] = 'true'
77
+ sh 'bundle update'
78
+ sh 'bundle exec rake spec:ci'
79
+ end
80
+ end
81
+ end
82
+
83
+ desc 'executed in CI'
84
+ task :ci => [:compile, 'spec:ci']
85
+
86
+ task :default => [:clobber, :compile, :spec]
87
+ rescue LoadError => e
88
+ puts 'RSpec is not installed, skipping test task definitions: ' + e.message
89
+ end
90
+
91
+ current_yard_version_name = Concurrent::VERSION.split('.')[0..2].join('.')
92
+
93
+ begin
94
+ require 'yard'
95
+ require 'md_ruby_eval'
96
+ require_relative 'support/yard_full_types'
97
+
98
+ common_yard_options = ['--no-yardopts',
99
+ '--no-document',
100
+ '--no-private',
101
+ '--embed-mixins',
102
+ '--markup', 'markdown',
103
+ '--title', 'Concurrent Ruby',
104
+ '--template', 'default',
105
+ '--template-path', 'yard-template',
106
+ '--default-return', 'undocumented']
107
+
108
+ desc 'Generate YARD Documentation (signpost, master)'
109
+ task :yard => ['yard:signpost', 'yard:master']
110
+
111
+ namespace :yard do
112
+
113
+ desc '* eval markdown files'
114
+ task :eval_md do
115
+ Dir.chdir File.join(__dir__, 'docs-source') do
116
+ sh 'bundle exec md-ruby-eval --auto'
117
+ end
118
+ end
119
+
120
+ task :update_readme do
121
+ Dir.chdir __dir__ do
122
+ content = File.read(File.join('README.md')).
123
+ gsub(/\[([\w ]+)\]\(http:\/\/ruby-concurrency\.github\.io\/concurrent-ruby\/master\/.*\)/) do |_|
124
+ case $1
125
+ when 'LockFreeLinkedSet'
126
+ "{Concurrent::Edge::#{$1} #{$1}}"
127
+ when '.dataflow'
128
+ '{Concurrent.dataflow Concurrent.dataflow}'
129
+ when 'thread pool'
130
+ '{file:thread_pools.md thread pool}'
131
+ else
132
+ "{Concurrent::#{$1} #{$1}}"
133
+ end
134
+ end
135
+ File.write 'tmp/README.md', content
136
+ end
137
+ end
138
+
139
+ define_yard_task = -> name do
140
+ desc "* of #{name} into subdir #{name}"
141
+ YARD::Rake::YardocTask.new(name) do |yard|
142
+ yard.options.push(
143
+ '--output-dir', "docs/#{name}",
144
+ '--main', 'tmp/README.md',
145
+ *common_yard_options)
146
+ yard.files = ['./lib/**/*.rb',
147
+ './lib-edge/**/*.rb',
148
+ './ext/concurrent_ruby_ext/**/*.c',
149
+ '-',
150
+ 'docs-source/thread_pools.md',
151
+ 'docs-source/promises.out.md',
152
+ 'LICENSE.md',
153
+ 'CHANGELOG.md']
154
+ end
155
+ Rake::Task[name].prerequisites.push 'yard:eval_md', 'yard:update_readme'
156
+ end
157
+
158
+ define_yard_task.call current_yard_version_name
159
+ define_yard_task.call 'master'
160
+
161
+ desc "* signpost for versions"
162
+ YARD::Rake::YardocTask.new(:signpost) do |yard|
163
+ yard.options.push(
164
+ '--output-dir', 'docs',
165
+ '--main', 'docs-source/signpost.md',
166
+ *common_yard_options)
167
+ yard.files = ['no-lib']
168
+ end
169
+
170
+ define_uptodate_task = -> name do
171
+ namespace name do
172
+ desc "** ensure that #{name} generated documentation is matching the source code"
173
+ task :uptodate do
174
+ Dir.chdir(__dir__) do
175
+ begin
176
+ FileUtils.cp_r 'docs', 'docs-copy', verbose: true
177
+ Rake::Task["yard:#{name}"].invoke
178
+ sh 'diff -r docs/ docs-copy/'
179
+ ensure
180
+ FileUtils.rm_rf 'docs-copy', verbose: true
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ define_uptodate_task.call current_yard_version_name
188
+ define_uptodate_task.call 'master'
189
+ end
190
+
191
+ rescue LoadError => e
192
+ puts 'YARD is not installed, skipping documentation task definitions: ' + e.message
193
+ end
194
+
195
+ desc 'build, test, and publish the gem'
196
+ task :release => ['release:checks', 'release:build', 'release:test', 'release:publish']
197
+
198
+ namespace :release do
199
+ # Depends on environment of @pitr-ch
200
+
201
+ mri_version = '2.5.1'
202
+ jruby_version = 'jruby-9.1.17.0'
203
+
204
+ task :checks => "yard:#{current_yard_version_name}:uptodate" do
205
+ Dir.chdir(__dir__) do
206
+ begin
207
+ STDOUT.puts "Is this a final release build? (Do git checks?) (y/n)"
208
+ input = STDIN.gets.strip.downcase
209
+ end until %w(y n).include?(input)
210
+ if input == 'y'
211
+ sh 'test -z "$(git status --porcelain)"'
212
+ sh 'git fetch'
213
+ sh 'test $(git show-ref --verify --hash refs/heads/master) = $(git show-ref --verify --hash refs/remotes/github/master)'
214
+ end
215
+ end
216
+ end
217
+
218
+ desc '* build all *.gem files necessary for release'
219
+ task :build => 'repackage:all'
220
+
221
+ desc '* test actual installed gems instead of cloned repository on MRI and JRuby'
222
+ task :test do
223
+ Dir.chdir(__dir__) do
224
+ old = ENV['RBENV_VERSION']
225
+
226
+ ENV['RBENV_VERSION'] = mri_version
227
+ sh 'rbenv version'
228
+ sh 'bundle exec rake spec:installed'
229
+
230
+ ENV['RBENV_VERSION'] = jruby_version
231
+ sh 'rbenv version'
232
+ sh 'bundle exec rake spec:installed'
233
+
234
+ puts 'Windows build is untested'
235
+
236
+ ENV['RBENV_VERSION'] = old
237
+ end
238
+ end
239
+
240
+ desc '* do all nested steps'
241
+ task :publish => ['publish:ask', 'publish:tag', 'publish:rubygems', 'publish:post_steps']
242
+
243
+ namespace :publish do
244
+ task :ask do
245
+ begin
246
+ STDOUT.puts "Do you want to publish? (y/n)"
247
+ input = STDIN.gets.strip.downcase
248
+ end until %w(y n).include?(input)
249
+ raise 'reconsidered' if input == 'n'
250
+ end
251
+
252
+ desc '** tag HEAD with current version and push to github'
253
+ task :tag do
254
+ Dir.chdir(__dir__) do
255
+ sh "git tag v#{Concurrent::VERSION}"
256
+ sh "git tag edge-v#{Concurrent::EDGE_VERSION}"
257
+ sh "git push github v#{Concurrent::VERSION} edge-v#{Concurrent::EDGE_VERSION}"
258
+ end
259
+ end
260
+
261
+ desc '** push all *.gem files to rubygems'
262
+ task :rubygems do
263
+ Dir.chdir(__dir__) do
264
+ sh "gem push pkg/concurrent-ruby-#{Concurrent::VERSION}.gem"
265
+ sh "gem push pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem"
266
+ sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}.gem"
267
+ sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x64-mingw32.gem"
268
+ sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x86-mingw32.gem"
269
+ end
270
+ end
271
+
272
+ desc '** print post release steps'
273
+ task :post_steps do
274
+ puts 'Manually: create a release on GitHub with relevant changelog part'
275
+ puts 'Manually: send email same as release with relevant changelog part'
276
+ puts 'Manually: tweet'
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,17 @@
1
+ import org.jruby.Ruby;
2
+ import org.jruby.runtime.load.BasicLibraryService;
3
+
4
+ import java.io.IOException;
5
+
6
+ public class ConcurrentRubyService implements BasicLibraryService {
7
+
8
+ public boolean basicLoad(final Ruby runtime) throws IOException {
9
+ new com.concurrent_ruby.ext.AtomicReferenceLibrary().load(runtime, false);
10
+ new com.concurrent_ruby.ext.JavaAtomicBooleanLibrary().load(runtime, false);
11
+ new com.concurrent_ruby.ext.JavaAtomicFixnumLibrary().load(runtime, false);
12
+ new com.concurrent_ruby.ext.JavaSemaphoreLibrary().load(runtime, false);
13
+ new com.concurrent_ruby.ext.SynchronizationLibrary().load(runtime, false);
14
+ new com.concurrent_ruby.ext.JRubyMapBackendLibrary().load(runtime, false);
15
+ return true;
16
+ }
17
+ }
@@ -0,0 +1,175 @@
1
+ package com.concurrent_ruby.ext;
2
+
3
+ import java.lang.reflect.Field;
4
+ import java.io.IOException;
5
+ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
6
+ import org.jruby.Ruby;
7
+ import org.jruby.RubyClass;
8
+ import org.jruby.RubyModule;
9
+ import org.jruby.RubyNumeric;
10
+ import org.jruby.RubyObject;
11
+ import org.jruby.anno.JRubyClass;
12
+ import org.jruby.anno.JRubyMethod;
13
+ import org.jruby.runtime.ObjectAllocator;
14
+ import org.jruby.runtime.ThreadContext;
15
+ import org.jruby.runtime.builtin.IRubyObject;
16
+ import org.jruby.runtime.load.Library;
17
+
18
+ /**
19
+ * This library adds an atomic reference type to JRuby for use in the atomic
20
+ * library. We do a native version to avoid the implicit value coercion that
21
+ * normally happens through JI.
22
+ *
23
+ * @author headius
24
+ */
25
+ public class AtomicReferenceLibrary implements Library {
26
+ public void load(Ruby runtime, boolean wrap) throws IOException {
27
+ RubyModule concurrentMod = runtime.defineModule("Concurrent");
28
+ RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicReference", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR);
29
+ try {
30
+ sun.misc.Unsafe.class.getMethod("getAndSetObject", Object.class);
31
+ atomicCls.setAllocator(JRUBYREFERENCE8_ALLOCATOR);
32
+ } catch (Exception e) {
33
+ // leave it as Java 6/7 version
34
+ }
35
+ atomicCls.defineAnnotatedMethods(JRubyReference.class);
36
+ }
37
+
38
+ private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() {
39
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
40
+ return new JRubyReference(runtime, klazz);
41
+ }
42
+ };
43
+
44
+ private static final ObjectAllocator JRUBYREFERENCE8_ALLOCATOR = new ObjectAllocator() {
45
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
46
+ return new JRubyReference8(runtime, klazz);
47
+ }
48
+ };
49
+
50
+ @JRubyClass(name="JRubyReference", parent="Object")
51
+ public static class JRubyReference extends RubyObject {
52
+ volatile IRubyObject reference;
53
+
54
+ static final sun.misc.Unsafe UNSAFE;
55
+ static final long referenceOffset;
56
+
57
+ static {
58
+ try {
59
+ UNSAFE = UnsafeHolder.U;
60
+ Class k = JRubyReference.class;
61
+ referenceOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("reference"));
62
+ } catch (Exception e) {
63
+ throw new RuntimeException(e);
64
+ }
65
+ }
66
+
67
+ public JRubyReference(Ruby runtime, RubyClass klass) {
68
+ super(runtime, klass);
69
+ }
70
+
71
+ @JRubyMethod
72
+ public IRubyObject initialize(ThreadContext context) {
73
+ UNSAFE.putObject(this, referenceOffset, context.nil);
74
+ return context.nil;
75
+ }
76
+
77
+ @JRubyMethod
78
+ public IRubyObject initialize(ThreadContext context, IRubyObject value) {
79
+ UNSAFE.putObject(this, referenceOffset, value);
80
+ return context.nil;
81
+ }
82
+
83
+ @JRubyMethod(name = {"get", "value"})
84
+ public IRubyObject get() {
85
+ return reference;
86
+ }
87
+
88
+ @JRubyMethod(name = {"set", "value="})
89
+ public IRubyObject set(IRubyObject newValue) {
90
+ UNSAFE.putObjectVolatile(this, referenceOffset, newValue);
91
+ return newValue;
92
+ }
93
+
94
+ @JRubyMethod(name = {"compare_and_set", "compare_and_swap"})
95
+ public IRubyObject compare_and_set(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) {
96
+ Ruby runtime = context.runtime;
97
+
98
+ if (expectedValue instanceof RubyNumeric) {
99
+ // numerics are not always idempotent in Ruby, so we need to do slower logic
100
+ return compareAndSetNumeric(context, expectedValue, newValue);
101
+ }
102
+
103
+ return runtime.newBoolean(UNSAFE.compareAndSwapObject(this, referenceOffset, expectedValue, newValue));
104
+ }
105
+
106
+ @JRubyMethod(name = {"get_and_set", "swap"})
107
+ public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) {
108
+ // less-efficient version for Java 6 and 7
109
+ while (true) {
110
+ IRubyObject oldValue = get();
111
+ if (UNSAFE.compareAndSwapObject(this, referenceOffset, oldValue, newValue)) {
112
+ return oldValue;
113
+ }
114
+ }
115
+ }
116
+
117
+ private IRubyObject compareAndSetNumeric(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) {
118
+ Ruby runtime = context.runtime;
119
+
120
+ // loop until:
121
+ // * reference CAS would succeed for same-valued objects
122
+ // * current and expected have different values as determined by #equals
123
+ while (true) {
124
+ IRubyObject current = reference;
125
+
126
+ if (!(current instanceof RubyNumeric)) {
127
+ // old value is not numeric, CAS fails
128
+ return runtime.getFalse();
129
+ }
130
+
131
+ RubyNumeric currentNumber = (RubyNumeric)current;
132
+ if (!currentNumber.equals(expectedValue)) {
133
+ // current number does not equal expected, fail CAS
134
+ return runtime.getFalse();
135
+ }
136
+
137
+ // check that current has not changed, or else allow loop to repeat
138
+ boolean success = UNSAFE.compareAndSwapObject(this, referenceOffset, current, newValue);
139
+ if (success) {
140
+ // value is same and did not change in interim...success
141
+ return runtime.getTrue();
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ private static final class UnsafeHolder {
148
+ private UnsafeHolder(){}
149
+
150
+ public static final sun.misc.Unsafe U = loadUnsafe();
151
+
152
+ private static sun.misc.Unsafe loadUnsafe() {
153
+ try {
154
+ Class unsafeClass = Class.forName("sun.misc.Unsafe");
155
+ Field f = unsafeClass.getDeclaredField("theUnsafe");
156
+ f.setAccessible(true);
157
+ return (sun.misc.Unsafe) f.get(null);
158
+ } catch (Exception e) {
159
+ return null;
160
+ }
161
+ }
162
+ }
163
+
164
+ public static class JRubyReference8 extends JRubyReference {
165
+ public JRubyReference8(Ruby runtime, RubyClass klass) {
166
+ super(runtime, klass);
167
+ }
168
+
169
+ @Override
170
+ public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) {
171
+ // efficient version for Java 8
172
+ return (IRubyObject)UNSAFE.getAndSetObject(this, referenceOffset, newValue);
173
+ }
174
+ }
175
+ }