xthread 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/cond.c +152 -0
- data/extconf.rb +4 -0
- data/fifo.c +269 -0
- data/lib/xthread/monitor.rb +282 -0
- data/lib/xthread.rb +252 -0
- data/monitor.c +404 -0
- data/queue.c +393 -0
- data/xthread.c +28 -0
- data/xthread.gemspec +32 -0
- data/xthread.h +65 -0
- metadata +79 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (C) 2010 Keiju Ishitsuka. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions
|
5
|
+
are met:
|
6
|
+
1. Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
2. Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
13
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
14
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
15
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
16
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
17
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
18
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
19
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
20
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
21
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
22
|
+
SUCH DAMAGE.
|
data/cond.c
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
/************************************************
|
2
|
+
|
3
|
+
cond.c -
|
4
|
+
|
5
|
+
Copyright (C) 2011 Keiju Ishitsuka
|
6
|
+
Copyright (C) 2011 Penta Advanced Laboratories, Inc.
|
7
|
+
|
8
|
+
************************************************/
|
9
|
+
|
10
|
+
#include "ruby.h"
|
11
|
+
|
12
|
+
#include "xthread.h"
|
13
|
+
|
14
|
+
VALUE rb_cXThreadConditionVariable;
|
15
|
+
|
16
|
+
typedef struct rb_xthread_cond_struct
|
17
|
+
{
|
18
|
+
VALUE waiters;
|
19
|
+
/* VALUE waiters_mutex; */
|
20
|
+
} xthread_cond_t;
|
21
|
+
|
22
|
+
#define GetXThreadCondPtr(obj, tobj) \
|
23
|
+
TypedData_Get_Struct((obj), xthread_cond_t, &xthread_cond_data_type, (tobj))
|
24
|
+
|
25
|
+
static void
|
26
|
+
xthread_cond_mark(void *ptr)
|
27
|
+
{
|
28
|
+
xthread_cond_t *cv = (xthread_cond_t*)ptr;
|
29
|
+
|
30
|
+
rb_gc_mark(cv->waiters);
|
31
|
+
/* rb_gc_mark(cv->waiters_mutex); */
|
32
|
+
}
|
33
|
+
|
34
|
+
static void
|
35
|
+
xthread_cond_free(void *ptr)
|
36
|
+
{
|
37
|
+
ruby_xfree(ptr);
|
38
|
+
}
|
39
|
+
|
40
|
+
static size_t
|
41
|
+
xthread_cond_memsize(const void *ptr)
|
42
|
+
{
|
43
|
+
return ptr ? sizeof(xthread_cond_t) : 0;
|
44
|
+
}
|
45
|
+
|
46
|
+
static const rb_data_type_t xthread_cond_data_type = {
|
47
|
+
"xthread_cond",
|
48
|
+
{xthread_cond_mark, xthread_cond_free, xthread_cond_memsize,},
|
49
|
+
};
|
50
|
+
|
51
|
+
static VALUE
|
52
|
+
xthread_cond_alloc(VALUE klass)
|
53
|
+
{
|
54
|
+
VALUE volatile obj;
|
55
|
+
xthread_cond_t *cv;
|
56
|
+
|
57
|
+
obj = TypedData_Make_Struct(klass, xthread_cond_t,
|
58
|
+
&xthread_cond_data_type, cv);
|
59
|
+
cv->waiters = rb_xthread_fifo_new();
|
60
|
+
/* cv->waiters_mutex = rb_mutex_new(); */
|
61
|
+
return obj;
|
62
|
+
}
|
63
|
+
|
64
|
+
/*
|
65
|
+
* call-seq:
|
66
|
+
* ConditionVariable.new -> condition_variable
|
67
|
+
*
|
68
|
+
* Creates a new ConditionVariable
|
69
|
+
*/
|
70
|
+
static VALUE
|
71
|
+
xthread_cond_initialize(VALUE self)
|
72
|
+
{
|
73
|
+
return self;
|
74
|
+
}
|
75
|
+
|
76
|
+
VALUE
|
77
|
+
rb_xthread_cond_new(void)
|
78
|
+
{
|
79
|
+
return xthread_cond_alloc(rb_cXThreadConditionVariable);
|
80
|
+
}
|
81
|
+
|
82
|
+
VALUE
|
83
|
+
rb_xthread_cond_wait(VALUE self, VALUE mutex, VALUE timeout)
|
84
|
+
{
|
85
|
+
xthread_cond_t *cv;
|
86
|
+
VALUE th = rb_thread_current();
|
87
|
+
|
88
|
+
GetXThreadCondPtr(self, cv);
|
89
|
+
|
90
|
+
/* rb_mutex_lock(cv->waiters_mutex); */
|
91
|
+
rb_xthread_fifo_push(cv->waiters, th);
|
92
|
+
/* rb_mutex_unlock(cv->waiters_mutex); */
|
93
|
+
|
94
|
+
rb_mutex_sleep(mutex, timeout);
|
95
|
+
|
96
|
+
return self;
|
97
|
+
}
|
98
|
+
|
99
|
+
static VALUE
|
100
|
+
xthread_cond_wait(int argc, VALUE *argv, VALUE self)
|
101
|
+
{
|
102
|
+
VALUE mutex;
|
103
|
+
VALUE timeout;
|
104
|
+
|
105
|
+
rb_scan_args(argc, argv, "11", &mutex, &timeout);
|
106
|
+
return rb_xthread_cond_wait(self, mutex, timeout);
|
107
|
+
}
|
108
|
+
|
109
|
+
VALUE
|
110
|
+
rb_xthread_cond_signal(VALUE self)
|
111
|
+
{
|
112
|
+
VALUE th;
|
113
|
+
xthread_cond_t *cv;
|
114
|
+
GetXThreadCondPtr(self, cv);
|
115
|
+
|
116
|
+
/* rb_mutex_lock(cv->waiters_mutex); */
|
117
|
+
th = rb_xthread_fifo_pop(cv->waiters);
|
118
|
+
/* rb_mutex_unlock(cv->waiters_mutex); */
|
119
|
+
if (th != Qnil) {
|
120
|
+
rb_thread_wakeup(th);
|
121
|
+
}
|
122
|
+
|
123
|
+
return self;
|
124
|
+
}
|
125
|
+
|
126
|
+
VALUE
|
127
|
+
rb_xthread_cond_broadcast(VALUE self)
|
128
|
+
{
|
129
|
+
xthread_cond_t *cv;
|
130
|
+
VALUE waiters0;
|
131
|
+
VALUE th;
|
132
|
+
|
133
|
+
GetXThreadCondPtr(self, cv);
|
134
|
+
|
135
|
+
while ((th = rb_xthread_fifo_pop(cv->waiters)) != Qnil) {
|
136
|
+
rb_thread_wakeup(th);
|
137
|
+
}
|
138
|
+
|
139
|
+
return self;
|
140
|
+
}
|
141
|
+
|
142
|
+
void
|
143
|
+
Init_XThreadCond(void)
|
144
|
+
{
|
145
|
+
rb_cXThreadConditionVariable =
|
146
|
+
rb_define_class_under(rb_mXThread, "ConditionVariable", rb_cObject);
|
147
|
+
rb_define_alloc_func(rb_cXThreadConditionVariable, xthread_cond_alloc);
|
148
|
+
rb_define_method(rb_cXThreadConditionVariable, "initialize", xthread_cond_initialize, 0);
|
149
|
+
rb_define_method(rb_cXThreadConditionVariable, "wait", xthread_cond_wait, -1);
|
150
|
+
rb_define_method(rb_cXThreadConditionVariable, "signal", rb_xthread_cond_signal, 0);
|
151
|
+
rb_define_method(rb_cXThreadConditionVariable, "broadcast", rb_xthread_cond_broadcast, 0);
|
152
|
+
}
|
data/extconf.rb
ADDED
data/fifo.c
ADDED
@@ -0,0 +1,269 @@
|
|
1
|
+
/**********************************************************************
|
2
|
+
|
3
|
+
fifo.c -
|
4
|
+
|
5
|
+
Copyright (C) 2011 Keiju Ishitsuka
|
6
|
+
Copyright (C) 2011 Penta Advanced Laboratories, Inc.
|
7
|
+
|
8
|
+
**********************************************************************/
|
9
|
+
|
10
|
+
#include "ruby.h"
|
11
|
+
|
12
|
+
#include "xthread.h"
|
13
|
+
|
14
|
+
#define FIFO_DEFAULT_CAPA 16
|
15
|
+
|
16
|
+
VALUE rb_mXThread;
|
17
|
+
VALUE rb_cXThreadFifo;
|
18
|
+
|
19
|
+
typedef struct rb_xthread_fifo_struct
|
20
|
+
{
|
21
|
+
long push;
|
22
|
+
long pop;
|
23
|
+
long capa;
|
24
|
+
|
25
|
+
VALUE *elements;
|
26
|
+
} xthread_fifo_t;
|
27
|
+
|
28
|
+
#define GetXThreadFifoPtr(obj, tobj) \
|
29
|
+
TypedData_Get_Struct((obj), xthread_fifo_t, &xthread_fifo_data_type, (tobj))
|
30
|
+
|
31
|
+
static void
|
32
|
+
xthread_fifo_mark(void *ptr)
|
33
|
+
{
|
34
|
+
xthread_fifo_t *fifo = (xthread_fifo_t*)ptr;
|
35
|
+
|
36
|
+
if (fifo->push < fifo->capa) {
|
37
|
+
long i;
|
38
|
+
for (i = fifo->pop; i < fifo->push; i++) {
|
39
|
+
rb_gc_mark(fifo->elements[i]);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
else {
|
43
|
+
long i;
|
44
|
+
for (i = 0; i < fifo->push - fifo->capa; i++) {
|
45
|
+
rb_gc_mark(fifo->elements[i]);
|
46
|
+
}
|
47
|
+
|
48
|
+
for (i = fifo->pop; i < fifo->capa; i++) {
|
49
|
+
rb_gc_mark(fifo->elements[i]);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
static void
|
55
|
+
xthread_fifo_free(void *ptr)
|
56
|
+
{
|
57
|
+
xthread_fifo_t *fifo = (xthread_fifo_t*)ptr;
|
58
|
+
|
59
|
+
ruby_xfree(fifo->elements);
|
60
|
+
ruby_xfree(ptr);
|
61
|
+
}
|
62
|
+
|
63
|
+
static size_t
|
64
|
+
xthread_fifo_memsize(const void *ptr)
|
65
|
+
{
|
66
|
+
xthread_fifo_t *fifo = (xthread_fifo_t*)ptr;
|
67
|
+
|
68
|
+
return ptr ? sizeof(xthread_fifo_t) + fifo->capa * sizeof(VALUE): 0;
|
69
|
+
}
|
70
|
+
|
71
|
+
static const rb_data_type_t xthread_fifo_data_type = {
|
72
|
+
"xthread_fifo",
|
73
|
+
{xthread_fifo_mark, xthread_fifo_free, xthread_fifo_memsize,},
|
74
|
+
};
|
75
|
+
|
76
|
+
static VALUE
|
77
|
+
xthread_fifo_alloc(VALUE klass)
|
78
|
+
{
|
79
|
+
VALUE volatile obj;
|
80
|
+
xthread_fifo_t *fifo;
|
81
|
+
|
82
|
+
obj = TypedData_Make_Struct(klass, xthread_fifo_t, &xthread_fifo_data_type, fifo);
|
83
|
+
|
84
|
+
fifo->push = 0;
|
85
|
+
fifo->pop = 0;
|
86
|
+
|
87
|
+
fifo->capa = FIFO_DEFAULT_CAPA;
|
88
|
+
fifo->elements = NULL;
|
89
|
+
|
90
|
+
return obj;
|
91
|
+
}
|
92
|
+
|
93
|
+
static void
|
94
|
+
xthread_fifo_resize_double_capa(xthread_fifo_t *fifo)
|
95
|
+
{
|
96
|
+
long new_capa = fifo->capa * 2;
|
97
|
+
|
98
|
+
REALLOC_N(fifo->elements, VALUE, new_capa);
|
99
|
+
|
100
|
+
if (fifo->push > fifo->capa) {
|
101
|
+
if (fifo->capa - fifo->pop <= fifo->push - fifo->capa) {
|
102
|
+
MEMCPY(&fifo->elements[fifo->pop + fifo->capa],
|
103
|
+
&fifo->elements[fifo->pop], VALUE, fifo->capa - fifo->pop);
|
104
|
+
fifo->pop += fifo->capa;
|
105
|
+
fifo->push += fifo->capa;
|
106
|
+
}
|
107
|
+
else {
|
108
|
+
MEMCPY(&fifo->elements[fifo->capa],
|
109
|
+
fifo->elements, VALUE, fifo->push - fifo->capa);
|
110
|
+
}
|
111
|
+
}
|
112
|
+
fifo->capa = new_capa;
|
113
|
+
}
|
114
|
+
|
115
|
+
static VALUE
|
116
|
+
xthread_fifo_initialize(VALUE self)
|
117
|
+
{
|
118
|
+
xthread_fifo_t *fifo;
|
119
|
+
GetXThreadFifoPtr(self, fifo);
|
120
|
+
|
121
|
+
fifo->elements = ALLOC_N(VALUE, fifo->capa);
|
122
|
+
return self;
|
123
|
+
}
|
124
|
+
|
125
|
+
VALUE
|
126
|
+
rb_xthread_fifo_new(void)
|
127
|
+
{
|
128
|
+
VALUE self;
|
129
|
+
self = xthread_fifo_alloc(rb_cXThreadFifo);
|
130
|
+
xthread_fifo_initialize(self);
|
131
|
+
return self;
|
132
|
+
}
|
133
|
+
|
134
|
+
VALUE
|
135
|
+
rb_xthread_fifo_push(VALUE self, VALUE item)
|
136
|
+
{
|
137
|
+
xthread_fifo_t *fifo;
|
138
|
+
int signal_p = 0;
|
139
|
+
|
140
|
+
GetXThreadFifoPtr(self, fifo);
|
141
|
+
|
142
|
+
if (fifo->push < fifo->capa) {
|
143
|
+
fifo->elements[fifo->push++] = item;
|
144
|
+
return self;
|
145
|
+
}
|
146
|
+
|
147
|
+
if (fifo->push - fifo->capa < fifo->pop) {
|
148
|
+
fifo->elements[fifo->push - fifo->capa] = item;
|
149
|
+
fifo->push++;
|
150
|
+
return self;
|
151
|
+
}
|
152
|
+
|
153
|
+
xthread_fifo_resize_double_capa(fifo);
|
154
|
+
return rb_xthread_fifo_push(self, item);
|
155
|
+
}
|
156
|
+
|
157
|
+
VALUE
|
158
|
+
rb_xthread_fifo_pop(VALUE self)
|
159
|
+
{
|
160
|
+
xthread_fifo_t *fifo;
|
161
|
+
VALUE item;
|
162
|
+
|
163
|
+
GetXThreadFifoPtr(self, fifo);
|
164
|
+
|
165
|
+
if (fifo->push == fifo->pop)
|
166
|
+
return Qnil;
|
167
|
+
|
168
|
+
item = fifo->elements[fifo->pop++];
|
169
|
+
if(fifo->pop >= fifo->capa) {
|
170
|
+
fifo->pop -= fifo->capa;
|
171
|
+
fifo->push -= fifo->capa;
|
172
|
+
}
|
173
|
+
return item;
|
174
|
+
}
|
175
|
+
|
176
|
+
VALUE
|
177
|
+
rb_xthread_fifo_empty_p(VALUE self)
|
178
|
+
{
|
179
|
+
xthread_fifo_t *fifo;
|
180
|
+
GetXThreadFifoPtr(self, fifo);
|
181
|
+
|
182
|
+
if (fifo->push == fifo->pop)
|
183
|
+
return Qtrue;
|
184
|
+
return Qfalse;
|
185
|
+
}
|
186
|
+
|
187
|
+
VALUE
|
188
|
+
rb_xthread_fifo_clear(VALUE self)
|
189
|
+
{
|
190
|
+
xthread_fifo_t *fifo;
|
191
|
+
GetXThreadFifoPtr(self, fifo);
|
192
|
+
|
193
|
+
fifo->push = 0;
|
194
|
+
fifo->pop = 0;
|
195
|
+
return self;
|
196
|
+
}
|
197
|
+
|
198
|
+
VALUE
|
199
|
+
rb_xthread_fifo_length(VALUE self)
|
200
|
+
{
|
201
|
+
xthread_fifo_t *fifo;
|
202
|
+
GetXThreadFifoPtr(self, fifo);
|
203
|
+
|
204
|
+
return LONG2NUM(fifo->push - fifo->pop);
|
205
|
+
}
|
206
|
+
|
207
|
+
VALUE
|
208
|
+
rb_xthread_fifo_to_a(VALUE self)
|
209
|
+
{
|
210
|
+
VALUE ary;
|
211
|
+
xthread_fifo_t *fifo;
|
212
|
+
GetXThreadFifoPtr(self, fifo);
|
213
|
+
|
214
|
+
ary = rb_ary_new2(fifo->push - fifo->pop);
|
215
|
+
|
216
|
+
if (fifo->push < fifo->capa) {
|
217
|
+
long i;
|
218
|
+
for (i = fifo->pop; i < fifo->push; i++) {
|
219
|
+
rb_ary_push(ary, fifo->elements[i]);
|
220
|
+
}
|
221
|
+
}
|
222
|
+
else {
|
223
|
+
long i;
|
224
|
+
for (i = 0; i < fifo->push - fifo->capa; i++) {
|
225
|
+
rb_ary_push(ary, fifo->elements[i]);
|
226
|
+
}
|
227
|
+
|
228
|
+
for (i = fifo->pop; i < fifo->capa; i++) {
|
229
|
+
rb_ary_push(ary, fifo->elements[i]);
|
230
|
+
}
|
231
|
+
}
|
232
|
+
return ary;
|
233
|
+
}
|
234
|
+
|
235
|
+
VALUE
|
236
|
+
rb_xthread_fifo_inspect(VALUE self)
|
237
|
+
{
|
238
|
+
xthread_fifo_t *fifo;
|
239
|
+
VALUE str;
|
240
|
+
|
241
|
+
GetXThreadFifoPtr(self, fifo);
|
242
|
+
|
243
|
+
str = rb_sprintf("<%s ", rb_obj_classname(self), (void*)self);
|
244
|
+
rb_str_append(str, rb_inspect(rb_xthread_fifo_to_a(self)));
|
245
|
+
rb_str_cat2(str, ">");
|
246
|
+
return str;
|
247
|
+
}
|
248
|
+
|
249
|
+
void
|
250
|
+
Init_XThreadFifo()
|
251
|
+
{
|
252
|
+
rb_cXThreadFifo = rb_define_class_under(rb_mXThread, "Fifo", rb_cObject);
|
253
|
+
|
254
|
+
rb_define_alloc_func(rb_cXThreadFifo, xthread_fifo_alloc);
|
255
|
+
rb_define_method(rb_cXThreadFifo, "initialize", xthread_fifo_initialize, 0);
|
256
|
+
rb_define_method(rb_cXThreadFifo, "pop", rb_xthread_fifo_pop, 0);
|
257
|
+
rb_define_alias(rb_cXThreadFifo, "shift", "pop");
|
258
|
+
rb_define_alias(rb_cXThreadFifo, "deq", "pop");
|
259
|
+
rb_define_method(rb_cXThreadFifo, "push", rb_xthread_fifo_push, 1);
|
260
|
+
rb_define_alias(rb_cXThreadFifo, "<<", "push");
|
261
|
+
rb_define_alias(rb_cXThreadFifo, "enq", "push");
|
262
|
+
rb_define_method(rb_cXThreadFifo, "empty?", rb_xthread_fifo_empty_p, 0);
|
263
|
+
rb_define_method(rb_cXThreadFifo, "clear", rb_xthread_fifo_clear, 0);
|
264
|
+
rb_define_method(rb_cXThreadFifo, "length", rb_xthread_fifo_length, 0);
|
265
|
+
rb_define_alias(rb_cXThreadFifo, "size", "length");
|
266
|
+
rb_define_method(rb_cXThreadFifo, "to_a", rb_xthread_fifo_to_a, 0);
|
267
|
+
rb_define_method(rb_cXThreadFifo, "inspect", rb_xthread_fifo_inspect, 0);
|
268
|
+
|
269
|
+
}
|
@@ -0,0 +1,282 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
= xthread/monitor.rb
|
4
|
+
|
5
|
+
This Monitor is using XThread::ConditionVariable.
|
6
|
+
|
7
|
+
Original by monitor.rb
|
8
|
+
Copyright (C) 2001 Shugo Maeda <shugo@ruby-lang.org>
|
9
|
+
|
10
|
+
This library is distributed under the terms of the Ruby license.
|
11
|
+
You can freely distribute/modify this library.
|
12
|
+
|
13
|
+
== example
|
14
|
+
|
15
|
+
This is a simple example.
|
16
|
+
|
17
|
+
require 'monitor.rb'
|
18
|
+
|
19
|
+
buf = []
|
20
|
+
buf.extend(MonitorMixin)
|
21
|
+
empty_cond = buf.new_cond
|
22
|
+
|
23
|
+
# consumer
|
24
|
+
Thread.start do
|
25
|
+
loop do
|
26
|
+
buf.synchronize do
|
27
|
+
empty_cond.wait_while { buf.empty? }
|
28
|
+
print buf.shift
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# producer
|
34
|
+
while line = ARGF.gets
|
35
|
+
buf.synchronize do
|
36
|
+
buf.push(line)
|
37
|
+
empty_cond.signal
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
The consumer thread waits for the producer thread to push a line
|
42
|
+
to buf while buf.empty?, and the producer thread (main thread)
|
43
|
+
reads a line from ARGF and push it to buf, then call
|
44
|
+
empty_cond.signal.
|
45
|
+
|
46
|
+
=end
|
47
|
+
|
48
|
+
require 'xthread'
|
49
|
+
|
50
|
+
#
|
51
|
+
# Adds monitor functionality to an arbitrary object by mixing the module with
|
52
|
+
# +include+. For example:
|
53
|
+
#
|
54
|
+
# require 'monitor'
|
55
|
+
#
|
56
|
+
# buf = []
|
57
|
+
# buf.extend(MonitorMixin)
|
58
|
+
# empty_cond = buf.new_cond
|
59
|
+
#
|
60
|
+
# # consumer
|
61
|
+
# Thread.start do
|
62
|
+
# loop do
|
63
|
+
# buf.synchronize do
|
64
|
+
# empty_cond.wait_while { buf.empty? }
|
65
|
+
# print buf.shift
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# # producer
|
71
|
+
# while line = ARGF.gets
|
72
|
+
# buf.synchronize do
|
73
|
+
# buf.push(line)
|
74
|
+
# empty_cond.signal
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# The consumer thread waits for the producer thread to push a line
|
79
|
+
# to buf while buf.empty?, and the producer thread (main thread)
|
80
|
+
# reads a line from ARGF and push it to buf, then call
|
81
|
+
# empty_cond.signal.
|
82
|
+
#
|
83
|
+
module XThread
|
84
|
+
module RBMonitorMixin
|
85
|
+
#
|
86
|
+
# FIXME: This isn't documented in Nutshell.
|
87
|
+
#
|
88
|
+
# Since MonitorMixin.new_cond returns a ConditionVariable, and the example
|
89
|
+
# above calls while_wait and signal, this class should be documented.
|
90
|
+
#
|
91
|
+
class RBConditionVariable
|
92
|
+
class Timeout < Exception; end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Releases the lock held in the associated monitor and waits; reacquires the lock on wakeup.
|
96
|
+
#
|
97
|
+
# If +timeout+ is given, this method returns after +timeout+ seconds passed,
|
98
|
+
# even if no other thread doesn't signal.
|
99
|
+
#
|
100
|
+
def wait(timeout = nil)
|
101
|
+
@monitor.__send__(:mon_check_owner)
|
102
|
+
count = @monitor.__send__(:mon_exit_for_cond)
|
103
|
+
begin
|
104
|
+
@cond.wait(@monitor.instance_variable_get("@mon_mutex"), timeout)
|
105
|
+
return true
|
106
|
+
ensure
|
107
|
+
@monitor.__send__(:mon_enter_for_cond, count)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Calls wait repeatedly while the given block yields a truthy value.
|
113
|
+
#
|
114
|
+
def wait_while
|
115
|
+
while yield
|
116
|
+
wait
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# Calls wait repeatedly until the given block yields a truthy value.
|
122
|
+
#
|
123
|
+
def wait_until
|
124
|
+
until yield
|
125
|
+
wait
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Wakes up the first thread in line waiting for this lock.
|
131
|
+
#
|
132
|
+
def signal
|
133
|
+
@monitor.__send__(:mon_check_owner)
|
134
|
+
@cond.signal
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Wakes up all threads waiting for this lock.
|
139
|
+
#
|
140
|
+
def broadcast
|
141
|
+
@monitor.__send__(:mon_check_owner)
|
142
|
+
@cond.broadcast
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def initialize(monitor)
|
148
|
+
@monitor = monitor
|
149
|
+
@cond = ::XThread::ConditionVariable.new
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.extend_object(obj)
|
154
|
+
super(obj)
|
155
|
+
obj.__send__(:mon_initialize)
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# Attempts to enter exclusive section. Returns +false+ if lock fails.
|
160
|
+
#
|
161
|
+
def mon_try_enter
|
162
|
+
if @mon_owner != Thread.current
|
163
|
+
unless @mon_mutex.try_lock
|
164
|
+
return false
|
165
|
+
end
|
166
|
+
@mon_owner = Thread.current
|
167
|
+
end
|
168
|
+
@mon_count += 1
|
169
|
+
return true
|
170
|
+
end
|
171
|
+
# For backward compatibility
|
172
|
+
alias try_mon_enter mon_try_enter
|
173
|
+
|
174
|
+
#
|
175
|
+
# Enters exclusive section.
|
176
|
+
#
|
177
|
+
def mon_enter
|
178
|
+
if @mon_owner != Thread.current
|
179
|
+
@mon_mutex.lock
|
180
|
+
@mon_owner = Thread.current
|
181
|
+
end
|
182
|
+
@mon_count += 1
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Leaves exclusive section.
|
187
|
+
#
|
188
|
+
def mon_exit
|
189
|
+
mon_check_owner
|
190
|
+
@mon_count -=1
|
191
|
+
if @mon_count == 0
|
192
|
+
@mon_owner = nil
|
193
|
+
@mon_mutex.unlock
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
# Enters exclusive section and executes the block. Leaves the exclusive
|
199
|
+
# section automatically when the block exits. See example under
|
200
|
+
# +MonitorMixin+.
|
201
|
+
#
|
202
|
+
def mon_synchronize
|
203
|
+
mon_enter
|
204
|
+
begin
|
205
|
+
yield
|
206
|
+
ensure
|
207
|
+
mon_exit
|
208
|
+
end
|
209
|
+
end
|
210
|
+
alias synchronize mon_synchronize
|
211
|
+
|
212
|
+
#
|
213
|
+
# Creates a new MonitorMixin::ConditionVariable associated with the
|
214
|
+
# receiver.
|
215
|
+
#
|
216
|
+
def new_cond
|
217
|
+
return RBConditionVariable.new(self)
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
|
222
|
+
def initialize(*args)
|
223
|
+
super
|
224
|
+
mon_initialize
|
225
|
+
end
|
226
|
+
|
227
|
+
def mon_initialize
|
228
|
+
@mon_owner = nil
|
229
|
+
@mon_count = 0
|
230
|
+
@mon_mutex = Mutex.new
|
231
|
+
end
|
232
|
+
|
233
|
+
def mon_check_owner
|
234
|
+
if @mon_owner != Thread.current
|
235
|
+
raise ThreadError, "current thread not owner"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def mon_enter_for_cond(count)
|
240
|
+
@mon_owner = Thread.current
|
241
|
+
@mon_count = count
|
242
|
+
end
|
243
|
+
|
244
|
+
def mon_exit_for_cond
|
245
|
+
count = @mon_count
|
246
|
+
@mon_owner = nil
|
247
|
+
@mon_count = 0
|
248
|
+
return count
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
class RBMonitor
|
253
|
+
include RBMonitorMixin
|
254
|
+
alias try_enter try_mon_enter
|
255
|
+
alias enter mon_enter
|
256
|
+
alias exit mon_exit
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
# Documentation comments:
|
262
|
+
# - All documentation comes from Nutshell.
|
263
|
+
# - MonitorMixin.new_cond appears in the example, but is not documented in
|
264
|
+
# Nutshell.
|
265
|
+
# - All the internals (internal modules Accessible and Initializable, class
|
266
|
+
# ConditionVariable) appear in RDoc. It might be good to hide them, by
|
267
|
+
# making them private, or marking them :nodoc:, etc.
|
268
|
+
# - The entire example from the RD section at the top is replicated in the RDoc
|
269
|
+
# comment for MonitorMixin. Does the RD section need to remain?
|
270
|
+
# - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but
|
271
|
+
# not synchronize.
|
272
|
+
# - mon_owner is in Nutshell, but appears as an accessor in a separate module
|
273
|
+
# here, so is hard/impossible to RDoc. Some other useful accessors
|
274
|
+
# (mon_count and some queue stuff) are also in this module, and don't appear
|
275
|
+
# directly in the RDoc output.
|
276
|
+
# - in short, it may be worth changing the code layout in this file to make the
|
277
|
+
# documentation easier
|
278
|
+
|
279
|
+
# Local variables:
|
280
|
+
# mode: Ruby
|
281
|
+
# tab-width: 8
|
282
|
+
# End:
|