1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
|
## monitor.rb
# Author: Shugo Maeda <[email protected]>
# Version: $Revision: 0.1 $
# USAGE:
#
# foo = Foo.new
# foo.extend(MonitorMixin)
# cond = foo.new_cond
#
# thread1:
# foo.synchronize {
# ...
# cond.wait_until { foo.done? }
# ...
# }
#
# thread2:
# foo.synchronize {
# foo.do_something
# cond.signal
# }
# ATTENTION:
#
# If you include MonitorMixin and override `initialize', you should
# call `super'.
# If you include MonitorMixin to built-in classes, you should override
# `new' to call `mon_initialize'.
## Code:
require "final"
module MonitorMixin
RCS_ID = %q$Id: monitor.rb,v 0.1 1998/03/01 08:40:18 shugo Exp shugo $
module Primitive
include MonitorMixin
MON_OWNER_TABLE = {}
MON_COUNT_TABLE = {}
MON_ENTERING_QUEUE_TABLE = {}
MON_WAITING_QUEUE_TABLE = {}
FINALIZER = Proc.new { |id|
MON_OWNER_TABLE.delete(id)
MON_COUNT_TABLE.delete(id)
MON_ENTERING_QUEUE_TABLE.delete(id)
MON_WAITING_QUEUE_TABLE.delete(id)
}
def self.extend_object(obj)
super(obj)
obj.mon_initialize
end
def mon_initialize
MON_OWNER_TABLE[id] = nil
MON_COUNT_TABLE[id] = 0
MON_ENTERING_QUEUE_TABLE[id] = []
MON_WAITING_QUEUE_TABLE[id] = []
ObjectSpace.define_finalizer(self, FINALIZER)
end
def mon_owner
return MON_OWNER_TABLE[id]
end
def mon_count
return MON_COUNT_TABLE[id]
end
def mon_entering_queue
return MON_ENTERING_QUEUE_TABLE[id]
end
def mon_waiting_queue
return MON_WAITING_QUEUE_TABLE[id]
end
def set_mon_owner(val)
return MON_OWNER_TABLE[id] = val
end
def set_mon_count(val)
return MON_COUNT_TABLE[id] = val
end
private :mon_count, :mon_entering_queue, :mon_waiting_queue,
:set_mon_owner, :set_mon_count
end
module NonPrimitive
include MonitorMixin
attr_reader :mon_owner, :mon_count,
:mon_entering_queue, :mon_waiting_queue
def self.extend_object(obj)
super(obj)
obj.mon_initialize
end
def mon_initialize
@mon_owner = nil
@mon_count = 0
@mon_entering_queue = []
@mon_waiting_queue = []
end
def set_mon_owner(val)
@mon_owner = val
end
def set_mon_count(val)
@mon_count = val
end
private :mon_count, :mon_entering_queue, :mon_waiting_queue,
:set_mon_owner, :set_mon_count
end
def self.extendable_module(obj)
if Fixnum === obj or TrueClass === obj or FalseClass === obj or
NilClass === obj
raise TypeError, "MonitorMixin can't extend #{obj.type}"
else
begin
obj.instance_eval("@mon_owner")
return NonPrimitive
rescue TypeError
return Primitive
end
end
end
def self.extend_object(obj)
obj.extend(extendable_module(obj))
end
def self.includable_module(klass)
if klass.instance_of?(Module)
return NonPrimitive
end
begin
dummy = klass.new
return extendable_module(dummy)
rescue ArgumentError
if klass.singleton_methods.include?("new")
return Primitive
else
return NonPrimitive
end
rescue NameError
raise TypeError, "#{klass} can't include MonitorMixin"
end
end
def self.append_features(klass)
mod = includable_module(klass)
klass.module_eval("include mod")
end
def initialize(*args)
super
mon_initialize
end
def try_mon_enter
result = false
Thread.critical = true
if mon_owner.nil?
set_mon_owner(Thread.current)
end
if mon_owner == Thread.current
set_mon_count(mon_count + 1)
result = true
end
Thread.critical = false
return result
end
def mon_enter
Thread.critical = true
while mon_owner != nil && mon_owner != Thread.current
mon_entering_queue.push(Thread.current)
Thread.stop
Thread.critical = true
end
set_mon_owner(Thread.current)
set_mon_count(mon_count + 1)
Thread.critical = false
end
def mon_exit
if mon_owner != Thread.current
raise ThreadError, "current thread not owner"
end
Thread.critical = true
set_mon_count(mon_count - 1)
if mon_count == 0
set_mon_owner(nil)
if mon_waiting_queue.empty?
t = mon_entering_queue.shift
else
t = mon_waiting_queue.shift
end
end
t.wakeup if t
Thread.critical = false
Thread.pass
end
def mon_synchronize
mon_enter
begin
yield
ensure
mon_exit
end
end
alias synchronize mon_synchronize
class ConditionVariable
def initialize(monitor)
@monitor = monitor
@waiters = []
end
def wait
if @monitor.mon_owner != Thread.current
raise ThreadError, "current thread not owner"
end
@monitor.instance_eval(<<MON_EXIT)
Thread.critical = true
_count = mon_count
set_mon_count(0)
set_mon_owner(nil)
if mon_waiting_queue.empty?
t = mon_entering_queue.shift
else
t = mon_waiting_queue.shift
end
t.wakeup if t
Thread.critical = false
MON_EXIT
Thread.critical = true
@waiters.push(Thread.current)
Thread.stop
@monitor.instance_eval(<<MON_ENTER)
Thread.critical = true
while mon_owner != nil && mon_owner != Thread.current
mon_waiting_queue.push(Thread.current)
Thread.stop
Thread.critical = true
end
set_mon_owner(Thread.current)
set_mon_count(_count)
Thread.critical = false
MON_ENTER
end
def wait_while
while yield
wait
end
end
def wait_until
until yield
wait
end
end
def signal
if @monitor.mon_owner != Thread.current
raise ThreadError, "current thread not owner"
end
Thread.critical = true
t = @waiters.shift
t.wakeup if t
Thread.critical = false
Thread.pass
end
def broadcast
if @monitor.mon_owner != Thread.current
raise ThreadError, "current thread not owner"
end
Thread.critical = true
for t in @waiters
t.wakeup
end
@waiters.clear
Thread.critical = false
Thread.pass
end
def count_waiters
return @waiters.length
end
end
def new_cond
return ConditionVariable.new(self)
end
end
class Monitor
include MonitorMixin
alias try_enter try_mon_enter
alias enter mon_enter
alias exit mon_exit
alias owner mon_owner
end
## monitor.rb ends here
|