rubysl-monitor 2.0.0 → 2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +10 -3
- data/MRI_LICENSE +56 -0
- data/lib/monitor.rb +300 -1
- data/lib/rubysl/monitor.rb +1 -0
- data/lib/rubysl/monitor/version.rb +1 -1
- data/rubysl-monitor.gemspec +1 -0
- metadata +28 -14
- data/lib/rubysl/monitor/monitor.rb +0 -300
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58131016ac8f1dad3c38a2e60589dabbf189495e
|
4
|
+
data.tar.gz: ef3fd837b8753599ae54ec05b174ba8045bfbf8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca0bfdd83ac029c99a1806a2b7b8591341846dfe63fe998f8f35642771735f60da0a076c2e29ac42e276d4e6952ed12e2cdbb427027edd850255275c001684dd
|
7
|
+
data.tar.gz: ce649beccca122344d9387a09203ca412ec40a6b6fb73f9124a04cba9d91b42284e1f4cf91c55544a94fe3043a6a9543e0e9c5327a2ea8b50cdcc4a86cdfd5d7
|
data/.travis.yml
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
language: ruby
|
2
2
|
env:
|
3
3
|
- RUBYLIB=lib
|
4
|
-
|
4
|
+
- RUBYLIB=
|
5
|
+
script: mspec spec
|
5
6
|
rvm:
|
6
|
-
-
|
7
|
-
- rbx-
|
7
|
+
- 2.0.0
|
8
|
+
- rbx-2.2.1
|
9
|
+
matrix:
|
10
|
+
exclude:
|
11
|
+
- rvm: 2.0.0
|
12
|
+
env: RUBYLIB=lib
|
13
|
+
- rvm: rbx-2.2.1
|
14
|
+
env: RUBYLIB=
|
data/MRI_LICENSE
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
|
2
|
+
You can redistribute it and/or modify it under either the terms of the
|
3
|
+
2-clause BSDL (see the file BSDL), or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
13
|
+
make them Freely Available, such as by posting said
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
15
|
+
the author to include your modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) give non-standard binaries non-standard names, with
|
21
|
+
instructions on where to get the original software distribution.
|
22
|
+
|
23
|
+
d) make other distribution arrangements with the author.
|
24
|
+
|
25
|
+
3. You may distribute the software in object code or binary form,
|
26
|
+
provided that you do at least ONE of the following:
|
27
|
+
|
28
|
+
a) distribute the binaries and library files of the software,
|
29
|
+
together with instructions (in the manual page or equivalent)
|
30
|
+
on where to get the original distribution.
|
31
|
+
|
32
|
+
b) accompany the distribution with the machine-readable source of
|
33
|
+
the software.
|
34
|
+
|
35
|
+
c) give non-standard binaries non-standard names, with
|
36
|
+
instructions on where to get the original software distribution.
|
37
|
+
|
38
|
+
d) make other distribution arrangements with the author.
|
39
|
+
|
40
|
+
4. You may modify and include the part of the software into any other
|
41
|
+
software (possibly commercial). But some files in the distribution
|
42
|
+
are not written by the author, so that they are not under these terms.
|
43
|
+
|
44
|
+
For the list of those files and their copying conditions, see the
|
45
|
+
file LEGAL.
|
46
|
+
|
47
|
+
5. The scripts and library files supplied as input to or produced as
|
48
|
+
output from the software do not automatically fall under the
|
49
|
+
copyright of the software, but belong to whomever generated them,
|
50
|
+
and may be sold commercially, and may be aggregated with this
|
51
|
+
software.
|
52
|
+
|
53
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
54
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
55
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
56
|
+
PURPOSE.
|
data/lib/monitor.rb
CHANGED
@@ -1 +1,300 @@
|
|
1
|
-
|
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
|
+
#
|
8
|
+
|
9
|
+
require 'thread'
|
10
|
+
|
11
|
+
#
|
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
|
26
|
+
#
|
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.
|
89
|
+
#
|
90
|
+
module MonitorMixin
|
91
|
+
#
|
92
|
+
# FIXME: This isn't documented in Nutshell.
|
93
|
+
#
|
94
|
+
# Since MonitorMixin.new_cond returns a ConditionVariable, and the example
|
95
|
+
# above calls while_wait and signal, this class should be documented.
|
96
|
+
#
|
97
|
+
class ConditionVariable
|
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
|
+
#
|
106
|
+
def wait(timeout = nil)
|
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
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Calls wait repeatedly while the given block yields a truthy value.
|
119
|
+
#
|
120
|
+
def wait_while
|
121
|
+
while yield
|
122
|
+
wait
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Calls wait repeatedly until the given block yields a truthy value.
|
128
|
+
#
|
129
|
+
def wait_until
|
130
|
+
until yield
|
131
|
+
wait
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Wakes up the first thread in line waiting for this lock.
|
137
|
+
#
|
138
|
+
def signal
|
139
|
+
@monitor.__send__(:mon_check_owner)
|
140
|
+
@cond.signal
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
# Wakes up all threads waiting for this lock.
|
145
|
+
#
|
146
|
+
def broadcast
|
147
|
+
@monitor.__send__(:mon_check_owner)
|
148
|
+
@cond.broadcast
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def initialize(monitor)
|
154
|
+
@monitor = monitor
|
155
|
+
@cond = ::ConditionVariable.new
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.extend_object(obj)
|
160
|
+
super(obj)
|
161
|
+
obj.__send__(:mon_initialize)
|
162
|
+
end
|
163
|
+
|
164
|
+
#
|
165
|
+
# Attempts to enter exclusive section. Returns +false+ if lock fails.
|
166
|
+
#
|
167
|
+
def mon_try_enter
|
168
|
+
if @mon_owner != Thread.current
|
169
|
+
unless @mon_mutex.try_lock
|
170
|
+
return false
|
171
|
+
end
|
172
|
+
@mon_owner = Thread.current
|
173
|
+
end
|
174
|
+
@mon_count += 1
|
175
|
+
return true
|
176
|
+
end
|
177
|
+
# For backward compatibility
|
178
|
+
alias try_mon_enter mon_try_enter
|
179
|
+
|
180
|
+
#
|
181
|
+
# Enters exclusive section.
|
182
|
+
#
|
183
|
+
def mon_enter
|
184
|
+
if @mon_owner != Thread.current
|
185
|
+
@mon_mutex.lock
|
186
|
+
@mon_owner = Thread.current
|
187
|
+
end
|
188
|
+
@mon_count += 1
|
189
|
+
end
|
190
|
+
|
191
|
+
#
|
192
|
+
# Leaves exclusive section.
|
193
|
+
#
|
194
|
+
def mon_exit
|
195
|
+
mon_check_owner
|
196
|
+
@mon_count -=1
|
197
|
+
if @mon_count == 0
|
198
|
+
@mon_owner = nil
|
199
|
+
@mon_mutex.unlock
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
#
|
204
|
+
# Enters exclusive section and executes the block. Leaves the exclusive
|
205
|
+
# section automatically when the block exits. See example under
|
206
|
+
# +MonitorMixin+.
|
207
|
+
#
|
208
|
+
def mon_synchronize
|
209
|
+
mon_enter
|
210
|
+
begin
|
211
|
+
yield
|
212
|
+
ensure
|
213
|
+
mon_exit
|
214
|
+
end
|
215
|
+
end
|
216
|
+
alias synchronize mon_synchronize
|
217
|
+
|
218
|
+
#
|
219
|
+
# Creates a new MonitorMixin::ConditionVariable associated with the
|
220
|
+
# receiver.
|
221
|
+
#
|
222
|
+
def new_cond
|
223
|
+
return ConditionVariable.new(self)
|
224
|
+
end
|
225
|
+
|
226
|
+
private
|
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.
|
231
|
+
def initialize(*args)
|
232
|
+
super
|
233
|
+
mon_initialize
|
234
|
+
end
|
235
|
+
|
236
|
+
# Initializes the MonitorMixin after being included in a class or when an
|
237
|
+
# object has been extended with the MonitorMixin
|
238
|
+
def mon_initialize
|
239
|
+
@mon_owner = nil
|
240
|
+
@mon_count = 0
|
241
|
+
@mon_mutex = Mutex.new
|
242
|
+
end
|
243
|
+
|
244
|
+
def mon_check_owner
|
245
|
+
if @mon_owner != Thread.current
|
246
|
+
raise ThreadError, "current thread not owner"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def mon_enter_for_cond(count)
|
251
|
+
@mon_owner = Thread.current
|
252
|
+
@mon_count = count
|
253
|
+
end
|
254
|
+
|
255
|
+
def mon_exit_for_cond
|
256
|
+
count = @mon_count
|
257
|
+
@mon_owner = nil
|
258
|
+
@mon_count = 0
|
259
|
+
return count
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
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
|
272
|
+
#
|
273
|
+
class Monitor
|
274
|
+
include MonitorMixin
|
275
|
+
alias try_enter try_mon_enter
|
276
|
+
alias enter mon_enter
|
277
|
+
alias exit mon_exit
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
# Documentation comments:
|
282
|
+
# - All documentation comes from Nutshell.
|
283
|
+
# - MonitorMixin.new_cond appears in the example, but is not documented in
|
284
|
+
# Nutshell.
|
285
|
+
# - All the internals (internal modules Accessible and Initializable, class
|
286
|
+
# ConditionVariable) appear in RDoc. It might be good to hide them, by
|
287
|
+
# making them private, or marking them :nodoc:, etc.
|
288
|
+
# - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but
|
289
|
+
# not synchronize.
|
290
|
+
# - mon_owner is in Nutshell, but appears as an accessor in a separate module
|
291
|
+
# here, so is hard/impossible to RDoc. Some other useful accessors
|
292
|
+
# (mon_count and some queue stuff) are also in this module, and don't appear
|
293
|
+
# directly in the RDoc output.
|
294
|
+
# - in short, it may be worth changing the code layout in this file to make the
|
295
|
+
# documentation easier
|
296
|
+
|
297
|
+
# Local variables:
|
298
|
+
# mode: Ruby
|
299
|
+
# tab-width: 8
|
300
|
+
# End:
|
data/lib/rubysl/monitor.rb
CHANGED
data/rubysl-monitor.gemspec
CHANGED
metadata
CHANGED
@@ -1,57 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubysl-monitor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: '2.1'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Shirai
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-10-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.3'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '10.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: mspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '1.5'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubysl-prettyprint
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.0'
|
55
69
|
description: Ruby standard library Monitor.
|
56
70
|
email:
|
57
71
|
- brixen@gmail.com
|
@@ -59,15 +73,15 @@ executables: []
|
|
59
73
|
extensions: []
|
60
74
|
extra_rdoc_files: []
|
61
75
|
files:
|
62
|
-
- .gitignore
|
63
|
-
- .travis.yml
|
76
|
+
- ".gitignore"
|
77
|
+
- ".travis.yml"
|
64
78
|
- Gemfile
|
65
79
|
- LICENSE
|
80
|
+
- MRI_LICENSE
|
66
81
|
- README.md
|
67
82
|
- Rakefile
|
68
83
|
- lib/monitor.rb
|
69
84
|
- lib/rubysl/monitor.rb
|
70
|
-
- lib/rubysl/monitor/monitor.rb
|
71
85
|
- lib/rubysl/monitor/version.rb
|
72
86
|
- rubysl-monitor.gemspec
|
73
87
|
homepage: https://github.com/rubysl/rubysl-monitor
|
@@ -80,17 +94,17 @@ require_paths:
|
|
80
94
|
- lib
|
81
95
|
required_ruby_version: !ruby/object:Gem::Requirement
|
82
96
|
requirements:
|
83
|
-
- - ~>
|
97
|
+
- - "~>"
|
84
98
|
- !ruby/object:Gem::Version
|
85
99
|
version: '2.0'
|
86
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
101
|
requirements:
|
88
|
-
- -
|
102
|
+
- - ">="
|
89
103
|
- !ruby/object:Gem::Version
|
90
104
|
version: '0'
|
91
105
|
requirements: []
|
92
106
|
rubyforge_project:
|
93
|
-
rubygems_version: 2.
|
107
|
+
rubygems_version: 2.5.1
|
94
108
|
signing_key:
|
95
109
|
specification_version: 4
|
96
110
|
summary: Ruby standard library Monitor.
|
@@ -1,300 +0,0 @@
|
|
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
|
-
#
|
8
|
-
|
9
|
-
require 'thread'
|
10
|
-
|
11
|
-
#
|
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
|
26
|
-
#
|
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.
|
89
|
-
#
|
90
|
-
module MonitorMixin
|
91
|
-
#
|
92
|
-
# FIXME: This isn't documented in Nutshell.
|
93
|
-
#
|
94
|
-
# Since MonitorMixin.new_cond returns a ConditionVariable, and the example
|
95
|
-
# above calls while_wait and signal, this class should be documented.
|
96
|
-
#
|
97
|
-
class ConditionVariable
|
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
|
-
#
|
106
|
-
def wait(timeout = nil)
|
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
|
115
|
-
end
|
116
|
-
|
117
|
-
#
|
118
|
-
# Calls wait repeatedly while the given block yields a truthy value.
|
119
|
-
#
|
120
|
-
def wait_while
|
121
|
-
while yield
|
122
|
-
wait
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
#
|
127
|
-
# Calls wait repeatedly until the given block yields a truthy value.
|
128
|
-
#
|
129
|
-
def wait_until
|
130
|
-
until yield
|
131
|
-
wait
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
#
|
136
|
-
# Wakes up the first thread in line waiting for this lock.
|
137
|
-
#
|
138
|
-
def signal
|
139
|
-
@monitor.__send__(:mon_check_owner)
|
140
|
-
@cond.signal
|
141
|
-
end
|
142
|
-
|
143
|
-
#
|
144
|
-
# Wakes up all threads waiting for this lock.
|
145
|
-
#
|
146
|
-
def broadcast
|
147
|
-
@monitor.__send__(:mon_check_owner)
|
148
|
-
@cond.broadcast
|
149
|
-
end
|
150
|
-
|
151
|
-
private
|
152
|
-
|
153
|
-
def initialize(monitor)
|
154
|
-
@monitor = monitor
|
155
|
-
@cond = ::ConditionVariable.new
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def self.extend_object(obj)
|
160
|
-
super(obj)
|
161
|
-
obj.__send__(:mon_initialize)
|
162
|
-
end
|
163
|
-
|
164
|
-
#
|
165
|
-
# Attempts to enter exclusive section. Returns +false+ if lock fails.
|
166
|
-
#
|
167
|
-
def mon_try_enter
|
168
|
-
if @mon_owner != Thread.current
|
169
|
-
unless @mon_mutex.try_lock
|
170
|
-
return false
|
171
|
-
end
|
172
|
-
@mon_owner = Thread.current
|
173
|
-
end
|
174
|
-
@mon_count += 1
|
175
|
-
return true
|
176
|
-
end
|
177
|
-
# For backward compatibility
|
178
|
-
alias try_mon_enter mon_try_enter
|
179
|
-
|
180
|
-
#
|
181
|
-
# Enters exclusive section.
|
182
|
-
#
|
183
|
-
def mon_enter
|
184
|
-
if @mon_owner != Thread.current
|
185
|
-
@mon_mutex.lock
|
186
|
-
@mon_owner = Thread.current
|
187
|
-
end
|
188
|
-
@mon_count += 1
|
189
|
-
end
|
190
|
-
|
191
|
-
#
|
192
|
-
# Leaves exclusive section.
|
193
|
-
#
|
194
|
-
def mon_exit
|
195
|
-
mon_check_owner
|
196
|
-
@mon_count -=1
|
197
|
-
if @mon_count == 0
|
198
|
-
@mon_owner = nil
|
199
|
-
@mon_mutex.unlock
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
#
|
204
|
-
# Enters exclusive section and executes the block. Leaves the exclusive
|
205
|
-
# section automatically when the block exits. See example under
|
206
|
-
# +MonitorMixin+.
|
207
|
-
#
|
208
|
-
def mon_synchronize
|
209
|
-
mon_enter
|
210
|
-
begin
|
211
|
-
yield
|
212
|
-
ensure
|
213
|
-
mon_exit
|
214
|
-
end
|
215
|
-
end
|
216
|
-
alias synchronize mon_synchronize
|
217
|
-
|
218
|
-
#
|
219
|
-
# Creates a new MonitorMixin::ConditionVariable associated with the
|
220
|
-
# receiver.
|
221
|
-
#
|
222
|
-
def new_cond
|
223
|
-
return ConditionVariable.new(self)
|
224
|
-
end
|
225
|
-
|
226
|
-
private
|
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.
|
231
|
-
def initialize(*args)
|
232
|
-
super
|
233
|
-
mon_initialize
|
234
|
-
end
|
235
|
-
|
236
|
-
# Initializes the MonitorMixin after being included in a class or when an
|
237
|
-
# object has been extended with the MonitorMixin
|
238
|
-
def mon_initialize
|
239
|
-
@mon_owner = nil
|
240
|
-
@mon_count = 0
|
241
|
-
@mon_mutex = Mutex.new
|
242
|
-
end
|
243
|
-
|
244
|
-
def mon_check_owner
|
245
|
-
if @mon_owner != Thread.current
|
246
|
-
raise ThreadError, "current thread not owner"
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
def mon_enter_for_cond(count)
|
251
|
-
@mon_owner = Thread.current
|
252
|
-
@mon_count = count
|
253
|
-
end
|
254
|
-
|
255
|
-
def mon_exit_for_cond
|
256
|
-
count = @mon_count
|
257
|
-
@mon_owner = nil
|
258
|
-
@mon_count = 0
|
259
|
-
return count
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
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
|
272
|
-
#
|
273
|
-
class Monitor
|
274
|
-
include MonitorMixin
|
275
|
-
alias try_enter try_mon_enter
|
276
|
-
alias enter mon_enter
|
277
|
-
alias exit mon_exit
|
278
|
-
end
|
279
|
-
|
280
|
-
|
281
|
-
# Documentation comments:
|
282
|
-
# - All documentation comes from Nutshell.
|
283
|
-
# - MonitorMixin.new_cond appears in the example, but is not documented in
|
284
|
-
# Nutshell.
|
285
|
-
# - All the internals (internal modules Accessible and Initializable, class
|
286
|
-
# ConditionVariable) appear in RDoc. It might be good to hide them, by
|
287
|
-
# making them private, or marking them :nodoc:, etc.
|
288
|
-
# - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but
|
289
|
-
# not synchronize.
|
290
|
-
# - mon_owner is in Nutshell, but appears as an accessor in a separate module
|
291
|
-
# here, so is hard/impossible to RDoc. Some other useful accessors
|
292
|
-
# (mon_count and some queue stuff) are also in this module, and don't appear
|
293
|
-
# directly in the RDoc output.
|
294
|
-
# - in short, it may be worth changing the code layout in this file to make the
|
295
|
-
# documentation easier
|
296
|
-
|
297
|
-
# Local variables:
|
298
|
-
# mode: Ruby
|
299
|
-
# tab-width: 8
|
300
|
-
# End:
|