rubysl-monitor 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0611fdd2b6a1443e13577664e38ea505b906bfab
4
- data.tar.gz: fcf2eaf0f16be03c284b10166cc33c658922d93e
3
+ metadata.gz: 7362242f7582618baaf408e9c8eb46b1bc5c85e2
4
+ data.tar.gz: bb1f3e3984987a4143417554aa7526e59b76d54e
5
5
  SHA512:
6
- metadata.gz: 942b331aab1098f3a7de9f8e3295bbec7e550507473f941861e37766ae2a699a428e25011a7aef8e51e52db7653a5d0bd9382315e1ec9e279990f8d93233e910
7
- data.tar.gz: ad8e8567390e5a64d68ddeeeccad617e77a66cd540f13d33eb9a3aad4546da8d60aa2f5946bcd9b56e3c9d2fe2ec92db5185b8bb41ed1be632584123f6e226a9
6
+ metadata.gz: 9f7a6cca87b357fc057709c176dfb7c7104d6fca0e6c3b446c4768c6045933111f88aa54184726f75467d8b9d9c8a0df00b8dc4eacb41e833c2419046e98b1fa
7
+ data.tar.gz: 94bc9048a65ea57066ef16059b37aa07b1d973ba795da46693acdfe0d43fffc2b033b8ebfe6c0f23bc36d6105ad38c6271c94214bd5bee4f9df6ae3507e21aa2
@@ -3,5 +3,5 @@ env:
3
3
  - RUBYLIB=lib
4
4
  script: bundle exec mspec
5
5
  rvm:
6
- - 1.8.7
7
- - rbx-nightly-18mode
6
+ - 1.9.3
7
+ - rbx-nightly-19mode
@@ -1,82 +1,91 @@
1
- =begin
2
-
3
- = monitor.rb
4
-
5
- Copyright (C) 2001 Shugo Maeda <shugo@ruby-lang.org>
6
- Copyright (C) 2008 MenTaLguY <mental@rydia.net>
7
-
8
- This library is distributed under the terms of the Ruby license.
9
- You can freely distribute/modify this library.
10
-
11
- == example
12
-
13
- This is a simple example.
14
-
15
- require 'monitor.rb'
16
-
17
- buf = []
18
- buf.extend(MonitorMixin)
19
- empty_cond = buf.new_cond
20
-
21
- # consumer
22
- Thread.start do
23
- loop do
24
- buf.synchronize do
25
- empty_cond.wait_while { buf.empty? }
26
- print buf.shift
27
- end
28
- end
29
- end
30
-
31
- # producer
32
- while line = ARGF.gets
33
- buf.synchronize do
34
- buf.push(line)
35
- empty_cond.signal
36
- end
37
- end
38
-
39
- The consumer thread waits for the producer thread to push a line
40
- to buf while buf.empty?, and the producer thread (main thread)
41
- reads a line from ARGF and push it to buf, then call
42
- empty_cond.signal.
1
+ # = monitor.rb
2
+ #
3
+ # Copyright (C) 2001 Shugo Maeda <shugo@ruby-lang.org>
4
+ #
5
+ # This library is distributed under the terms of the Ruby license.
6
+ # You can freely distribute/modify this library.
7
+ #
43
8
 
44
- =end
45
-
46
9
  require 'thread'
47
10
 
48
11
  #
49
- # Adds monitor functionality to an arbitrary object by mixing the module with
50
- # +include+. For example:
12
+ # In concurrent programming, a monitor is an object or module intended to be
13
+ # used safely by more than one thread. The defining characteristic of a
14
+ # monitor is that its methods are executed with mutual exclusion. That is, at
15
+ # each point in time, at most one thread may be executing any of its methods.
16
+ # This mutual exclusion greatly simplifies reasoning about the implementation
17
+ # of monitors compared to reasoning about parallel code that updates a data
18
+ # structure.
19
+ #
20
+ # You can read more about the general principles on the Wikipedia page for
21
+ # Monitors[http://en.wikipedia.org/wiki/Monitor_%28synchronization%29]
22
+ #
23
+ # == Examples
24
+ #
25
+ # === Simple object.extend
51
26
  #
