atomic-1.1.16/0000755000004100000410000000000012314212444013101 5ustar www-datawww-dataatomic-1.1.16/Rakefile0000644000004100000410000000321012314212444014542 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http:#www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'rake' require 'rake/testtask' task :default => :test desc "Run tests" Rake::TestTask.new :test do |t| t.libs << "lib" t.libs << "ext" t.test_files = FileList["test/**/*.rb"] end desc "Run benchmarks" task :bench do exec "ruby -Ilib -Iext test/bench_atomic.rb" end if defined?(JRUBY_VERSION) require 'ant' directory "pkg/classes" desc "Clean up build artifacts" task :clean do rm_rf "pkg/classes" rm_rf "lib/refqueue.jar" end desc "Compile the extension" task :compile_java => "pkg/classes" do |t| ant.javac :srcdir => "ext", :destdir => t.prerequisites.first, :source => "1.5", :target => "1.5", :debug => true, :classpath => "${java.class.path}:${sun.boot.class.path}" end desc "Build the jar" task :jar => :compile_java do ant.jar :basedir => "pkg/classes", :destfile => "lib/atomic_reference.jar", :includes => "**/*.class" end task :compile => :jar else require "rake/extensiontask" Rake::ExtensionTask.new "atomic" do |ext| ext.ext_dir = 'ext' ext.name ='atomic_reference' end end task :package => :compile task :test => :compile atomic-1.1.16/Gemfile0000644000004100000410000000013212314212444014370 0ustar www-datawww-datasource "https://rubygems.org" gem 'rake-compiler' gem 'minitest', :group => :development atomic-1.1.16/examples/0000755000004100000410000000000012314212444014717 5ustar www-datawww-dataatomic-1.1.16/examples/atomic_example.rb0000644000004100000410000000146712314212444020243 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'atomic' my_atomic = Atomic.new(0) my_atomic.update {|v| v + 1} puts "new value: #{my_atomic.value}" begin my_atomic.try_update {|v| v + 1} rescue Atomic::ConcurrentUpdateError => cue # deal with it (retry, propagate, etc) end puts "new value: #{my_atomic.value}" atomic-1.1.16/examples/graph_atomic_bench.rb0000644000004100000410000000423112314212444021040 0ustar www-datawww-data#!/usr/bin/env ruby # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'optparse' conf = { :vary => "threads", :lock => "atomic" } OptionParser.new do |opts| opts.on("-l", "--lock atomic|mutex") do |l| conf[:lock] = l end opts.on("-v", "--vary threads|speed") do |v| conf[:vary] = v end opts.on("-h", "--help"){ puts opts; exit } end.parse!(ARGV) result = File.open("results_#{conf[:lock]}_#{conf[:vary]}.csv", "w") if conf[:vary] == "threads" # Vary the number of concurrent threads that update the value. # # There is a total count of 1mio updates that is distributed # between the number of threads. # # A pair number of threads is used so that even add and odd substract 1. # This avoid creating instances for Bignum since the number should # stay in the Fixnum range. # (1..100).each do |i| i = i * 2 ret = [] 10.times do ret << `ruby ./bench_atomic_1.rb -l #{conf[:lock]} -t #{i}`.to_f end line = ([i] + ret).join(', ') puts line result.puts line end elsif conf[:vary] == "speed" # Varies the execution time of the update block # by using long calulation (MD5) # # NOTE: Thread.pass and sleep() are not usable by the atomic # lock. It needs to run the whole block without hitting # another atomic update otherwise it has to retry # # The expected result is that the atomic lock's performance # will hit a certain threshold where it will be worse than mutexes. # (1..30).each do |i| ret = [] 10.times do ret << `ruby ./bench_atomic_1.rb -l #{conf[:lock]} -s #{i}`.to_f end line = ([i] + ret).join(', ') puts line result.puts line end end atomic-1.1.16/examples/bench_atomic_1.rb0000644000004100000410000000566112314212444020107 0ustar www-datawww-data#!/usr/bin/env ruby # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. $: << File.expand_path('../../lib', __FILE__) require 'optparse' require 'thread' require 'benchmark' require 'atomic' Thread.abort_on_exception = true $conf = { :lock => "atomic", :num_threads => 100, :count => 100_000, :count_per_thread => nil, :slow => nil, } OptionParser.new do |opts| opts.on("-c", "--count NUM") do |n| $conf[:count] = n.to_i end opts.on("-p", "--count-per-thread") do |n| $conf[:count_per_thread] = n.to_i end opts.on("-t", "--num-threads NUM") do |n| $conf[:num_threads] = n.to_i end opts.on("-s", "--slow NUM") do |n| $conf[:slow] = n.to_i end opts.on("-l", "--lock atomic|mutex") do |x| $conf[:lock] = x end opts.on("-h", "--help"){ puts opts; exit } end.parse!(ARGV) unless $conf[:count_per_thread] $conf[:count_per_thread] = $conf[:count] / $conf[:num_threads] end $conf.delete(:count) if $conf[:slow].to_i > 0 require 'digest/md5' def slow_down $conf[:slow].times do |i| Digest::MD5.hexdigest(i.to_s) end end ret = [] 10.times do m = Benchmark.measure{ slow_down } ret << m.real end $conf[:slow_time] = [ret.min, ret.max] else def slow_down; end end $stderr.puts $conf.inspect def para_prepare(&block) num_threads = $conf[:num_threads] count = $conf[:count_per_thread] if num_threads % 2 > 0 raise ArgumentError, "num_threads must be a multiple of two" end # Keep those threads together tg = ThreadGroup.new num_threads.times do |i| diff = (i % 2 == 0) ? 1 : -1 t = Thread.new do nil until $go count.times do yield diff end end tg.add(t) end # Make sure all threads are started while tg.list.find{|t| t.status != "run"} Thread.pass end # For good measure GC.start $go = false tg end $tg = nil if $conf[:lock] == "atomic" $atom = Atomic.new(0) $tg = para_prepare do |diff| $atom.update do |x| slow_down x + diff end end else $lock = Mutex.new $value = 0 $tg = para_prepare do |diff| $lock.synchronize do slow_down $value += diff end end end # Run ! # # NOTE: It seems to me that this measurement method # is sensible to how the system dispatches his resources. # # More precise caluclation could be done using # getrusage's times ret = Benchmark.measure do $go = true $tg.list.each{|t| t.join} $go = false end puts ret.real atomic-1.1.16/examples/bench_atomic.rb0000644000004100000410000000460612314212444017665 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'benchmark' require 'atomic' require 'thread' Thread.abort_on_exception = true $go = false # for synchronizing parallel threads # number of updates on the value N = ARGV[1] ? ARGV[1].to_i : 100_000 # number of threads for parallel test M = ARGV[0] ? ARGV[0].to_i : 100 puts "*** Sequential updates ***" Benchmark.bm(10) do |x| value = 0 x.report "no lock" do N.times do value += 1 end end @lock = Mutex.new x.report "mutex" do value = 0 N.times do @lock.synchronize do value += 1 end end end @atom = Atomic.new(0) x.report "atomic" do N.times do @atom.update{|x| x += 1} end end end def para_setup(num_threads, count, &block) if num_threads % 2 > 0 raise ArgumentError, "num_threads must be a multiple of two" end raise ArgumentError, "need block" unless block_given? # Keep those threads together tg = ThreadGroup.new num_threads.times do |i| diff = (i % 2 == 0) ? 1 : -1 t = Thread.new do nil until $go count.times do yield diff end end tg.add(t) end # Make sure all threads are started while tg.list.find{|t| t.status != "run"} Thread.pass end # For good measure GC.start tg end def para_run(tg) $go = true tg.list.each{|t| t.join} $go = false end puts "*** Parallel updates ***" Benchmark.bm(10) do |bm| # This is not secure value = 0 tg = para_setup(M, N/M) do |diff| value += diff end bm.report("no lock"){ para_run(tg) } value = 0 @lock = Mutex.new tg = para_setup(M, N/M) do |diff| @lock.synchronize do value += diff end end bm.report("mutex"){ para_run(tg) } raise unless value == 0 @atom = Atomic.new(0) tg = para_setup(M, N/M) do |diff| @atom.update{|x| x + diff} end bm.report("atomic"){ para_run(tg) } raise unless @atom.value == 0 end atomic-1.1.16/.travis.yml0000644000004100000410000000023212314212444015207 0ustar www-datawww-datalanguage: ruby rvm: - 2.0.0 - 1.9.3 - 1.8.7 - jruby-18mode # JRuby in 1.8 mode - jruby-19mode # JRuby in 1.9 mode - rbx-2 jdk: - oraclejdk8 atomic-1.1.16/lib/0000755000004100000410000000000012314212444013647 5ustar www-datawww-dataatomic-1.1.16/lib/atomic/0000755000004100000410000000000012314212444015123 5ustar www-datawww-dataatomic-1.1.16/lib/atomic/concurrent_update_error.rb0000644000004100000410000000136612314212444022413 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. class Atomic class ConcurrentUpdateError < ThreadError # frozen pre-allocated backtrace to speed ConcurrentUpdateError CONC_UP_ERR_BACKTRACE = ['backtrace elided; set verbose to enable'].freeze end endatomic-1.1.16/lib/atomic/direct_update.rb0000644000004100000410000000234412314212444020267 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'atomic/concurrent_update_error' # Define update methods that use direct paths class Atomic # Pass the current value to the given block, replacing it # with the block's result. May retry if the value changes # during the block's execution. def update true until compare_and_set(old_value = get, new_value = yield(old_value)) new_value end def try_update old_value = get new_value = yield old_value unless compare_and_set(old_value, new_value) if $VERBOSE raise ConcurrentUpdateError, "Update failed" else raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE end end new_value end endatomic-1.1.16/lib/atomic/ruby.rb0000644000004100000410000000117712314212444016437 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'atomic_reference' require 'atomic/direct_update' require 'atomic/numeric_cas_wrapper'atomic-1.1.16/lib/atomic/jruby.rb0000644000004100000410000000113212314212444016600 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'atomic_reference' require 'atomic/direct_update'atomic-1.1.16/lib/atomic/rbx.rb0000644000004100000410000000141612314212444016245 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # extend Rubinius's version adding aliases and numeric logic class Atomic < Rubinius::AtomicReference alias value get alias value= set alias swap get_and_set end require 'atomic/direct_update' require 'atomic/numeric_cas_wrapper' atomic-1.1.16/lib/atomic/numeric_cas_wrapper.rb0000644000004100000410000000176712314212444021513 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. class Atomic alias _compare_and_set compare_and_set def compare_and_set(expected, new) if expected.kind_of? Numeric while true old = get return false unless old.kind_of? Numeric return false unless old == expected result = _compare_and_set(old, new) return result if result end else _compare_and_set(expected, new) end end alias compare_and_swap compare_and_set endatomic-1.1.16/lib/atomic/delegated_update.rb0000644000004100000410000000237612314212444020740 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'atomic/concurrent_update_error' # Define update methods that delegate to @ref field class Atomic # Pass the current value to the given block, replacing it # with the block's result. May retry if the value changes # during the block's execution. def update true until @ref.compare_and_set(old_value = @ref.get, new_value = yield(old_value)) new_value end def try_update old_value = @ref.get new_value = yield old_value unless @ref.compare_and_set(old_value, new_value) if $VERBOSE raise ConcurrentUpdateError, "Update failed" else raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE end end new_value end endatomic-1.1.16/lib/atomic/fallback.rb0000644000004100000410000000252412314212444017212 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'thread' require 'atomic/direct_update' # Portable/generic (but not very memory or scheduling-efficient) fallback class Atomic #:nodoc: all def initialize(value = nil) @mutex = Mutex.new @value = value end def get @mutex.synchronize { @value } end alias value get def set(new_value) @mutex.synchronize { @value = new_value } end alias value= set def get_and_set(new_value) @mutex.synchronize do old_value = @value @value = new_value old_value end end alias swap get_and_set def compare_and_set(old_value, new_value) return false unless @mutex.try_lock begin return false unless @value.equal? old_value @value = new_value ensure @mutex.unlock end true end require 'atomic/numeric_cas_wrapper' endatomic-1.1.16/lib/atomic.rb0000644000004100000410000000166412314212444015457 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. begin # force fallback impl with FORCE_ATOMIC_FALLBACK=1 if /[^0fF]/ =~ ENV['FORCE_ATOMIC_FALLBACK'] ruby_engine = 'fallback' else ruby_engine = defined?(RUBY_ENGINE)? RUBY_ENGINE : 'ruby' end require "atomic/#{ruby_engine}" rescue LoadError warn "#{__FILE__}:#{__LINE__}: unsupported Ruby engine `#{RUBY_ENGINE}', using less-efficient Atomic impl" require 'atomic/fallback' endatomic-1.1.16/atomic.gemspec0000644000004100000410000000145112314212444015723 0ustar www-datawww-data# -*- encoding: utf-8 -*- # Update these to get proper version and commit history Gem::Specification.new do |s| s.name = %q{atomic} s.version = "1.1.16" s.authors = ["Charles Oliver Nutter", "MenTaLguY", "Sokolov Yura"] s.date = Time.now.strftime('%Y-%m-%d') s.summary = "An atomic reference implementation for JRuby, Rubinius, and MRI" s.description = s.summary s.email = ["headius@headius.com", "mental@rydia.net", "funny.falcon@gmail.com"] s.homepage = "http://github.com/headius/ruby-atomic" s.require_paths = ["lib"] s.licenses = ["Apache-2.0"] s.test_files = Dir["test/test*.rb"] if defined?(JRUBY_VERSION) s.files = Dir['lib/atomic_reference.jar'] s.platform = 'java' else s.extensions = 'ext/extconf.rb' end s.files += `git ls-files`.lines.map(&:chomp) end atomic-1.1.16/metadata.yml0000644000004100000410000000330212314212444015402 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: atomic version: !ruby/object:Gem::Version version: 1.1.16 platform: ruby authors: - Charles Oliver Nutter - MenTaLguY - Sokolov Yura autorequire: bindir: bin cert_chain: [] date: 2014-03-17 00:00:00.000000000 Z dependencies: [] description: An atomic reference implementation for JRuby, Rubinius, and MRI email: - headius@headius.com - mental@rydia.net - funny.falcon@gmail.com executables: [] extensions: - ext/extconf.rb extra_rdoc_files: [] files: - ".gitignore" - ".travis.yml" - Gemfile - LICENSE - README.md - Rakefile - atomic.gemspec - examples/atomic_example.rb - examples/bench_atomic.rb - examples/bench_atomic_1.rb - examples/graph_atomic_bench.rb - ext/AtomicReferenceService.java - ext/atomic_reference.c - ext/extconf.rb - ext/org/jruby/ext/atomic/AtomicReferenceLibrary.java - lib/atomic.rb - lib/atomic/concurrent_update_error.rb - lib/atomic/delegated_update.rb - lib/atomic/direct_update.rb - lib/atomic/fallback.rb - lib/atomic/jruby.rb - lib/atomic/numeric_cas_wrapper.rb - lib/atomic/rbx.rb - lib/atomic/ruby.rb - test/test_atomic.rb homepage: http://github.com/headius/ruby-atomic licenses: - Apache-2.0 metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.2.0 signing_key: specification_version: 4 summary: An atomic reference implementation for JRuby, Rubinius, and MRI test_files: - test/test_atomic.rb has_rdoc: atomic-1.1.16/test/0000755000004100000410000000000012314212444014060 5ustar www-datawww-dataatomic-1.1.16/test/test_atomic.rb0000644000004100000410000001012412314212444016716 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http:#www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'minitest/autorun' require 'atomic' class TestAtomic < MiniTest::Test def test_construct atomic = Atomic.new assert_equal nil, atomic.value atomic = Atomic.new(0) assert_equal 0, atomic.value end def test_value atomic = Atomic.new(0) atomic.value = 1 assert_equal 1, atomic.value end def test_update # use a number outside JRuby's fixnum cache range, to ensure identity is preserved atomic = Atomic.new(1000) res = atomic.update {|v| v + 1} assert_equal 1001, atomic.value assert_equal 1001, res end def test_try_update # use a number outside JRuby's fixnum cache range, to ensure identity is preserved atomic = Atomic.new(1000) res = atomic.try_update {|v| v + 1} assert_equal 1001, atomic.value assert_equal 1001, res end def test_swap atomic = Atomic.new(1000) res = atomic.swap(1001) assert_equal 1001, atomic.value assert_equal 1000, res end def test_try_update_fails # use a number outside JRuby's fixnum cache range, to ensure identity is preserved atomic = Atomic.new(1000) assert_raises Atomic::ConcurrentUpdateError do # assigning within block exploits implementation detail for test atomic.try_update{|v| atomic.value = 1001 ; v + 1} end end def test_update_retries tries = 0 # use a number outside JRuby's fixnum cache range, to ensure identity is preserved atomic = Atomic.new(1000) # assigning within block exploits implementation detail for test atomic.update{|v| tries += 1 ; atomic.value = 1001 ; v + 1} assert_equal 2, tries end def test_numeric_cas atomic = Atomic.new(0) # 9-bit idempotent Fixnum (JRuby) max_8 = 2**256 - 1 min_8 = -(2**256) atomic.set(max_8) max_8.upto(max_8 + 2) do |i| assert atomic.compare_and_swap(i, i+1), "CAS failed for numeric #{i} => #{i + 1}" end atomic.set(min_8) min_8.downto(min_8 - 2) do |i| assert atomic.compare_and_swap(i, i-1), "CAS failed for numeric #{i} => #{i - 1}" end # 64-bit idempotent Fixnum (MRI, Rubinius) max_64 = 2**62 - 1 min_64 = -(2**62) atomic.set(max_64) max_64.upto(max_64 + 2) do |i| assert atomic.compare_and_swap(i, i+1), "CAS failed for numeric #{i} => #{i + 1}" end atomic.set(min_64) min_64.downto(min_64 - 2) do |i| assert atomic.compare_and_swap(i, i-1), "CAS failed for numeric #{i} => #{i - 1}" end # 64-bit overflow into Bignum (JRuby) max_64 = 2**63 - 1 min_64 = (-2**63) atomic.set(max_64) max_64.upto(max_64 + 2) do |i| assert atomic.compare_and_swap(i, i+1), "CAS failed for numeric #{i} => #{i + 1}" end atomic.set(min_64) min_64.downto(min_64 - 2) do |i| assert atomic.compare_and_swap(i, i-1), "CAS failed for numeric #{i} => #{i - 1}" end # non-idempotent Float (JRuby, Rubinius, MRI < 2.0.0 or 32-bit) atomic.set(1.0 + 0.1) assert atomic.compare_and_set(1.0 + 0.1, 1.2), "CAS failed for #{1.0 + 0.1} => 1.2" # Bignum atomic.set(2**100) assert atomic.compare_and_set(2**100, 0), "CAS failed for #{2**100} => 0" # Rational require 'rational' unless ''.respond_to? :to_r atomic.set(Rational(1,3)) assert atomic.compare_and_set(Rational(1,3), 0), "CAS failed for #{Rational(1,3)} => 0" # Complex require 'complex' unless ''.respond_to? :to_c atomic.set(Complex(1,2)) assert atomic.compare_and_set(Complex(1,2), 0), "CAS failed for #{Complex(1,2)} => 0" end end atomic-1.1.16/.gitignore0000644000004100000410000000012512314212444015067 0ustar www-datawww-data.*.sw? lib/atomic_reference.jar /nbproject ext/*.bundle ext/*.so ext/*.jar pkg *.gem atomic-1.1.16/LICENSE0000644000004100000410000002201612314212444014107 0ustar www-datawww-dataApache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS atomic-1.1.16/checksums.yaml.gz0000444000004100000410000000041412314212444016366 0ustar www-datawww-data&SeQdAb:бΕV!Fz"=+rgrR<0sDtn!ifuner0DkgQvI|LWw=:n'wh u0@ɁSGNoՀ猅$06so;_<&/˹nKNd;4ׄRQ.]!42#}atomic-1.1.16/ext/0000755000004100000410000000000012314212444013701 5ustar www-datawww-dataatomic-1.1.16/ext/extconf.rb0000644000004100000410000000232112314212444015672 0ustar www-datawww-data# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'mkmf' extension_name = 'atomic_reference' dir_config(extension_name) have_header "libkern/OSAtomic.h" def compiler_is_gcc if CONFIG["GCC"] && CONFIG["GCC"] != "" return true elsif ( # This could stand to be more generic... but I am afraid. CONFIG["CC"] =~ /\bgcc\b/ ) return true end return false end if compiler_is_gcc case CONFIG["arch"] when /mswin32|mingw|solaris/ $CFLAGS += " -march=native" when 'i686-linux' $CFLAGS += " -march=i686" end end try_run(< #if defined(__sun) #include #endif #ifdef HAVE_LIBKERN_OSATOMIC_H #include #endif static void ir_mark(void *value) { rb_gc_mark_maybe((VALUE) value); } static VALUE ir_alloc(VALUE klass) { return rb_data_object_alloc(klass, (void *) Qnil, ir_mark, NULL); } static VALUE ir_initialize(int argc, VALUE* argv, VALUE self) { VALUE value = Qnil; if (rb_scan_args(argc, argv, "01", &value) == 1) { value = argv[0]; } DATA_PTR(self) = (void *) value; return Qnil; } static VALUE ir_get(VALUE self) { return (VALUE) DATA_PTR(self); } static VALUE ir_set(VALUE self, VALUE new_value) { DATA_PTR(self) = (void *) new_value; return new_value; } static VALUE ir_get_and_set(VALUE self, VALUE new_value) { VALUE old_value; old_value = (VALUE) DATA_PTR(self); DATA_PTR(self) = (void *) new_value; return old_value; } static VALUE ir_compare_and_set(volatile VALUE self, VALUE expect_value, VALUE new_value) { #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 if (OSAtomicCompareAndSwap64(expect_value, new_value, &DATA_PTR(self))) { return Qtrue; } #elif defined(__sun) /* Assuming VALUE is uintptr_t */ /* Based on the definition of uintptr_t from /usr/include/sys/int_types.h */ #if defined(_LP64) || defined(_I32LPx) /* 64-bit: uintptr_t === unsigned long */ if (atomic_cas_ulong((uintptr_t *) &DATA_PTR(self), expect_value, new_value)) { return Qtrue; } #else /* 32-bit: uintptr_t === unsigned int */ if (atomic_cas_uint((uintptr_t *) &DATA_PTR(self), expect_value, new_value)) { return Qtrue; } #endif #elif defined _MSC_VER && defined _M_AMD64 if (InterlockedCompareExchange64((LONGLONG*)&DATA_PTR(self), new_value, expect_value)) { return Qtrue; } #elif defined _MSC_VER && defined _M_IX86 if (InterlockedCompareExchange((LONG*)&DATA_PTR(self), new_value, expect_value)) { return Qtrue; } #else if (__sync_bool_compare_and_swap(&DATA_PTR(self), expect_value, new_value)) { return Qtrue; } #endif return Qfalse; } void Init_atomic_reference() { VALUE cAtomic; cAtomic = rb_define_class_under(rb_cObject, "Atomic", rb_cObject); rb_define_alloc_func(cAtomic, ir_alloc); rb_define_method(cAtomic, "initialize", ir_initialize, -1); rb_define_method(cAtomic, "get", ir_get, 0); rb_define_method(cAtomic, "value", ir_get, 0); rb_define_method(cAtomic, "set", ir_set, 1); rb_define_method(cAtomic, "value=", ir_set, 1); rb_define_method(cAtomic, "get_and_set", ir_get_and_set, 1); rb_define_method(cAtomic, "swap", ir_get_and_set, 1); rb_define_method(cAtomic, "compare_and_set", ir_compare_and_set, 2); rb_define_method(cAtomic, "compare_and_swap", ir_compare_and_set, 2); } atomic-1.1.16/ext/org/0000755000004100000410000000000012314212444014470 5ustar www-datawww-dataatomic-1.1.16/ext/org/jruby/0000755000004100000410000000000012314212444015623 5ustar www-datawww-dataatomic-1.1.16/ext/org/jruby/ext/0000755000004100000410000000000012314212444016423 5ustar www-datawww-dataatomic-1.1.16/ext/org/jruby/ext/atomic/0000755000004100000410000000000012314212444017677 5ustar www-datawww-dataatomic-1.1.16/ext/org/jruby/ext/atomic/AtomicReferenceLibrary.java0000644000004100000410000001546512314212444025135 0ustar www-datawww-data// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package org.jruby.ext.atomic; import java.lang.reflect.Field; import java.io.IOException; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyModule; import org.jruby.RubyNumeric; import org.jruby.RubyObject; import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.load.Library; /** * This library adds an atomic reference type to JRuby for use in the atomic * library. We do a native version to avoid the implicit value coercion that * normally happens through JI. * * @author headius */ public class AtomicReferenceLibrary implements Library { public void load(Ruby runtime, boolean wrap) throws IOException { RubyClass atomicCls = runtime.defineClass("Atomic", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); try { sun.misc.Unsafe.class.getMethod("getAndSetObject", Object.class); atomicCls.setAllocator(JRUBYREFERENCE8_ALLOCATOR); } catch (Exception e) { // leave it as Java 6/7 version } atomicCls.defineAnnotatedMethods(JRubyReference.class); } private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klazz) { return new JRubyReference(runtime, klazz); } }; private static final ObjectAllocator JRUBYREFERENCE8_ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klazz) { return new JRubyReference8(runtime, klazz); } }; @JRubyClass(name="JRubyReference", parent="Object") public static class JRubyReference extends RubyObject { volatile IRubyObject reference; static final sun.misc.Unsafe UNSAFE; static final long referenceOffset; static { try { UNSAFE = UnsafeHolder.U; Class k = JRubyReference.class; referenceOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("reference")); } catch (Exception e) { throw new RuntimeException(e); } } public JRubyReference(Ruby runtime, RubyClass klass) { super(runtime, klass); } @JRubyMethod public IRubyObject initialize(ThreadContext context) { UNSAFE.putObject(this, referenceOffset, context.nil); return context.nil; } @JRubyMethod public IRubyObject initialize(ThreadContext context, IRubyObject value) { UNSAFE.putObject(this, referenceOffset, value); return context.nil; } @JRubyMethod(name = {"get", "value"}) public IRubyObject get() { return reference; } @JRubyMethod(name = {"set", "value="}) public IRubyObject set(IRubyObject newValue) { UNSAFE.putObjectVolatile(this, referenceOffset, newValue); return newValue; } @JRubyMethod(name = {"compare_and_set", "compare_and_swap"}) public IRubyObject compare_and_set(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) { Ruby runtime = context.runtime; if (expectedValue instanceof RubyNumeric) { // numerics are not always idempotent in Ruby, so we need to do slower logic return compareAndSetNumeric(context, expectedValue, newValue); } return runtime.newBoolean(UNSAFE.compareAndSwapObject(this, referenceOffset, expectedValue, newValue)); } @JRubyMethod(name = {"get_and_set", "swap"}) public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) { // less-efficient version for Java 6 and 7 while (true) { IRubyObject oldValue = get(); if (UNSAFE.compareAndSwapObject(this, referenceOffset, oldValue, newValue)) { return oldValue; } } } private IRubyObject compareAndSetNumeric(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) { Ruby runtime = context.runtime; // loop until: // * reference CAS would succeed for same-valued objects // * current and expected have different values as determined by #equals while (true) { IRubyObject current = reference; if (!(current instanceof RubyNumeric)) { // old value is not numeric, CAS fails return runtime.getFalse(); } RubyNumeric currentNumber = (RubyNumeric)current; if (!currentNumber.equals(expectedValue)) { // current number does not equal expected, fail CAS return runtime.getFalse(); } // check that current has not changed, or else allow loop to repeat boolean success = UNSAFE.compareAndSwapObject(this, referenceOffset, current, newValue); if (success) { // value is same and did not change in interim...success return runtime.getTrue(); } } } } private static final class UnsafeHolder { private UnsafeHolder(){} public static final sun.misc.Unsafe U = loadUnsafe(); private static sun.misc.Unsafe loadUnsafe() { try { Class unsafeClass = Class.forName("sun.misc.Unsafe"); Field f = unsafeClass.getDeclaredField("theUnsafe"); f.setAccessible(true); return (sun.misc.Unsafe) f.get(null); } catch (Exception e) { return null; } } } public static class JRubyReference8 extends JRubyReference { public JRubyReference8(Ruby runtime, RubyClass klass) { super(runtime, klass); } @Override public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) { // efficient version for Java 8 return (IRubyObject)UNSAFE.getAndSetObject(this, referenceOffset, newValue); } } } atomic-1.1.16/README.md0000644000004100000410000000444412314212444014366 0ustar www-datawww-dataatomic: An atomic reference implementation for JRuby, Rubinius, and MRI. ======================================================================== [![Build Status](https://travis-ci.org/headius/ruby-atomic.png?branch=master)](https://travis-ci.org/headius/ruby-atomic) Summary ======= This library provides: * an Atomic class that guarantees atomic updates to its contained value The Atomic class provides accessors for the contained "value" plus two update methods: * update will run the provided block, passing the current value and replacing it with the block result if the value has not been changed in the meantime. It may run the block repeatedly if there are other concurrent updates in progress. * try_update will run the provided block, passing the current value and replacing it with the block result. If the value changes before the update can happen, it will throw an Atomic::ConcurrentUpdateError. The atomic repository is at http://github.com/headius/ruby-atomic. Usage ===== The simplest way to use "atomic" is to call the "update" or "try_update" methods. "try_update" and "update" both call the given block, passing the current value and using the block's result as the new value. If the value is updated by another thread before the block completes, "try update" raises a ConcurrentUpdateError and "update" retries the block. Because "update" may call the block several times when multiple threads are all updating the same value, the block's logic should be kept as simple as possible. ```ruby require 'atomic' my_atomic = Atomic.new(0) my_atomic.update {|v| v + 1} begin my_atomic.try_update {|v| v + 1} rescue Atomic::ConcurrentUpdateError => cue # deal with it (retry, propagate, etc) end ``` It's also possible to use the regular get/set operations on the Atomic, if you want to avoid the exception and respond to contended changes in some other way. ```ruby my_atomic = Atomic.new(0) my_atomic.value # => 0 my_atomic.value = 1 my_atomic.swap(2) # => 1 my_atomic.compare_and_swap(2, 3) # => true, updated to 3 my_atomic.compare_and_swap(2, 3) # => false, current is not 2 ``` Building ======== As of 1.1.0, JDK8 is required to build the atomic gem, since it attempts to use the new atomic Unsafe.getAndSetObject method only in JDK8. The resulting code should still work fine as far back as Java 5.