fastthread 0.6.4.1 → 1.0
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/Rakefile +1 -1
- data/ext/fastthread/fastthread.c +809 -758
- metadata +3 -3
data/Rakefile
CHANGED
data/ext/fastthread/fastthread.c
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
*
|
5
5
|
* Copyright 2006-2007 MenTaLguY <mental@rydia.net>
|
6
6
|
*
|
7
|
+
* RDoc taken from original.
|
8
|
+
*
|
7
9
|
* This file is made available under the same terms as Ruby.
|
8
10
|
*/
|
9
11
|
|
@@ -11,1125 +13,1174 @@
|
|
11
13
|
#include <intern.h>
|
12
14
|
#include <rubysig.h>
|
13
15
|
|
14
|
-
static VALUE avoid_mem_pools;
|
15
|
-
|
16
|
-
#ifndef USE_MEM_POOLS
|
17
|
-
#define USE_MEM_POOLS !RTEST(avoid_mem_pools)
|
18
|
-
#endif
|
19
|
-
|
20
16
|
static VALUE rb_cMutex;
|
21
17
|
static VALUE rb_cConditionVariable;
|
22
|
-
/* post-1.8.5 Ruby exposes rb_eThreadError; earlier versions do not */
|
23
|
-
static VALUE private_eThreadError;
|
24
18
|
static VALUE rb_cQueue;
|
25
19
|
static VALUE rb_cSizedQueue;
|
20
|
+
/* earlier versions of ruby do not export rb_eThreadError */
|
21
|
+
static VALUE private_eThreadError;
|
26
22
|
|
27
|
-
static VALUE
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
23
|
+
static VALUE set_critical(VALUE value);
|
24
|
+
|
25
|
+
/*
|
26
|
+
* call-seq:
|
27
|
+
* Thread.exclusive { block } => obj
|
28
|
+
*
|
29
|
+
* Wraps a block in Thread.critical, restoring the original value
|
30
|
+
* upon exit from the critical section, and returns the value of the
|
31
|
+
* block.
|
32
|
+
*/
|
33
33
|
|
34
34
|
typedef struct _Entry {
|
35
|
-
|
36
|
-
|
35
|
+
VALUE value;
|
36
|
+
struct _Entry *next;
|
37
37
|
} Entry;
|
38
38
|
|
39
39
|
typedef struct _List {
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
Entry *entries;
|
41
|
+
Entry *last_entry;
|
42
|
+
Entry *entry_pool;
|
43
|
+
unsigned long size;
|
44
44
|
} List;
|
45
45
|
|
46
|
-
static void init_list _((List *));
|
47
|
-
|
48
46
|
static void
|
49
|
-
init_list(list)
|
50
|
-
List *list;
|
47
|
+
init_list(List *list)
|
51
48
|
{
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
49
|
+
list->entries = NULL;
|
50
|
+
list->last_entry = NULL;
|
51
|
+
list->entry_pool = NULL;
|
52
|
+
list->size = 0;
|
56
53
|
}
|
57
54
|
|
58
|
-
static void mark_list _((List *));
|
59
|
-
|
60
55
|
static void
|
61
|
-
mark_list(list)
|
62
|
-
List *list;
|
56
|
+
mark_list(List *list)
|
63
57
|
{
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
58
|
+
Entry *entry;
|
59
|
+
for (entry = list->entries; entry; entry = entry->next) {
|
60
|
+
rb_gc_mark(entry->value);
|
61
|
+
}
|
68
62
|
}
|
69
63
|
|
70
|
-
static void free_entries _((Entry *));
|
71
|
-
|
72
64
|
static void
|
73
|
-
free_entries(first)
|
74
|
-
Entry *first;
|
65
|
+
free_entries(Entry *first)
|
75
66
|
{
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
67
|
+
Entry *next;
|
68
|
+
while (first) {
|
69
|
+
next = first->next;
|
70
|
+
xfree(first);
|
71
|
+
first = next;
|
72
|
+
}
|
82
73
|
}
|
83
74
|
|
84
|
-
static void finalize_list _((List *));
|
85
|
-
|
86
75
|
static void
|
87
|
-
finalize_list(list)
|
88
|
-
List *list;
|
76
|
+
finalize_list(List *list)
|
89
77
|
{
|
90
|
-
|
91
|
-
|
78
|
+
free_entries(list->entries);
|
79
|
+
free_entries(list->entry_pool);
|
92
80
|
}
|
93
81
|
|
94
|
-
static void push_list _((List *, VALUE));
|
95
|
-
|
96
82
|
static void
|
97
|
-
push_list(list, value)
|
98
|
-
List *list;
|
99
|
-
VALUE value;
|
83
|
+
push_list(List *list, VALUE value)
|
100
84
|
{
|
101
|
-
|
85
|
+
Entry *entry;
|
102
86
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
87
|
+
if (list->entry_pool) {
|
88
|
+
entry = list->entry_pool;
|
89
|
+
list->entry_pool = entry->next;
|
90
|
+
} else {
|
91
|
+
entry = ALLOC(Entry);
|
92
|
+
}
|
109
93
|
|
110
|
-
|
111
|
-
|
94
|
+
entry->value = value;
|
95
|
+
entry->next = NULL;
|
112
96
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
97
|
+
if (list->last_entry) {
|
98
|
+
list->last_entry->next = entry;
|
99
|
+
} else {
|
100
|
+
list->entries = entry;
|
101
|
+
}
|
102
|
+
list->last_entry = entry;
|
119
103
|
|
120
|
-
|
104
|
+
++list->size;
|
121
105
|
}
|
122
106
|
|
123
|
-
static void push_multiple_list _((List *, VALUE *, unsigned));
|
124
|
-
|
125
107
|
static void
|
126
|
-
push_multiple_list(list, values, count)
|
127
|
-
List *list;
|
128
|
-
VALUE *values;
|
129
|
-
unsigned count;
|
108
|
+
push_multiple_list(List *list, VALUE *values, unsigned count)
|
130
109
|
{
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
110
|
+
unsigned i;
|
111
|
+
for (i = 0; i < count; i++) {
|
112
|
+
push_list(list, values[i]);
|
113
|
+
}
|
135
114
|
}
|
136
115
|
|
137
|
-
static void recycle_entries _((List *, Entry *, Entry *));
|
138
|
-
|
139
116
|
static void
|
140
|
-
recycle_entries(list, first_entry, last_entry)
|
141
|
-
List *list;
|
142
|
-
Entry *first_entry;
|
143
|
-
Entry *last_entry;
|
117
|
+
recycle_entries(List *list, Entry *first_entry, Entry *last_entry)
|
144
118
|
{
|
145
|
-
|
119
|
+
#ifdef USE_MEM_POOLS
|
146
120
|
last_entry->next = list->entry_pool;
|
147
121
|
list->entry_pool = first_entry;
|
148
|
-
|
122
|
+
#else
|
149
123
|
last_entry->next = NULL;
|
150
124
|
free_entries(first_entry);
|
151
|
-
|
125
|
+
#endif
|
152
126
|
}
|
153
127
|
|
154
|
-
static VALUE shift_list _((List *));
|
155
|
-
|
156
128
|
static VALUE
|
157
|
-
shift_list(list)
|
158
|
-
List *list;
|
129
|
+
shift_list(List *list)
|
159
130
|
{
|
160
|
-
|
161
|
-
|
131
|
+
Entry *entry;
|
132
|
+
VALUE value;
|
162
133
|
|
163
|
-
|
164
|
-
|
134
|
+
entry = list->entries;
|
135
|
+
if (!entry) return Qundef;
|
165
136
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
137
|
+
list->entries = entry->next;
|
138
|
+
if (entry == list->last_entry) {
|
139
|
+
list->last_entry = NULL;
|
140
|
+
}
|
170
141
|
|
171
|
-
|
142
|
+
--list->size;
|
172
143
|
|
173
|
-
|
174
|
-
|
144
|
+
value = entry->value;
|
145
|
+
recycle_entries(list, entry, entry);
|
175
146
|
|
176
|
-
|
147
|
+
return value;
|
177
148
|
}
|
178
149
|
|
179
|
-
static void remove_one _((List *, VALUE));
|
180
|
-
|
181
150
|
static void
|
182
|
-
remove_one(list, value)
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
151
|
+
remove_one(List *list, VALUE value)
|
152
|
+
{
|
153
|
+
Entry **ref;
|
154
|
+
Entry *prev;
|
155
|
+
Entry *entry;
|
156
|
+
|
157
|
+
for (ref = &list->entries, prev = NULL, entry = list->entries;
|
158
|
+
entry != NULL;
|
159
|
+
ref = &entry->next, prev = entry, entry = entry->next) {
|
160
|
+
if (entry->value == value) {
|
161
|
+
*ref = entry->next;
|
162
|
+
list->size--;
|
163
|
+
if (!entry->next) {
|
164
|
+
list->last_entry = prev;
|
165
|
+
}
|
166
|
+
recycle_entries(list, entry, entry);
|
167
|
+
break;
|
168
|
+
}
|
196
169
|
}
|
197
|
-
}
|
198
170
|
}
|
199
171
|
|
200
|
-
static void clear_list _((List *));
|
201
|
-
|
202
172
|
static void
|
203
|
-
clear_list(list)
|
204
|
-
List *list;
|
173
|
+
clear_list(List *list)
|
205
174
|
{
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
175
|
+
if (list->last_entry) {
|
176
|
+
recycle_entries(list, list->entries, list->last_entry);
|
177
|
+
list->entries = NULL;
|
178
|
+
list->last_entry = NULL;
|
179
|
+
list->size = 0;
|
180
|
+
}
|
212
181
|
}
|
213
182
|
|
214
|
-
static VALUE array_from_list _((List const *));
|
215
|
-
|
216
183
|
static VALUE
|
217
|
-
array_from_list(list)
|
218
|
-
List const *list;
|
184
|
+
array_from_list(List const *list)
|
219
185
|
{
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
186
|
+
VALUE ary;
|
187
|
+
Entry *entry;
|
188
|
+
ary = rb_ary_new();
|
189
|
+
for (entry = list->entries; entry; entry = entry->next) {
|
190
|
+
rb_ary_push(ary, entry->value);
|
191
|
+
}
|
192
|
+
return ary;
|
227
193
|
}
|
228
194
|
|
229
|
-
static VALUE wake_thread _((VALUE));
|
230
|
-
|
231
195
|
static VALUE
|
232
|
-
wake_thread(thread)
|
233
|
-
VALUE thread;
|
196
|
+
wake_thread(VALUE thread)
|
234
197
|
{
|
235
|
-
|
236
|
-
|
198
|
+
return rb_rescue2(rb_thread_wakeup, thread,
|
199
|
+
NULL, Qundef, private_eThreadError, 0);
|
237
200
|
}
|
238
201
|
|
239
|
-
static VALUE run_thread _((VALUE));
|
240
|
-
|
241
202
|
static VALUE
|
242
|
-
run_thread(thread)
|
243
|
-
VALUE thread;
|
203
|
+
run_thread(VALUE thread)
|
244
204
|
{
|
245
|
-
|
246
|
-
|
205
|
+
return rb_rescue2(rb_thread_run, thread,
|
206
|
+
NULL, Qundef, private_eThreadError, 0);
|
247
207
|
}
|
248
208
|
|
249
|
-
static VALUE wake_one _((List *));
|
250
|
-
|
251
209
|
static VALUE
|
252
|
-
wake_one(list)
|
253
|
-
List *list;
|
210
|
+
wake_one(List *list)
|
254
211
|
{
|
255
|
-
|
212
|
+
VALUE waking;
|
256
213
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
214
|
+
waking = Qnil;
|
215
|
+
while (list->entries && !RTEST(waking)) {
|
216
|
+
waking = wake_thread(shift_list(list));
|
217
|
+
}
|
261
218
|
|
262
|
-
|
219
|
+
return waking;
|
263
220
|
}
|
264
221
|
|
265
|
-
static VALUE wake_all _((List *));
|
266
|
-
|
267
222
|
static VALUE
|
268
|
-
wake_all(list)
|
269
|
-
List *list;
|
223
|
+
wake_all(List *list)
|
270
224
|
{
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
225
|
+
while (list->entries) {
|
226
|
+
wake_one(list);
|
227
|
+
}
|
228
|
+
return Qnil;
|
275
229
|
}
|
276
230
|
|
277
|
-
static VALUE wait_list_inner _((List *));
|
278
|
-
|
279
231
|
static VALUE
|
280
|
-
wait_list_inner(list)
|
281
|
-
List *list;
|
232
|
+
wait_list_inner(List *list)
|
282
233
|
{
|
283
|
-
|
284
|
-
|
285
|
-
|
234
|
+
push_list(list, rb_thread_current());
|
235
|
+
rb_thread_stop();
|
236
|
+
return Qnil;
|
286
237
|
}
|
287
238
|
|
288
|
-
static VALUE wait_list_cleanup _((List *));
|
289
|
-
|
290
239
|
static VALUE
|
291
|
-
wait_list_cleanup(list)
|
292
|
-
List *list;
|
240
|
+
wait_list_cleanup(List *list)
|
293
241
|
{
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
rb_thread_critical = 0;
|
298
|
-
return Qnil;
|
242
|
+
/* cleanup in case of spurious wakeups */
|
243
|
+
remove_one(list, rb_thread_current());
|
244
|
+
return Qnil;
|
299
245
|
}
|
300
246
|
|
301
|
-
static void wait_list _((List *));
|
302
|
-
|
303
247
|
static void
|
304
|
-
wait_list(list)
|
305
|
-
List *list;
|
248
|
+
wait_list(List *list)
|
306
249
|
{
|
307
|
-
|
250
|
+
rb_ensure(wait_list_inner, (VALUE)list, wait_list_cleanup, (VALUE)list);
|
308
251
|
}
|
309
252
|
|
310
|
-
static void assert_no_survivors _((List *, const char *, void *));
|
311
|
-
|
312
253
|
static void
|
313
|
-
assert_no_survivors(waiting, label, addr)
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
{
|
318
|
-
|
319
|
-
|
320
|
-
if (RTEST(wake_thread(entry->value))) {
|
321
|
-
rb_bug("%s %p freed with live thread(s) waiting", label, addr);
|
254
|
+
assert_no_survivors(List *waiting, const char *label, void *addr)
|
255
|
+
{
|
256
|
+
Entry *entry;
|
257
|
+
for (entry = waiting->entries; entry; entry = entry->next) {
|
258
|
+
if (RTEST(wake_thread(entry->value))) {
|
259
|
+
rb_bug("%s %p freed with live thread(s) waiting", label, addr);
|
260
|
+
}
|
322
261
|
}
|
323
|
-
}
|
324
262
|
}
|
325
263
|
|
264
|
+
/*
|
265
|
+
* Document-class: Mutex
|
266
|
+
*
|
267
|
+
* Mutex implements a simple semaphore that can be used to coordinate access to
|
268
|
+
* shared data from multiple concurrent threads.
|
269
|
+
*
|
270
|
+
* Example:
|
271
|
+
*
|
272
|
+
* require 'thread'
|
273
|
+
* semaphore = Mutex.new
|
274
|
+
*
|
275
|
+
* a = Thread.new {
|
276
|
+
* semaphore.synchronize {
|
277
|
+
* # access shared resource
|
278
|
+
* }
|
279
|
+
* }
|
280
|
+
*
|
281
|
+
* b = Thread.new {
|
282
|
+
* semaphore.synchronize {
|
283
|
+
* # access shared resource
|
284
|
+
* }
|
285
|
+
* }
|
286
|
+
*
|
287
|
+
*/
|
288
|
+
|
326
289
|
typedef struct _Mutex {
|
327
|
-
|
328
|
-
|
290
|
+
VALUE owner;
|
291
|
+
List waiting;
|
329
292
|
} Mutex;
|
330
293
|
|
331
|
-
static void mark_mutex _((Mutex *));
|
332
|
-
|
333
294
|
static void
|
334
|
-
mark_mutex(mutex)
|
335
|
-
Mutex *mutex;
|
295
|
+
mark_mutex(Mutex *mutex)
|
336
296
|
{
|
337
|
-
|
338
|
-
|
297
|
+
rb_gc_mark(mutex->owner);
|
298
|
+
mark_list(&mutex->waiting);
|
339
299
|
}
|
340
300
|
|
341
|
-
static void finalize_mutex _((Mutex *));
|
342
|
-
|
343
301
|
static void
|
344
|
-
finalize_mutex(mutex)
|
345
|
-
Mutex *mutex;
|
302
|
+
finalize_mutex(Mutex *mutex)
|
346
303
|
{
|
347
|
-
|
304
|
+
finalize_list(&mutex->waiting);
|
348
305
|
}
|
349
306
|
|
350
|
-
static void free_mutex _((Mutex *));
|
351
|
-
|
352
307
|
static void
|
353
|
-
free_mutex(mutex)
|
354
|
-
Mutex *mutex;
|
308
|
+
free_mutex(Mutex *mutex)
|
355
309
|
{
|
356
|
-
|
357
|
-
|
358
|
-
|
310
|
+
assert_no_survivors(&mutex->waiting, "mutex", mutex);
|
311
|
+
finalize_mutex(mutex);
|
312
|
+
xfree(mutex);
|
359
313
|
}
|
360
314
|
|
361
|
-
static void init_mutex _((Mutex *));
|
362
|
-
|
363
315
|
static void
|
364
|
-
init_mutex(mutex)
|
365
|
-
Mutex *mutex;
|
316
|
+
init_mutex(Mutex *mutex)
|
366
317
|
{
|
367
|
-
|
368
|
-
|
318
|
+
mutex->owner = Qnil;
|
319
|
+
init_list(&mutex->waiting);
|
369
320
|
}
|
370
321
|
|
371
|
-
|
322
|
+
/*
|
323
|
+
* Document-method: new
|
324
|
+
* call-seq: Mutex.new
|
325
|
+
*
|
326
|
+
* Creates a new Mutex
|
327
|
+
*
|
328
|
+
*/
|
372
329
|
|
373
330
|
static VALUE
|
374
|
-
rb_mutex_alloc(klass)
|
375
|
-
VALUE klass;
|
331
|
+
rb_mutex_alloc(VALUE klass)
|
376
332
|
{
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
333
|
+
Mutex *mutex;
|
334
|
+
mutex = ALLOC(Mutex);
|
335
|
+
init_mutex(mutex);
|
336
|
+
return Data_Wrap_Struct(klass, mark_mutex, free_mutex, mutex);
|
381
337
|
}
|
382
338
|
|
383
|
-
|
339
|
+
/*
|
340
|
+
* Document-method: locked?
|
341
|
+
* call-seq: locked?
|
342
|
+
*
|
343
|
+
* Returns +true+ if this lock is currently held by some thread.
|
344
|
+
*
|
345
|
+
*/
|
384
346
|
|
385
347
|
static VALUE
|
386
|
-
rb_mutex_locked_p(self)
|
387
|
-
VALUE self;
|
348
|
+
rb_mutex_locked_p(VALUE self)
|
388
349
|
{
|
389
|
-
|
390
|
-
|
391
|
-
|
350
|
+
Mutex *mutex;
|
351
|
+
Data_Get_Struct(self, Mutex, mutex);
|
352
|
+
return RTEST(mutex->owner) ? Qtrue : Qfalse;
|
392
353
|
}
|
393
354
|
|
394
|
-
|
355
|
+
/*
|
356
|
+
* Document-method: try_lock
|
357
|
+
* call-seq: try_lock
|
358
|
+
*
|
359
|
+
* Attempts to obtain the lock and returns immediately. Returns +true+ if the
|
360
|
+
* lock was granted.
|
361
|
+
*
|
362
|
+
*/
|
395
363
|
|
396
364
|
static VALUE
|
397
|
-
rb_mutex_try_lock(self)
|
398
|
-
VALUE self;
|
365
|
+
rb_mutex_try_lock(VALUE self)
|
399
366
|
{
|
400
|
-
|
401
|
-
VALUE result;
|
367
|
+
Mutex *mutex;
|
402
368
|
|
403
|
-
|
369
|
+
Data_Get_Struct(self, Mutex, mutex);
|
404
370
|
|
405
|
-
|
371
|
+
if (RTEST(mutex->owner))
|
372
|
+
return Qfalse;
|
406
373
|
|
407
|
-
rb_thread_critical = 1;
|
408
|
-
if (!RTEST(mutex->owner)) {
|
409
374
|
mutex->owner = rb_thread_current();
|
410
|
-
|
411
|
-
}
|
412
|
-
rb_thread_critical = 0;
|
413
|
-
|
414
|
-
return result;
|
375
|
+
return Qtrue;
|
415
376
|
}
|
416
377
|
|
417
|
-
|
378
|
+
/*
|
379
|
+
* Document-method: lock
|
380
|
+
* call-seq: lock
|
381
|
+
*
|
382
|
+
* Attempts to grab the lock and waits if it isn't available.
|
383
|
+
*
|
384
|
+
*/
|
418
385
|
|
419
|
-
static
|
420
|
-
lock_mutex(mutex)
|
421
|
-
Mutex *mutex;
|
386
|
+
static VALUE
|
387
|
+
lock_mutex(Mutex *mutex)
|
422
388
|
{
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
rb_thread_critical = 1;
|
389
|
+
VALUE current;
|
390
|
+
current = rb_thread_current();
|
427
391
|
|
428
|
-
while (RTEST(mutex->owner)) {
|
429
|
-
wait_list(&mutex->waiting);
|
430
392
|
rb_thread_critical = 1;
|
431
|
-
}
|
432
|
-
mutex->owner = current;
|
433
393
|
|
434
|
-
|
435
|
-
|
394
|
+
while (RTEST(mutex->owner)) {
|
395
|
+
wait_list(&mutex->waiting);
|
396
|
+
rb_thread_critical = 1;
|
397
|
+
}
|
398
|
+
mutex->owner = current;
|
436
399
|
|
437
|
-
|
400
|
+
rb_thread_critical = 0;
|
401
|
+
return Qnil;
|
402
|
+
}
|
438
403
|
|
439
404
|
static VALUE
|
440
|
-
rb_mutex_lock(self)
|
441
|
-
VALUE self;
|
405
|
+
rb_mutex_lock(VALUE self)
|
442
406
|
{
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
407
|
+
Mutex *mutex;
|
408
|
+
Data_Get_Struct(self, Mutex, mutex);
|
409
|
+
lock_mutex(mutex);
|
410
|
+
return self;
|
447
411
|
}
|
448
412
|
|
449
|
-
|
413
|
+
/*
|
414
|
+
* Document-method: unlock
|
415
|
+
*
|
416
|
+
* Releases the lock. Returns +nil+ if ref wasn't locked.
|
417
|
+
*
|
418
|
+
*/
|
450
419
|
|
451
420
|
static VALUE
|
452
|
-
unlock_mutex_inner(mutex)
|
453
|
-
Mutex *mutex;
|
421
|
+
unlock_mutex_inner(Mutex *mutex)
|
454
422
|
{
|
455
|
-
|
423
|
+
VALUE waking;
|
456
424
|
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
mutex->owner = Qnil;
|
461
|
-
waking = wake_one(&mutex->waiting);
|
425
|
+
if (!RTEST(mutex->owner)) {
|
426
|
+
return Qundef;
|
427
|
+
}
|
462
428
|
|
463
|
-
|
464
|
-
|
429
|
+
mutex->owner = Qnil;
|
430
|
+
waking = wake_one(&mutex->waiting);
|
465
431
|
|
466
|
-
|
432
|
+
return waking;
|
433
|
+
}
|
467
434
|
|
468
435
|
static VALUE
|
469
|
-
set_critical(value)
|
470
|
-
VALUE value;
|
436
|
+
set_critical(VALUE value)
|
471
437
|
{
|
472
|
-
|
473
|
-
|
438
|
+
rb_thread_critical = (int)value;
|
439
|
+
return Qundef;
|
474
440
|
}
|
475
441
|
|
476
|
-
static VALUE unlock_mutex _((Mutex *));
|
477
|
-
|
478
442
|
static VALUE
|
479
|
-
unlock_mutex(mutex)
|
480
|
-
Mutex *mutex;
|
443
|
+
unlock_mutex(Mutex *mutex)
|
481
444
|
{
|
482
|
-
|
445
|
+
VALUE waking;
|
483
446
|
|
484
|
-
|
485
|
-
|
447
|
+
rb_thread_critical = 1;
|
448
|
+
waking = rb_ensure(unlock_mutex_inner, (VALUE)mutex, set_critical, 0);
|
486
449
|
|
487
|
-
|
488
|
-
|
489
|
-
|
450
|
+
if (waking == Qundef) {
|
451
|
+
return Qfalse;
|
452
|
+
}
|
490
453
|
|
491
|
-
|
492
|
-
|
493
|
-
|
454
|
+
if (RTEST(waking)) {
|
455
|
+
run_thread(waking);
|
456
|
+
}
|
494
457
|
|
495
|
-
|
458
|
+
return Qtrue;
|
496
459
|
}
|
497
460
|
|
498
|
-
static VALUE rb_mutex_unlock _((VALUE));
|
499
|
-
|
500
461
|
static VALUE
|
501
|
-
rb_mutex_unlock(self)
|
502
|
-
VALUE self;
|
462
|
+
rb_mutex_unlock(VALUE self)
|
503
463
|
{
|
504
|
-
|
505
|
-
|
464
|
+
Mutex *mutex;
|
465
|
+
Data_Get_Struct(self, Mutex, mutex);
|
506
466
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
467
|
+
if (RTEST(unlock_mutex(mutex))) {
|
468
|
+
return self;
|
469
|
+
} else {
|
470
|
+
return Qnil;
|
471
|
+
}
|
512
472
|
}
|
513
473
|
|
514
|
-
|
474
|
+
/*
|
475
|
+
* Document-method: exclusive_unlock
|
476
|
+
* call-seq: exclusive_unlock { ... }
|
477
|
+
*
|
478
|
+
* If the mutex is locked, unlocks the mutex, wakes one waiting thread, and
|
479
|
+
* yields in a critical section.
|
480
|
+
*
|
481
|
+
*/
|
515
482
|
|
516
483
|
static VALUE
|
517
|
-
rb_mutex_exclusive_unlock_inner(mutex)
|
518
|
-
Mutex *mutex;
|
484
|
+
rb_mutex_exclusive_unlock_inner(Mutex *mutex)
|
519
485
|
{
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
486
|
+
VALUE waking;
|
487
|
+
waking = unlock_mutex_inner(mutex);
|
488
|
+
rb_yield(Qundef);
|
489
|
+
return waking;
|
524
490
|
}
|
525
491
|
|
526
|
-
static VALUE rb_mutex_exclusive_unlock _((VALUE));
|
527
|
-
|
528
492
|
static VALUE
|
529
|
-
rb_mutex_exclusive_unlock(self)
|
530
|
-
VALUE self;
|
493
|
+
rb_mutex_exclusive_unlock(VALUE self)
|
531
494
|
{
|
532
|
-
|
533
|
-
|
534
|
-
|
495
|
+
Mutex *mutex;
|
496
|
+
VALUE waking;
|
497
|
+
Data_Get_Struct(self, Mutex, mutex);
|
535
498
|
|
536
|
-
|
537
|
-
|
499
|
+
rb_thread_critical = 1;
|
500
|
+
waking = rb_ensure(rb_mutex_exclusive_unlock_inner, (VALUE)mutex, set_critical, 0);
|
538
501
|
|
539
|
-
|
540
|
-
|
541
|
-
|
502
|
+
if (waking == Qundef) {
|
503
|
+
return Qnil;
|
504
|
+
}
|
542
505
|
|
543
|
-
|
544
|
-
|
545
|
-
|
506
|
+
if (RTEST(waking)) {
|
507
|
+
run_thread(waking);
|
508
|
+
}
|
546
509
|
|
547
|
-
|
510
|
+
return self;
|
548
511
|
}
|
549
512
|
|
550
|
-
|
513
|
+
/*
|
514
|
+
* Document-method: synchronize
|
515
|
+
* call-seq: synchronize { ... }
|
516
|
+
*
|
517
|
+
* Obtains a lock, runs the block, and releases the lock when the block
|
518
|
+
* completes. See the example under Mutex.
|
519
|
+
*
|
520
|
+
*/
|
551
521
|
|
552
522
|
static VALUE
|
553
|
-
rb_mutex_synchronize(self)
|
554
|
-
VALUE self;
|
523
|
+
rb_mutex_synchronize(VALUE self)
|
555
524
|
{
|
556
|
-
|
557
|
-
|
525
|
+
rb_mutex_lock(self);
|
526
|
+
return rb_ensure(rb_yield, Qundef, rb_mutex_unlock, self);
|
558
527
|
}
|
559
528
|
|
529
|
+
/*
|
530
|
+
* Document-class: ConditionVariable
|
531
|
+
*
|
532
|
+
* ConditionVariable objects augment class Mutex. Using condition variables,
|
533
|
+
* it is possible to suspend while in the middle of a critical section until a
|
534
|
+
* resource becomes available.
|
535
|
+
*
|
536
|
+
* Example:
|
537
|
+
*
|
538
|
+
* require 'thread'
|
539
|
+
*
|
540
|
+
* mutex = Mutex.new
|
541
|
+
* resource = ConditionVariable.new
|
542
|
+
*
|
543
|
+
* a = Thread.new {
|
544
|
+
* mutex.synchronize {
|
545
|
+
* # Thread 'a' now needs the resource
|
546
|
+
* resource.wait(mutex)
|
547
|
+
* # 'a' can now have the resource
|
548
|
+
* }
|
549
|
+
* }
|
550
|
+
*
|
551
|
+
* b = Thread.new {
|
552
|
+
* mutex.synchronize {
|
553
|
+
* # Thread 'b' has finished using the resource
|
554
|
+
* resource.signal
|
555
|
+
* }
|
556
|
+
* }
|
557
|
+
*
|
558
|
+
*/
|
559
|
+
|
560
560
|
typedef struct _ConditionVariable {
|
561
|
-
|
561
|
+
List waiting;
|
562
562
|
} ConditionVariable;
|
563
563
|
|
564
|
-
static void mark_condvar _((ConditionVariable *));
|
565
|
-
|
566
564
|
static void
|
567
|
-
mark_condvar(condvar)
|
568
|
-
ConditionVariable *condvar;
|
565
|
+
mark_condvar(ConditionVariable *condvar)
|
569
566
|
{
|
570
|
-
|
567
|
+
mark_list(&condvar->waiting);
|
571
568
|
}
|
572
569
|
|
573
|
-
static void finalize_condvar _((ConditionVariable *));
|
574
|
-
|
575
570
|
static void
|
576
|
-
finalize_condvar(condvar)
|
577
|
-
ConditionVariable *condvar;
|
571
|
+
finalize_condvar(ConditionVariable *condvar)
|
578
572
|
{
|
579
|
-
|
573
|
+
finalize_list(&condvar->waiting);
|
580
574
|
}
|
581
575
|
|
582
|
-
static void free_condvar _((ConditionVariable *));
|
583
|
-
|
584
576
|
static void
|
585
|
-
free_condvar(condvar)
|
586
|
-
ConditionVariable *condvar;
|
577
|
+
free_condvar(ConditionVariable *condvar)
|
587
578
|
{
|
588
|
-
|
589
|
-
|
590
|
-
|
579
|
+
assert_no_survivors(&condvar->waiting, "condition variable", condvar);
|
580
|
+
finalize_condvar(condvar);
|
581
|
+
xfree(condvar);
|
591
582
|
}
|
592
583
|
|
593
|
-
static void init_condvar _((ConditionVariable *));
|
594
|
-
|
595
584
|
static void
|
596
|
-
init_condvar(condvar)
|
597
|
-
ConditionVariable *condvar;
|
585
|
+
init_condvar(ConditionVariable *condvar)
|
598
586
|
{
|
599
|
-
|
587
|
+
init_list(&condvar->waiting);
|
600
588
|
}
|
601
589
|
|
602
|
-
|
590
|
+
/*
|
591
|
+
* Document-method: new
|
592
|
+
* call-seq: ConditionVariable.new
|
593
|
+
*
|
594
|
+
* Creates a new ConditionVariable
|
595
|
+
*
|
596
|
+
*/
|
603
597
|
|
604
598
|
static VALUE
|
605
|
-
rb_condvar_alloc(klass)
|
606
|
-
VALUE klass;
|
599
|
+
rb_condvar_alloc(VALUE klass)
|
607
600
|
{
|
608
|
-
|
601
|
+
ConditionVariable *condvar;
|
609
602
|
|
610
|
-
|
611
|
-
|
603
|
+
condvar = ALLOC(ConditionVariable);
|
604
|
+
init_condvar(condvar);
|
612
605
|
|
613
|
-
|
606
|
+
return Data_Wrap_Struct(klass, mark_condvar, free_condvar, condvar);
|
614
607
|
}
|
615
608
|
|
616
|
-
|
609
|
+
/*
|
610
|
+
* Document-method: wait
|
611
|
+
* call-seq: wait
|
612
|
+
*
|
613
|
+
* Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
|
614
|
+
*
|
615
|
+
*/
|
617
616
|
|
618
617
|
static void
|
619
|
-
wait_condvar(condvar, mutex)
|
620
|
-
ConditionVariable *condvar;
|
621
|
-
Mutex *mutex;
|
618
|
+
wait_condvar(ConditionVariable *condvar, Mutex *mutex)
|
622
619
|
{
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
rb_raise(private_eThreadError, "Not owner");
|
631
|
-
}
|
632
|
-
mutex->owner = Qnil;
|
633
|
-
wait_list(&condvar->waiting);
|
634
|
-
|
635
|
-
lock_mutex(mutex);
|
620
|
+
rb_thread_critical = 1;
|
621
|
+
if (rb_thread_current() != mutex->owner) {
|
622
|
+
rb_thread_critical = 0;
|
623
|
+
rb_raise(private_eThreadError, "not owner of the synchronization mutex");
|
624
|
+
}
|
625
|
+
unlock_mutex_inner(mutex);
|
626
|
+
rb_ensure(wait_list, (VALUE)&condvar->waiting, lock_mutex, (VALUE)mutex);
|
636
627
|
}
|
637
628
|
|
638
|
-
static VALUE legacy_exclusive_unlock _((VALUE));
|
639
|
-
|
640
629
|
static VALUE
|
641
|
-
legacy_exclusive_unlock(mutex)
|
642
|
-
VALUE mutex;
|
630
|
+
legacy_exclusive_unlock(VALUE mutex)
|
643
631
|
{
|
644
|
-
|
632
|
+
return rb_funcall(mutex, rb_intern("exclusive_unlock"), 0);
|
645
633
|
}
|
646
634
|
|
647
635
|
typedef struct {
|
648
|
-
|
649
|
-
|
636
|
+
ConditionVariable *condvar;
|
637
|
+
VALUE mutex;
|
650
638
|
} legacy_wait_args;
|
651
639
|
|
652
|
-
static VALUE legacy_wait _((VALUE, legacy_wait_args *));
|
653
|
-
|
654
640
|
static VALUE
|
655
|
-
legacy_wait(unused, args)
|
656
|
-
VALUE unused;
|
657
|
-
legacy_wait_args *args;
|
641
|
+
legacy_wait(VALUE unused, legacy_wait_args *args)
|
658
642
|
{
|
659
|
-
|
660
|
-
|
661
|
-
|
643
|
+
wait_list(&args->condvar->waiting);
|
644
|
+
rb_funcall(args->mutex, rb_intern("lock"), 0);
|
645
|
+
return Qnil;
|
662
646
|
}
|
663
647
|
|
664
|
-
static VALUE rb_condvar_wait _((VALUE, VALUE));
|
665
|
-
|
666
648
|
static VALUE
|
667
|
-
rb_condvar_wait(self, mutex_v)
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
wait_condvar(condvar, mutex);
|
684
|
-
}
|
649
|
+
rb_condvar_wait(VALUE self, VALUE mutex_v)
|
650
|
+
{
|
651
|
+
ConditionVariable *condvar;
|
652
|
+
Data_Get_Struct(self, ConditionVariable, condvar);
|
653
|
+
|
654
|
+
if (CLASS_OF(mutex_v) != rb_cMutex) {
|
655
|
+
/* interoperate with legacy mutex */
|
656
|
+
legacy_wait_args args;
|
657
|
+
args.condvar = condvar;
|
658
|
+
args.mutex = mutex_v;
|
659
|
+
rb_iterate(legacy_exclusive_unlock, mutex_v, legacy_wait, (VALUE)&args);
|
660
|
+
} else {
|
661
|
+
Mutex *mutex;
|
662
|
+
Data_Get_Struct(mutex_v, Mutex, mutex);
|
663
|
+
wait_condvar(condvar, mutex);
|
664
|
+
}
|
685
665
|
|
686
|
-
|
666
|
+
return self;
|
687
667
|
}
|
688
668
|
|
689
|
-
|
669
|
+
/*
|
670
|
+
* Document-method: broadcast
|
671
|
+
* call-seq: broadcast
|
672
|
+
*
|
673
|
+
* Wakes up all threads waiting for this condition.
|
674
|
+
*
|
675
|
+
*/
|
690
676
|
|
691
677
|
static VALUE
|
692
|
-
rb_condvar_broadcast(self)
|
693
|
-
VALUE self;
|
678
|
+
rb_condvar_broadcast(VALUE self)
|
694
679
|
{
|
695
|
-
|
680
|
+
ConditionVariable *condvar;
|
696
681
|
|
697
|
-
|
682
|
+
Data_Get_Struct(self, ConditionVariable, condvar);
|
698
683
|
|
699
|
-
|
700
|
-
|
701
|
-
|
684
|
+
rb_thread_critical = 1;
|
685
|
+
rb_ensure(wake_all, (VALUE)&condvar->waiting, set_critical, 0);
|
686
|
+
rb_thread_schedule();
|
702
687
|
|
703
|
-
|
688
|
+
return self;
|
704
689
|
}
|
705
690
|
|
706
|
-
|
691
|
+
/*
|
692
|
+
* Document-method: signal
|
693
|
+
* call-seq: signal
|
694
|
+
*
|
695
|
+
* Wakes up the first thread in line waiting for this condition.
|
696
|
+
*
|
697
|
+
*/
|
707
698
|
|
708
699
|
static void
|
709
|
-
signal_condvar(condvar)
|
710
|
-
ConditionVariable *condvar;
|
700
|
+
signal_condvar(ConditionVariable *condvar)
|
711
701
|
{
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
702
|
+
VALUE waking;
|
703
|
+
rb_thread_critical = 1;
|
704
|
+
waking = rb_ensure(wake_one, (VALUE)&condvar->waiting, set_critical, 0);
|
705
|
+
if (RTEST(waking)) {
|
706
|
+
run_thread(waking);
|
707
|
+
}
|
718
708
|
}
|
719
709
|
|
720
|
-
static VALUE rb_condvar_signal _((VALUE));
|
721
|
-
|
722
710
|
static VALUE
|
723
|
-
rb_condvar_signal(self)
|
724
|
-
VALUE self;
|
711
|
+
rb_condvar_signal(VALUE self)
|
725
712
|
{
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
713
|
+
ConditionVariable *condvar;
|
714
|
+
Data_Get_Struct(self, ConditionVariable, condvar);
|
715
|
+
signal_condvar(condvar);
|
716
|
+
return self;
|
730
717
|
}
|
731
718
|
|
719
|
+
/*
|
720
|
+
* Document-class: Queue
|
721
|
+
*
|
722
|
+
* This class provides a way to synchronize communication between threads.
|
723
|
+
*
|
724
|
+
* Example:
|
725
|
+
*
|
726
|
+
* require 'thread'
|
727
|
+
*
|
728
|
+
* queue = Queue.new
|
729
|
+
*
|
730
|
+
* producer = Thread.new do
|
731
|
+
* 5.times do |i|
|
732
|
+
* sleep rand(i) # simulate expense
|
733
|
+
* queue << i
|
734
|
+
* puts "#{i} produced"
|
735
|
+
* end
|
736
|
+
* end
|
737
|
+
*
|
738
|
+
* consumer = Thread.new do
|
739
|
+
* 5.times do |i|
|
740
|
+
* value = queue.pop
|
741
|
+
* sleep rand(i/2) # simulate expense
|
742
|
+
* puts "consumed #{value}"
|
743
|
+
* end
|
744
|
+
* end
|
745
|
+
*
|
746
|
+
* consumer.join
|
747
|
+
*
|
748
|
+
*/
|
749
|
+
|
732
750
|
typedef struct _Queue {
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
751
|
+
Mutex mutex;
|
752
|
+
ConditionVariable value_available;
|
753
|
+
ConditionVariable space_available;
|
754
|
+
List values;
|
755
|
+
unsigned long capacity;
|
738
756
|
} Queue;
|
739
757
|
|
740
|
-
static void mark_queue _((Queue *));
|
741
|
-
|
742
758
|
static void
|
743
|
-
mark_queue(queue)
|
744
|
-
Queue *queue;
|
759
|
+
mark_queue(Queue *queue)
|
745
760
|
{
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
761
|
+
mark_mutex(&queue->mutex);
|
762
|
+
mark_condvar(&queue->value_available);
|
763
|
+
mark_condvar(&queue->space_available);
|
764
|
+
mark_list(&queue->values);
|
750
765
|
}
|
751
766
|
|
752
|
-
static void finalize_queue _((Queue *));
|
753
|
-
|
754
767
|
static void
|
755
|
-
finalize_queue(queue)
|
756
|
-
Queue *queue;
|
768
|
+
finalize_queue(Queue *queue)
|
757
769
|
{
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
770
|
+
finalize_mutex(&queue->mutex);
|
771
|
+
finalize_condvar(&queue->value_available);
|
772
|
+
finalize_condvar(&queue->space_available);
|
773
|
+
finalize_list(&queue->values);
|
762
774
|
}
|
763
775
|
|
764
|
-
static void free_queue _((Queue *));
|
765
|
-
|
766
776
|
static void
|
767
|
-
free_queue(queue)
|
768
|
-
Queue *queue;
|
777
|
+
free_queue(Queue *queue)
|
769
778
|
{
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
779
|
+
assert_no_survivors(&queue->mutex.waiting, "queue", queue);
|
780
|
+
assert_no_survivors(&queue->space_available.waiting, "queue", queue);
|
781
|
+
assert_no_survivors(&queue->value_available.waiting, "queue", queue);
|
782
|
+
finalize_queue(queue);
|
783
|
+
xfree(queue);
|
775
784
|
}
|
776
785
|
|
777
|
-
static void init_queue _((Queue *));
|
778
|
-
|
779
786
|
static void
|
780
|
-
init_queue(queue)
|
781
|
-
Queue *queue;
|
787
|
+
init_queue(Queue *queue)
|
782
788
|
{
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
789
|
+
init_mutex(&queue->mutex);
|
790
|
+
init_condvar(&queue->value_available);
|
791
|
+
init_condvar(&queue->space_available);
|
792
|
+
init_list(&queue->values);
|
793
|
+
queue->capacity = 0;
|
788
794
|
}
|
789
795
|
|
790
|
-
|
796
|
+
/*
|
797
|
+
* Document-method: new
|
798
|
+
* call-seq: new
|
799
|
+
*
|
800
|
+
* Creates a new queue.
|
801
|
+
*
|
802
|
+
*/
|
791
803
|
|
792
804
|
static VALUE
|
793
|
-
rb_queue_alloc(klass)
|
794
|
-
VALUE klass;
|
805
|
+
rb_queue_alloc(VALUE klass)
|
795
806
|
{
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
807
|
+
Queue *queue;
|
808
|
+
queue = ALLOC(Queue);
|
809
|
+
init_queue(queue);
|
810
|
+
return Data_Wrap_Struct(klass, mark_queue, free_queue, queue);
|
800
811
|
}
|
801
812
|
|
802
|
-
static VALUE rb_queue_marshal_load _((VALUE, VALUE));
|
803
|
-
|
804
813
|
static VALUE
|
805
|
-
rb_queue_marshal_load(self, data)
|
806
|
-
VALUE self;
|
807
|
-
VALUE data;
|
814
|
+
rb_queue_marshal_load(VALUE self, VALUE data)
|
808
815
|
{
|
809
|
-
|
810
|
-
|
811
|
-
|
816
|
+
Queue *queue;
|
817
|
+
VALUE array;
|
818
|
+
Data_Get_Struct(self, Queue, queue);
|
812
819
|
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
820
|
+
array = rb_marshal_load(data);
|
821
|
+
if (TYPE(array) != T_ARRAY) {
|
822
|
+
rb_raise(rb_eRuntimeError, "expected Array of queue data");
|
823
|
+
}
|
824
|
+
if (RARRAY(array)->len < 1) {
|
825
|
+
rb_raise(rb_eRuntimeError, "missing capacity value");
|
826
|
+
}
|
827
|
+
queue->capacity = NUM2ULONG(rb_ary_shift(array));
|
828
|
+
push_multiple_list(&queue->values, RARRAY(array)->ptr, (unsigned)RARRAY(array)->len);
|
822
829
|
|
823
|
-
|
830
|
+
return self;
|
824
831
|
}
|
825
832
|
|
826
|
-
static VALUE rb_queue_marshal_dump _((VALUE));
|
827
|
-
|
828
833
|
static VALUE
|
829
|
-
rb_queue_marshal_dump(self)
|
830
|
-
VALUE self;
|
834
|
+
rb_queue_marshal_dump(VALUE self)
|
831
835
|
{
|
832
|
-
|
833
|
-
|
834
|
-
|
836
|
+
Queue *queue;
|
837
|
+
VALUE array;
|
838
|
+
Data_Get_Struct(self, Queue, queue);
|
835
839
|
|
836
|
-
|
837
|
-
|
838
|
-
|
840
|
+
array = array_from_list(&queue->values);
|
841
|
+
rb_ary_unshift(array, ULONG2NUM(queue->capacity));
|
842
|
+
return rb_marshal_dump(array, Qnil);
|
839
843
|
}
|
840
844
|
|
841
|
-
|
845
|
+
/*
|
846
|
+
* Document-method: clear
|
847
|
+
* call-seq: clear
|
848
|
+
*
|
849
|
+
* Removes all objects from the queue.
|
850
|
+
*
|
851
|
+
*/
|
842
852
|
|
843
853
|
static VALUE
|
844
|
-
rb_queue_clear(self)
|
845
|
-
VALUE self;
|
854
|
+
rb_queue_clear(VALUE self)
|
846
855
|
{
|
847
|
-
|
848
|
-
|
856
|
+
Queue *queue;
|
857
|
+
Data_Get_Struct(self, Queue, queue);
|
849
858
|
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
859
|
+
lock_mutex(&queue->mutex);
|
860
|
+
clear_list(&queue->values);
|
861
|
+
signal_condvar(&queue->space_available);
|
862
|
+
unlock_mutex(&queue->mutex);
|
854
863
|
|
855
|
-
|
864
|
+
return self;
|
856
865
|
}
|
857
866
|
|
858
|
-
|
867
|
+
/*
|
868
|
+
* Document-method: empty?
|
869
|
+
* call-seq: empty?
|
870
|
+
*
|
871
|
+
* Returns +true+ if the queue is empty.
|
872
|
+
*
|
873
|
+
*/
|
859
874
|
|
860
875
|
static VALUE
|
861
|
-
rb_queue_empty_p(self)
|
862
|
-
VALUE self;
|
876
|
+
rb_queue_empty_p(VALUE self)
|
863
877
|
{
|
864
|
-
|
865
|
-
|
866
|
-
|
878
|
+
Queue *queue;
|
879
|
+
VALUE result;
|
880
|
+
Data_Get_Struct(self, Queue, queue);
|
867
881
|
|
868
|
-
|
869
|
-
|
870
|
-
|
882
|
+
lock_mutex(&queue->mutex);
|
883
|
+
result = queue->values.size == 0 ? Qtrue : Qfalse;
|
884
|
+
unlock_mutex(&queue->mutex);
|
871
885
|
|
872
|
-
|
886
|
+
return result;
|
873
887
|
}
|
874
888
|
|
875
|
-
|
889
|
+
/*
|
890
|
+
* Document-method: length
|
891
|
+
* call-seq: length
|
892
|
+
*
|
893
|
+
* Returns the length of the queue.
|
894
|
+
*
|
895
|
+
*/
|
876
896
|
|
877
897
|
static VALUE
|
878
|
-
rb_queue_length(self)
|
879
|
-
VALUE self;
|
898
|
+
rb_queue_length(VALUE self)
|
880
899
|
{
|
881
|
-
|
882
|
-
|
883
|
-
|
900
|
+
Queue *queue;
|
901
|
+
VALUE result;
|
902
|
+
Data_Get_Struct(self, Queue, queue);
|
884
903
|
|
885
|
-
|
886
|
-
|
887
|
-
|
904
|
+
lock_mutex(&queue->mutex);
|
905
|
+
result = ULONG2NUM(queue->values.size);
|
906
|
+
unlock_mutex(&queue->mutex);
|
888
907
|
|
889
|
-
|
908
|
+
return result;
|
890
909
|
}
|
891
910
|
|
892
|
-
|
911
|
+
/*
|
912
|
+
* Document-method: num_waiting
|
913
|
+
* call-seq: num_waiting
|
914
|
+
*
|
915
|
+
* Returns the number of threads waiting on the queue.
|
916
|
+
*
|
917
|
+
*/
|
893
918
|
|
894
919
|
static VALUE
|
895
|
-
rb_queue_num_waiting(self)
|
896
|
-
VALUE self;
|
920
|
+
rb_queue_num_waiting(VALUE self)
|
897
921
|
{
|
898
|
-
|
899
|
-
|
900
|
-
|
922
|
+
Queue *queue;
|
923
|
+
VALUE result;
|
924
|
+
Data_Get_Struct(self, Queue, queue);
|
901
925
|
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
926
|
+
lock_mutex(&queue->mutex);
|
927
|
+
result = ULONG2NUM(queue->value_available.waiting.size +
|
928
|
+
queue->space_available.waiting.size);
|
929
|
+
unlock_mutex(&queue->mutex);
|
906
930
|
|
907
|
-
|
931
|
+
return result;
|
908
932
|
}
|
909
933
|
|
910
|
-
|
934
|
+
/*
|
935
|
+
* Document-method: pop
|
936
|
+
* call_seq: pop(non_block=false)
|
937
|
+
*
|
938
|
+
* Retrieves data from the queue. If the queue is empty, the calling thread is
|
939
|
+
* suspended until data is pushed onto the queue. If +non_block+ is true, the
|
940
|
+
* thread isn't suspended, and an exception is raised.
|
941
|
+
*
|
942
|
+
*/
|
911
943
|
|
912
944
|
static VALUE
|
913
|
-
rb_queue_pop(argc, argv, self)
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
} else {
|
928
|
-
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
|
929
|
-
}
|
930
|
-
|
931
|
-
lock_mutex(&queue->mutex);
|
932
|
-
if ( !queue->values.entries && !should_block ) {
|
933
|
-
unlock_mutex(&queue->mutex);
|
934
|
-
rb_raise(private_eThreadError, "queue empty");
|
935
|
-
}
|
945
|
+
rb_queue_pop(int argc, VALUE *argv, VALUE self)
|
946
|
+
{
|
947
|
+
Queue *queue;
|
948
|
+
int should_block;
|
949
|
+
VALUE result;
|
950
|
+
Data_Get_Struct(self, Queue, queue);
|
951
|
+
|
952
|
+
if (argc == 0) {
|
953
|
+
should_block = 1;
|
954
|
+
} else if (argc == 1) {
|
955
|
+
should_block = !RTEST(argv[0]);
|
956
|
+
} else {
|
957
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
|
958
|
+
}
|
936
959
|
|
937
|
-
|
938
|
-
|
939
|
-
|
960
|
+
lock_mutex(&queue->mutex);
|
961
|
+
if (!queue->values.entries && !should_block) {
|
962
|
+
unlock_mutex(&queue->mutex);
|
963
|
+
rb_raise(private_eThreadError, "queue empty");
|
964
|
+
}
|
940
965
|
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
}
|
945
|
-
unlock_mutex(&queue->mutex);
|
966
|
+
while (!queue->values.entries) {
|
967
|
+
wait_condvar(&queue->value_available, &queue->mutex);
|
968
|
+
}
|
946
969
|
|
947
|
-
|
970
|
+
result = shift_list(&queue->values);
|
971
|
+
if (queue->capacity && queue->values.size < queue->capacity) {
|
972
|
+
signal_condvar(&queue->space_available);
|
973
|
+
}
|
974
|
+
unlock_mutex(&queue->mutex);
|
975
|
+
|
976
|
+
return result;
|
948
977
|
}
|
949
978
|
|
950
|
-
|
979
|
+
/*
|
980
|
+
* Document-method: push
|
981
|
+
* call-seq: push(obj)
|
982
|
+
*
|
983
|
+
* Pushes +obj+ to the queue.
|
984
|
+
*
|
985
|
+
*/
|
951
986
|
|
952
987
|
static VALUE
|
953
|
-
rb_queue_push(self, value)
|
954
|
-
VALUE self;
|
955
|
-
VALUE value;
|
988
|
+
rb_queue_push(VALUE self, VALUE value)
|
956
989
|
{
|
957
|
-
|
958
|
-
|
990
|
+
Queue *queue;
|
991
|
+
Data_Get_Struct(self, Queue, queue);
|
959
992
|
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
993
|
+
lock_mutex(&queue->mutex);
|
994
|
+
while (queue->capacity && queue->values.size >= queue->capacity) {
|
995
|
+
wait_condvar(&queue->space_available, &queue->mutex);
|
996
|
+
}
|
997
|
+
push_list(&queue->values, value);
|
998
|
+
signal_condvar(&queue->value_available);
|
999
|
+
unlock_mutex(&queue->mutex);
|
967
1000
|
|
968
|
-
|
1001
|
+
return self;
|
969
1002
|
}
|
970
1003
|
|
971
|
-
|
1004
|
+
/*
|
1005
|
+
* Document-class: SizedQueue
|
1006
|
+
*
|
1007
|
+
* This class represents queues of specified size capacity. The push operation
|
1008
|
+
* may be blocked if the capacity is full.
|
1009
|
+
*
|
1010
|
+
* See Queue for an example of how a SizedQueue works.
|
1011
|
+
*
|
1012
|
+
*/
|
1013
|
+
|
1014
|
+
/*
|
1015
|
+
* Document-method: new
|
1016
|
+
* call-seq: new
|
1017
|
+
*
|
1018
|
+
* Creates a fixed-length queue with a maximum size of +max+.
|
1019
|
+
*
|
1020
|
+
*/
|
1021
|
+
|
1022
|
+
/*
|
1023
|
+
* Document-method: max
|
1024
|
+
* call-seq: max
|
1025
|
+
*
|
1026
|
+
* Returns the maximum size of the queue.
|
1027
|
+
*
|
1028
|
+
*/
|
972
1029
|
|
973
1030
|
static VALUE
|
974
|
-
rb_sized_queue_max(self)
|
975
|
-
VALUE self;
|
1031
|
+
rb_sized_queue_max(VALUE self)
|
976
1032
|
{
|
977
|
-
|
978
|
-
|
979
|
-
|
1033
|
+
Queue *queue;
|
1034
|
+
VALUE result;
|
1035
|
+
Data_Get_Struct(self, Queue, queue);
|
980
1036
|
|
981
|
-
|
982
|
-
|
983
|
-
|
1037
|
+
lock_mutex(&queue->mutex);
|
1038
|
+
result = ULONG2NUM(queue->capacity);
|
1039
|
+
unlock_mutex(&queue->mutex);
|
984
1040
|
|
985
|
-
|
1041
|
+
return result;
|
986
1042
|
}
|
987
1043
|
|
988
|
-
|
1044
|
+
/*
|
1045
|
+
* Document-method: max=
|
1046
|
+
* call-seq: max=(size)
|
1047
|
+
*
|
1048
|
+
* Sets the maximum size of the queue.
|
1049
|
+
*
|
1050
|
+
*/
|
989
1051
|
|
990
1052
|
static VALUE
|
991
|
-
rb_sized_queue_max_set(self, value)
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
new_capacity = NUM2ULONG(value);
|
1001
|
-
|
1002
|
-
if ( new_capacity < 1 ) {
|
1003
|
-
rb_raise(rb_eArgError, "value must be positive");
|
1004
|
-
}
|
1005
|
-
|
1006
|
-
lock_mutex(&queue->mutex);
|
1007
|
-
if ( queue->capacity && new_capacity > queue->capacity ) {
|
1008
|
-
difference = new_capacity - queue->capacity;
|
1009
|
-
} else {
|
1010
|
-
difference = 0;
|
1011
|
-
}
|
1012
|
-
queue->capacity = new_capacity;
|
1013
|
-
for ( ; difference > 0 ; --difference ) {
|
1014
|
-
signal_condvar(&queue->space_available);
|
1015
|
-
}
|
1016
|
-
unlock_mutex(&queue->mutex);
|
1053
|
+
rb_sized_queue_max_set(VALUE self, VALUE value)
|
1054
|
+
{
|
1055
|
+
Queue *queue;
|
1056
|
+
unsigned long new_capacity;
|
1057
|
+
unsigned long difference;
|
1058
|
+
Data_Get_Struct(self, Queue, queue);
|
1059
|
+
|
1060
|
+
new_capacity = NUM2ULONG(value);
|
1017
1061
|
|
1018
|
-
|
1062
|
+
if (new_capacity < 1) {
|
1063
|
+
rb_raise(rb_eArgError, "value must be positive");
|
1064
|
+
}
|
1065
|
+
|
1066
|
+
lock_mutex(&queue->mutex);
|
1067
|
+
if (queue->capacity && new_capacity > queue->capacity) {
|
1068
|
+
difference = new_capacity - queue->capacity;
|
1069
|
+
} else {
|
1070
|
+
difference = 0;
|
1071
|
+
}
|
1072
|
+
queue->capacity = new_capacity;
|
1073
|
+
for (; difference > 0; --difference) {
|
1074
|
+
signal_condvar(&queue->space_available);
|
1075
|
+
}
|
1076
|
+
unlock_mutex(&queue->mutex);
|
1077
|
+
|
1078
|
+
return self;
|
1019
1079
|
}
|
1020
1080
|
|
1021
|
-
/*
|
1081
|
+
/*
|
1082
|
+
* Document-method: push
|
1083
|
+
* call-seq: push(obj)
|
1084
|
+
*
|
1085
|
+
* Pushes +obj+ to the queue. If there is no space left in the queue, waits
|
1086
|
+
* until space becomes available.
|
1087
|
+
*
|
1088
|
+
*/
|
1022
1089
|
|
1023
|
-
|
1090
|
+
/*
|
1091
|
+
* Document-method: pop
|
1092
|
+
* call-seq: pop(non_block=false)
|
1093
|
+
*
|
1094
|
+
* Retrieves data from the queue and runs a waiting thread, if any.
|
1095
|
+
*
|
1096
|
+
*/
|
1097
|
+
|
1098
|
+
/* for marshalling mutexes and condvars */
|
1024
1099
|
|
1025
1100
|
static VALUE
|
1026
|
-
dummy_load(self, string)
|
1027
|
-
VALUE self;
|
1028
|
-
VALUE string;
|
1101
|
+
dummy_load(VALUE self, VALUE string)
|
1029
1102
|
{
|
1030
|
-
|
1103
|
+
return Qnil;
|
1104
|
+
}
|
1105
|
+
|
1106
|
+
static VALUE
|
1107
|
+
dummy_dump(VALUE self)
|
1108
|
+
{
|
1109
|
+
return rb_str_new2("");
|
1031
1110
|
}
|
1032
1111
|
|
1033
|
-
static VALUE dummy_dump _((VALUE));
|
1034
1112
|
|
1035
1113
|
static VALUE
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
rb_define_method(rb_cSizedQueue, "length", rb_queue_length, 0);
|
1093
|
-
rb_define_method(rb_cSizedQueue, "num_waiting", rb_queue_num_waiting, 0);
|
1094
|
-
rb_define_method(rb_cSizedQueue, "pop", rb_queue_pop, -1);
|
1095
|
-
rb_define_method(rb_cSizedQueue, "push", rb_queue_push, 1);
|
1096
|
-
rb_define_method(rb_cSizedQueue, "max", rb_sized_queue_max, 0);
|
1097
|
-
rb_define_method(rb_cSizedQueue, "max=", rb_sized_queue_max_set, 1);
|
1098
|
-
rb_alias(rb_cSizedQueue, rb_intern("<<"), rb_intern("push"));
|
1099
|
-
rb_alias(rb_cSizedQueue, rb_intern("deq"), rb_intern("pop"));
|
1100
|
-
rb_alias(rb_cSizedQueue, rb_intern("shift"), rb_intern("pop"));
|
1101
|
-
|
1102
|
-
return Qnil;
|
1114
|
+
setup_classes(VALUE unused)
|
1115
|
+
{
|
1116
|
+
rb_mod_remove_const(rb_cObject, ID2SYM(rb_intern("Mutex")));
|
1117
|
+
rb_cMutex = rb_define_class("Mutex", rb_cObject);
|
1118
|
+
rb_define_alloc_func(rb_cMutex, rb_mutex_alloc);
|
1119
|
+
rb_define_method(rb_cMutex, "marshal_load", dummy_load, 1);
|
1120
|
+
rb_define_method(rb_cMutex, "marshal_dump", dummy_dump, 0);
|
1121
|
+
rb_define_method(rb_cMutex, "locked?", rb_mutex_locked_p, 0);
|
1122
|
+
rb_define_method(rb_cMutex, "try_lock", rb_mutex_try_lock, 0);
|
1123
|
+
rb_define_method(rb_cMutex, "lock", rb_mutex_lock, 0);
|
1124
|
+
rb_define_method(rb_cMutex, "unlock", rb_mutex_unlock, 0);
|
1125
|
+
rb_define_method(rb_cMutex, "exclusive_unlock", rb_mutex_exclusive_unlock, 0);
|
1126
|
+
rb_define_method(rb_cMutex, "synchronize", rb_mutex_synchronize, 0);
|
1127
|
+
|
1128
|
+
rb_mod_remove_const(rb_cObject, ID2SYM(rb_intern("ConditionVariable")));
|
1129
|
+
rb_cConditionVariable = rb_define_class("ConditionVariable", rb_cObject);
|
1130
|
+
rb_define_alloc_func(rb_cConditionVariable, rb_condvar_alloc);
|
1131
|
+
rb_define_method(rb_cConditionVariable, "marshal_load", dummy_load, 1);
|
1132
|
+
rb_define_method(rb_cConditionVariable, "marshal_dump", dummy_dump, 0);
|
1133
|
+
rb_define_method(rb_cConditionVariable, "wait", rb_condvar_wait, 1);
|
1134
|
+
rb_define_method(rb_cConditionVariable, "broadcast", rb_condvar_broadcast, 0);
|
1135
|
+
rb_define_method(rb_cConditionVariable, "signal", rb_condvar_signal, 0);
|
1136
|
+
|
1137
|
+
rb_mod_remove_const(rb_cObject, ID2SYM(rb_intern("Queue")));
|
1138
|
+
rb_cQueue = rb_define_class("Queue", rb_cObject);
|
1139
|
+
rb_define_alloc_func(rb_cQueue, rb_queue_alloc);
|
1140
|
+
rb_define_method(rb_cQueue, "marshal_load", rb_queue_marshal_load, 1);
|
1141
|
+
rb_define_method(rb_cQueue, "marshal_dump", rb_queue_marshal_dump, 0);
|
1142
|
+
rb_define_method(rb_cQueue, "clear", rb_queue_clear, 0);
|
1143
|
+
rb_define_method(rb_cQueue, "empty?", rb_queue_empty_p, 0);
|
1144
|
+
rb_define_method(rb_cQueue, "length", rb_queue_length, 0);
|
1145
|
+
rb_define_method(rb_cQueue, "num_waiting", rb_queue_num_waiting, 0);
|
1146
|
+
rb_define_method(rb_cQueue, "pop", rb_queue_pop, -1);
|
1147
|
+
rb_define_method(rb_cQueue, "push", rb_queue_push, 1);
|
1148
|
+
rb_alias(rb_cQueue, rb_intern("enq"), rb_intern("push"));
|
1149
|
+
rb_alias(rb_cQueue, rb_intern("<<"), rb_intern("push"));
|
1150
|
+
rb_alias(rb_cQueue, rb_intern("deq"), rb_intern("pop"));
|
1151
|
+
rb_alias(rb_cQueue, rb_intern("shift"), rb_intern("pop"));
|
1152
|
+
rb_alias(rb_cQueue, rb_intern("size"), rb_intern("length"));
|
1153
|
+
|
1154
|
+
rb_mod_remove_const(rb_cObject, ID2SYM(rb_intern("SizedQueue")));
|
1155
|
+
rb_cSizedQueue = rb_define_class("SizedQueue", rb_cQueue);
|
1156
|
+
rb_define_method(rb_cSizedQueue, "initialize", rb_sized_queue_max_set, 1);
|
1157
|
+
rb_define_method(rb_cSizedQueue, "clear", rb_queue_clear, 0);
|
1158
|
+
rb_define_method(rb_cSizedQueue, "empty?", rb_queue_empty_p, 0);
|
1159
|
+
rb_define_method(rb_cSizedQueue, "length", rb_queue_length, 0);
|
1160
|
+
rb_define_method(rb_cSizedQueue, "num_waiting", rb_queue_num_waiting, 0);
|
1161
|
+
rb_define_method(rb_cSizedQueue, "pop", rb_queue_pop, -1);
|
1162
|
+
rb_define_method(rb_cSizedQueue, "push", rb_queue_push, 1);
|
1163
|
+
rb_define_method(rb_cSizedQueue, "max", rb_sized_queue_max, 0);
|
1164
|
+
rb_define_method(rb_cSizedQueue, "max=", rb_sized_queue_max_set, 1);
|
1165
|
+
rb_alias(rb_cSizedQueue, rb_intern("<<"), rb_intern("push"));
|
1166
|
+
rb_alias(rb_cSizedQueue, rb_intern("deq"), rb_intern("pop"));
|
1167
|
+
rb_alias(rb_cSizedQueue, rb_intern("shift"), rb_intern("pop"));
|
1168
|
+
|
1169
|
+
return Qnil;
|
1103
1170
|
}
|
1104
1171
|
|
1105
1172
|
void
|
1106
1173
|
Init_fastthread()
|
1107
1174
|
{
|
1108
|
-
|
1109
|
-
VALUE fastthread_avoid_mem_pools;
|
1110
|
-
int saved_critical;
|
1111
|
-
int i;
|
1112
|
-
|
1113
|
-
avoid_mem_pools = Qnil;
|
1114
|
-
fastthread_avoid_mem_pools = rb_str_new2("$fastthread_avoid_mem_pools");
|
1115
|
-
global_variables = rb_f_global_variables();
|
1116
|
-
for ( i = 0 ; i < RARRAY(global_variables)->len ; i++ ) {
|
1117
|
-
if (RTEST(rb_equal(RARRAY(global_variables)->ptr[i], fastthread_avoid_mem_pools))) {
|
1118
|
-
avoid_mem_pools = rb_gv_get("$fastthread_avoid_mem_pools");
|
1119
|
-
break;
|
1120
|
-
}
|
1121
|
-
}
|
1122
|
-
|
1123
|
-
rb_global_variable(&avoid_mem_pools);
|
1124
|
-
rb_define_variable("$fastthread_avoid_mem_pools", &avoid_mem_pools);
|
1175
|
+
int saved_critical;
|
1125
1176
|
|
1126
|
-
|
1177
|
+
rb_require("thread");
|
1127
1178
|
|
1128
|
-
|
1179
|
+
private_eThreadError = rb_const_get(rb_cObject, rb_intern("ThreadError"));
|
1129
1180
|
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1181
|
+
/* ensure that classes get replaced atomically */
|
1182
|
+
saved_critical = rb_thread_critical;
|
1183
|
+
rb_thread_critical = 1;
|
1184
|
+
rb_ensure(setup_classes, Qnil, set_critical, (VALUE)saved_critical);
|
1134
1185
|
}
|
1135
1186
|
|