concurrent-ruby 1.0.0.pre4 → 1.0.0.pre5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -1
  3. data/lib/concurrent/agent.rb +5 -1
  4. data/lib/concurrent/async.rb +77 -38
  5. data/lib/concurrent/atom.rb +2 -1
  6. data/lib/concurrent/atomic/atomic_boolean.rb +1 -1
  7. data/lib/concurrent/atomic/atomic_fixnum.rb +1 -1
  8. data/lib/concurrent/atomic/atomic_reference.rb +1 -1
  9. data/lib/concurrent/atomic/semaphore.rb +1 -1
  10. data/lib/concurrent/atomic_reference/jruby+truffle.rb +1 -0
  11. data/lib/concurrent/atomic_reference/jruby.rb +1 -1
  12. data/lib/concurrent/atomic_reference/ruby.rb +1 -1
  13. data/lib/concurrent/concern/dereferenceable.rb +9 -24
  14. data/lib/concurrent/concern/obligation.rb +11 -8
  15. data/lib/concurrent/delay.rb +1 -1
  16. data/lib/concurrent/exchanger.rb +30 -17
  17. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +6 -1
  18. data/lib/concurrent/ivar.rb +1 -1
  19. data/lib/concurrent/lazy_register.rb +11 -8
  20. data/lib/concurrent/map.rb +1 -1
  21. data/lib/concurrent/maybe.rb +6 -3
  22. data/lib/concurrent/mvar.rb +26 -2
  23. data/lib/concurrent/promise.rb +1 -1
  24. data/lib/concurrent/synchronization.rb +3 -0
  25. data/lib/concurrent/synchronization/abstract_lockable_object.rb +1 -20
  26. data/lib/concurrent/synchronization/abstract_object.rb +1 -27
  27. data/lib/concurrent/synchronization/jruby_object.rb +26 -18
  28. data/lib/concurrent/synchronization/lockable_object.rb +29 -16
  29. data/lib/concurrent/synchronization/mri_object.rb +27 -19
  30. data/lib/concurrent/synchronization/object.rb +48 -53
  31. data/lib/concurrent/synchronization/rbx_lockable_object.rb +9 -8
  32. data/lib/concurrent/synchronization/rbx_object.rb +29 -21
  33. data/lib/concurrent/synchronization/volatile.rb +34 -0
  34. data/lib/concurrent/timer_task.rb +0 -1
  35. data/lib/concurrent/tvar.rb +3 -1
  36. data/lib/concurrent/utility/engine.rb +4 -0
  37. data/lib/concurrent/utility/native_extension_loader.rb +6 -3
  38. data/lib/concurrent/version.rb +2 -2
  39. metadata +4 -2
@@ -8,9 +8,9 @@ module Concurrent
8
8
  MriMonitorLockableObject
9
9
  when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
10
10
  MriMutexLockableObject
11
- when defined? JRubyLockableObject
11
+ when Concurrent.on_jruby?
12
12
  JRubyLockableObject
13
- when Concurrent.on_rbx?
13
+ when Concurrent.on_rbx? || Concurrent.on_truffle?
14
14
  RbxLockableObject
15
15
  else
16
16
  warn 'Possibly unsupported Ruby implementation'
@@ -18,20 +18,36 @@ module Concurrent
18
18
  end
19
19
  private_constant :LockableObjectImplementation
20
20
 
21
- # TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing
21
+ # Safe synchronization under any Ruby implementation.
22
+ # It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
23
+ # Provides a single layer which can improve its implementation over time without changes needed to
24
+ # the classes using it. Use {Synchronization::Object} not this abstract class.
25
+ #
26
+ # @note this object does not support usage together with
27
+ # [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
28
+ # and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
29
+ # `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
30
+ # `Thread#wakeup` will not work on all platforms.
31
+ #
32
+ # @see {Event} implementation as an example of this class use
33
+ #
34
+ # @example simple
35
+ # class AnClass < Synchronization::Object
36
+ # def initialize
37
+ # super
38
+ # synchronize { @value = 'asd' }
39
+ # end
40
+ #
41
+ # def value
42
+ # synchronize { @value }
43
+ # end
44
+ # end
45
+ #
46
+ # @!visibility private
22
47
  class LockableObject < LockableObjectImplementation
23
48
 
49
+ # TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing
24
50
  # TODO (pitr 12-Sep-2015): we inherit too much ourselves :/
25
- def self.allow_only_direct_descendants!
26
- this = self
27
- singleton_class.send :define_method, :inherited do |child|
28
- # super child
29
-
30
- if child.superclass != this
31
- warn "all children of #{this} are final, subclassing is not supported use composition."
32
- end
33
- end
34
- end
35
51
 
