atomic 1.1.7 → 1.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5e0ab48fc1844ea59b735a512b398743faadb711
4
+ data.tar.gz: 9965516fa2a0b20e14dcf5e3864cfd4b13645800
5
+ SHA512:
6
+ metadata.gz: 0dcae6c43515b160e14272624c823e6d31caf285338e4c92c438e4487611f0b73a5c2bc70e0ce44ec7507e1334bca351419f026e95691d5fba6aa9b77224e205
7
+ data.tar.gz: a0383c6c8d551068ec91b8ced9799f36964af4468450cab279588d5aa4988fc1680441d9c8a24aa3324ab4b4ff46d4dc93447635eea6b613379512978da71bca
data/.gitignore CHANGED
@@ -3,3 +3,5 @@ lib/atomic_reference.jar
3
3
  ext/*.bundle
4
4
  ext/*.so
5
5
  ext/*.jar
6
+ pkg
7
+ *.gem
@@ -1,8 +1,8 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  # Update these to get proper version and commit history
4
- new_version = "1.1.7"
5
- old_version = "1.1.6"
4
+ new_version = "1.1.8"
5
+ old_version = "1.1.7"
6
6
 
7
7
  git_lines = `git log --oneline #{old_version}...#{new_version}`.lines.map {|str| "* #{str}"}.join
8
8
  doc_lines = File.readlines("README.rdoc")
@@ -12,7 +12,6 @@ description = <<EOS
12
12
  == Changes since #{old_version}
13
13
 
14
14
  #{git_lines}
15
- #{doc_lines[2..-1].join("\n")}
16
15
  EOS
17
16
 
18
17
  Gem::Specification.new do |s|
@@ -25,6 +24,7 @@ Gem::Specification.new do |s|
25
24
  s.homepage = "http://github.com/headius/ruby-atomic"
26
25
  s.require_paths = ["lib"]
27
26
  s.summary = "An atomic reference implementation for JRuby, Rubinius, and MRI"
27
+ s.licenses = ["Apache-2.0"]
28
28
  s.test_files = Dir["test/test*.rb"]
29
29
  if defined?(JRUBY_VERSION)
30
30
  s.files = Dir['lib/atomic_reference.jar']
@@ -14,7 +14,12 @@ require 'mkmf'
14
14
  extension_name = 'atomic_reference'
15
15
  dir_config(extension_name)
16
16
 
17
- try_run(<<CODE) && ($defs << '-DHAVE_GCC_CAS')
17
+ case CONFIG["arch"]
18
+ when /mswin32|mingw/
19
+ $CFLAGS += " -march=native"
20
+ end
21
+
22
+ try_run(<<CODE,$CFLAGS) && ($defs << '-DHAVE_GCC_CAS')
18
23
  int main() {
19
24
  int i = 1;
20
25
  __sync_bool_compare_and_swap(&i, 1, 4);
@@ -22,9 +27,4 @@ int main() {
22
27
  }
23
28
  CODE
24
29
 
25
- case CONFIG["arch"]
26
- when /mswin32|mingw/
27
- $CFLAGS += " -march=native"
28
- end
29
-
30
30
  create_makefile(extension_name)
@@ -17,6 +17,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
17
17
  import org.jruby.Ruby;
18
18
  import org.jruby.RubyClass;
19
19
  import org.jruby.RubyModule;
20
+ import org.jruby.RubyNumeric;
20
21
  import org.jruby.RubyObject;
21
22
  import org.jruby.anno.JRubyClass;
22
23
  import org.jruby.anno.JRubyMethod;
@@ -103,8 +104,15 @@ public class AtomicReferenceLibrary implements Library {
103
104
  }
104
105
 
105
106
  @JRubyMethod(name = {"compare_and_set", "compare_and_swap"})
106
- public IRubyObject compare_and_set(ThreadContext context, IRubyObject oldValue, IRubyObject newValue) {
107
- return context.runtime.newBoolean(UNSAFE.compareAndSwapObject(this, referenceOffset, oldValue, newValue));
107
+ public IRubyObject compare_and_set(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) {
108
+ Ruby runtime = context.runtime;
109
+
110
+ if (expectedValue instanceof RubyNumeric) {
111
+ // numerics are not always idempotent in Ruby, so we need to do slower logic
112
+ return compareAndSetNumeric(context, expectedValue, newValue);
113
+ }
114
+
115
+ return runtime.newBoolean(UNSAFE.compareAndSwapObject(this, referenceOffset, expectedValue, newValue));
108
116
  }
109
117
 
110
118
  @JRubyMethod(name = {"get_and_set", "swap"})
@@ -117,6 +125,35 @@ public class AtomicReferenceLibrary implements Library {
117
125
  }
118
126
  }
119
127
  }
128
+
129
+ private IRubyObject compareAndSetNumeric(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) {
130
+ Ruby runtime = context.runtime;
131
+
132
+ // loop until:
133
+ // * reference CAS would succeed for same-valued objects
134
+ // * current and expected have different values as determined by #equals
135
+ while (true) {
136
+ IRubyObject current = reference;
137
+
138
+ if (!(current instanceof RubyNumeric)) {
139
+ // old value is not numeric, CAS fails
140
+ return runtime.getFalse();
141
+ }
142
+
143
+ RubyNumeric currentNumber = (RubyNumeric)current;
144
+ if (!currentNumber.equals(expectedValue)) {
145
+ // current number does not equal expected, fail CAS
146
+ return runtime.getFalse();
147
+ }
148
+
149
+ // check that current has not changed, or else allow loop to repeat
150
+ boolean success = UNSAFE.compareAndSwapObject(this, referenceOffset, current, newValue);
151
+ if (success) {
152
+ // value is same and did not change in interim...success
153
+ return runtime.getTrue();
154
+ }
155
+ }
156
+ }
120
157
  }
121
158
 
122
159
  public static class JRubyReference8 extends JRubyReference {
@@ -11,8 +11,15 @@
11
11
  # limitations under the License.
12
12
 
13
13
  begin
14
- ruby_engine = defined?(RUBY_ENGINE)? RUBY_ENGINE : 'ruby'
14
+ # force fallback impl with FORCE_ATOMIC_FALLBACK=1
15
+ if /[^0fF]/ =~ ENV['FORCE_ATOMIC_FALLBACK']
16
+ ruby_engine = 'fallback'
17
+ else
18
+ ruby_engine = defined?(RUBY_ENGINE)? RUBY_ENGINE : 'ruby'
19
+ end
20
+
15
21
  require "atomic/#{ruby_engine}"
16
22
  rescue LoadError
23
+ warn "#{__FILE__}:#{__LINE__}: unsupported Ruby engine `#{RUBY_ENGINE}', using less-efficient Atomic impl"
17
24
  require 'atomic/fallback'
18
25
  end
@@ -1,3 +1,15 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
1
13
  class Atomic
2
14
  class ConcurrentUpdateError < ThreadError
3
15
  # frozen pre-allocated backtrace to speed ConcurrentUpdateError
@@ -1,3 +1,15 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
1
13
  require 'atomic/concurrent_update_error'
2
14
 
3
15
  # Define update methods that delegate to @ref field
@@ -1,3 +1,15 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
1
13
  require 'atomic/concurrent_update_error'
2
14
 
3
15
  # Define update methods that use direct paths
@@ -1,4 +1,14 @@
1
- warn "#{__FILE__}:#{__LINE__}: unsupported Ruby engine `#{RUBY_ENGINE}', using less-efficient Atomic impl"
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
2
12
 
3
13
  require 'thread'
4
14
  require 'atomic/direct_update'
@@ -39,5 +49,6 @@ class Atomic #:nodoc: all
39
49
  end
40
50
  true
41
51
  end
42
- alias compare_and_swap compare_and_set
52
+
53
+ require 'atomic/numeric_cas_wrapper'
43
54
  end
@@ -1,2 +1,14 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
1
13
  require 'atomic_reference'
2
14
  require 'atomic/direct_update'
@@ -0,0 +1,32 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ class Atomic
14
+ alias _compare_and_set compare_and_set
15
+ def compare_and_set(expected, new)
16
+ if expected.kind_of? Numeric
17
+ while true
18
+ old = get
19
+
20
+ return false unless old.kind_of? Numeric
21
+
22
+ return false unless old == expected
23
+
24
+ result = _compare_and_set(old, new)
25
+ return result if result
26
+ end
27
+ else
28
+ _compare_and_set(expected, new)
29
+ end
30
+ end
31
+ alias compare_and_swap compare_and_set
32
+ end
@@ -1,11 +1,21 @@
1
- Atomic = Rubinius::AtomicReference
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
2
12
 
3
- require 'atomic/direct_update'
4
-
5
- # define additional aliases
6
- class Atomic
13
+ # extend Rubinius's version adding aliases and numeric logic
14
+ class Atomic < Rubinius::AtomicReference
7
15
  alias value get
8
16
  alias value= set
9
- alias compare_and_swap compare_and_set
10
17
  alias swap get_and_set
11
- end
18
+ end
19
+
20
+ require 'atomic/direct_update'
21
+ require 'atomic/numeric_cas_wrapper'
@@ -1,2 +1,15 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
1
13
  require 'atomic_reference'
2
- require 'atomic/direct_update'
14
+ require 'atomic/direct_update'
15
+ require 'atomic/numeric_cas_wrapper'
@@ -72,4 +72,68 @@ class TestAtomic < Test::Unit::TestCase
72
72
  atomic.update{|v| tries += 1 ; atomic.value = 1001 ; v + 1}
73
73
  assert_equal 2, tries
74
74
  end
75
+
76
+ def test_numeric_cas
77
+ atomic = Atomic.new(0)
78
+
79
+ # 9-bit idempotent Fixnum (JRuby)
80
+ max_8 = 2**256 - 1
81
+ min_8 = -(2**256)
82
+
83
+ atomic.set(max_8)
84
+ max_8.upto(max_8 + 2) do |i|
85
+ assert atomic.compare_and_swap(i, i+1), "CAS failed for numeric #{i} => #{i + 1}"
86
+ end
87
+
88
+ atomic.set(min_8)
89
+ min_8.downto(min_8 - 2) do |i|
90
+ assert atomic.compare_and_swap(i, i-1), "CAS failed for numeric #{i} => #{i - 1}"
91
+ end
92
+
93
+ # 64-bit idempotent Fixnum (MRI, Rubinius)
94
+ max_64 = 2**62 - 1
95
+ min_64 = -(2**62)
96
+
97
+ atomic.set(max_64)
98
+ max_64.upto(max_64 + 2) do |i|
99
+ assert atomic.compare_and_swap(i, i+1), "CAS failed for numeric #{i} => #{i + 1}"
100
+ end
101
+
102
+ atomic.set(min_64)
103
+ min_64.downto(min_64 - 2) do |i|
104
+ assert atomic.compare_and_swap(i, i-1), "CAS failed for numeric #{i} => #{i - 1}"
105
+ end
106
+
107
+ # 64-bit overflow into Bignum (JRuby)
108
+ max_64 = 2**63 - 1
109
+ min_64 = (-2**63)
110
+
111
+ atomic.set(max_64)
112
+ max_64.upto(max_64 + 2) do |i|
113
+ assert atomic.compare_and_swap(i, i+1), "CAS failed for numeric #{i} => #{i + 1}"
114
+ end
115
+
116
+ atomic.set(min_64)
117
+ min_64.downto(min_64 - 2) do |i|
118
+ assert atomic.compare_and_swap(i, i-1), "CAS failed for numeric #{i} => #{i - 1}"
119
+ end
120
+
121
+ # non-idempotent Float (JRuby, Rubinius, MRI < 2.0.0 or 32-bit)
122
+ atomic.set(1.0 + 0.1)
123
+ assert atomic.compare_and_set(1.0 + 0.1, 1.2), "CAS failed for #{1.0 + 0.1} => 1.2"
124
+
125
+ # Bignum
126
+ atomic.set(2**100)
127
+ assert atomic.compare_and_set(2**100, 0), "CAS failed for #{2**100} => 0"
128
+
129
+ # Rational
130
+ require 'rational' unless ''.respond_to? :to_r
131
+ atomic.set(Rational(1,3))
132
+ assert atomic.compare_and_set(Rational(1,3), 0), "CAS failed for #{Rational(1,3)} => 0"
133
+
134
+ # Complex
135
+ require 'complex' unless ''.respond_to? :to_c
136
+ atomic.set(Complex(1,2))
137
+ assert atomic.compare_and_set(Complex(1,2), 0), "CAS failed for #{Complex(1,2)} => 0"
138
+ end
75
139
  end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atomic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.7
5
- prerelease:
4
+ version: 1.1.8
6
5
  platform: ruby
7
6
  authors:
8
7
  - Charles Oliver Nutter
@@ -11,39 +10,26 @@ authors:
11
10
  autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2013-04-11 00:00:00.000000000 Z
13
+ date: 2013-04-16 00:00:00.000000000 Z
15
14
  dependencies: []
16
- description: ! "= An atomic reference implementation for JRuby, Rubinius, and MRI.\n\n\n==
17
- Changes since 1.1.6\n\n* fa74e58 Bump version to 1.1.7.\n* 9b58cf0 Merge pull request
18
- #20 from lazyspark/master\n* 14fc4a9 Removed compiled gem from src\n* 7d0ade6 Changing
19
- the arch to native, to make it successfully compile using mingw\n\n== Summary\n\n\n\nThis
20
- library provides:\n\n\n\n* an Atomic class that guarantees atomic updates to its
21
- contained value\n\n\n\nThe Atomic class provides accessors for the contained \"value\"
22
- plus two update methods:\n\n\n\n* update will run the provided block, passing the
23
- current value and replacing it with the block result iff the value has not been
24
- changed in the mean time. It may run the block repeatedly if there are other concurrent
25
- updates in progress.\n\n* try_update will run the provided block, passing the current
26
- value and replacing it with the block result. If the value changes before the update
27
- can happen, it will throw Atomic::ConcurrentUpdateError.\n\n\n\nThe atomic repository
28
- is at http://github.com/headius/ruby-atomic.\n\n\n\n== Usage\n\n\n\nThe simplest
29
- way to use \"atomic\" is to call the \"update\" or \"try_update\" methods.\n\n\n\n\"try_update\"
30
- and \"update\" both call the given block, passing the current value and using the
31
- block's result as the new value. If the value is updated by another thread before
32
- the block completes, \"try update\" raises a ConcurrentUpdateError and \"update\"
33
- retries the block. Because \"update\" may call the block several times when multiple
34
- threads are all updating the same value, the block's logic should be kept as simple
35
- as possible.\n\n\n\n require 'atomic'\n\n\n\n my_atomic = Atomic.new(0)\n\n
36
- \ my_atomic.update {|v| v + 1}\n\n begin\n\n my_atomic.try_update {|v|
37
- v + 1}\n\n rescue Atomic::ConcurrentUpdateError => cue\n\n # deal with it
38
- (retry, propagate, etc)\n\n end\n\n\n\nIt's also possible to use the regular
39
- get/set operations on the Atomic, if you want to avoid the exception and respond
40
- to contended changes in some other way.\n\n\n\n my_atomic = Atomic.new(0)\n\n
41
- \ my_atomic.value # => 0\n\n my_atomic.value = 1\n\n my_atomic.swap(2) #
42
- => 1\n\n my_atomic.compare_and_swap(2, 3) # => true, updated to 3\n\n my_atomic.compare_and_swap(2,
43
- 3) # => false, current is not 2\n\n\n\n== Building\n\n\n\nAs of 1.1.0, JDK8 is required
44
- to build the atomic gem, since it attempts to use the new atomic Unsafe.getAndSetObject
45
- method only in JDK8. The resulting code should still work fine as far back as Java
46
- 5.\n"
15
+ description: |+
16
+ = An atomic reference implementation for JRuby, Rubinius, and MRI.
17
+
18
+
19
+ == Changes since 1.1.7
20
+
21
+ * 0b1cd2d Add license to gemspec.
22
+ * e8d43f1 Limit gem description to just commit log.
23
+ * 6869b89 Version 1.1.8.
24
+ * ed84820 Add numeric CAS logic for fallback, MRI, and Rubinius.
25
+ * 40eca8e Make test work under 1.8-compat.
26
+ * b6398e5 Add some missing license headers.
27
+ * c291231 Allow forcing fallback impl.
28
+ * 6c2ce92 Fix #21 by moving win32 CFLAGS tweak above other test compiles.
29
+ * e59e92b Add CAS tests for more numeric types.
30
+ * 0749145 Add pkg/ and *.gem to gitignore.
31
+ * 073b4c1 Partial fix for numeric idempotence differences across Rubies.
32
+
47
33
  email:
48
34
  - headius@headius.com
49
35
  - mental@rydia.net
@@ -74,31 +60,32 @@ files:
74
60
  - lib/atomic/direct_update.rb
75
61
  - lib/atomic/fallback.rb
76
62
  - lib/atomic/jruby.rb
63
+ - lib/atomic/numeric_cas_wrapper.rb
77
64
  - lib/atomic/rbx.rb
78
65
  - lib/atomic/ruby.rb
79
66
  homepage: http://github.com/headius/ruby-atomic
80
- licenses: []
67
+ licenses:
68
+ - Apache-2.0
69
+ metadata: {}
81
70
  post_install_message:
82
71
  rdoc_options: []
83
72
  require_paths:
84
73
  - lib
85
74
  required_ruby_version: !ruby/object:Gem::Requirement
86
- none: false
87
75
  requirements:
88
- - - ! '>='
76
+ - - '>='
89
77
  - !ruby/object:Gem::Version
90
78
  version: '0'
91
79
  required_rubygems_version: !ruby/object:Gem::Requirement
92
- none: false
93
80
  requirements:
94
- - - ! '>='
81
+ - - '>='
95
82
  - !ruby/object:Gem::Version
96
83
  version: '0'
97
84
  requirements: []
98
85
  rubyforge_project:
99
- rubygems_version: 1.8.24
86
+ rubygems_version: 2.0.3
100
87
  signing_key:
101
- specification_version: 3
88
+ specification_version: 4
102
89
  summary: An atomic reference implementation for JRuby, Rubinius, and MRI
103
90
  test_files:
104
91
  - test/test_atomic.rb