xthread 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|