52
- # require 'monitor.rb'
53
- #
54
- # buf = []
55
- # buf.extend(MonitorMixin)
56
- # empty_cond = buf.new_cond
57
- #
58
- # # consumer
59
- # Thread.start do
60
- # loop do
61
- # buf.synchronize do
62
- # empty_cond.wait_while { buf.empty? }
63
- # print buf.shift
64
- # end
65
- # end
66
- # end
67
- #
68
- # # producer
69
- # while line = ARGF.gets
70
- # buf.synchronize do
71
- # buf.push(line)
72
- # empty_cond.signal
73
- # end
74
- # end
75
- #
76
- # The consumer thread waits for the producer thread to push a line
77
- # to buf while buf.empty?, and the producer thread (main thread)
78
- # reads a line from ARGF and push it to buf, then call
79
- # empty_cond.signal.
27
+ # require 'monitor.rb'
28
+ #
29
+ # buf = []
30
+ # buf.extend(MonitorMixin)
31
+ # empty_cond = buf.new_cond
32
+ #
33
+ # # consumer
34
+ # Thread.start do
35
+ # loop do
36
+ # buf.synchronize do
37
+ # empty_cond.wait_while { buf.empty? }
38
+ # print buf.shift
39
+ # end
40
+ # end
41
+ # end
42
+ #
43
+ # # producer
44
+ # while line = ARGF.gets
45
+ # buf.synchronize do
46
+ # buf.push(line)
47
+ # empty_cond.signal
48
+ # end
49
+ # end
50
+ #
51
+ # The consumer thread waits for the producer thread to push a line to buf
52
+ # while <tt>buf.empty?</tt>. The producer thread (main thread) reads a
53
+ # line from ARGF and pushes it into buf then calls <tt>empty_cond.signal</tt>
54
+ # to notify the consumer thread of new data.
55
+ #
56
+ # === Simple Class include
57
+ #
58
+ # require 'monitor'
59
+ #
60
+ # class SynchronizedArray < Array
61
+ #
62
+ # include MonitorMixin
63
+ #
64
+ # def initialize(*args)
65
+ # super(*args)
66
+ # end
67
+ #
68
+ # alias :old_shift :shift
69
+ # alias :old_unshift :unshift
70
+ #
71
+ # def shift(n=1)
72
+ # self.synchronize do
73
+ # self.old_shift(n)
74
+ # end
75
+ # end
76
+ #
77
+ # def unshift(item)
78
+ # self.synchronize do
79
+ # self.old_unshift(item)
80
+ # end
81
+ # end
82
+ #
83
+ # # other methods ...
84
+ # end
85
+ #
86
+ # +SynchronizedArray+ implements an Array with synchronized access to items.
87
+ # This Class is implemented as subclass of Array which includes the
88
+ # MonitorMixin module.
80
89
  #
81
90
  module MonitorMixin
82
91
  #
@@ -86,74 +95,84 @@ module MonitorMixin
86
95
  # above calls while_wait and signal, this class should be documented.
87
96
  #
88
97
  class ConditionVariable
89
- # Create a new timer with the argument timeout, and add the
90
- # current thread to the list of waiters. Then the thread is
91
- # stopped. It will be resumed when a corresponding #signal
92
- # occurs.
98
+ class Timeout < Exception; end
99
+
100
+ #
101
+ # Releases the lock held in the associated monitor and waits; reacquires the lock on wakeup.
102
+ #
103
+ # If +timeout+ is given, this method returns after +timeout+ seconds passed,
104
+ # even if no other thread doesn't signal.
105
+ #
93
106
  def wait(timeout = nil)
94
- condition = @condition
95
- @monitor.instance_eval { mon_wait_for_cond(condition, timeout) }
107
+ @monitor.__send__(:mon_check_owner)
108
+ count = @monitor.__send__(:mon_exit_for_cond)
109
+ begin
110
+ @cond.wait(@monitor.instance_variable_get(:@mon_mutex), timeout)
111
+ return true
112
+ ensure
113
+ @monitor.__send__(:mon_enter_for_cond, count)
114
+ end
96
115
  end
97
116
 
98
- # call #wait while the supplied block returns +true+.
117
+ #
118
+ # Calls wait repeatedly while the given block yields a truthy value.
119
+ #
99
120
  def wait_while
100
121
  while yield
101
- wait
122
+ wait
102
123
  end
103
124
  end
104
-
105
- # call #wait until the supplied block returns +true+.
125
+
126
+ #
127
+ # Calls wait repeatedly until the given block yields a truthy value.
128
+ #
106
129
  def wait_until
107
130
  until yield
108
- wait
131
+ wait
109
132
  end
110
133
  end
111
-
112
- # Wake up and run the next waiter
134
+
135
+ #
136
+ # Wakes up the first thread in line waiting for this lock.
137
+ #
113
138
  def signal
114
- condition = @condition
115
- @monitor.instance_eval { mon_signal_cond(condition) }
116
- nil
139
+ @monitor.__send__(:mon_check_owner)
140
+ @cond.signal
117
141
  end
118
-
119
- # Wake up all the waiters.
142
+
143
+ #
144
+ # Wakes up all threads waiting for this lock.
145
+ #
120
146
  def broadcast
