atomic 0.0.1
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.
- data/README.txt +33 -0
- data/Rakefile +10 -0
- data/atomic.gemspec +15 -0
- data/examples/atomic_example.rb +12 -0
- data/lib/atomic.rb +79 -0
- data/test/test_atomic.rb +35 -0
- metadata +72 -0
data/README.txt
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
atomic: An atomic reference implementation for JRuby and green or GIL-threaded
|
2
|
+
Ruby implementations (MRI 1.8/1.9, Rubinius)
|
3
|
+
|
4
|
+
== Summary ==
|
5
|
+
|
6
|
+
This library provides:
|
7
|
+
|
8
|
+
* an Atomic class that guarantees atomic updates to its contained value
|
9
|
+
|
10
|
+
The Atomic class provides accessors for the contained "value" plus two update
|
11
|
+
methods:
|
12
|
+
|
13
|
+
* update will run the provided block, passing the current value and replacing
|
14
|
+
it with the block result iff the value has not been changed in the mean time.
|
15
|
+
It may run the block repeatedly if there are other concurrent updates in
|
16
|
+
progress.
|
17
|
+
* try_update will run the provided block, passing the current value and
|
18
|
+
replacing it with the block result. If the value changes before the update
|
19
|
+
can happen, it will throw Atomic::ConcurrentUpdateError.
|
20
|
+
|
21
|
+
The atomic repository is at http://github.com/headius/ruby-atomic.
|
22
|
+
|
23
|
+
== Usage ==
|
24
|
+
|
25
|
+
require 'atomic'
|
26
|
+
|
27
|
+
my_atomic = Atomic.new(0)
|
28
|
+
my_atomic.update {|v| v + 1}
|
29
|
+
begin
|
30
|
+
my_atomic.try_update {|v| v + 1}
|
31
|
+
rescue Atomic::ConcurrentUpdateError => cue
|
32
|
+
# deal with it (retry, propagate, etc)
|
33
|
+
end
|
data/Rakefile
ADDED
data/atomic.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{atomic}
|
5
|
+
s.version = "0.0.1"
|
6
|
+
s.authors = ["Charles Oliver Nutter", "MenTaLguY"]
|
7
|
+
s.date = Time.now.strftime('YYYY-MM-DD')
|
8
|
+
s.description = "An atomic reference implementation for JRuby and green or GIL-threaded impls"
|
9
|
+
s.email = ["headius@headius.com", "mental@rydia.net"]
|
10
|
+
s.files = Dir['{lib,examples,test}/**/*'] + Dir['{*.txt,*.gemspec,Rakefile}']
|
11
|
+
s.homepage = "http://github.com/headius/ruby-atomic"
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.summary = "An atomic reference implementation for JRuby and green or GIL-threaded impls"
|
14
|
+
s.test_files = Dir["test/test*.rb"]
|
15
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'atomic'
|
2
|
+
|
3
|
+
my_atomic = Atomic.new(0)
|
4
|
+
my_atomic.update {|v| v + 1}
|
5
|
+
puts "new value: #{my_atomic.value}"
|
6
|
+
|
7
|
+
begin
|
8
|
+
my_atomic.try_update {|v| v + 1}
|
9
|
+
rescue Atomic::ConcurrentUpdateError => cue
|
10
|
+
# deal with it (retry, propagate, etc)
|
11
|
+
end
|
12
|
+
puts "new value: #{my_atomic.value}"
|
data/lib/atomic.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
base_atomic = Class.new do
|
2
|
+
class ConcurrentUpdateError < ThreadError
|
3
|
+
end
|
4
|
+
|
5
|
+
def initialize(value=nil)
|
6
|
+
@ref = InternalReference.new(value)
|
7
|
+
end
|
8
|
+
|
9
|
+
def value
|
10
|
+
@ref.get
|
11
|
+
end
|
12
|
+
|
13
|
+
def value=(new_value)
|
14
|
+
@ref.set(new_value)
|
15
|
+
new_value
|
16
|
+
end
|
17
|
+
|
18
|
+
# Pass the current value to the given block, replacing it
|
19
|
+
# with the block's result. May retry if the value changes
|
20
|
+
# during the block's execution.
|
21
|
+
def update
|
22
|
+
begin
|
23
|
+
try_update { |v| yield v }
|
24
|
+
rescue ConcurrentUpdateError
|
25
|
+
retry
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def try_update
|
30
|
+
old_value = @ref.get
|
31
|
+
new_value = yield old_value
|
32
|
+
unless @ref.compare_and_set(old_value, new_value)
|
33
|
+
raise ConcurrentUpdateError, "Update failed"
|
34
|
+
end
|
35
|
+
old_value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
if defined? RUBY_ENGINE && RUBY_ENGINE == "jruby"
|
40
|
+
require 'java'
|
41
|
+
Atomic = Class.new(base_atomic) do
|
42
|
+
InternalReference = java.util.concurrent.atomic.AtomicReference
|
43
|
+
end
|
44
|
+
else
|
45
|
+
Atomic = Class.new(base_atomic) do
|
46
|
+
class InternalReference
|
47
|
+
attr_accessor :value
|
48
|
+
alias_method :get, :value
|
49
|
+
alias_method :set, :value=
|
50
|
+
|
51
|
+
def initialize(value)
|
52
|
+
@value = value
|
53
|
+
end
|
54
|
+
|
55
|
+
def compare_and_set(old_value, new_value)
|
56
|
+
_exclusive do
|
57
|
+
return false unless @value.equal? old_value
|
58
|
+
@value = new_value
|
59
|
+
end
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
HAS_EXCLUSIVE = defined? Thread.exclusive
|
64
|
+
def _exclusive
|
65
|
+
if HAS_EXCLUSIVE
|
66
|
+
Thread.exclusive {yield}
|
67
|
+
else
|
68
|
+
begin
|
69
|
+
Thread.critical = true
|
70
|
+
yield
|
71
|
+
ensure
|
72
|
+
Thread.critical = false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
private :_exclusive
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/test/test_atomic.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'atomic'
|
3
|
+
|
4
|
+
class TestAtomic < Test::Unit::TestCase
|
5
|
+
def test_construct
|
6
|
+
atomic = Atomic.new
|
7
|
+
assert_equal nil, atomic.value
|
8
|
+
|
9
|
+
atomic = Atomic.new(0)
|
10
|
+
assert_equal 0, atomic.value
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_value
|
14
|
+
atomic = Atomic.new(0)
|
15
|
+
atomic.value = 1
|
16
|
+
|
17
|
+
assert_equal 1, atomic.value
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_update
|
21
|
+
atomic = Atomic.new(0)
|
22
|
+
atomic.update {|v| v + 1}
|
23
|
+
|
24
|
+
assert_equal 1, atomic.value
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_try_update
|
28
|
+
atomic = Atomic.new(0)
|
29
|
+
atomic.try_update {|v| v + 1}
|
30
|
+
|
31
|
+
assert_equal 1, atomic.value
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO: Test the ConcurrentUpdateError cases
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: atomic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Charles Oliver Nutter
|
13
|
+
- MenTaLguY
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-08 00:03:10.533000 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: An atomic reference implementation for JRuby and green or GIL-threaded impls
|
23
|
+
email:
|
24
|
+
- headius@headius.com
|
25
|
+
- mental@rydia.net
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files: []
|
31
|
+
|
32
|
+
files:
|
33
|
+
- lib/atomic.rb
|
34
|
+
- examples/atomic_example.rb
|
35
|
+
- test/test_atomic.rb
|
36
|
+
- README.txt
|
37
|
+
- atomic.gemspec
|
38
|
+
- Rakefile
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/headius/ruby-atomic
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.3.7
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: An atomic reference implementation for JRuby and green or GIL-threaded impls
|
71
|
+
test_files:
|
72
|
+
- test/test_atomic.rb
|