36
52
  # @!method initialize(*args, &block)
37
53
  # @!macro synchronization_object_method_initialize
@@ -39,9 +55,6 @@ module Concurrent
39
55
  # @!method synchronize
40
56
  # @!macro synchronization_object_method_synchronize
41
57
 
42
- # @!method initialize(*args, &block)
43
- # @!macro synchronization_object_method_ns_initialize
44
-
45
58
  # @!method wait_until(timeout = nil, &condition)
46
59
  # @!macro synchronization_object_method_ns_wait_until
47
60
 
@@ -1,35 +1,43 @@
1
1
  module Concurrent
2
2
  module Synchronization
3
3
 
4
- # @!visibility private
5
- # @!macro internal_implementation_note
6
- class MriObject < AbstractObject
4
+ module MriAttrVolatile
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
7
8
 
8
- def initialize
9
- # nothing to do
9
+ module ClassMethods
10
+ def attr_volatile(*names)
11
+ names.each do |name|
12
+ ivar = :"@volatile_#{name}"
13
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
14
+ def #{name}
15
+ #{ivar}
16
+ end
17
+
18
+ def #{name}=(value)
19
+ #{ivar} = value
20
+ end
21
+ RUBY
22
+ end
23
+ names.map { |n| [n, :"#{n}="] }.flatten
24
+ end
10
25
  end
11
26
 
12
27
  def full_memory_barrier
13
28
  # relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
14
29
  # https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
15
30
  end
31
+ end
16
32
 
17
- def self.attr_volatile(*names)
18
- names.each do |name|
19
- ivar = :"@volatile_#{name}"
20
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
21
- def #{name}
22
- #{ivar}
23
- end
33
+ # @!visibility private
34
+ # @!macro internal_implementation_note
35
+ class MriObject < AbstractObject
36
+ include MriAttrVolatile
24
37
 
25
- def #{name}=(value)
26
- #{ivar} = value
27
- end
28
- RUBY
29
- end
30
- names.map { |n| [n, :"#{n}="] }.flatten
38
+ def initialize
39
+ # nothing to do
31
40
  end
32
41
  end
33
-
34
42
  end
35
43
  end
@@ -6,52 +6,32 @@ module Concurrent
6
6
  ObjectImplementation = case
7
7
  when Concurrent.on_cruby?
8
8
  MriObject
9
- when defined? JRubyObject
9
+ when Concurrent.on_jruby?
10
10
  JRubyObject
11
- when Concurrent.on_rbx?
11
+ when Concurrent.on_rbx? || Concurrent.on_truffle?
12
12
  RbxObject
13
13
  else
14
- warn 'Possibly unsupported Ruby implementation'
15
14
  MriObject
16
15
  end
17
16
  private_constant :ObjectImplementation
18
17
 
19
- # TODO fix documentation
20
- # @!macro [attach] synchronization_object
21
- #
22
- # Safe synchronization under any Ruby implementation.
23
- # It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
24
- # Provides a single layer which can improve its implementation over time without changes needed to
25
- # the classes using it. Use {Synchronization::Object} not this abstract class.
26
- #
27
- # @note this object does not support usage together with
28
- # [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
29
- # and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
30
- # `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
31
- # `Thread#wakeup` will not work on all platforms.
32
- #
33
- # @see {Event} implementation as an example of this class use
34
- #
35
- # @example simple
36
- # class AnClass < Synchronization::Object
37
- # def initialize
38
- # super
39
- # synchronize { @value = 'asd' }
40
- # end
41
- #
42
- # def value
43
- # synchronize { @value }
44
- # end
45
- # end
46
- #
18
+ # Abstract object providing final, volatile, ans CAS extensions to build other concurrent abstractions.
19
+ # - final instance variables see {Object.safe_initialization!}
20
+ # - volatile instance variables see {Object.attr_volatile}
21
+ # - volatile instance variables see {Object.attr_volatile_with_cas}
47
22
  class Object < ObjectImplementation
48
23
 
24
+ # @!method self.attr_volatile(*names)
25
+ # Creates methods for reading and writing (as `attr_accessor` does) to a instance variable with
26
+ # volatile (Java) semantic. The instance variable should be accessed oly through generated methods.
27
+ #
28
+ # @param [Array<Symbol>] names of the instance variables to be volatile
29
+ # @return [Array<Symbol>] names of defined method names
30
+
49
31
  # Has to be called by children.
