Ruby  2.4.2p198(2017-09-14revision59899)
thread_sync.c
Go to the documentation of this file.
1 /* included by thread.c */
2 
5 
6 /* Mutex */
7 
8 typedef struct rb_mutex_struct {
9  rb_nativethread_lock_t lock;
11  struct rb_thread_struct volatile *th;
15 } rb_mutex_t;
16 
17 #if defined(HAVE_WORKING_FORK)
18 static void rb_mutex_abandon_all(rb_mutex_t *mutexes);
19 static void rb_mutex_abandon_keeping_mutexes(rb_thread_t *th);
20 static void rb_mutex_abandon_locking_mutex(rb_thread_t *th);
21 #endif
22 static const char* rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t volatile *th);
23 
24 /*
25  * Document-class: Mutex
26  *
27  * Mutex implements a simple semaphore that can be used to coordinate access to
28  * shared data from multiple concurrent threads.
29  *
30  * Example:
31  *
32  * require 'thread'
33  * semaphore = Mutex.new
34  *
35  * a = Thread.new {
36  * semaphore.synchronize {
37  * # access shared resource
38  * }
39  * }
40  *
41  * b = Thread.new {
42  * semaphore.synchronize {
43  * # access shared resource
44  * }
45  * }
46  *
47  */
48 
49 #define GetMutexPtr(obj, tobj) \
50  TypedData_Get_Struct((obj), rb_mutex_t, &mutex_data_type, (tobj))
51 
52 #define mutex_mark NULL
53 
54 static void
55 mutex_free(void *ptr)
56 {
57  if (ptr) {
58  rb_mutex_t *mutex = ptr;
59  if (mutex->th) {
60  /* rb_warn("free locked mutex"); */
61  const char *err = rb_mutex_unlock_th(mutex, mutex->th);
62  if (err) rb_bug("%s", err);
63  }
64  native_mutex_destroy(&mutex->lock);
65  native_cond_destroy(&mutex->cond);
66  }
67  ruby_xfree(ptr);
68 }
69 
70 static size_t
71 mutex_memsize(const void *ptr)
72 {
73  return sizeof(rb_mutex_t);
74 }
75 
77  "mutex",
80 };
81 
82 VALUE
84 {
85  if (rb_typeddata_is_kind_of(obj, &mutex_data_type)) {
86  return Qtrue;
87  }
88  else {
89  return Qfalse;
90  }
91 }
92 
93 static VALUE
95 {
96  VALUE obj;
97  rb_mutex_t *mutex;
98 
99  obj = TypedData_Make_Struct(klass, rb_mutex_t, &mutex_data_type, mutex);
100  native_mutex_initialize(&mutex->lock);
101  native_cond_initialize(&mutex->cond, RB_CONDATTR_CLOCK_MONOTONIC);
102  return obj;
103 }
104 
105 /*
106  * call-seq:
107  * Mutex.new -> mutex
108  *
109  * Creates a new Mutex
110  */
111 static VALUE
113 {
114  return self;
115 }
116 
117 VALUE
119 {
120  return mutex_alloc(rb_cMutex);
121 }
122 
123 /*
124  * call-seq:
125  * mutex.locked? -> true or false
126  *
127  * Returns +true+ if this lock is currently held by some thread.
128  */
129 VALUE
131 {
132  rb_mutex_t *mutex;
133  GetMutexPtr(self, mutex);
134  return mutex->th ? Qtrue : Qfalse;
135 }
136 
137 static void
139 {
140  rb_mutex_t *mutex;
141  GetMutexPtr(self, mutex);
142 
143  if (th->keeping_mutexes) {
144  mutex->next_mutex = th->keeping_mutexes;
145  }
146  th->keeping_mutexes = mutex;
147 }
148 
149 /*
150  * call-seq:
151  * mutex.try_lock -> true or false
152  *
153  * Attempts to obtain the lock and returns immediately. Returns +true+ if the
154  * lock was granted.
155  */
156 VALUE
158 {
159  rb_mutex_t *mutex;
160  VALUE locked = Qfalse;
161  GetMutexPtr(self, mutex);
162 
163  native_mutex_lock(&mutex->lock);
164  if (mutex->th == 0) {
166  mutex->th = th;
167  locked = Qtrue;
168 
169  mutex_locked(th, self);
170  }
171  native_mutex_unlock(&mutex->lock);
172 
173  return locked;
174 }
175 
176 static int
177 lock_func(rb_thread_t *th, rb_mutex_t *mutex, int timeout_ms)
178 {
179  int interrupted = 0;
180  int err = 0;
181 
182  mutex->cond_waiting++;
183  for (;;) {
184  if (!mutex->th) {
185  mutex->th = th;
186  break;
187  }
188  if (RUBY_VM_INTERRUPTED(th)) {
189  interrupted = 1;
190  break;
191  }
192  if (err == ETIMEDOUT) {
193  interrupted = 2;
194  break;
195  }
196 
197  if (timeout_ms) {
198  struct timespec timeout_rel;
199  struct timespec timeout;
200 
201  timeout_rel.tv_sec = 0;
202  timeout_rel.tv_nsec = timeout_ms * 1000 * 1000;
203  timeout = native_cond_timeout(&mutex->cond, timeout_rel);
204  err = native_cond_timedwait(&mutex->cond, &mutex->lock, &timeout);
205  }
206  else {
207  native_cond_wait(&mutex->cond, &mutex->lock);
208  err = 0;
209  }
210  }
211  mutex->cond_waiting--;
212 
213  return interrupted;
214 }
215 
216 static void
217 lock_interrupt(void *ptr)
218 {
219  rb_mutex_t *mutex = (rb_mutex_t *)ptr;
220  native_mutex_lock(&mutex->lock);
221  if (mutex->cond_waiting > 0)
222  native_cond_broadcast(&mutex->cond);
223  native_mutex_unlock(&mutex->lock);
224 }
225 
226 /*
227  * At maximum, only one thread can use cond_timedwait and watch deadlock
228  * periodically. Multiple polling thread (i.e. concurrent deadlock check)
229  * introduces new race conditions. [Bug #6278] [ruby-core:44275]
230  */
232 
233 /*
234  * call-seq:
235  * mutex.lock -> self
236  *
237  * Attempts to grab the lock and waits if it isn't available.
238  * Raises +ThreadError+ if +mutex+ was locked by the current thread.
239  */
240 VALUE
242 {
244  rb_mutex_t *mutex;
245  GetMutexPtr(self, mutex);
246 
247  /* When running trap handler */
248  if (!mutex->allow_trap && th->interrupt_mask & TRAP_INTERRUPT_MASK) {
249  rb_raise(rb_eThreadError, "can't be called from trap context");
250  }
251 
252  if (rb_mutex_trylock(self) == Qfalse) {
253  if (mutex->th == th) {
254  rb_raise(rb_eThreadError, "deadlock; recursive locking");
255  }
256 
257  while (mutex->th != th) {
258  int interrupted;
259  enum rb_thread_status prev_status = th->status;
260  volatile int timeout_ms = 0;
261  struct rb_unblock_callback oldubf;
262 
263  set_unblock_function(th, lock_interrupt, mutex, &oldubf, FALSE);
265  th->locking_mutex = self;
266 
267  native_mutex_lock(&mutex->lock);
268  th->vm->sleeper++;
269  /*
270  * Carefully! while some contended threads are in lock_func(),
271  * vm->sleepr is unstable value. we have to avoid both deadlock
272  * and busy loop.
273  */
274  if ((vm_living_thread_num(th->vm) == th->vm->sleeper) &&
275  !patrol_thread) {
276  timeout_ms = 100;
277  patrol_thread = th;
278  }
279 
281  interrupted = lock_func(th, mutex, (int)timeout_ms);
282  native_mutex_unlock(&mutex->lock);
283  GVL_UNLOCK_END();
284 
285  if (patrol_thread == th)
286  patrol_thread = NULL;
287 
288  reset_unblock_function(th, &oldubf);
289 
290  th->locking_mutex = Qfalse;
291  if (mutex->th && interrupted == 2) {
292  rb_check_deadlock(th->vm);
293  }
294  if (th->status == THREAD_STOPPED_FOREVER) {
295  th->status = prev_status;
296  }
297  th->vm->sleeper--;
298 
299  if (mutex->th == th) mutex_locked(th, self);
300 
301  if (interrupted) {
303  }
304  }
305  }
306  return self;
307 }
308 
309 /*
310  * call-seq:
311  * mutex.owned? -> true or false
312  *
313  * Returns +true+ if this lock is currently held by current thread.
314  */
315 VALUE
317 {
318  VALUE owned = Qfalse;
320  rb_mutex_t *mutex;
321 
322  GetMutexPtr(self, mutex);
323 
324  if (mutex->th == th)
325  owned = Qtrue;
326 
327  return owned;
328 }
329 
330 static const char *
332 {
333  const char *err = NULL;
334 
335  native_mutex_lock(&mutex->lock);
336 
337  if (mutex->th == 0) {
338  err = "Attempt to unlock a mutex which is not locked";
339  }
340  else if (mutex->th != th) {
341  err = "Attempt to unlock a mutex which is locked by another thread";
342  }
343  else {
344  mutex->th = 0;
345  if (mutex->cond_waiting > 0)
346  native_cond_signal(&mutex->cond);
347  }
348 
349  native_mutex_unlock(&mutex->lock);
350 
351  if (!err) {
352  rb_mutex_t *volatile *th_mutex = &th->keeping_mutexes;
353  while (*th_mutex != mutex) {
354  th_mutex = &(*th_mutex)->next_mutex;
355  }
356  *th_mutex = mutex->next_mutex;
357  mutex->next_mutex = NULL;
358  }
359 
360  return err;
361 }
362 
363 /*
364  * call-seq:
365  * mutex.unlock -> self
366  *
367  * Releases the lock.
368  * Raises +ThreadError+ if +mutex+ wasn't locked by the current thread.
369  */
370 VALUE
372 {
373  const char *err;
374  rb_mutex_t *mutex;
375  GetMutexPtr(self, mutex);
376 
377  err = rb_mutex_unlock_th(mutex, GET_THREAD());
378  if (err) rb_raise(rb_eThreadError, "%s", err);
379 
380  return self;
381 }
382 
383 #if defined(HAVE_WORKING_FORK)
384 static void
385 rb_mutex_abandon_keeping_mutexes(rb_thread_t *th)
386 {
387  if (th->keeping_mutexes) {
388  rb_mutex_abandon_all(th->keeping_mutexes);
389  }
390  th->keeping_mutexes = NULL;
391 }
392 
393 static void
394 rb_mutex_abandon_locking_mutex(rb_thread_t *th)
395 {
396  rb_mutex_t *mutex;
397 
398  if (!th->locking_mutex) return;
399 
400  GetMutexPtr(th->locking_mutex, mutex);
401  if (mutex->th == th)
402  rb_mutex_abandon_all(mutex);
403  th->locking_mutex = Qfalse;
404 }
405 
406 static void
407 rb_mutex_abandon_all(rb_mutex_t *mutexes)
408 {
409  rb_mutex_t *mutex;
410 
411  while (mutexes) {
412  mutex = mutexes;
413  mutexes = mutex->next_mutex;
414  mutex->th = 0;
415  mutex->next_mutex = 0;
416  }
417 }
418 #endif
419 
420 static VALUE
422 {
424  return Qnil;
425 }
426 
427 static VALUE
429 {
430  struct timeval *t = (struct timeval *)time;
431  sleep_timeval(GET_THREAD(), *t, 0); /* permit spurious check */
432  return Qnil;
433 }
434 
435 VALUE
436 rb_mutex_sleep(VALUE self, VALUE timeout)
437 {
438  time_t beg, end;
439  struct timeval t;
440 
441  if (!NIL_P(timeout)) {
442  t = rb_time_interval(timeout);
443  }
444  rb_mutex_unlock(self);
445  beg = time(0);
446  if (NIL_P(timeout)) {
448  }
449  else {
451  }
452  end = time(0) - beg;
453  return INT2FIX(end);
454 }
455 
456 /*
457  * call-seq:
458  * mutex.sleep(timeout = nil) -> number
459  *
460  * Releases the lock and sleeps +timeout+ seconds if it is given and
461  * non-nil or forever. Raises +ThreadError+ if +mutex+ wasn't locked by
462  * the current thread.
463  *
464  * When the thread is next woken up, it will attempt to reacquire
465  * the lock.
466  *
467  * Note that this method can wakeup without explicit Thread#wakeup call.
468  * For example, receiving signal and so on.
469  */
470 static VALUE
472 {
473  VALUE timeout;
474 
475  rb_scan_args(argc, argv, "01", &timeout);
476  return rb_mutex_sleep(self, timeout);
477 }
478 
479 /*
480  * call-seq:
481  * mutex.synchronize { ... } -> result of the block
482  *
483  * Obtains a lock, runs the block, and releases the lock when the block
484  * completes. See the example under +Mutex+.
485  */
486 
487 VALUE
489 {
490  rb_mutex_lock(mutex);
491  return rb_ensure(func, arg, rb_mutex_unlock, mutex);
492 }
493 
494 /*
495  * call-seq:
496  * mutex.synchronize { ... } -> result of the block
497  *
498  * Obtains a lock, runs the block, and releases the lock when the block
499  * completes. See the example under +Mutex+.
500  */
501 static VALUE
503 {
504  if (!rb_block_given_p()) {
505  rb_raise(rb_eThreadError, "must be called with a block");
506  }
507 
508  return rb_mutex_synchronize(self, rb_yield, Qundef);
509 }
510 
512 {
513  rb_mutex_t *m;
514  GetMutexPtr(self, m);
515 
516  m->allow_trap = val;
517 }
518 
519 /* Queue */
520 
521 enum {
527 };
528 
529 #define QUEUE_CLOSED FL_USER5
530 
531 #define GET_QUEUE_QUE(q) get_array((q), QUEUE_QUE)
532 #define GET_QUEUE_WAITERS(q) get_array((q), QUEUE_WAITERS)
533 #define GET_SZQUEUE_WAITERS(q) get_array((q), SZQUEUE_WAITERS)
534 #define GET_SZQUEUE_MAX(q) RSTRUCT_GET((q), SZQUEUE_MAX)
535 #define GET_SZQUEUE_ULONGMAX(q) NUM2ULONG(GET_SZQUEUE_MAX(q))
536 
537 static VALUE
539 {
540  return rb_ary_tmp_new(1);
541 }
542 
543 static VALUE
544 get_array(VALUE obj, int idx)
545 {
546  VALUE ary = RSTRUCT_GET(obj, idx);
547  if (!RB_TYPE_P(ary, T_ARRAY)) {
548  rb_raise(rb_eTypeError, "%+"PRIsVALUE" not initialized", obj);
549  }
550  return ary;
551 }
552 
553 static void
555 {
556  VALUE thread;
557 
558  while (!NIL_P(thread = rb_ary_shift(list))) {
559  if (RTEST(rb_thread_wakeup_alive(thread))) break;
560  }
561 }
562 
563 static void
565 {
566  VALUE thread;
567  long i;
568 
569  for (i=0; i<RARRAY_LEN(list); i++) {
570  thread = RARRAY_AREF(list, i);
571  rb_thread_wakeup_alive(thread);
572  }
573  rb_ary_clear(list);
574 }
575 
576 static unsigned long
578 {
579  VALUE que = GET_QUEUE_QUE(self);
580  return RARRAY_LEN(que);
581 }
582 
583 static unsigned long
585 {
586  VALUE waiters = GET_QUEUE_WAITERS(self);
587  return RARRAY_LEN(waiters);
588 }
589 
590 static unsigned long
592 {
593  VALUE waiters = GET_SZQUEUE_WAITERS(self);
594  return RARRAY_LEN(waiters);
595 }
596 
597 static int
599 {
600  return FL_TEST_RAW(self, QUEUE_CLOSED) != 0;
601 }
602 
603 static void
605 {
606  rb_raise(rb_eClosedQueueError, "queue closed");
607 }
608 
609 static VALUE
611 {
612  assert(queue_length(self) == 0);
613  return Qnil;
614 }
615 
616 static VALUE
617 queue_do_close(VALUE self, int is_szq)
618 {
619  if (!queue_closed_p(self)) {
620  FL_SET(self, QUEUE_CLOSED);
621 
622  if (queue_num_waiting(self) > 0) {
623  VALUE waiters = GET_QUEUE_WAITERS(self);
624  wakeup_all_threads(waiters);
625  }
626 
627  if (is_szq && szqueue_num_waiting_producer(self) > 0) {
628  VALUE waiters = GET_SZQUEUE_WAITERS(self);
629  wakeup_all_threads(waiters);
630  }
631  }
632 
633  return self;
634 }
635 
636 /*
637  * Document-class: Queue
638  *
639  * The Queue class implements multi-producer, multi-consumer queues.
640  * It is especially useful in threaded programming when information
641  * must be exchanged safely between multiple threads. The Queue class
642  * implements all the required locking semantics.
643  *
644  * The class implements FIFO type of queue. In a FIFO queue, the first
645  * tasks added are the first retrieved.
646  *
647  * Example:
648  *
649  * require 'thread'
650  * queue = Queue.new
651  *
652  * producer = Thread.new do
653  * 5.times do |i|
654  * sleep rand(i) # simulate expense
655  * queue << i
656  * puts "#{i} produced"
657  * end
658  * end
659  *
660  * consumer = Thread.new do
661  * 5.times do |i|
662  * value = queue.pop
663  * sleep rand(i/2) # simulate expense
664  * puts "consumed #{value}"
665  * end
666  * end
667  *
668  */
669 
670 /*
671  * Document-method: Queue::new
672  *
673  * Creates a new queue instance.
674  */
675 
676 static VALUE
678 {
681  return self;
682 }
683 
684 static VALUE
686 {
687  if (queue_closed_p(self)) {
689  }
690  rb_ary_push(GET_QUEUE_QUE(self), obj);
692  return self;
693 }
694 
695 /*
696  * Document-method: Queue#close
697  * call-seq:
698  * close
699  *
700  * Closes the queue. A closed queue cannot be re-opened.
701  *
702  * After the call to close completes, the following are true:
703  *
704  * - +closed?+ will return true
705  *
706  * - +close+ will be ignored.
707  *
708  * - calling enq/push/<< will return nil.
709  *
710  * - when +empty?+ is false, calling deq/pop/shift will return an object
711  * from the queue as usual.
712  *
713  * ClosedQueueError is inherited from StopIteration, so that you can break loop block.
714  *
715  * Example:
716  *
717  * q = Queue.new
718  * Thread.new{
719  * while e = q.deq # wait for nil to break loop
720  * # ...
721  * end
722  * }
723  * q.close
724  */
725 
726 static VALUE
728 {
729  return queue_do_close(self, FALSE);
730 }
731 
732 /*
733  * Document-method: Queue#closed?
734  * call-seq: closed?
735  *
736  * Returns +true+ if the queue is closed.
737  */
738 
739 static VALUE
741 {
742  return queue_closed_p(self) ? Qtrue : Qfalse;
743 }
744 
745 /*
746  * Document-method: Queue#push
747  * call-seq:
748  * push(object)
749  * enq(object)
750  * <<(object)
751  *
752  * Pushes the given +object+ to the queue.
753  */
754 
755 static VALUE
757 {
758  return queue_do_push(self, obj);
759 }
760 
764 };
765 
766 static VALUE
768 {
769  rb_ary_delete(p->waiting, p->th);
770  return Qnil;
771 }
772 
773 static VALUE
775 {
777  return Qnil;
778 }
779 
780 static VALUE
781 queue_do_pop(VALUE self, int should_block)
782 {
783  struct waiting_delete args;
784  args.waiting = GET_QUEUE_WAITERS(self);
785  args.th = rb_thread_current();
786 
787  while (queue_length(self) == 0) {
788  if (!should_block) {
789  rb_raise(rb_eThreadError, "queue empty");
790  }
791  else if (queue_closed_p(self)) {
792  return queue_closed_result(self);
793  }
794  else {
795  assert(queue_length(self) == 0);
796  assert(queue_closed_p(self) == 0);
797 
798  rb_ary_push(args.waiting, args.th);
800  }
801  }
802 
803  return rb_ary_shift(GET_QUEUE_QUE(self));
804 }
805 
806 static int
808 {
809  int should_block = 1;
810  rb_check_arity(argc, 0, 1);
811  if (argc > 0) {
812  should_block = !RTEST(argv[0]);
813  }
814  return should_block;
815 }
816 
817 /*
818  * Document-method: Queue#pop
819  * call-seq:
820  * pop(non_block=false)
821  * deq(non_block=false)
822  * shift(non_block=false)
823  *
824  * Retrieves data from the queue.
825  *
826  * If the queue is empty, the calling thread is suspended until data is pushed
827  * onto the queue. If +non_block+ is true, the thread isn't suspended, and
828  * +ThreadError+ is raised.
829  */
830 
831 static VALUE
833 {
834  int should_block = queue_pop_should_block(argc, argv);
835  return queue_do_pop(self, should_block);
836 }
837 
838 /*
839  * Document-method: Queue#empty?
840  * call-seq: empty?
841  *
842  * Returns +true+ if the queue is empty.
843  */
844 
845 static VALUE
847 {
848  return queue_length(self) == 0 ? Qtrue : Qfalse;
849 }
850 
851 /*
852  * Document-method: Queue#clear
853  *
854  * Removes all objects from the queue.
855  */
856 
857 static VALUE
859 {
861  return self;
862 }
863 
864 /*
865  * Document-method: Queue#length
866  * call-seq:
867  * length
868  * size
869  *
870  * Returns the length of the queue.
871  */
872 
873 static VALUE
875 {
876  unsigned long len = queue_length(self);
877  return ULONG2NUM(len);
878 }
879 
880 /*
881  * Document-method: Queue#num_waiting
882  *
883  * Returns the number of threads waiting on the queue.
884  */
885 
886 static VALUE
888 {
889  unsigned long len = queue_num_waiting(self);
890  return ULONG2NUM(len);
891 }
892 
893 /*
894  * Document-class: SizedQueue
895  *
896  * This class represents queues of specified size capacity. The push operation
897  * may be blocked if the capacity is full.
898  *
899  * See Queue for an example of how a SizedQueue works.
900  */
901 
902 /*
903  * Document-method: SizedQueue::new
904  * call-seq: new(max)
905  *
906  * Creates a fixed-length queue with a maximum size of +max+.
907  */
908 
909 static VALUE
911 {
912  long max;
913 
914  max = NUM2LONG(vmax);
915  if (max <= 0) {
916  rb_raise(rb_eArgError, "queue size must be positive");
917  }
918 
922  RSTRUCT_SET(self, SZQUEUE_MAX, vmax);
923 
924  return self;
925 }
926 
927 /*
928  * Document-method: SizedQueue#close
929  * call-seq:
930  * close
931  *
932  * Similar to Queue#close.
933  *
934  * The difference is behavior with waiting enqueuing threads.
935  *
936  * If there are waiting enqueuing threads, they are interrupted by
937  * raising ClosedQueueError('queue closed').
938  */
939 static VALUE
941 {
942  return queue_do_close(self, TRUE);
943 }
944 
945 /*
946  * Document-method: SizedQueue#max
947  *
948  * Returns the maximum size of the queue.
949  */
950 
951 static VALUE
953 {
954  return GET_SZQUEUE_MAX(self);
955 }
956 
957 /*
958  * Document-method: SizedQueue#max=
959  * call-seq: max=(number)
960  *
961  * Sets the maximum size of the queue to the given +number+.
962  */
963 
964 static VALUE
966 {
967  long max = NUM2LONG(vmax), diff = 0;
968  VALUE t;
969 
970  if (max <= 0) {
971  rb_raise(rb_eArgError, "queue size must be positive");
972  }
973  if ((unsigned long)max > GET_SZQUEUE_ULONGMAX(self)) {
974  diff = max - GET_SZQUEUE_ULONGMAX(self);
975  }
976  RSTRUCT_SET(self, SZQUEUE_MAX, vmax);
977  while (diff-- > 0 && !NIL_P(t = rb_ary_shift(GET_SZQUEUE_WAITERS(self)))) {
979  }
980  return vmax;
981 }
982 
983 static int
985 {
986  int should_block = 1;
987  rb_check_arity(argc, 1, 2);
988  if (argc > 1) {
989  should_block = !RTEST(argv[1]);
990  }
991  return should_block;
992 }
993 
994 /*
995  * Document-method: SizedQueue#push
996  * call-seq:
997  * push(object, non_block=false)
998  * enq(object, non_block=false)
999  * <<(object)
1000  *
1001  * Pushes +object+ to the queue.
1002  *
1003  * If there is no space left in the queue, waits until space becomes
1004  * available, unless +non_block+ is true. If +non_block+ is true, the
1005  * thread isn't suspended, and +ThreadError+ is raised.
1006  */
1007 
1008 static VALUE
1010 {
1011  struct waiting_delete args;
1012  int should_block = szqueue_push_should_block(argc, argv);
1013  args.waiting = GET_SZQUEUE_WAITERS(self);
1014  args.th = rb_thread_current();
1015 
1016  while (queue_length(self) >= GET_SZQUEUE_ULONGMAX(self)) {
1017  if (!should_block) {
1018  rb_raise(rb_eThreadError, "queue full");
1019  }
1020  else if (queue_closed_p(self)) {
1021  goto closed;
1022  }
1023  else {
1024  rb_ary_push(args.waiting, args.th);
1026  }
1027  }
1028 
1029  if (queue_closed_p(self)) {
1030  closed:
1032  }
1033 
1034  return queue_do_push(self, argv[0]);
1035 }
1036 
1037 static VALUE
1038 szqueue_do_pop(VALUE self, int should_block)
1039 {
1040  VALUE retval = queue_do_pop(self, should_block);
1041 
1042  if (queue_length(self) < GET_SZQUEUE_ULONGMAX(self)) {
1044  }
1045 
1046  return retval;
1047 }
1048 
1049 /*
1050  * Document-method: SizedQueue#pop
1051  * call-seq:
1052  * pop(non_block=false)
1053  * deq(non_block=false)
1054  * shift(non_block=false)
1055  *
1056  * Retrieves data from the queue.
1057  *
1058  * If the queue is empty, the calling thread is suspended until data is pushed
1059  * onto the queue. If +non_block+ is true, the thread isn't suspended, and
1060  * +ThreadError+ is raised.
1061  */
1062 
1063 static VALUE
1065 {
1066  int should_block = queue_pop_should_block(argc, argv);
1067  return szqueue_do_pop(self, should_block);
1068 }
1069 
1070 /*
1071  * Document-method: Queue#clear
1072  *
1073  * Removes all objects from the queue.
1074  */
1075 
1076 static VALUE
1078 {
1079  rb_ary_clear(GET_QUEUE_QUE(self));
1081  return self;
1082 }
1083 
1084 /*
1085  * Document-method: SizedQueue#num_waiting
1086  *
1087  * Returns the number of threads waiting on the queue.
1088  */
1089 
1090 static VALUE
1092 {
1093  long len = queue_num_waiting(self) + szqueue_num_waiting_producer(self);
1094  return ULONG2NUM(len);
1095 }
1096 
1097 /* ConditionalVariable */
1098 
1099 enum {
1102 };
1103 
1104 #define GET_CONDVAR_WAITERS(cv) get_array((cv), CONDVAR_WAITERS)
1105 
1106 /*
1107  * Document-class: ConditionVariable
1108  *
1109  * ConditionVariable objects augment class Mutex. Using condition variables,
1110  * it is possible to suspend while in the middle of a critical section until a
1111  * resource becomes available.
1112  *
1113  * Example:
1114  *
1115  * require 'thread'
1116  *
1117  * mutex = Mutex.new
1118  * resource = ConditionVariable.new
1119  *
1120  * a = Thread.new {
1121  * mutex.synchronize {
1122  * # Thread 'a' now needs the resource
1123  * resource.wait(mutex)
1124  * # 'a' can now have the resource
1125  * }
1126  * }
1127  *
1128  * b = Thread.new {
1129  * mutex.synchronize {
1130  * # Thread 'b' has finished using the resource
1131  * resource.signal
1132  * }
1133  * }
1134  */
1135 
1136 /*
1137  * Document-method: ConditionVariable::new
1138  *
1139  * Creates a new condition variable instance.
1140  */
1141 
1142 static VALUE
1144 {
1146  return self;
1147 }
1148 
1149 struct sleep_call {
1152 };
1153 
1154 static ID id_sleep;
1155 
1156 static VALUE
1158 {
1159  struct sleep_call *p = (struct sleep_call *)args;
1160  return rb_funcallv(p->mutex, id_sleep, 1, &p->timeout);
1161 }
1162 
1163 static VALUE
1165 {
1166  return rb_ary_delete(ary, rb_thread_current());
1167 }
1168 
1169 /*
1170  * Document-method: ConditionVariable#wait
1171  * call-seq: wait(mutex, timeout=nil)
1172  *
1173  * Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
1174  *
1175  * If +timeout+ is given, this method returns after +timeout+ seconds passed,
1176  * even if no other thread doesn't signal.
1177  */
1178 
1179 static VALUE
1181 {
1182  VALUE waiters = GET_CONDVAR_WAITERS(self);
1183  VALUE mutex, timeout;
1184  struct sleep_call args;
1185 
1186  rb_scan_args(argc, argv, "11", &mutex, &timeout);
1187 
1188  args.mutex = mutex;
1189  args.timeout = timeout;
1190  rb_ary_push(waiters, rb_thread_current());
1191  rb_ensure(do_sleep, (VALUE)&args, delete_current_thread, waiters);
1192 
1193  return self;
1194 }
1195 
1196 /*
1197  * Document-method: ConditionVariable#signal
1198  *
1199  * Wakes up the first thread in line waiting for this lock.
1200  */
1201 
1202 static VALUE
1204 {
1206  return self;
1207 }
1208 
1209 /*
1210  * Document-method: ConditionVariable#broadcast
1211  *
1212  * Wakes up all threads waiting for this lock.
1213  */
1214 
1215 static VALUE
1217 {
1219  return self;
1220 }
1221 
1222 /* :nodoc: */
1223 static VALUE
1225 {
1226  rb_raise(rb_eTypeError, "can't dump %"PRIsVALUE, rb_obj_class(obj));
1227  UNREACHABLE;
1228 }
1229 
1230 static void
1231 alias_global_const(const char *name, VALUE klass)
1232 {
1233  rb_define_const(rb_cObject, name, klass);
1234 }
1235 
1236 static void
1238 {
1239 #if 0
1240  rb_cConditionVariable = rb_define_class("ConditionVariable", rb_cObject); /* teach rdoc ConditionVariable */
1241  rb_cQueue = rb_define_class("Queue", rb_cObject); /* teach rdoc Queue */
1242  rb_cSizedQueue = rb_define_class("SizedQueue", rb_cObject); /* teach rdoc SizedQueue */
1243 #endif
1244 
1245  /* Mutex */
1248  rb_define_method(rb_cMutex, "initialize", mutex_initialize, 0);
1250  rb_define_method(rb_cMutex, "try_lock", rb_mutex_trylock, 0);
1253  rb_define_method(rb_cMutex, "sleep", mutex_sleep, -1);
1256 
1257  /* Queue */
1259  rb_cThread,
1261  "que", "waiters", NULL);
1262 
1264 
1265  rb_define_method(rb_cQueue, "initialize", rb_queue_initialize, 0);
1266  rb_undef_method(rb_cQueue, "initialize_copy");
1267  rb_define_method(rb_cQueue, "marshal_dump", undumpable, 0);
1275  rb_define_method(rb_cQueue, "num_waiting", rb_queue_num_waiting, 0);
1276 
1277  rb_define_alias(rb_cQueue, "enq", "push");
1278  rb_define_alias(rb_cQueue, "<<", "push");
1279  rb_define_alias(rb_cQueue, "deq", "pop");
1280  rb_define_alias(rb_cQueue, "shift", "pop");
1281  rb_define_alias(rb_cQueue, "size", "length");
1282 
1284  rb_cThread,
1285  "SizedQueue", rb_cQueue, rb_struct_alloc_noinit,
1286  "que", "waiters", "queue_waiters", "size", NULL);
1287 
1296 
1297  rb_define_alias(rb_cSizedQueue, "enq", "push");
1298  rb_define_alias(rb_cSizedQueue, "<<", "push");
1299  rb_define_alias(rb_cSizedQueue, "deq", "pop");
1300  rb_define_alias(rb_cSizedQueue, "shift", "pop");
1301 
1302  /* CVar */
1304  rb_cThread,
1305  "ConditionVariable", rb_cObject, rb_struct_alloc_noinit,
1306  "waiters", NULL);
1307 
1308  id_sleep = rb_intern("sleep");
1309 
1311  rb_undef_method(rb_cConditionVariable, "initialize_copy");
1312  rb_define_method(rb_cConditionVariable, "marshal_dump", undumpable, 0);
1316 
1317 #define ALIAS_GLOBAL_CONST(name) \
1318  alias_global_const(#name, rb_c##name)
1319 
1320  ALIAS_GLOBAL_CONST(Mutex);
1321  ALIAS_GLOBAL_CONST(Queue);
1322  ALIAS_GLOBAL_CONST(SizedQueue);
1323  ALIAS_GLOBAL_CONST(ConditionVariable);
1324  rb_provide("thread.rb");
1325 }
#define GetMutexPtr(obj, tobj)
Definition: thread_sync.c:49
static int vm_living_thread_num(rb_vm_t *vm)
Definition: thread.c:187
VALUE rb_mutex_lock(VALUE self)
Definition: thread_sync.c:241
struct timeval rb_time_interval(VALUE num)
Definition: time.c:2286
static size_t mutex_memsize(const void *ptr)
Definition: thread_sync.c:71
VALUE rb_mutex_sleep(VALUE self, VALUE timeout)
Definition: thread_sync.c:436
rb_nativethread_cond_t cond
Definition: thread_sync.c:10
rb_vm_t * vm
Definition: vm_core.h:703
static VALUE rb_condvar_signal(VALUE self)
Definition: thread_sync.c:1203
static VALUE mutex_alloc(VALUE klass)
Definition: thread_sync.c:94
#define GET_SZQUEUE_ULONGMAX(q)
Definition: thread_sync.c:535
struct rb_mutex_struct * next_mutex
Definition: thread_sync.c:12
#define RARRAY_LEN(a)
Definition: ruby.h:1026
void rb_bug(const char *fmt,...)
Definition: error.c:482
#define FALSE
Definition: nkf.h:174
#define RUBY_TYPED_FREE_IMMEDIATELY
Definition: ruby.h:1145
static VALUE undumpable(VALUE obj)
Definition: thread_sync.c:1224
VALUE rb_mutex_synchronize(VALUE mutex, VALUE(*func)(VALUE arg), VALUE arg)
Definition: thread_sync.c:488
static VALUE queue_delete_from_waiting(struct waiting_delete *p)
Definition: thread_sync.c:767
#define GET_SZQUEUE_MAX(q)
Definition: thread_sync.c:534
static int max(int a, int b)
Definition: strftime.c:142
static VALUE rb_queue_closed_p(VALUE self)
Definition: thread_sync.c:740
VALUE rb_mutex_trylock(VALUE self)
Definition: thread_sync.c:157
static VALUE queue_do_push(VALUE self, VALUE obj)
Definition: thread_sync.c:685
static VALUE rb_cMutex
Definition: thread_sync.c:3
static void alias_global_const(const char *name, VALUE klass)
Definition: thread_sync.c:1231
static VALUE szqueue_do_pop(VALUE self, int should_block)
Definition: thread_sync.c:1038
static int lock_func(rb_thread_t *th, rb_mutex_t *mutex, int timeout_ms)
Definition: thread_sync.c:177
#define Qtrue
Definition: ruby.h:437
static VALUE rb_queue_num_waiting(VALUE self)
Definition: thread_sync.c:887
VALUE rb_ary_shift(VALUE ary)
Definition: array.c:1000
VALUE rb_mutex_unlock(VALUE self)
Definition: thread_sync.c:371
static VALUE rb_queue_length(VALUE self)
Definition: thread_sync.c:874
struct rb_thread_struct volatile * th
Definition: thread_sync.c:11
static void wakeup_first_thread(VALUE list)
Definition: thread_sync.c:554
VALUE rb_eTypeError
Definition: error.c:762
#define rb_check_arity
Definition: intern.h:303
VALUE rb_struct_alloc_noinit(VALUE)
Definition: struct.c:328
#define UNREACHABLE
Definition: ruby.h:46
#define ULONG2NUM(x)
Definition: ruby.h:1574
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:905
SSL_METHOD *(* func)(void)
Definition: ossl_ssl.c:54
static VALUE rb_queue_initialize(VALUE self)
Definition: thread_sync.c:677
static VALUE rb_cQueue
Definition: thread_sync.c:3
VALUE rb_ary_tmp_new(long capa)
Definition: array.c:532
#define QUEUE_CLOSED
Definition: thread_sync.c:529
static unsigned long queue_num_waiting(VALUE self)
Definition: thread_sync.c:584
static VALUE rb_mutex_synchronize_m(VALUE self, VALUE args)
Definition: thread_sync.c:502
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:693
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2207
VALUE rb_ary_clear(VALUE ary)
Definition: array.c:3487
static void wakeup_all_threads(VALUE list)
Definition: thread_sync.c:564
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
static VALUE rb_szqueue_clear(VALUE self)
Definition: thread_sync.c:1077
rb_nativethread_lock_t lock
Definition: thread_sync.c:9
void rb_mutex_allow_trap(VALUE self, int val)
Definition: thread_sync.c:511
#define T_ARRAY
Definition: ruby.h:498
#define assert(x)
Definition: dlmalloc.c:1176
static const rb_thread_t * patrol_thread
Definition: thread_sync.c:231
void rb_undef_method(VALUE klass, const char *name)
Definition: class.c:1533
static VALUE queue_do_pop(VALUE self, int should_block)
Definition: thread_sync.c:781
#define GET_THREAD()
Definition: vm_core.h:1513
time_t tv_sec
Definition: missing.h:61
static void rb_thread_sleep_deadly_allow_spurious_wakeup(void)
Definition: thread.c:1170
#define RB_TYPE_P(obj, type)
Definition: ruby.h:527
#define RUBY_VM_CHECK_INTS_BLOCKING(th)
Definition: thread.c:171
static VALUE rb_cSizedQueue
Definition: thread_sync.c:3
static VALUE rb_queue_pop(int argc, VALUE *argv, VALUE self)
Definition: thread_sync.c:832
int rb_block_given_p(void)
Definition: eval.c:797
VALUE locking_mutex
Definition: vm_core.h:766
#define val
RUBY_EXTERN VALUE rb_cObject
Definition: ruby.h:1872
static VALUE rb_szqueue_max_get(VALUE self)
Definition: thread_sync.c:952
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Definition: error.c:720
static VALUE rb_szqueue_initialize(VALUE self, VALUE vmax)
Definition: thread_sync.c:910
static VALUE rb_queue_push(VALUE self, VALUE obj)
Definition: thread_sync.c:756
static VALUE get_array(VALUE obj, int idx)
Definition: thread_sync.c:544
VALUE rb_thread_current(void)
Definition: thread.c:2504
#define NIL_P(v)
Definition: ruby.h:451
long tv_nsec
Definition: missing.h:62
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:646
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:2734
static int szqueue_push_should_block(int argc, const VALUE *argv)
Definition: thread_sync.c:984
int argc
Definition: ruby.c:183
rb_thread_status
Definition: vm_core.h:649
#define Qfalse
Definition: ruby.h:436
static VALUE rb_eClosedQueueError
Definition: thread_sync.c:4
volatile int sleeper
Definition: vm_core.h:502
int err
Definition: win32.c:135
#define GVL_UNLOCK_BEGIN()
Definition: thread.c:141
VALUE rb_thread_wakeup_alive(VALUE)
Definition: thread.c:2390
static VALUE rb_condvar_broadcast(VALUE self)
Definition: thread_sync.c:1216
#define GET_SZQUEUE_WAITERS(q)
Definition: thread_sync.c:533
static VALUE rb_mutex_sleep_forever(VALUE time)
Definition: thread_sync.c:421
#define GET_CONDVAR_WAITERS(cv)
Definition: thread_sync.c:1104
static VALUE ary_buf_new(void)
Definition: thread_sync.c:538
static VALUE rb_mutex_wait_for(VALUE time)
Definition: thread_sync.c:428
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition: class.c:1758
static VALUE rb_condvar_wait(int argc, VALUE *argv, VALUE self)
Definition: thread_sync.c:1180
#define ALIAS_GLOBAL_CONST(name)
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1020
static VALUE do_sleep(VALUE args)
Definition: thread_sync.c:1157
static const char * rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t volatile *th)
Definition: thread_sync.c:331
#define TRUE
Definition: nkf.h:175
static void lock_interrupt(void *ptr)
Definition: thread_sync.c:217
struct rb_mutex_struct * keeping_mutexes
Definition: vm_core.h:767
static int set_unblock_function(rb_thread_t *th, rb_unblock_function_t *func, void *arg, struct rb_unblock_callback *old, int fail_if_interrupted)
Definition: thread.c:374
VALUE rb_ary_delete(VALUE ary, VALUE item)
Definition: array.c:2992
VALUE rb_obj_is_mutex(VALUE obj)
Definition: thread_sync.c:83
void ruby_xfree(void *x)
Definition: gc.c:8017
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1919
static VALUE rb_szqueue_push(int argc, VALUE *argv, VALUE self)
Definition: thread_sync.c:1009
static ID id_sleep
Definition: thread_sync.c:1154
static VALUE rb_szqueue_pop(int argc, VALUE *argv, VALUE self)
Definition: thread_sync.c:1064
#define PRIsVALUE
Definition: ruby.h:135
unsigned long ID
Definition: ruby.h:86
static void rb_check_deadlock(rb_vm_t *vm)
Definition: thread.c:4946
#define GVL_UNLOCK_END()
Definition: thread.c:146
#define Qnil
Definition: ruby.h:438
VALUE rb_eStopIteration
Definition: enumerator.c:109
VALUE rb_mutex_owned_p(VALUE self)
Definition: thread_sync.c:316
static VALUE rb_szqueue_close(VALUE self)
Definition: thread_sync.c:940
static VALUE rb_condvar_initialize(VALUE self)
Definition: thread_sync.c:1143
#define FL_TEST_RAW(x, f)
Definition: ruby.h:1283
VALUE rb_mutex_new(void)
Definition: thread_sync.c:118
unsigned long VALUE
Definition: ruby.h:85
static VALUE rb_szqueue_num_waiting(VALUE self)
Definition: thread_sync.c:1091
RUBY_EXTERN VALUE rb_cThread
Definition: ruby.h:1909
static VALUE queue_closed_result(VALUE self)
Definition: thread_sync.c:610
static int queue_pop_should_block(int argc, const VALUE *argv)
Definition: thread_sync.c:807
VALUE rb_ensure(VALUE(*b_proc)(ANYARGS), VALUE data1, VALUE(*e_proc)(ANYARGS), VALUE data2)
Definition: eval.c:923
static void reset_unblock_function(rb_thread_t *th, const struct rb_unblock_callback *old)
Definition: thread.c:400
#define rb_funcallv
Definition: console.c:21
static void mutex_locked(rb_thread_t *th, VALUE self)
Definition: thread_sync.c:138
register unsigned int len
Definition: zonetab.h:51
static VALUE rb_queue_clear(VALUE self)
Definition: thread_sync.c:858
enum rb_thread_status status
Definition: vm_core.h:738
static VALUE rb_szqueue_max_set(VALUE self, VALUE vmax)
Definition: thread_sync.c:965
#define RSTRUCT_SET(st, idx, v)
Definition: ruby.h:1195
static unsigned long szqueue_num_waiting_producer(VALUE self)
Definition: thread_sync.c:591
#define RSTRUCT_GET(st, idx)
Definition: ruby.h:1196
#define INT2FIX(i)
Definition: ruby.h:232
static void raise_closed_queue_error(VALUE self)
Definition: thread_sync.c:604
#define GET_QUEUE_QUE(q)
Definition: thread_sync.c:531
static VALUE delete_current_thread(VALUE ary)
Definition: thread_sync.c:1164
#define RARRAY_AREF(a, i)
Definition: ruby.h:1040
unsigned long interrupt_mask
Definition: vm_core.h:762
static VALUE queue_sleep(VALUE arg)
Definition: thread_sync.c:774
#define RTEST(v)
Definition: ruby.h:450
static Bigint * diff(Bigint *a, Bigint *b)
Definition: util.c:1507
static VALUE rb_cConditionVariable
Definition: thread_sync.c:3
struct rb_encoding_entry * list
Definition: encoding.c:55
#define ETIMEDOUT
Definition: win32.h:549
static VALUE mutex_sleep(int argc, VALUE *argv, VALUE self)
Definition: thread_sync.c:471
VALUE timeout
Definition: thread_sync.c:1151
VALUE rb_struct_define_without_accessor_under(VALUE outer, const char *class_name, VALUE super, rb_alloc_func_t alloc,...)
Definition: struct.c:384
static VALUE mutex_initialize(VALUE self)
Definition: thread_sync.c:112
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: ruby.h:1182
static unsigned long queue_length(VALUE self)
Definition: thread_sync.c:577
static VALUE rb_queue_close(VALUE self)
Definition: thread_sync.c:727
static VALUE queue_do_close(VALUE self, int is_szq)
Definition: thread_sync.c:617
static void sleep_timeval(rb_thread_t *th, struct timeval time, int spurious_check)
Definition: thread.c:1114
const char * name
Definition: nkf.c:208
#define FL_SET(x, f)
Definition: ruby.h:1290
VALUE rb_mutex_locked_p(VALUE self)
Definition: thread_sync.c:130
#define RUBY_VM_INTERRUPTED(th)
Definition: vm_core.h:1552
struct rb_mutex_struct rb_mutex_t
static void mutex_free(void *ptr)
Definition: thread_sync.c:55
#define rb_intern(str)
#define GET_QUEUE_WAITERS(q)
Definition: thread_sync.c:532
#define NULL
Definition: _sdbm.c:102
#define Qundef
Definition: ruby.h:439
#define mutex_mark
Definition: thread_sync.c:52
static VALUE rb_queue_empty_p(VALUE self)
Definition: thread_sync.c:846
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
void rb_provide(const char *)
Definition: load.c:572
VALUE rb_eThreadError
Definition: eval.c:814
VALUE rb_eArgError
Definition: error.c:763
#define NUM2LONG(x)
Definition: ruby.h:648
static const rb_data_type_t mutex_data_type
Definition: thread_sync.c:76
char ** argv
Definition: ruby.c:184
static int queue_closed_p(VALUE self)
Definition: thread_sync.c:598
static void Init_thread_sync(void)
Definition: thread_sync.c:1237
VALUE rb_obj_class(VALUE)
Definition: object.c:229