concurrent-ruby 0.8.0 → 0.9.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -2
  3. data/README.md +103 -54
  4. data/lib/concurrent.rb +34 -14
  5. data/lib/concurrent/async.rb +164 -50
  6. data/lib/concurrent/atom.rb +171 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +3 -0
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +10 -32
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +27 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +25 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +226 -112
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +10 -0
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +46 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +206 -26
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_cached_thread_pool.rb +18 -16
  46. data/lib/concurrent/executor/java_fixed_thread_pool.rb +11 -18
  47. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  48. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  49. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +9 -18
  50. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +10 -21
  51. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  52. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  53. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  54. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  55. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  56. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  57. data/lib/concurrent/executor/thread_pool_executor.rb +72 -60
  58. data/lib/concurrent/executor/timer_set.rb +96 -84
  59. data/lib/concurrent/executors.rb +1 -1
  60. data/lib/concurrent/future.rb +70 -38
  61. data/lib/concurrent/immutable_struct.rb +89 -0
  62. data/lib/concurrent/ivar.rb +152 -60
  63. data/lib/concurrent/lazy_register.rb +40 -20
  64. data/lib/concurrent/maybe.rb +226 -0
  65. data/lib/concurrent/mutable_struct.rb +227 -0
  66. data/lib/concurrent/mvar.rb +44 -43
  67. data/lib/concurrent/promise.rb +208 -134
  68. data/lib/concurrent/scheduled_task.rb +339 -43
  69. data/lib/concurrent/settable_struct.rb +127 -0
  70. data/lib/concurrent/synchronization.rb +17 -0
  71. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  72. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  73. data/lib/concurrent/synchronization/condition.rb +53 -0
  74. data/lib/concurrent/synchronization/java_object.rb +35 -0
  75. data/lib/concurrent/synchronization/lock.rb +32 -0
  76. data/lib/concurrent/synchronization/monitor_object.rb +24 -0
  77. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  78. data/lib/concurrent/synchronization/object.rb +78 -0
  79. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  80. data/lib/concurrent/timer_task.rb +87 -100
  81. data/lib/concurrent/tvar.rb +42 -38
  82. data/lib/concurrent/utilities.rb +3 -1
  83. data/lib/concurrent/utility/at_exit.rb +97 -0
  84. data/lib/concurrent/utility/engine.rb +40 -0
  85. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  86. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  87. data/lib/concurrent/utility/processor_counter.rb +156 -0
  88. data/lib/concurrent/utility/timeout.rb +18 -14
  89. data/lib/concurrent/utility/timer.rb +11 -6
  90. data/lib/concurrent/version.rb +2 -1
  91. data/lib/concurrent_ruby.rb +1 -0
  92. metadata +47 -83
  93. data/lib/concurrent/actor.rb +0 -103
  94. data/lib/concurrent/actor/behaviour.rb +0 -70
  95. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  96. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  97. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  98. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  99. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  100. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  101. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  102. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  103. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  104. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  105. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  106. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  107. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  108. data/lib/concurrent/actor/context.rb +0 -154
  109. data/lib/concurrent/actor/core.rb +0 -217
  110. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  111. data/lib/concurrent/actor/envelope.rb +0 -41
  112. data/lib/concurrent/actor/errors.rb +0 -27
  113. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  114. data/lib/concurrent/actor/public_delegations.rb +0 -40
  115. data/lib/concurrent/actor/reference.rb +0 -81
  116. data/lib/concurrent/actor/root.rb +0 -37
  117. data/lib/concurrent/actor/type_check.rb +0 -48
  118. data/lib/concurrent/actor/utils.rb +0 -10
  119. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  120. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  121. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  122. data/lib/concurrent/actor/utils/pool.rb +0 -59
  123. data/lib/concurrent/actress.rb +0 -3
  124. data/lib/concurrent/agent.rb +0 -209
  125. data/lib/concurrent/atomic.rb +0 -92
  126. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  127. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  128. data/lib/concurrent/atomic/synchronization.rb +0 -51
  129. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  130. data/lib/concurrent/channel/channel.rb +0 -41
  131. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  132. data/lib/concurrent/channel/waitable_list.rb +0 -40
  133. data/lib/concurrent/channels.rb +0 -5
  134. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  135. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  136. data/lib/concurrent/collections.rb +0 -3
  137. data/lib/concurrent/dereferenceable.rb +0 -108
  138. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  139. data/lib/concurrent/logging.rb +0 -20
  140. data/lib/concurrent/obligation.rb +0 -171
  141. data/lib/concurrent/observable.rb +0 -73
  142. data/lib/concurrent/options_parser.rb +0 -52
  143. data/lib/concurrent/utility/processor_count.rb +0 -152
  144. data/lib/extension_helper.rb +0 -37
