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/lib/xthread.rb ADDED
@@ -0,0 +1,252 @@
1
+ #
2
+ # xthread.rb -
3
+ # Copyright (C) 2011 Keiju Ishitsuka
4
+ # Copyright (C) 2011 Penta Advanced Laboratories, Inc.
5
+ #
6
+ #
7
+
8
+ require "xthread.so"
9
+
10
+ unless defined? Thread
11
+ raise "Thread not available for this ruby interpreter"
12
+ end
13
+
14
+ unless defined? ThreadError
15
+ class ThreadError < StandardError
16
+ end
17
+ end
18
+
19
+ if $DEBUG
20
+ Thread.abort_on_exception = true
21
+ end
22
+
23
+ module XThread
24
+ module MonitorMixin
25
+ def self.extend_object(obj)
26
+ super(obj)
27
+ obj.__send__(:mon_initialize)
28
+ end
29
+
30
+ def mon_initialize
31
+ @_monitor = Monitor.new
32
+ end
33
+
34
+ def mon_try_enter
35
+ @_monitor.try_enter
36
+ end
37
+
38
+ def mon_enter
39
+ @_monitor.enter
40
+ end
41
+
42
+ def mon_exit
43
+ @_monitor.exit
44
+ end
45
+
46
+ def mon_synchronize(&block)
47
+ @_monitor.synchronize(&block)
48
+ end
49
+
50
+ def new_cond
51
+ @_monitor.new_cond
52
+ end
53
+
54
+ end
55
+ end
56
+
57
+ module XThread
58
+ class RBQueue
59
+ def initialize
60
+ @que = []
61
+ @que.taint # enable tainted comunication
62
+ self.taint
63
+ @mutex = Mutex.new
64
+ @cond = XThread::ConditionVariable.new
65
+ end
66
+
67
+ #
68
+ # Pushes +obj+ to the queue.
69
+ #
70
+ def push(obj)
71
+ @mutex.synchronize do
72
+ @que.push obj
73
+ @cond.signal
74
+ end
75
+ end
76
+
77
+ #
78
+ # Alias of push
79
+ #
80
+ alias << push
81
+
82
+ #
83
+ # Alias of push
84
+ #
85
+ alias enq push
86
+
87
+ #
88
+ # Retrieves data from the queue. If the queue is empty, the calling thread is
89
+ # suspended until data is pushed onto the queue. If +non_block+ is true, the
90
+ # thread isn't suspended, and an exception is raised.
91
+ #
92
+ def pop(non_block=false)
93
+ @mutex.synchronize{
94
+ while true
95
+ if @que.empty?
96
+ @cond.wait(@mutex)
97
+ else
98
+ return @que.shift
99
+ end
100
+ end
101
+ }
102
+ end
103
+
104
+ #
105
+ # Alias of pop
106
+ #
107
+ alias shift pop
108
+
109
+ #
110
+ # Alias of pop
111
+ #
112
+ alias deq pop
113
+
114
+ #
115
+ # Returns +true+ if the queue is empty.
116
+ #
117
+ def empty?
118
+ @que.empty?
119
+ end
120
+
121
+ #
122
+ # Removes all objects from the queue.
123
+ #
124
+ def clear
125
+ @que.clear
126
+ end
127
+
128
+ #
129
+ # Returns the length of the queue.
130
+ #
131
+ def length
132
+ @que.length
133
+ end
134
+
135
+ #
136
+ # Alias of length.
137
+ #
138
+ alias size length
139
+
140
+ #
141
+ # Returns the number of threads waiting on the queue.
142
+ #
143
+ # def num_waiting
144
+ # @waiting.size
145
+ # end
146
+ end
147
+
148
+ #
149
+ # This class represents queues of specified size capacity. The push operation
150
+ # may be blocked if the capacity is full.
151
+ #
152
+ # See Queue for an example of how a SizedQueue works.
153
+ #
154
+ class RBSizedQueue < RBQueue
155
+ #
156
+ # Creates a fixed-length queue with a maximum size of +max+.
157
+ #
158
+ def initialize(max)
159
+ raise ArgumentError, "queue size must be positive" unless max > 0
160
+ @max = max
161
+ @cond_wait = ConditionVariable.new
162
+ super()
163
+ end
164
+
165
+ #
166
+ # Returns the maximum size of the queue.
167
+ #
168
+ def max
169
+ @max
170
+ end
171
+
172
+ #
173
+ # Sets the maximum size of the queue.
174
+ #
175
+ def max=(max)
176
+ diff = nil
177
+ @mutex.synchronize {
178
+ if max <= @max
179
+ @max = max
180
+ else
181
+ diff = max - @max
182
+ @max = max
183
+ end
184
+ }
185
+ if diff
186
+ diff.times do
187
+ @cond_wait.signal
188
+ end
189
+ end
190
+ max
191
+ end
192
+
193
+ #
194
+ # Pushes +obj+ to the queue. If there is no space left in the queue, waits
195
+ # until space becomes available.
196
+ #
197
+ def push(obj)
198
+ @mutex.synchronize{
199
+ while true
200
+ break if @que.length < @max
201
+ @cond_wait.wait(@mutex)
202
+ end
203
+
204
+ @que.push obj
205
+ @cond.signal
206
+ }
207
+ end
208
+
209
+ #
210
+ # Alias of push
211
+ #
212
+ alias << push
213
+
214
+ #
215
+ # Alias of push
216
+ #
217
+ alias enq push
218
+
219
+ #
220
+ # Retrieves data from the queue and runs a waiting thread, if any.
221
+ #
222
+ def pop(*args)
223
+ retval = super
224
+ @mutex.synchronize {
225
+ if @que.length < @max
226
+ @cond_wait.signal
227
+ end
228
+ }
229
+ retval
230
+ end
231
+
232
+ #
233
+ # Alias of pop
234
+ #
235
+ alias shift pop
236
+
237
+ #
238
+ # Alias of pop
239
+ #
240
+ alias deq pop
241
+
242
+ #
243
+ # Returns the number of threads waiting on the queue.
244
+ #
245
+ # def num_waiting
246
+ # @waiting.size + @queue_wait.size
247
+ # end
248
+ end
249
+
250
+ # Documentation comments:
251
+ # - How do you make RDoc inherit documentation from superclass?
252
+ end
data/monitor.c ADDED
@@ -0,0 +1,404 @@
1
+ /**********************************************************************
2
+
3
+ monitor.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_cXThreadMonitor;
15
+ VALUE rb_cXThreadMonitorCond;
16
+
17
+ typedef struct rb_xthread_monitor_struct
18
+ {
19
+ VALUE owner;
20
+ long count;
21
+ VALUE mutex;
22
+ } xthread_monitor_t;
23
+
24
+ #define GetXThreadMonitorPtr(obj, tobj) \
25
+ TypedData_Get_Struct((obj), xthread_monitor_t, &xthread_monitor_data_type, (tobj))
26
+
27
+ #define XTHREAD_MONITOR_CHECK_OWNER(obj) \
28
+ { \
29
+ xthread_monitor_t *mon; \
30
+ VALUE th = rb_thread_current(); \
31
+ GetXThreadMonitorPtr(obj, mon); \
32
+ if (mon->owner != th) { \
33
+ rb_raise(rb_eThreadError, "current thread not owner"); \
34
+ } \
35
+ }
36
+
37
+
38
+ static void
39
+ xthread_monitor_mark(void *ptr)
40
+ {
41
+ xthread_monitor_t *mon = (xthread_monitor_t*)ptr;
42
+
43
+ rb_gc_mark(mon->owner);
44
+ rb_gc_mark(mon->mutex);
45
+ }
46
+
47
+ static void
48
+ xthread_monitor_free(void *ptr)
49
+ {
50
+ ruby_xfree(ptr);
51
+ }
52
+
53
+ static size_t
54
+ xthread_monitor_memsize(const void *ptr)
55
+ {
56
+ return ptr ? sizeof(xthread_monitor_t) : 0;
57
+ }
58
+
59
+ static const rb_data_type_t xthread_monitor_data_type = {
60
+ "xthread_monitor",
61
+ {xthread_monitor_mark, xthread_monitor_free, xthread_monitor_memsize,},
62
+ };
63
+
64
+ static VALUE
65
+ xthread_monitor_alloc(VALUE klass)
66
+ {
67
+ VALUE volatile obj;
68
+ xthread_monitor_t *mon;
69
+
70
+ obj = TypedData_Make_Struct(klass, xthread_monitor_t, &xthread_monitor_data_type, mon);
71
+ mon->owner = Qnil;
72
+ mon->count = 0;
73
+ mon->mutex = rb_mutex_new();
74
+
75
+ return obj;
76
+ }
77
+
78
+ static VALUE
79
+ xthread_monitor_initialize(VALUE self)
80
+ {
81
+ return self;
82
+ }
83
+
84
+ VALUE
85
+ rb_xthread_monitor_new(void)
86
+ {
87
+ return xthread_monitor_alloc(rb_cXThreadMonitor);
88
+ }
89
+
90
+
91
+ /*
92
+ static VALUE
93
+ rb_xthread_monitor_check_owner(VALUE self)
94
+ {
95
+ xthread_monitor_t *mon;
96
+ VALUE th = rb_thread_current();
97
+
98
+ GetXThreadMonitorPtr(self, mon);
99
+
100
+ if (mon->owner != th) {
101
+ rb_raise(rb_eThreadError, "current thread not owner");
102
+ }
103
+ }
104
+ */
105
+
106
+ VALUE
107
+ rb_xthread_monitor_valid_owner_p(VALUE self)
108
+ {
109
+ xthread_monitor_t *mon;
110
+ VALUE th = rb_thread_current();
111
+
112
+ GetXThreadMonitorPtr(self, mon);
113
+
114
+ if (mon->owner == th) {
115
+ return Qtrue;
116
+ }
117
+ else {
118
+ return Qfalse;
119
+ }
120
+ }
121
+
122
+ VALUE
123
+ rb_xthread_monitor_try_enter(VALUE self)
124
+ {
125
+ xthread_monitor_t *mon;
126
+ VALUE th = rb_thread_current();
127
+
128
+ GetXThreadMonitorPtr(self, mon);
129
+
130
+ if (mon->owner != th) {
131
+ if (rb_mutex_trylock(mon->mutex) == Qfalse) {
132
+ return Qfalse;
133
+ }
134
+ mon->owner = th;
135
+ }
136
+ mon->count++;
137
+ return Qtrue;
138
+ }
139
+
140
+ VALUE
141
+ rb_xthread_monitor_enter(VALUE self)
142
+ {
143
+ xthread_monitor_t *mon;
144
+ VALUE th = rb_thread_current();
145
+
146
+ GetXThreadMonitorPtr(self, mon);
147
+ if (mon->owner != th) {
148
+ rb_mutex_lock(mon->mutex);
149
+ mon->owner = th;
150
+ }
151
+ mon->count += 1;
152
+ }
153
+
154
+ VALUE
155
+ rb_xthread_monitor_exit(VALUE self)
156
+ {
157
+ xthread_monitor_t *mon;
158
+ VALUE th = rb_thread_current();
159
+
160
+ GetXThreadMonitorPtr(self, mon);
161
+
162
+ XTHREAD_MONITOR_CHECK_OWNER(self);
163
+ mon->count--;
164
+ if(mon->count == 0) {
165
+ mon->owner = Qnil;
166
+ rb_mutex_unlock(mon->mutex);
167
+ }
168
+ }
169
+
170
+ VALUE
171
+ rb_xthread_monitor_synchronize(VALUE self, VALUE (*func)(VALUE arg), VALUE arg)
172
+ {
173
+ rb_xthread_monitor_enter(self);
174
+ return rb_ensure(func, arg, rb_xthread_monitor_exit, self);
175
+ }
176
+
177
+ static VALUE
178
+ xthread_monitor_synchronize(VALUE self)
179
+ {
180
+ return rb_xthread_monitor_synchronize(self, rb_yield, self);
181
+ }
182
+
183
+ VALUE
184
+ rb_xthread_monitor_new_cond(VALUE self)
185
+ {
186
+ rb_xthread_monitor_cond_new(self);
187
+ }
188
+
189
+ VALUE
190
+ rb_xthread_monitor_enter_for_cond(VALUE self, long count)
191
+ {
192
+ xthread_monitor_t *mon;
193
+ VALUE th = rb_thread_current();
194
+
195
+ GetXThreadMonitorPtr(self, mon);
196
+
197
+ mon->owner = th;
198
+ mon->count = count;
199
+ }
200
+
201
+ long
202
+ rb_xthread_monitor_exit_for_cond(VALUE self)
203
+ {
204
+ xthread_monitor_t *mon;
205
+ long count;
206
+
207
+ GetXThreadMonitorPtr(self, mon);
208
+
209
+ count = mon->count;
210
+ mon->owner = Qnil;
211
+ mon->count = 0;
212
+ return count;
213
+ }
214
+
215
+ typedef struct rb_xthread_monitor_cond_struct
216
+ {
217
+ VALUE monitor;
218
+ VALUE cond;
219
+
220
+ } xthread_monitor_cond_t;
221
+
222
+ #define GetXThreadMonitorCondPtr(obj, tobj) \
223
+ TypedData_Get_Struct((obj), xthread_monitor_cond_t, &xthread_monitor_cond_data_type, (tobj))
224
+
225
+ static void
226
+ xthread_monitor_cond_mark(void *ptr)
227
+ {
228
+ xthread_monitor_cond_t *cv = (xthread_monitor_cond_t*)ptr;
229
+
230
+ rb_gc_mark(cv->monitor);
231
+ rb_gc_mark(cv->cond);
232
+ }
233
+
234
+ static void
235
+ xthread_monitor_cond_free(void *ptr)
236
+ {
237
+ ruby_xfree(ptr);
238
+ }
239
+
240
+ static size_t
241
+ xthread_monitor_cond_memsize(const void *ptr)
242
+ {
243
+ return ptr ? sizeof(xthread_monitor_cond_t) : 0;
244
+ }
245
+
246
+ static const rb_data_type_t xthread_monitor_cond_data_type = {
247
+ "xthread_monitor_cond",
248
+ {xthread_monitor_cond_mark, xthread_monitor_cond_free, xthread_monitor_cond_memsize,},
249
+ };
250
+
251
+ static VALUE
252
+ xthread_monitor_cond_alloc(VALUE klass)
253
+ {
254
+ VALUE volatile obj;
255
+ xthread_monitor_cond_t *cv;
256
+
257
+ obj = TypedData_Make_Struct(klass,
258
+ xthread_monitor_cond_t, &xthread_monitor_cond_data_type, cv);
259
+
260
+ cv->monitor = Qnil;
261
+ cv->cond = rb_xthread_cond_new();
262
+ return obj;
263
+ }
264
+
265
+ static VALUE
266
+ xthread_monitor_cond_initialize(VALUE self, VALUE mon)
267
+ {
268
+ xthread_monitor_cond_t *cv;
269
+ GetXThreadMonitorCondPtr(self, cv);
270
+
271
+ cv->monitor = mon;
272
+ return self;
273
+ }
274
+
275
+ VALUE
276
+ rb_xthread_monitor_cond_new(VALUE mon)
277
+ {
278
+ VALUE self;
279
+
280
+ self = xthread_monitor_cond_alloc(rb_cXThreadMonitorCond);
281
+ xthread_monitor_cond_initialize(self, mon);
282
+ return self;
283
+ }
284
+
285
+ struct xthread_monitor_cond_wait_arg {
286
+ VALUE cond;
287
+ VALUE monitor;
288
+ VALUE timeout;
289
+ long count;
290
+ };
291
+
292
+ static VALUE
293
+ rb_xthread_monitor_cond_wait_cond(struct xthread_monitor_cond_wait_arg *arg)
294
+ {
295
+ xthread_monitor_t *mon;
296
+ VALUE v_mon = arg->monitor;
297
+ GetXThreadMonitorPtr(v_mon, mon);
298
+
299
+ rb_xthread_cond_wait(arg->cond, mon->mutex, arg->timeout);
300
+ return Qtrue;
301
+ }
302
+
303
+ static VALUE
304
+ rb_xthread_monitor_cond_wait_enter(struct xthread_monitor_cond_wait_arg *arg)
305
+ {
306
+ rb_xthread_monitor_enter_for_cond(arg->monitor, arg->count);
307
+ }
308
+
309
+ VALUE
310
+ rb_xthread_monitor_cond_wait(VALUE self, VALUE timeout)
311
+ {
312
+ xthread_monitor_cond_t *cv;
313
+ struct xthread_monitor_cond_wait_arg arg;
314
+
315
+ GetXThreadMonitorCondPtr(self, cv);
316
+
317
+ XTHREAD_MONITOR_CHECK_OWNER(cv->monitor);
318
+ arg.cond = cv->cond;
319
+ arg.monitor = cv->monitor;
320
+ arg.timeout = timeout;
321
+ arg.count = rb_xthread_monitor_exit_for_cond(cv->monitor);
322
+
323
+ return rb_ensure(rb_xthread_monitor_cond_wait_cond, (VALUE)&arg,
324
+ rb_xthread_monitor_cond_wait_enter, (VALUE)&arg);
325
+ }
326
+
327
+ static VALUE
328
+ xthread_monitor_cond_wait(int argc, VALUE *argv, VALUE self)
329
+ {
330
+ VALUE timeout;
331
+
332
+ rb_scan_args(argc, argv, "01", &timeout);
333
+ return rb_xthread_monitor_cond_wait(self, timeout);
334
+ }
335
+
336
+ VALUE
337
+ rb_xthread_monitor_cond_wait_while(VALUE self)
338
+ {
339
+ while (RTEST(rb_yield)) {
340
+ rb_xthread_monitor_cond_wait(self, Qnil);
341
+ }
342
+ }
343
+
344
+ VALUE
345
+ rb_xthread_monitor_cond_wait_until(VALUE self)
346
+ {
347
+ while (!RTEST(rb_yield)) {
348
+ rb_xthread_monitor_cond_wait(self, Qnil);
349
+ }
350
+ }
351
+
352
+ VALUE
353
+ rb_xthread_monitor_cond_signal(VALUE self)
354
+ {
355
+ xthread_monitor_cond_t *cv;
356
+ GetXThreadMonitorCondPtr(self, cv);
357
+
358
+ XTHREAD_MONITOR_CHECK_OWNER(cv->monitor);
359
+ rb_xthread_cond_signal(cv->cond);
360
+ }
361
+
362
+ VALUE
363
+ rb_xthread_monitor_cond_broadcast(VALUE self)
364
+ {
365
+ xthread_monitor_cond_t *cv;
366
+ GetXThreadMonitorCondPtr(self, cv);
367
+
368
+ XTHREAD_MONITOR_CHECK_OWNER(cv->monitor);
369
+ rb_xthread_cond_broadcast(cv->cond);
370
+ }
371
+
372
+ void
373
+ Init_XThreadMonitor(void)
374
+ {
375
+ rb_cXThreadMonitor =
376
+ rb_define_class_under(rb_mXThread, "Monitor", rb_cObject);
377
+ rb_define_alloc_func(rb_cXThreadMonitor, xthread_monitor_alloc);
378
+ rb_define_method(rb_cXThreadMonitor, "initialize", xthread_monitor_initialize, 0);
379
+ rb_define_method(rb_cXThreadMonitor, "try_enter", rb_xthread_monitor_try_enter, 0);
380
+ rb_define_method(rb_cXThreadMonitor, "enter", rb_xthread_monitor_enter, 0);
381
+ rb_define_method(rb_cXThreadMonitor, "exit", rb_xthread_monitor_exit, 0);
382
+ rb_define_method(rb_cXThreadMonitor, "synchronize", xthread_monitor_synchronize, 0);
383
+ rb_define_method(rb_cXThreadMonitor, "new_cond", rb_xthread_monitor_new_cond, 0);
384
+ rb_define_method(rb_cXThreadMonitor, "synchronize", xthread_monitor_synchronize, 0);
385
+
386
+ rb_cXThreadMonitorCond =
387
+ rb_define_class_under(rb_cXThreadMonitor, "ConditionVariable", rb_cObject);
388
+ rb_define_alloc_func(rb_cXThreadMonitorCond, xthread_monitor_cond_alloc);
389
+ rb_define_method(rb_cXThreadMonitorCond,
390
+ "initialize", xthread_monitor_cond_initialize, 0);
391
+
392
+ rb_define_method(rb_cXThreadMonitorCond,
393
+ "wait", xthread_monitor_cond_wait, -1);
394
+ rb_define_method(rb_cXThreadMonitorCond,
395
+ "wait_while", rb_xthread_monitor_cond_wait_while, 0);
396
+ rb_define_method(rb_cXThreadMonitorCond,
397
+ "wait_until", rb_xthread_monitor_cond_wait_until, 0);
398
+
399
+ rb_define_method(rb_cXThreadMonitorCond,
400
+ "signal", rb_xthread_monitor_cond_signal, 0);
401
+ rb_define_method(rb_cXThreadMonitorCond,
402
+ "broadcast", rb_xthread_monitor_cond_broadcast, 0);
403
+
404
+ }