121
- condition = @condition
122
- @monitor.instance_eval { mon_broadcast_cond(condition) }
123
- nil
124
- end
125
-
126
- def count_waiters
127
- condition = @condition
128
- @monitor.instance_eval { mon_count_cond_waiters(condition) }
147
+ @monitor.__send__(:mon_check_owner)
148
+ @cond.broadcast
129
149
  end
130
-
150
+
131
151
  private
132
152
 
133
- def initialize(monitor, condition)
153
+ def initialize(monitor)
134
154
  @monitor = monitor
135
- @condition = condition
155
+ @cond = ::ConditionVariable.new
136
156
  end
137
157
  end
138
-
158
+
139
159
  def self.extend_object(obj)
140
160
  super(obj)
141
- obj.instance_eval {mon_initialize()}
161
+ obj.__send__(:mon_initialize)
142
162
  end
143
-
163
+
144
164
  #
145
165
  # Attempts to enter exclusive section. Returns +false+ if lock fails.
146
166
  #
147
167
  def mon_try_enter
148
- @mon_mutex.synchronize do
149
- @mon_owner = Thread.current unless @mon_owner
150
- if @mon_owner == Thread.current
151
- @mon_count += 1
152
- true
153
- else
154
- false
168
+ if @mon_owner != Thread.current
169
+ unless @mon_mutex.try_lock
170
+ return false
155
171
  end
172
+ @mon_owner = Thread.current
156
173
  end
174
+ @mon_count += 1
175
+ return true
157
176
  end
158
177
  # For backward compatibility
159
178
  alias try_mon_enter mon_try_enter
@@ -162,21 +181,22 @@ module MonitorMixin
162
181
  # Enters exclusive section.
163
182
  #
164
183
  def mon_enter
165
- @mon_mutex.synchronize do
166
- mon_acquire(@mon_entering_cond)
167
- @mon_count += 1
184
+ if @mon_owner != Thread.current
185
+ @mon_mutex.lock
186
+ @mon_owner = Thread.current
168
187
  end
188
+ @mon_count += 1
169
189
  end
170
-
190
+
171
191
  #
172
192
  # Leaves exclusive section.
173
193
  #
174
194
  def mon_exit
175
- @mon_mutex.synchronize do
176
- mon_check_owner
177
- @mon_count -= 1
178
- mon_release if @mon_count.zero?
179
- nil
195
+ mon_check_owner
196
+ @mon_count -=1
197
+ if @mon_count == 0
198
+ @mon_owner = nil
199
+ @mon_mutex.unlock
180
200
  end
181
201
  end
182
202
 
@@ -194,121 +214,62 @@ module MonitorMixin
194
214
  end
195
215
  end
196
216
  alias synchronize mon_synchronize
197
-
217
+
198
218
  #
199
- # FIXME: This isn't documented in Nutshell.
200
- #
201
- # Create a new condition variable for this monitor.
202
- # This facilitates control of the monitor with #signal and #wait.
219
+ # Creates a new MonitorMixin::ConditionVariable associated with the
220
+ # receiver.
203
221
  #
204
222
  def new_cond
205
- condition = ::ConditionVariable.new
206
- condition.instance_eval { @mon_n_waiters = 0 }
207
- return ConditionVariable.new(self, condition)
223
+ return ConditionVariable.new(self)
208
224
  end
209
225
 
210
226
  private
211
227
 
228
+ # Use <tt>extend MonitorMixin</tt> or <tt>include MonitorMixin</tt> instead
229
+ # of this constructor. Have look at the examples above to understand how to
230
+ # use this module.
212
231
  def initialize(*args)
213
232
  super
214
233
  mon_initialize
215
234
  end
216
235
 
217
- # called by initialize method to set defaults for instance variables.
236
+ # Initializes the MonitorMixin after being included in a class or when an
237
+ # object has been extended with the MonitorMixin
218
238
  def mon_initialize
219
- @mon_mutex = Mutex.new
220
239
  @mon_owner = nil
221
240
  @mon_count = 0
222
- @mon_total_waiting = 0
223
- @mon_entering_cond = ::ConditionVariable.new
224
- @mon_waiting_cond = ::ConditionVariable.new
225
- self
241
+ @mon_mutex = Mutex.new
226
242
  end
227
243
 
228
- # Throw a ThreadError exception if the current thread
229
- # does't own the monitor
230
244
  def mon_check_owner
231
- # called with @mon_mutex held
232
245
  if @mon_owner != Thread.current
233
246
  raise ThreadError, "current thread not owner"
234
247
  end
235
248
  end
236
249
 