@@ -0,0 +1,17 @@
1
+ require 'concurrent/utility/engine'
2
+ require 'concurrent/synchronization/abstract_object'
3
+ require 'concurrent/synchronization/java_object'
4
+ require 'concurrent/synchronization/mutex_object'
5
+ require 'concurrent/synchronization/monitor_object'
6
+ require 'concurrent/synchronization/rbx_object'
7
+ require 'concurrent/synchronization/object'
8
+
9
+ require 'concurrent/synchronization/condition'
10
+ require 'concurrent/synchronization/lock'
11
+
12
+ module Concurrent
13
+ # {include:file:doc/synchronization.md}
14
+ module Synchronization
15
+ end
16
+ end
17
+
@@ -0,0 +1,163 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # @!macro synchronization_object
5
+ # @!visibility private
6
+ class AbstractObject
7
+
8
+ # @!macro [attach] synchronization_object_method_initialize
9
+ #
10
+ # @abstract for helper ivar initialization if needed,
11
+ # otherwise it can be left empty. It has to call ns_initialize.
12
+ def initialize(*args, &block)
13
+ raise NotImplementedError
14
+ end
15
+
16
+ protected
17
+
18
+ # @!macro [attach] synchronization_object_method_synchronize
19
+ #
20
+ # @yield runs the block synchronized against this object,
21
+ # equivalent of java's `synchronize(this) {}`
22
+ # @note can by made public in descendants if required by `public :synchronize`
23
+ def synchronize
24
+ raise NotImplementedError
25
+ end
26
+
27
+ # @!macro [attach] synchronization_object_method_ns_initialize
28
+ #
29
+ # initialization of the object called inside synchronize block
30
+ # @note has to be called manually when required in children of this class
31
+ # @example
32
+ # class Child < Concurrent::Synchornization::Object
33
+ # def initialize(*args, &block)
34
+ # super(&nil)
35
+ # synchronize { ns_initialize(*args, &block) }
36
+ # end
37
+ #
38
+ # def ns_initialize(*args, &block)
39
+ # @args = args
40
+ # end
41
+ # end
42
+ def ns_initialize(*args, &block)
43
+ end
44
+
45
+ # @!macro [attach] synchronization_object_method_ns_wait_until
46
+ #
47
+ # Wait until condition is met or timeout passes,
48
+ # protects against spurious wake-ups.
49
+ # @param [Numeric, nil] timeout in seconds, `nil` means no timeout
50
+ # @yield condition to be met
51
+ # @yieldreturn [true, false]
52
+ # @return [true, false] if condition met
53
+ # @note only to be used inside synchronized block
54
+ # @note to provide direct access to this method in a descendant add method
55
+ # ```
56
+ # def wait_until(timeout = nil, &condition)
57
+ # synchronize { ns_wait_until(timeout, &condition) }
58
+ # end
59
+ # ```
60
+ def ns_wait_until(timeout = nil, &condition)
61
+ if timeout
62
+ wait_until = Concurrent.monotonic_time + timeout
63
+ loop do
64
+ now = Concurrent.monotonic_time
65
+ condition_result = condition.call
66
+ # 0.001 correction to avoid error when `wait_until - now` is smaller than 0.0005 and rounded to 0
67
+ # when passed to java #wait(long timeout)
68
+ return condition_result if (now + 0.001) >= wait_until || condition_result
69
+ ns_wait wait_until - now
70
+ end
71
+ else
72
+ ns_wait timeout until condition.call
73
+ true
74
+ end
75
+ end
76
+
77
+ # @!macro [attach] synchronization_object_method_ns_wait
78
+ #
79
+ # Wait until another thread calls #signal or #broadcast,
80
+ # spurious wake-ups can happen.
81
+ #
82
+ # @param [Numeric, nil] timeout in seconds, `nil` means no timeout
83
+ # @return [self]
84
+ # @note only to be used inside synchronized block
85
+ # @note to provide direct access to this method in a descendant add method
86
+ # ```
87
+ # def wait(timeout = nil)
88
+ # synchronize { ns_wait(timeout) }
89
+ # end
90
+ # ```
91
+ def ns_wait(timeout = nil)
92
+ raise NotImplementedError
93
+ end
94
+
95
+ # @!macro [attach] synchronization_object_method_ns_signal
96
+ #
97
+ # Signal one waiting thread.
98
+ # @return [self]
99
+ # @note only to be used inside synchronized block
100
+ # @note to provide direct access to this method in a descendant add method
101
+ # ```
102
+ # def signal
103
+ # synchronize { ns_signal }
104
+ # end
105
+ # ```
106
+ def ns_signal
107
+ raise NotImplementedError
108
+ end
109
+
110
+ # @!macro [attach] synchronization_object_method_ns_broadcast
111
+ #
112
+ # Broadcast to all waiting threads.
113
+ # @return [self]
114
+ # @note only to be used inside synchronized block
115
+ # @note to provide direct access to this method in a descendant add method
116
+ # ```
117
+ # def broadcast
118
+ # synchronize { ns_broadcast }
119
+ # end
120
+ # ```
121
+ def ns_broadcast
122
+ raise NotImplementedError
123
+ end
124
+
125
+ # @!macro [attach] synchronization_object_method_ensure_ivar_visibility
126
+ #
127
+ # Allows to construct immutable objects where all fields are visible after initialization, not requiring
128
+ # further synchronization on access.
129
+ # @example
130
+ # class AClass
131
+ # attr_reader :val
132
+ # def initialize(val)
133
+ # @val = val # final value, after assignment it's not changed (just convention, not enforced)
134
+ # ensure_ivar_visibility!
135
+ # # now it can be shared as Java's final field
136
+ # end
137
+ # end
138
+ def ensure_ivar_visibility!
139
+ raise NotImplementedError
140
+ end
141
+
142
+ # @!macro [attach] synchronization_object_method_self_attr_volatile
143
+ #
144
+ # creates methods for reading and writing to a instance variable with volatile (Java semantic) instance variable
145
+ # return [Array<Symbol>] names of defined method names
146
+ def self.attr_volatile(*names)
147
+ names.each do |name|
148
+ ivar = :"@volatile_#{name}"
149
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
150
+ def #{name}
151
+ #{ivar}
152
+ end
153
+
154
+ def #{name}=(value)
155
+ #{ivar} = value
156
+ end
157
+ RUBY
158
+ end
159
+ names.map { |n| [n, :"#{n}="] }.flatten
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,158 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # @!visibility private
5
+ # @!macro internal_implementation_note
6
+ module AbstractStruct
7
+
8
+ # @!visibility private
9
+ def initialize(*values)
10
+ super()
11
+ ns_initialize(*values)
12
+ ensure_ivar_visibility!
13
+ end
14
+
15
+ # @!macro [attach] struct_length
16
+ #
17
+ # Returns the number of struct members.
18
+ #
19
+ # @return [Fixnum] the number of struct members
20
+ def length
21
+ self.class::MEMBERS.length
22
+ end
23
+ alias_method :size, :length
24
+
25
+ # @!macro [attach] struct_members
26
+ #
27
+ # Returns the struct members as an array of symbols.
28
+ #
29
+ # @return [Array] the struct members as an array of symbols
30
+ def members
31
+ self.class::MEMBERS.dup
32
+ end
33
+
34
+ protected
35
+
36
+ # @!macro struct_values
37
+ #
38
+ # @!visibility private
39
+ def ns_values
40
+ @values.dup
41
+ end
42
+
43
+ # @!macro struct_values_at
44
+ #
45
+ # @!visibility private
46
+ def ns_values_at(indexes)
47
+ @values.values_at(*indexes)
48
+ end
49
+
50
+ # @!macro struct_to_h
51
+ #
52
+ # @!visibility private
53
+ def ns_to_h
54
+ length.times.reduce({}){|memo, i| memo[self.class::MEMBERS[i]] = @values[i]; memo}
55
+ end
56
+
57
+ # @!macro struct_get
58
+ #
59
+ # @!visibility private
60
+ def ns_get(member)
61
+ if member.is_a? Integer
62
+ if member >= @values.length
63
+ raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})")
64
+ end
65
+ @values[member]
66
+ else
67
+ send(member)
68
+ end
69
+ rescue NoMethodError
70
+ raise NameError.new("no member '#{member}' in struct")
71
+ end
72
+
73
+ # @!macro struct_equality
74
+ #
75
+ # @!visibility private
76
+ def ns_equality(other)
77
+ self.class == other.class && self.values == other.values
78
+ end
79
+
80
+ # @!macro struct_each
81
+ #
82
+ # @!visibility private
83
+ def ns_each
84
+ values.each{|value| yield value }
85
+ end
86
+
87
+ # @!macro struct_each_pair
88
+ #
89
+ # @!visibility private
90
+ def ns_each_pair
91
+ @values.length.times do |index|
92
+ yield self.class::MEMBERS[index], @values[index]
93
+ end
94
+ end
95
+
96
+ # @!macro struct_select
97
+ #
98
+ # @!visibility private
99
+ def ns_select
100
+ values.select{|value| yield value }
101
+ end
102
+
103
+ # @!macro struct_inspect
104
+ #
105
+ # @!visibility private
106
+ def ns_inspect
107
+ struct = pr_underscore(self.class.ancestors[1])
108
+ clazz = ((self.class.to_s =~ /^#<Class:/) == 0) ? '' : " #{self.class}"
109
+ "#<#{struct}#{clazz} #{ns_to_h}>"
110
+ end
111
+
112
+ # @!macro struct_merge
113
+ #
114
+ # @!visibility private
115
+ def ns_merge(other, &block)
116
+ self.class.new(*self.to_h.merge(other, &block).values)
117
+ end
118
+
119
+ # @!visibility private
120
+ def pr_underscore(clazz)
121
+ word = clazz.to_s
122
+ word.gsub!(/::/, '/')
123
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
124
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
125
+ word.tr!("-", "_")
126
+ word.downcase!
127
+ word
128
+ end
129
+
130
+ # @!visibility private
131
+ def self.define_struct_class(parent, base, name, members, &block)
132
+ clazz = Class.new(base || Object) do
133
+ include parent
134
+ self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze)
135
+ def ns_initialize(*values)
136
+ raise ArgumentError.new('struct size differs') if values.length > length
137
+ @values = values.fill(nil, values.length..length-1)
138
+ end
139
+ end
140
+ unless name.nil?
141
+ begin
142
+ parent.const_set(name, clazz)
143
+ parent.const_get(name)
144
+ rescue NameError
145
+ raise NameError.new("identifier #{name} needs to be constant")
146
+ end
147
+ end
148
+ members.each_with_index do |member, index|
149
+ clazz.send(:define_method, member) do
150
+ @values[index]
151
+ end
152
+ end
153
+ clazz.class_exec(&block) unless block.nil?
154
+ clazz
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,53 @@
1
+ module Concurrent
2
+ module Synchronization
3
+ class Condition < Object
4
+
5
+ singleton_class.send :alias_method, :private_new, :new
6
+ private_class_method :new
7
+
8
+ def initialize(lock)
9
+ @Lock = lock
10
+ ensure_ivar_visibility!
11
+ super()
12
+ end
13
+
14
+ def wait(timeout = nil)
15
+ @Lock.synchronize { ns_wait(timeout) }
16
+ end
17
+
18
+ def ns_wait(timeout = nil)
19
+ synchronize { super(timeout) }
20
+ end
21
+
22
+ def wait_until(timeout = nil, &condition)
23
+ @Lock.synchronize { ns_wait_until(timeout, &condition) }
24
+ end
25
+
26
+ def ns_wait_until(timeout = nil, &condition)
27
+ synchronize { super(timeout, &condition) }
28
+ end
29
+
30
+ def signal
31
+ @Lock.synchronize { ns_signal }
32
+ end
33
+
34
+ def ns_signal
35
+ synchronize { super }
36
+ end
37
+
38
+ def broadcast
39
+ @Lock.synchronize { ns_broadcast }
40
+ end
41
+
42
+ def ns_broadcast
43
+ synchronize { super }
44
+ end
45
+ end
46
+
47
+ class Object < Implementation
48
+ def new_condition
49
+ Condition.private_new(self)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,35 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native part first
2
+
3
+ module Concurrent
4
+ module Synchronization
5
+
6
+ if Concurrent.on_jruby?
7
+ require 'jruby'
8
+
9
+ # @!visibility private
10
+ # @!macro internal_implementation_note
11
+ class JavaObject < AbstractObject
12
+
13
+ def self.attr_volatile(*names)
14
+ names.each do |name|
15
+
16
+ ivar = :"@volatile_#{name}"
17
+
18
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
19
+ def #{name}
20
+ instance_variable_get_volatile(:#{ivar})
21
+ end
22
+
23
+ def #{name}=(value)
24
+ instance_variable_set_volatile(:#{ivar}, value)
25
+ end
26
+ RUBY
27
+
28
+ end
29
+ names.map { |n| [n, :"#{n}="] }.flatten
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ module Concurrent
2
+ module Synchronization
3
+ class Lock < Object
4
+
5
+ public :synchronize
6
+
7
+ def wait(timeout = nil)
8
+ synchronize { ns_wait(timeout) }
9
+ end
10
+
11
+ public :ns_wait
12
+
13
+ def wait_until(timeout = nil, &condition)
14
+ synchronize { ns_wait_until(timeout, &condition) }
15
+ end
16
+
17
+ public :ns_wait_until
18
+
19
+ def signal
20
+ synchronize { ns_signal }
21
+ end
22
+
23
+ public :ns_signal
24
+
25
+ def broadcast
26
+ synchronize { ns_broadcast }
27
+ end
28
+
29
+ public :ns_broadcast
30
+ end
31
+ end
32
+ end