50
- # Initializes default volatile fields with cas if any.
51
- # @param [Array<Object>] defaults values for fields, in same order as they are defined
52
- def initialize(*defaults)
53
- super()
54
- initialize_volatile_cas_fields(defaults)
32
+ def initialize
33
+ super
34
+ initialize_volatile_with_cas
55
35
  end
56
36
 
57
37
  # By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that
@@ -62,7 +42,7 @@ module Concurrent
62
42
  # safe_initialization!
63
43
  #
64
44
  # def initialize
65
- # @AFinalValue = 'value' # published safly, does not have to be synchronized
45
+ # @AFinalValue = 'value' # published safely, does not have to be synchronized
66
46
  # end
67
47
  # end
68
48
  def self.safe_initialization!
@@ -72,17 +52,20 @@ module Concurrent
72
52
  def self.new(*)
73
53
  object = super
74
54
  ensure
75
- object.ensure_ivar_visibility! if object
55
+ object.full_memory_barrier if object
76
56
  end
77
57
 
78
58
  @safe_initialization = true
79
59
  end
80
60
 
61
+ # @return [true, false] if this class is safely initialized.
81
62
  def self.safe_initialization?
82
- (defined?(@safe_initialization) && @safe_initialization) || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?)
63
+ @safe_initialization = false unless defined? @safe_initialization
64
+ @safe_initialization || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?)
83
65
  end
84
66
 
85
- # For testing purposes, quite slow.
67
+ # For testing purposes, quite slow. Injects assert code to new method which will raise if class instance contains
68
+ # any instance variables with CamelCase names and isn't {.safe_initialization?}.
86
69
  def self.ensure_safe_initialization_when_final_fields_are_present
87
70
  Object.class_eval do
88
71
  def self.new(*)
@@ -96,14 +79,22 @@ module Concurrent
96
79
  end
97
80
  end
98
81
 
99
- # TODO documentation
82
+ # Creates methods for reading and writing to a instance variable with
83
+ # volatile (Java) semantic as {.attr_volatile} does.
84
+ # The instance variable should be accessed oly through generated methods.
85
+ # This method generates following methods: `value`, `value=(new_value) #=> new_value`,
86
+ # `swap_value(new_value) #=> old_value`,
87
+ # `compare_and_set_value(expected, value) #=> true || false`, `update_value(&block)`.
88
+ # @param [Array<Symbol>] names of the instance variables to be volatile with CAS.
89
+ # @return [Array<Symbol>] names of defined method names.
100
90
  def self.attr_volatile_with_cas(*names)
101
91
  @volatile_cas_fields ||= []
102
92
  @volatile_cas_fields += names
103
93
  safe_initialization!
94
+ define_initialize_volatile_with_cas
104
95
 
105
96
  names.each do |name|
106
- ivar = :"@VolatileCas_#{name}"
97
+ ivar = :"@VolatileCas#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }}"
107
98
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
108
99
  def #{name}
109
100
  #{ivar}.get
@@ -126,11 +117,12 @@ module Concurrent
126
117
  end
127
118
  RUBY
128
119
  end
129
- names.map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}"] }.flatten
120
+ names.flat_map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}", :"update_#{n}"] }
130
121
  end
131
122
 
123
+ # @param [true,false] inherited should inherited volatile with CAS fields be returned?
124
+ # @return [Array<Symbol>] Returns defined volatile with CAS fields on this class.
132
125
  def self.volatile_cas_fields(inherited = true)
133
- # TODO (pitr 11-Sep-2015): maybe use constant for better optimisation on Truffle since it will not speculate on ivar being final
134
126
  @volatile_cas_fields ||= []
135
127
  ((superclass.volatile_cas_fields if superclass.respond_to?(:volatile_cas_fields) && inherited) || []) +
136
128
  @volatile_cas_fields
@@ -138,18 +130,21 @@ module Concurrent
138
130
 
139
131
  private
140
132
 
141
- def initialize_volatile_cas_fields(defaults)
142
- self.class.volatile_cas_fields.zip(defaults) do |name, default|
143
- instance_variable_set :"@VolatileCas_#{name}", AtomicReference.new(default)
144
- end
145
- nil
133
+ def self.define_initialize_volatile_with_cas
134
+ assignments = @volatile_cas_fields.map { |name| "@VolatileCas#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }} = AtomicReference.new(nil)" }.join("\n")
135
+ class_eval <<-RUBY
136
+ def initialize_volatile_with_cas
137
+ super
138
+ #{assignments}
139
+ end
140
+ RUBY
146
141
  end
147
142
 