237
- def mon_acquire(condition)
238
- # called with @mon_mutex held
239
- while @mon_owner && @mon_owner != Thread.current
240
- condition.wait @mon_mutex
241
- end
250
+ def mon_enter_for_cond(count)
242
251
  @mon_owner = Thread.current
252
+ @mon_count = count
243
253
  end
244
254
 
245
- def mon_release
246
- # called with @mon_mutex held
255
+ def mon_exit_for_cond
256
+ count = @mon_count
247
257
  @mon_owner = nil
248
- if @mon_total_waiting.nonzero?
249
- @mon_waiting_cond.signal
250
- else
251
- @mon_entering_cond.signal
252
- end
253
- end
254
-
255
- def mon_wait_for_cond(condition, timeout)
256
- @mon_mutex.synchronize do
257
- mon_check_owner
258
- count = @mon_count
259
- @mon_count = 0
260
- condition.instance_eval { @mon_n_waiters += 1 }
261
- begin
262
- mon_release
263
- if timeout
264
- condition.wait(@mon_mutex, timeout)
265
- else
266
- condition.wait(@mon_mutex)
267
- true
268
- end
269
- ensure
270
- @mon_total_waiting += 1
271
- # TODO: not interrupt-safe
272
- mon_acquire(@mon_waiting_cond)
273
- @mon_total_waiting -= 1
274
- @mon_count = count
275
- condition.instance_eval { @mon_n_waiters -= 1 }
276
- end
277
- end
278
- end
279
-
280
- def mon_signal_cond(condition)
281
- @mon_mutex.synchronize do
282
- mon_check_owner
283
- condition.signal
284
- end
285
- end
286
-
287
- def mon_broadcast_cond(condition)
288
- @mon_mutex.synchronize do
289
- mon_check_owner
290
- condition.broadcast
291
- end
292
- end
293
-
294
- def mon_count_cond_waiters(condition)
295
- @mon_mutex.synchronize do
296
- condition.instance_eval { @mon_n_waiters }
297
- end
258
+ @mon_count = 0
259
+ return count
298
260
  end
299
261
  end
300
262
 
301
- # Monitors provide means of mutual exclusion for Thread programming.
302
- # A critical region is created by means of the synchronize method,
303
- # which takes a block.
304
- # The condition variables (created with #new_cond) may be used
305
- # to control the execution of a monitor with #signal and #wait.
263
+ # Use the Monitor class when you want to have a lock object for blocks with
264
+ # mutual exclusion.
265
+ #
266
+ # require 'monitor'
267
+ #
268
+ # lock = Monitor.new
269
+ # lock.synchronize do
270
+ # # exclusive access
271
+ # end
306
272
  #
307
- # the Monitor class wraps MonitorMixin, and provides aliases
308
- # alias try_enter try_mon_enter
309
- # alias enter mon_enter
310
- # alias exit mon_exit
311
- # to access its methods more concisely.
312
273
  class Monitor
313
274
  include MonitorMixin
314
275
  alias try_enter try_mon_enter
@@ -324,8 +285,6 @@ end
324
285
  # - All the internals (internal modules Accessible and Initializable, class
325
286
  # ConditionVariable) appear in RDoc. It might be good to hide them, by
326
287
  # making them private, or marking them :nodoc:, etc.
327
- # - The entire example from the RD section at the top is replicated in the RDoc
328
- # comment for MonitorMixin. Does the RD section need to remain?
329
288
  # - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but
330
289
  # not synchronize.
331
290
  # - mon_owner is in Nutshell, but appears as an accessor in a separate module
@@ -1,5 +1,5 @@
1
1
  module RubySL
2
2
  module Monitor
3
- VERSION = "1.0.0"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
@@ -16,6 +16,8 @@ Gem::Specification.new do |spec|
16
16
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
17
  spec.require_paths = ["lib"]
18
18
 
19
+ spec.required_ruby_version = "~> 2.0"
20
+
19
21
  spec.add_development_dependency "bundler", "~> 1.3"
20
22
  spec.add_development_dependency "rake", "~> 10.0"
21
23
  spec.add_development_dependency "mspec", "~> 1.5"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubysl-monitor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Shirai
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-27 00:00:00.000000000 Z
11
+ date: 2013-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,9 +80,9 @@ require_paths:
80
80
  - lib
81
81
  required_ruby_version: !ruby/object:Gem::Requirement
82
82
  requirements:
83
- - - '>='
83
+ - - ~>
84
84
  - !ruby/object:Gem::Version
85
- version: '0'
85
+ version: '2.0'
86
86
  required_rubygems_version: !ruby/object:Gem::Requirement
87
87
  requirements:
88
88
  - - '>='