148
- # @!method ensure_ivar_visibility!
149
- # @!macro synchronization_object_method_ensure_ivar_visibility
143
+ private_class_method :define_initialize_volatile_with_cas
144
+
145
+ def initialize_volatile_with_cas
146
+ end
150
147
 
151
- # @!method self.attr_volatile(*names)
152
- # @!macro synchronization_object_method_self_attr_volatile
153
148
  end
154
149
  end
155
150
  end
@@ -18,15 +18,16 @@ module Concurrent
18
18
  if @__owner__ == Thread.current
19
19
  yield
20
20
  else
21
- Rubinius.lock(self)
22
- begin
23
- @__owner__ = Thread.current
24
- result = yield
25
- ensure
26
- @__owner__ = nil
27
- Rubinius.unlock(self)
28
- result
21
+ result = nil
22
+ Rubinius.synchronize(self) do
23
+ begin
24
+ @__owner__ = Thread.current
25
+ result = yield
26
+ ensure
27
+ @__owner__ = nil
28
+ end
29
29
  end
30
+ result
30
31
  end
31
32
  end
32
33
 
@@ -1,37 +1,45 @@
1
1
  module Concurrent
2
2
  module Synchronization
3
3
 
4
+ module RbxAttrVolatile
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
4
8
 
5
- # @!visibility private
6
- # @!macro internal_implementation_note
7
- class RbxObject < AbstractObject
8
- def initialize
9
- # nothing to do
9
+ module ClassMethods
10
+ def attr_volatile(*names)
11
+ names.each do |name|
12
+ ivar = :"@volatile_#{name}"
13
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
14
+ def #{name}
15
+ Rubinius.memory_barrier
16
+ #{ivar}
17
+ end
18
+
19
+ def #{name}=(value)
20
+ #{ivar} = value
21
+ Rubinius.memory_barrier
22
+ end
23
+ RUBY
24
+ end
25
+ names.map { |n| [n, :"#{n}="] }.flatten
26
+ end
10
27
  end
11
28
 
12
29
  def full_memory_barrier
13
30
  # Rubinius instance variables are not volatile so we need to insert barrier
14
31
  Rubinius.memory_barrier
15
32
  end
33
+ end
16
34
 
17
- def self.attr_volatile *names
18
- names.each do |name|
19
- ivar = :"@volatile_#{name}"
20
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
21
- def #{name}
22
- Rubinius.memory_barrier
23
- #{ivar}
24
- end
35
+ # @!visibility private
36
+ # @!macro internal_implementation_note
37
+ class RbxObject < AbstractObject
38
+ include RbxAttrVolatile
25
39
 
26
- def #{name}=(value)
27
- #{ivar} = value
28
- Rubinius.memory_barrier
29
- end
30
- RUBY
31
- end
32
- names.map { |n| [n, :"#{n}="] }.flatten
40
+ def initialize
41
+ # nothing to do
33
42
  end
34
43
  end
35
-
36
44
  end
37
45
  end
@@ -0,0 +1,34 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # Volatile adds the attr_volatile class method when included.
5
+ #
6
+ # @example
7
+ # class Foo
8
+ # include Concurrent::Synchronization::Volatile
9
+ #
10
+ # attr_volatile :bar
11
+ #
12
+ # def initialize
13
+ # self.bar = 1
14
+ # end
15
+ # end
16
+ #
17
+ # foo = Foo.new
18
+ # foo.bar
19
+ # => 1
20
+ # foo.bar = 2
21
+ # => 2
22
+
23
+ Volatile = case
24
+ when Concurrent.on_cruby?
25
+ MriAttrVolatile
26
+ when Concurrent.on_jruby?
27
+ JRubyAttrVolatile
28
+ when Concurrent.on_rbx? || Concurrent.on_truffle?
29
+ RbxAttrVolatile
30
+ else
31
+ MriAttrVolatile
32
+ end
33
+ end
34
+ end
@@ -271,7 +271,6 @@ module Concurrent
271
271
  private
272
272
 
273
273
  def ns_initialize(opts, &task)
274
- init_mutex(self)
275
274
  set_deref_options(opts)
276
275
 
277
276
  self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
@@ -1,4 +1,5 @@
1
1
  require 'set'
2
+ require 'concurrent/synchronization'
2
3
 
3
4
  module Concurrent
4
5
 
@@ -8,7 +9,8 @@ module Concurrent
8
9
  # @!macro thread_safe_variable_comparison
9
10
  #
10
11
  # {include:file:doc/tvar.md}
11
- class TVar
12
+ class TVar < Synchronization::Object
13
+ safe_initialization!
12
14
 
13
15
  # Create a new `TVar` with an initial value.
14
16
  def initialize(value)