+ --debug is now --imcc-debug; make this more consistent with -D.
[parrot.git] / src / thread.c
blobfa0c03a21517104cc2eaa919bcd36b419df44fc2
1 /*
2 Copyright (C) 2001-2007, The Perl Foundation.
3 $Id$
5 =head1 NAME
7 src/thread.c - Thread handling stuff
9 =head1 DESCRIPTION
11 Threads are created by creating new C<ParrotInterpreter> objects.
13 =head2 Functions
15 =over 4
17 =cut
21 #include "parrot/parrot.h"
22 #include "parrot/atomic.h"
24 /* HEADERIZER HFILE: include/parrot/thread.h */
26 /* HEADERIZER BEGIN: static */
28 static Parrot_Interp detach(UINTVAL tid);
29 PARROT_CAN_RETURN_NULL
30 static Shared_gc_info * get_pool(PARROT_INTERP)
31 __attribute__nonnull__(1);
33 PARROT_WARN_UNUSED_RESULT
34 static int is_suspended_for_gc(PARROT_INTERP)
35 __attribute__nonnull__(1);
37 PARROT_CAN_RETURN_NULL
38 static PMC * make_local_args_copy(PARROT_INTERP,
39 ARGIN(Parrot_Interp old_interp),
40 ARGIN_NULLOK(PMC *args))
41 __attribute__nonnull__(1)
42 __attribute__nonnull__(2);
44 PARROT_CAN_RETURN_NULL
45 static PMC * make_local_copy(PARROT_INTERP,
46 ARGIN(Parrot_Interp from),
47 ARGIN(PMC *arg))
48 __attribute__nonnull__(1)
49 __attribute__nonnull__(2)
50 __attribute__nonnull__(3);
52 static void mutex_unlock(ARGMOD(void *arg))
53 __attribute__nonnull__(1)
54 FUNC_MODIFIES(*arg);
56 static Parrot_Interp pt_check_tid(UINTVAL tid, ARGIN(const char *from))
57 __attribute__nonnull__(2);
59 static int pt_gc_count_threads(PARROT_INTERP)
60 __attribute__nonnull__(1);
62 static void pt_gc_wait_for_stage(PARROT_INTERP,
63 thread_gc_stage_enum from_stage,
64 thread_gc_stage_enum to_stage)
65 __attribute__nonnull__(1);
67 static void pt_gc_wakeup_check(PARROT_INTERP)
68 __attribute__nonnull__(1);
70 static void pt_ns_clone(
71 ARGOUT(Parrot_Interp d),
72 ARGOUT(PMC *dest_ns),
73 ARGIN(Parrot_Interp s),
74 ARGIN(PMC *source_ns))
75 __attribute__nonnull__(1)
76 __attribute__nonnull__(2)
77 __attribute__nonnull__(3)
78 __attribute__nonnull__(4)
79 FUNC_MODIFIES(d)
80 FUNC_MODIFIES(*dest_ns);
82 static void pt_suspend_all_for_gc(PARROT_INTERP)
83 __attribute__nonnull__(1);
85 static void pt_suspend_one_for_gc(PARROT_INTERP)
86 __attribute__nonnull__(1);
88 static void pt_thread_signal(NOTNULL(Parrot_Interp self), PARROT_INTERP)
89 __attribute__nonnull__(1)
90 __attribute__nonnull__(2);
92 static void pt_thread_wait(PARROT_INTERP)
93 __attribute__nonnull__(1);
95 PARROT_CAN_RETURN_NULL
96 static QUEUE_ENTRY * remove_queued_suspend_gc(PARROT_INTERP)
97 __attribute__nonnull__(1);
99 PARROT_CAN_RETURN_NULL
100 static void* thread_func(ARGIN_NULLOK(void *arg));
102 /* HEADERIZER END: static */
104 #if defined THREAD_DEBUG && THREAD_DEBUG
105 # define DEBUG_ONLY(x) x
106 #else
107 # define DEBUG_ONLY(x)
108 #endif
110 static int running_threads;
112 void Parrot_really_destroy(PARROT_INTERP, int exit_code, void *arg);
116 =item C<static PMC * make_local_copy>
118 Create a local copy of the PMC if necessary. (No copy is made if it
119 is marked shared.) This includes workarounds for Parrot_clone() not
120 doing the Right Thing with subroutines (specifically, code segments
121 aren't preserved and it is difficult to do so as long as
122 Parrot_clone() depends on freezing).
124 =cut
128 PARROT_CAN_RETURN_NULL
129 static PMC *
130 make_local_copy(PARROT_INTERP, ARGIN(Parrot_Interp from), ARGIN(PMC *arg))
132 PMC *ret_val;
133 STRING * const _sub = interp->vtables[enum_class_Sub]->whoami;
134 STRING * const _multi_sub = interp->vtables[enum_class_MultiSub]->whoami;
136 if (PMC_IS_NULL(arg)) {
137 ret_val = PMCNULL;
139 else if (PObj_is_PMC_shared_TEST(arg)) {
140 ret_val = arg;
142 else if (VTABLE_isa(from, arg, _multi_sub)) {
143 INTVAL i = 0;
144 const INTVAL n = VTABLE_elements(from, arg);
145 ret_val = pmc_new(interp, enum_class_MultiSub);
147 for (i = 0; i < n; ++i) {
148 PMC *const orig = VTABLE_get_pmc_keyed_int(from, arg, i);
149 PMC *const copy = make_local_copy(interp, from, orig);
150 VTABLE_push_pmc(interp, ret_val, copy);
153 else if (VTABLE_isa(from, arg, _sub)) {
154 /* this is a workaround for cloning subroutines not actually
155 * working as one might expect mainly because the segment is
156 * not correctly copied
158 ret_val = Parrot_clone(interp, arg);
159 PMC_sub(ret_val)->seg = PMC_sub(arg)->seg;
160 /* Skip vtable overrides and methods. */
161 if (PMC_sub(ret_val)->vtable_index == -1
162 && !(PMC_sub(ret_val)->comp_flags & SUB_COMP_FLAG_METHOD)) {
163 Parrot_store_sub_in_namespace(interp, ret_val);
166 else {
167 ret_val = Parrot_clone(interp, arg);
169 return ret_val;
174 =item C<static Shared_gc_info * get_pool>
176 Get the shared gc information. TODO: improve the docs here.
178 =cut
182 PARROT_CAN_RETURN_NULL
183 static Shared_gc_info *
184 get_pool(PARROT_INTERP)
186 return shared_gc_info;
191 =item C<void pt_free_pool>
193 Frees the shared gc information. This clears any global data hen joining all
194 threads at parent interpreter destruction.
196 =cut
200 void
201 pt_free_pool(PARROT_INTERP)
203 if (shared_gc_info) {
204 COND_DESTROY(shared_gc_info->gc_cond);
205 PARROT_ATOMIC_INT_DESTROY(shared_gc_info->gc_block_level);
206 mem_sys_free(shared_gc_info);
207 shared_gc_info = NULL;
213 =item C<static PMC * make_local_args_copy>
215 Make a local copy of the corresponding array of arguments.
217 =cut
221 PARROT_CAN_RETURN_NULL
222 static PMC *
223 make_local_args_copy(PARROT_INTERP, ARGIN(Parrot_Interp old_interp), ARGIN_NULLOK(PMC *args))
225 PMC *ret_val;
226 INTVAL old_size;
227 INTVAL i;
229 if (PMC_IS_NULL(args))
230 return PMCNULL;
232 old_size = VTABLE_get_integer(old_interp, args);
234 /* XXX should this be a different type? */
235 ret_val = pmc_new(interp, enum_class_FixedPMCArray);
236 VTABLE_set_integer_native(interp, ret_val, old_size);
238 for (i = 0; i < old_size; ++i) {
239 PMC * const copy = make_local_copy(interp, old_interp,
240 VTABLE_get_pmc_keyed_int(old_interp, args, i));
242 VTABLE_set_pmc_keyed_int(interp, ret_val, i, copy);
245 return ret_val;
250 =item C<PMC * pt_shared_fixup>
252 Fixup a PMC to be sharable. Right now, reassigns the vtable to one
253 owned by some master interpreter, so the PMC can be safely reused
254 after thread death.
256 In the future the PMC returned might be different than the one
257 passed, e.g., if we need to reallocate the PMC in a different
258 interpreter.
260 =cut
264 PARROT_CAN_RETURN_NULL
265 PMC *
266 pt_shared_fixup(PARROT_INTERP, ARGMOD(PMC *pmc))
268 /* TODO this will need to change for thread pools
269 * XXX should we have a separate interpreter for this?
271 INTVAL type_num;
272 Parrot_Interp master = interpreter_array[0];
273 const int is_ro = pmc->vtable->flags & VTABLE_IS_READONLY_FLAG;
275 /* This lock is paired with one in objects.c. It is necessary to protect
276 * against the master interpreter adding classes and consequently
277 * resizing its classname->type_id hashtable and/or expanding its vtable
278 * array.
279 * TODO investigate if a read-write lock results in substantially
280 * better performance.
282 LOCK_INTERPRETER(master);
283 type_num = pmc->vtable->base_type;
285 if (type_num == enum_type_undef) {
286 UNLOCK_INTERPRETER(master);
287 real_exception(interp, NULL, 1, "pt_shared_fixup: unsharable type");
288 return PMCNULL;
291 pmc->vtable = master->vtables[type_num];
293 UNLOCK_INTERPRETER(master);
295 if (is_ro)
296 pmc->vtable = pmc->vtable->ro_variant_vtable;
298 add_pmc_sync(interp, pmc);
300 PObj_is_PMC_shared_SET(pmc);
302 /* make sure metadata doesn't go away unexpectedly */
303 if (PMC_metadata(pmc))
304 PMC_metadata(pmc) = pt_shared_fixup(interp, PMC_metadata(pmc));
306 return pmc;
311 =item C<static void pt_thread_signal>
313 Wakeup a C<interp> which should have called pt_thread_wait().
315 =cut
319 static void
320 pt_thread_signal(NOTNULL(Parrot_Interp self), PARROT_INTERP)
322 COND_SIGNAL(interp->thread_data->interp_cond);
327 =item C<void pt_thread_wait_with>
329 Wait for this interpreter to be signalled through its condition variable,
330 dealing properly with GC issues. C<*mutex> is assumed locked on entry and
331 will be locked on exit from this function. If a GC run occurs in the middle of
332 this function, then a spurious wakeup may occur.
334 =cut
338 void
339 pt_thread_wait_with(PARROT_INTERP, ARGMOD(Parrot_mutex *mutex))
341 LOCK(interpreter_array_mutex);
342 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
343 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
344 /* fprintf(stderr, "%p: pt_thread_wait, before sleep, doing GC run\n",
345 * interp); */
346 UNLOCK(interpreter_array_mutex);
347 UNLOCK(*mutex);
349 pt_suspend_self_for_gc(interp);
351 LOCK(*mutex);
352 /* since we unlocked the mutex something bad may have occured */
353 return;
356 interp->thread_data->state |= THREAD_STATE_GC_WAKEUP;
358 UNLOCK(interpreter_array_mutex);
359 COND_WAIT(interp->thread_data->interp_cond, *mutex);
360 LOCK(interpreter_array_mutex);
362 interp->thread_data->state &= ~THREAD_STATE_GC_WAKEUP;
364 if (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
365 UNLOCK(*mutex);
366 /* XXX loop needed? */
367 do {
368 UNLOCK(interpreter_array_mutex);
369 /* fprintf(stderr, "%p: woken up, doing GC run\n", interp); */
370 pt_suspend_self_for_gc(interp);
371 LOCK(interpreter_array_mutex);
372 } while (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC);
374 UNLOCK(interpreter_array_mutex);
375 LOCK(*mutex);
377 else {
378 UNLOCK(interpreter_array_mutex);
384 =item C<static void pt_thread_wait>
386 Wait for us to be signalled. GC matters are handled correctly.
387 C<interpreter_array_mutex> is assumed held. Spurious wakeups may occur.
389 =cut
393 static void
394 pt_thread_wait(PARROT_INTERP)
396 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
397 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
398 /* fprintf(stderr, "%p: pt_thread_wait, before sleep, doing GC run\n",
399 * interp); */
401 UNLOCK(interpreter_array_mutex);
402 pt_suspend_self_for_gc(interp);
403 LOCK(interpreter_array_mutex);
405 /* while we were GCing, whatever we were waiting on might have
406 * changed */
407 return;
410 interp->thread_data->state |= THREAD_STATE_GC_WAKEUP;
412 COND_WAIT(interp->thread_data->interp_cond, interpreter_array_mutex);
414 interp->thread_data->state &= ~THREAD_STATE_GC_WAKEUP;
416 while (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
417 UNLOCK(interpreter_array_mutex);
418 /* fprintf(stderr, "%p: woken up, doing GC run\n", interp); */
419 pt_suspend_self_for_gc(interp);
420 LOCK(interpreter_array_mutex);
427 =item C<static void* thread_func>
429 The actual thread function.
431 =cut
435 PARROT_CAN_RETURN_NULL
436 static void*
437 thread_func(ARGIN_NULLOK(void *arg))
439 Parrot_exception exp;
440 int lo_var_ptr;
441 UINTVAL tid;
442 PMC *sub;
443 PMC *sub_arg;
444 PMC * const self = (PMC*) arg;
445 PMC *ret_val = NULL;
446 Parrot_Interp interp = (Parrot_Interp)PMC_data(self);
448 Parrot_block_DOD(interp);
449 Parrot_block_GC(interp);
451 /* need to set it here because argument passing can trigger GC */
452 interp->lo_var_ptr = &lo_var_ptr;
453 sub = (PMC *)PMC_struct_val(self);
454 sub_arg = PMC_pmc_val(self);
456 if (setjmp(exp.destination)) {
457 const Parrot_exception * const except = interp->exceptions;
458 /* caught exception */
459 /* XXX what should we really do here */
460 PIO_eprintf(interp,
461 "Unhandled exception in thread with tid %d "
462 "(message=%Ss, number=%d)\n",
463 interp->thread_data->tid,
464 except->msg,
465 except->error);
467 ret_val = PMCNULL;
469 else {
470 /* run normally */
471 push_new_c_exception_handler(interp, &exp);
472 Parrot_unblock_DOD(interp);
473 Parrot_unblock_GC(interp);
474 ret_val = Parrot_runops_fromc_args(interp, sub, "PF", sub_arg);
477 * thread is finito
479 LOCK(interpreter_array_mutex);
480 DEBUG_ONLY(fprintf(stderr, "marking an thread as finished\n"));
482 interp->thread_data->state |= THREAD_STATE_FINISHED;
483 tid = interp->thread_data->tid;
485 if (interp != interpreter_array[tid]) {
486 UNLOCK(interpreter_array_mutex);
487 PANIC(interp, "thread finished: interpreter mismatch");
489 if (interp->thread_data->state & THREAD_STATE_DETACHED) {
490 interpreter_array[tid] = NULL;
491 DEBUG_ONLY(fprintf(stderr, "really destroying an interpreter [exit while detached]\n"));
492 Parrot_really_destroy(interp, 0, NULL);
494 else if (interp->thread_data->state & THREAD_STATE_JOINED) {
495 pt_thread_signal(interp, interp->thread_data->joiner);
498 /* make sure we don't block a GC run */
499 pt_gc_wakeup_check(interp);
500 PARROT_ASSERT(interp->thread_data->state & THREAD_STATE_FINISHED);
502 UNLOCK(interpreter_array_mutex);
504 return ret_val;
509 =back
511 =head2 Helper functions used also for running plain interpreters
513 =over 4
515 =item C<void pt_clone_code>
517 Copy/clone the packfile/code from interpreter C<s> to C<d>. All
518 resources are created in C<d>.
520 =cut
524 void
525 pt_clone_code(Parrot_Interp d, Parrot_Interp s)
527 Parrot_block_DOD(d);
528 Interp_flags_SET(d, PARROT_EXTERN_CODE_FLAG);
529 d->code = NULL;
530 Parrot_switch_to_cs(d, s->code, 1);
531 Parrot_unblock_DOD(d);
536 =item C<static void pt_ns_clone>
538 Clone all globals from C<s> to C<d>.
540 =cut
544 static void
545 pt_ns_clone(ARGOUT(Parrot_Interp d), ARGOUT(PMC *dest_ns),
546 ARGIN(Parrot_Interp s), ARGIN(PMC *source_ns))
548 PMC * const iter = VTABLE_get_iter(s, source_ns);
549 const INTVAL n = VTABLE_elements(s, source_ns);
550 INTVAL i;
552 for (i = 0; i < n; ++i) {
553 /* XXX what if 'key' is a non-constant-pool string? */
554 STRING * const key = VTABLE_shift_string(s, iter);
555 PMC * const val = VTABLE_get_pmc_keyed_str(s, source_ns, key);
557 if (val->vtable->base_type == enum_class_NameSpace) {
558 PMC *sub_ns = VTABLE_get_pmc_keyed_str(d, dest_ns, key);
559 if (PMC_IS_NULL(sub_ns) || sub_ns->vtable->base_type !=
560 enum_class_NameSpace) {
561 sub_ns = pmc_new(d, enum_class_NameSpace);
562 VTABLE_set_pmc_keyed_str(d, dest_ns, key, sub_ns);
564 pt_ns_clone(d, sub_ns, s, val);
566 else {
567 PMC * const dval = VTABLE_get_pmc_keyed_str(d, dest_ns, key);
569 if (PMC_IS_NULL(dval)) {
570 PMC * const copy = make_local_copy(d, s, val);
572 /* Vtable overrides and methods were already cloned, so don't reclone them. */
573 if (!(val->vtable->base_type == enum_class_Sub
574 && (PMC_sub(val)->vtable_index != -1
575 || PMC_sub(val)->comp_flags & SUB_COMP_FLAG_METHOD))) {
576 VTABLE_set_pmc_keyed_str(d, dest_ns, key, copy);
585 =item C<void pt_clone_globals>
587 Copy global namespace when cloning new interpreter
589 =cut
593 void
594 pt_clone_globals(Parrot_Interp d, Parrot_Interp s)
596 Parrot_block_DOD(d);
597 pt_ns_clone(d, d->root_namespace, s, s->root_namespace);
598 Parrot_unblock_DOD(d);
603 =item C<void pt_thread_prepare_for_run>
605 Setup code, and TODO ...
607 =cut
611 void
612 pt_thread_prepare_for_run(Parrot_Interp d, Parrot_Interp s)
614 Parrot_setup_event_func_ptrs(d);
619 =back
621 =head2 ParrotThread methods
623 =over 4
625 =cut
631 =item C<PMC * pt_transfer_sub>
633 create a clone of the sub suitable for the other interpreter
635 =cut
639 PARROT_CAN_RETURN_NULL
640 PMC *
641 pt_transfer_sub(ARGOUT(Parrot_Interp d), ARGIN(Parrot_Interp s), ARGIN(PMC *sub))
643 #if defined THREAD_DEBUG && THREAD_DEBUG
644 PIO_eprintf(s, "copying over subroutine [%Ss]\n",
645 Parrot_full_sub_name(s, sub));
646 #endif
647 return make_local_copy(d, s, sub);
652 =item C<int pt_thread_run>
654 Run the C<*sub> PMC in a separate thread using interpreter in
655 C<*dest_interp>.
657 C<arg> should be an array of arguments for the subroutine.
660 =cut
665 pt_thread_run(PARROT_INTERP, ARGOUT(PMC* dest_interp), ARGIN(PMC* sub), ARGIN(PMC *arg))
667 PMC *old_dest_interp;
668 PMC *parent;
669 Interp * const interpreter = (Parrot_Interp)PMC_data(dest_interp);
671 Parrot_block_GC(interpreter);
672 Parrot_block_DOD(interpreter);
673 Parrot_block_GC(interp);
674 Parrot_block_DOD(interp);
676 /* make a copy of the ParrotThread PMC so we can use it
677 * to hold parameters to the new thread without it being
678 * garbage collected or otherwise changed by the parent thread.
679 * Also so the new thread's getinterp doesn't return an object
680 * owned by the wrong interpreter -- which would be very bad
681 * if the parent is destroyed before the child.
682 * XXX FIXME move this elsewhere? at least the set_pmc_keyed_int
684 old_dest_interp = dest_interp;
685 dest_interp = pmc_new_noinit(interpreter, enum_class_ParrotThread);
687 /* so it's not accidentally deleted */
688 PMC_data(old_dest_interp) = NULL;
689 PMC_data(dest_interp) = interpreter;
691 VTABLE_set_pmc_keyed_int(interpreter, interpreter->iglobals,
692 (INTVAL) IGLOBALS_INTERPRETER, dest_interp);
694 parent = VTABLE_get_pmc_keyed_int(interp, interp->iglobals,
695 IGLOBALS_INTERPRETER);
698 * TODO check if thread flags are consistent
700 if (interp->flags & PARROT_THR_COPY_INTERP)
701 clone_interpreter(interpreter, (Parrot_Interp)PMC_data(parent), PARROT_CLONE_DEFAULT);
703 * TODO thread pools
706 pt_thread_prepare_for_run(interpreter, interp);
708 PMC_struct_val(dest_interp) = pt_transfer_sub(interpreter, interp, sub);
709 PMC_pmc_val(dest_interp) = make_local_args_copy(interpreter, interp, arg);
712 * set regs according to pdd03
714 interpreter->current_object = dest_interp;
716 * create a joinable thread
718 interpreter->thread_data->state = THREAD_STATE_JOINABLE;
720 Parrot_unblock_GC(interpreter);
721 Parrot_unblock_DOD(interpreter);
722 Parrot_unblock_GC(interp);
723 Parrot_unblock_DOD(interp);
725 THREAD_CREATE_JOINABLE(interpreter->thread_data->thread,
726 thread_func, dest_interp);
728 /* check for pending GC */
729 LOCK(interpreter_array_mutex);
730 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED)
731 pt_suspend_one_for_gc(interpreter);
733 UNLOCK(interpreter_array_mutex);
734 return 0;
739 =item C<int pt_thread_run_1>
741 Runs a type 1 thread. Nothing is shared, both interpreters are free
742 running without any communication.
744 =cut
749 pt_thread_run_1(PARROT_INTERP, ARGOUT(PMC* dest_interp), ARGIN(PMC* sub), ARGIN(PMC *arg))
751 interp->flags |= PARROT_THR_TYPE_1;
752 return pt_thread_run(interp, dest_interp, sub, arg);
757 =item C<int pt_thread_run_2>
759 Runs a type 2 thread. No shared variables, threads are communicating by
760 sending messages.
762 =cut
767 pt_thread_run_2(PARROT_INTERP, ARGOUT(PMC* dest_interp), ARGIN(PMC* sub), ARGIN(PMC *arg))
769 interp->flags |= PARROT_THR_TYPE_2;
770 return pt_thread_run(interp, dest_interp, sub, arg);
775 =item C<int pt_thread_run_3>
777 Run a type 3 thread. Threads may have shared variables and are managed
778 in a thread pool.
780 =cut
785 pt_thread_run_3(PARROT_INTERP, ARGOUT(PMC* dest_interp), ARGIN(PMC* sub), ARGIN(PMC *arg))
787 interp->flags |= PARROT_THR_TYPE_3;
788 return pt_thread_run(interp, dest_interp, sub, arg);
793 =item C<void pt_thread_yield>
795 Relinquishes hold on the processor.
797 =cut
801 void
802 pt_thread_yield(void)
804 YIELD;
809 =item C<static Parrot_Interp pt_check_tid>
811 Helper function. Check if C<tid> is valid. The caller holds the mutex.
812 Returns the interpreter for C<tid>.
814 =cut
818 static Parrot_Interp
819 pt_check_tid(UINTVAL tid, ARGIN(const char *from))
821 if (tid >= n_interpreters) {
822 UNLOCK(interpreter_array_mutex);
823 internal_exception(1, "%s: illegal thread tid %d", from, tid);
825 if (tid == 0) {
826 UNLOCK(interpreter_array_mutex);
827 internal_exception(1, "%s: illegal thread tid %d (main)", from, tid);
829 if (!interpreter_array[tid]) {
830 UNLOCK(interpreter_array_mutex);
831 internal_exception(1, "%s: illegal thread tid %d - empty", from, tid);
833 return interpreter_array[tid];
838 =item C<static void mutex_unlock>
840 Unlocks the mutex C<*arg>.
842 =cut
846 static void
847 mutex_unlock(ARGMOD(void *arg))
849 UNLOCK(*(Parrot_mutex *) arg);
854 =item C<static int is_suspended_for_gc>
856 Returns true iff C<interp> is suspended so a global GC can
857 be performed. interpreter_array_mutex must be held.
859 =cut
863 PARROT_WARN_UNUSED_RESULT
864 static int
865 is_suspended_for_gc(PARROT_INTERP)
867 if (!interp)
868 return 1;
869 else if (interp->thread_data->wants_shared_gc)
870 return 1;
871 else if (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC)
872 return 1;
873 else if ((interp->thread_data->state & THREAD_STATE_FINISHED) ||
874 (interp->thread_data->state & THREAD_STATE_NOT_STARTED))
875 return 1;
876 else
877 return 0;
882 =item C<static QUEUE_ENTRY * remove_queued_suspend_gc>
884 Remove an event requesting that the interpreter suspend itself for a
885 garbage-collection run from the event queue.
887 =cut
891 PARROT_CAN_RETURN_NULL
892 static QUEUE_ENTRY *
893 remove_queued_suspend_gc(PARROT_INTERP)
895 parrot_event *ev = NULL;
896 QUEUE *queue = interp->task_queue;
897 QUEUE_ENTRY *prev = NULL;
898 QUEUE_ENTRY *cur;
900 queue_lock(queue);
901 cur = queue->head;
903 while (cur) {
904 ev = (parrot_event *)cur->data;
906 if (ev->type == EVENT_TYPE_SUSPEND_FOR_GC)
907 break;
909 prev = cur;
910 cur = cur->next;
913 if (cur) {
914 if (prev)
915 prev->next = cur->next;
916 else
917 queue->head = cur->next;
919 if (cur == queue->tail)
920 queue->tail = prev;
922 if (cur == queue->head)
923 queue->head = cur->next;
925 mem_sys_free(ev);
926 mem_sys_free(cur);
927 cur = NULL;
928 DEBUG_ONLY(fprintf(stderr, "%p: remove_queued_suspend_gc: got one\n", interp));
931 queue_unlock(queue);
932 return cur;
937 =item C<static int pt_gc_count_threads>
939 interpreter_array_mutex must be held
941 =cut
945 static int
946 pt_gc_count_threads(PARROT_INTERP)
948 UINTVAL i;
949 int count = 0;
951 for (i = 0; i < n_interpreters; ++i) {
952 Parrot_Interp cur;
953 cur = interpreter_array[i];
954 if (!cur)
955 continue;
956 if (cur->thread_data->state & (THREAD_STATE_NOT_STARTED |
957 THREAD_STATE_FINISHED))
958 continue;
959 ++count;
961 DEBUG_ONLY(fprintf(stderr, "found %d threads\n", count));
962 return count;
967 =item C<static void pt_gc_wait_for_stage>
969 RT#48260: Not yet documented!!!
971 =cut
975 static void
976 pt_gc_wait_for_stage(PARROT_INTERP, thread_gc_stage_enum from_stage,
977 thread_gc_stage_enum to_stage)
979 Shared_gc_info *info = shared_gc_info;
980 int thread_count;
982 DEBUG_ONLY(fprintf(stderr, "%p: gc_wait_for_stage: %d->%d\n", interp, from_stage, to_stage));
984 /* XXX well-timed thread death can mess this up */
985 LOCK(interpreter_array_mutex);
987 DEBUG_ONLY(fprintf(stderr, "%p: got lock\n", interp));
988 thread_count = pt_gc_count_threads(interp);
990 PARROT_ASSERT(info->gc_stage == from_stage);
991 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_NOT_STARTED));
992 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_FINISHED));
994 if (from_stage == 0)
995 PARROT_ASSERT(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC);
996 else
997 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC));
999 ++info->num_reached;
1001 DEBUG_ONLY(fprintf(stderr, "%p: gc_wait_for_stage: got %d\n", interp, info->num_reached));
1003 if (info->num_reached == thread_count) {
1004 info->gc_stage = to_stage;
1005 info->num_reached = 0;
1006 COND_BROADCAST(info->gc_cond);
1008 else {
1009 do {
1010 COND_WAIT(info->gc_cond, interpreter_array_mutex);
1011 } while (info->gc_stage != to_stage);
1013 UNLOCK(interpreter_array_mutex);
1019 =item C<static void pt_gc_wakeup_check>
1021 Check if we need to wake threads to perform garbage collection.
1022 This is called after thread death.
1023 interpreter_array_mutex is assumed held.
1025 =cut
1029 static void
1030 pt_gc_wakeup_check(PARROT_INTERP)
1032 Shared_gc_info *info = shared_gc_info;
1033 int thread_count;
1035 /* XXX: maybe a little hack; see RT #49532 */
1036 if (!info)
1037 return;
1039 thread_count = pt_gc_count_threads(interp);
1041 if (info->num_reached == thread_count) {
1042 PARROT_ASSERT(info->gc_stage == THREAD_GC_STAGE_NONE);
1043 info->gc_stage = THREAD_GC_STAGE_MARK;
1044 info->num_reached = 0;
1045 COND_BROADCAST(info->gc_cond);
1051 =item C<static void pt_suspend_one_for_gc>
1053 Suspend a single interpreter for GC. C<interpreter_array_mutex>
1054 assumed held.
1056 =cut
1060 static void
1061 pt_suspend_one_for_gc(PARROT_INTERP)
1063 DEBUG_ONLY(fprintf(stderr, "suspend one: %p\n", interp));
1064 if (is_suspended_for_gc(interp)) {
1065 DEBUG_ONLY(fprintf(stderr, "ignoring already suspended\n"));
1066 return;
1069 if (interp->thread_data->state & THREAD_STATE_GC_WAKEUP) {
1070 DEBUG_ONLY(fprintf(stderr, "just waking it up\n"));
1071 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1072 COND_SIGNAL(interp->thread_data->interp_cond);
1074 else {
1075 DEBUG_ONLY(fprintf(stderr, "queuing event\n"));
1076 interp->thread_data->state |= THREAD_STATE_SUSPEND_GC_REQUESTED;
1077 Parrot_cx_request_suspend_for_gc(interp);
1083 =item C<static void pt_suspend_all_for_gc>
1085 Get all threads to perform a GC run.
1087 =cut
1091 static void
1092 pt_suspend_all_for_gc(PARROT_INTERP)
1094 UINTVAL i;
1096 DEBUG_ONLY(fprintf(stderr, "suspend_all_for_gc [interp=%p]\n", interp));
1098 LOCK(interpreter_array_mutex);
1099 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1101 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
1102 DEBUG_ONLY(fprintf(stderr, "found while suspending all\n"));
1103 Parrot_cx_delete_suspend_for_gc(interp);
1104 interp->thread_data->state &= ~THREAD_STATE_SUSPEND_GC_REQUESTED;
1105 UNLOCK(interpreter_array_mutex);
1106 return;
1109 #if 0
1110 for (i = 0; i < n_interpreters; ++i) {
1111 Parrot_Interp other_interp;
1112 other_interp = interpreter_array[i];
1113 if (!other_interp)
1114 continue;
1116 if (is_suspended_for_gc(other_interp) &&
1117 other_interp != interp &&
1118 (other_interp->thread_data->state & THREAD_STATE_SUSPENDED_GC))
1120 PMC *successp;
1121 /* this means that someone else already got this far,
1122 * so we have a suspend event in our queue to ignore
1124 /* XXX still reachable? */
1125 DEBUG_ONLY(fprintf(stderr, "apparently someone else is doing it [%p]\n", other_interp));
1126 fprintf(stderr, "??? found later (%p)\n", other_interp);
1127 successp = Parrot_cx_delete_suspend_for_gc(interp);
1128 PARROT_ASSERT(successp);
1129 UNLOCK(interpreter_array_mutex);
1130 return;
1133 #endif
1135 /* now send all the non-suspended threads to suspend for GC */
1136 for (i = 0; i < n_interpreters; ++i) {
1137 Parrot_Interp other_interp = interpreter_array[i];
1139 if (interp == other_interp)
1140 continue;
1142 if (is_suspended_for_gc(other_interp))
1143 continue;
1145 pt_suspend_one_for_gc(other_interp);
1147 UNLOCK(interpreter_array_mutex);
1152 =item C<void pt_suspend_self_for_gc>
1154 Suspend this thread for a full GC run.
1156 XXX FIXME -- if GC is blocked, we need to do a GC run as soon
1157 as it becomes unblocked.
1159 =cut
1163 void
1164 pt_suspend_self_for_gc(PARROT_INTERP)
1166 PARROT_ASSERT(interp);
1167 PARROT_ASSERT(!interp->arena_base->DOD_block_level);
1168 DEBUG_ONLY(fprintf(stderr, "%p: suspend_self_for_gc\n", interp));
1169 /* since we are modifying our own state, we need to lock
1170 * the interpreter_array_mutex.
1172 LOCK(interpreter_array_mutex);
1173 DEBUG_ONLY(fprintf(stderr, "%p: got lock\n", interp));
1175 PARROT_ASSERT(interp->thread_data->state &
1176 (THREAD_STATE_SUSPEND_GC_REQUESTED | THREAD_STATE_SUSPENDED_GC));
1178 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
1179 DEBUG_ONLY(fprintf(stderr, "remove queued request\n"));
1180 while (!PMC_IS_NULL(Parrot_cx_delete_suspend_for_gc(interp)));
1181 DEBUG_ONLY(fprintf(stderr, "removed all queued requests\n"));
1182 interp->thread_data->state &= ~THREAD_STATE_SUSPEND_GC_REQUESTED;
1184 if (!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC)) {
1185 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1187 else {
1188 DEBUG_ONLY(fprintf(stderr, "no need to set suspended\n"));
1190 UNLOCK(interpreter_array_mutex);
1192 /* mark and sweep our world -- later callbacks will keep
1193 * it sync'd
1195 Parrot_dod_ms_run(interp, DOD_trace_stack_FLAG);
1197 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC));
1202 =item C<PMC* pt_thread_join>
1204 Join (wait for) a joinable thread.
1206 =cut
1210 PARROT_CAN_RETURN_NULL
1211 PMC*
1212 pt_thread_join(NOTNULL(Parrot_Interp parent), UINTVAL tid)
1214 int state;
1215 Parrot_Interp interp;
1217 LOCK(interpreter_array_mutex);
1219 interp = pt_check_tid(tid, "join");
1221 if (interp == parent)
1222 do_panic(parent, "Can't join self", __FILE__, __LINE__);
1224 if ((!(interp->thread_data->state & (THREAD_STATE_DETACHED
1225 | THREAD_STATE_JOINED)) &&
1226 !(interp->thread_data->state & THREAD_STATE_NOT_STARTED)) ||
1227 interp->thread_data->state == THREAD_STATE_FINISHED) {
1228 void *raw_retval = NULL;
1229 PMC *retval;
1231 interp->thread_data->state |= THREAD_STATE_JOINED;
1233 while (!(interp->thread_data->state & THREAD_STATE_FINISHED)) {
1234 interp->thread_data->joiner = parent;
1235 pt_thread_wait(parent);
1238 UNLOCK(interpreter_array_mutex);
1239 JOIN(interp->thread_data->thread, raw_retval);
1241 retval = (PMC *)raw_retval;
1243 * we need to push a cleanup handler here: if cloning
1244 * of the retval fails (e.g. it's a NULLPMC) this lock
1245 * isn't released until eternity or someone hits ^C
1247 * TODO This is needed for all places holding a lock for
1248 * non-trivial tasks
1249 * -leo
1250 * TODO remove that and replace it with proper exception
1251 * handling, so that a failing clone in the parent
1252 * just stops that thread
1253 * -leo
1255 LOCK(interpreter_array_mutex);
1256 CLEANUP_PUSH(mutex_unlock, &interpreter_array_mutex);
1258 if (retval) {
1259 PMC *parent_ret;
1261 * clone the PMC into caller, if its not a shared PMC
1262 * the PMC is not in the parents root set nor in the
1263 * stack so block DOD during clone
1264 * XXX should probably acquire the parent's interpreter mutex
1266 Parrot_block_DOD(parent);
1267 parent_ret = make_local_copy(parent, interp, retval);
1269 /* this PMC is living only in the stack of this currently
1270 * dying interpreter, so register it in parent's DOD registry
1271 * XXX is this still needed?
1273 dod_register_pmc(parent, parent_ret);
1274 Parrot_unblock_DOD(parent);
1275 retval = parent_ret;
1277 else {
1278 retval = PMCNULL;
1280 interpreter_array[tid] = NULL;
1281 running_threads--;
1283 DEBUG_ONLY(fprintf(stderr, "destroying an interpreter [join]\n"));
1284 if (Interp_debug_TEST(parent, PARROT_THREAD_DEBUG_FLAG))
1285 fprintf(stderr, "running threads %d\n", running_threads);
1287 /* reparent it so memory pool merging works */
1288 interp->parent_interpreter = parent;
1289 Parrot_really_destroy(interp, 0, NULL);
1291 CLEANUP_POP(1);
1293 * interpreter destruction is done - unregister the return
1294 * value, caller gets it now
1296 if (retval)
1297 dod_unregister_pmc(parent, retval);
1299 return retval;
1302 * when here thread was in wrong state
1304 state = interp->thread_data->state;
1305 UNLOCK(interpreter_array_mutex);
1306 real_exception(interp, NULL, 1, "join: illegal thread state %d tid %d",
1307 state, tid);
1308 return NULL;
1313 =item C<void pt_join_threads>
1315 Possibly wait for other running threads. This is called when destroying
1316 C<interp>.
1318 =cut
1322 void
1323 pt_join_threads(PARROT_INTERP)
1325 size_t i;
1326 pt_free_pool(interp);
1329 * if no threads were started - fine
1331 LOCK(interpreter_array_mutex);
1332 if (n_interpreters <= 1) {
1333 n_interpreters = 0;
1334 UNLOCK(interpreter_array_mutex);
1335 return;
1338 * only the first interpreter waits for other threads
1340 if (interp != interpreter_array[0]) {
1341 UNLOCK(interpreter_array_mutex);
1342 return;
1345 for (i = 1; i < n_interpreters; ++i) {
1346 Parrot_Interp thread_interp = interpreter_array[i];
1347 if (thread_interp == NULL)
1348 continue;
1349 if (thread_interp->thread_data->state == THREAD_STATE_JOINABLE ||
1350 (thread_interp->thread_data->state & THREAD_STATE_FINISHED)) {
1352 void *retval = NULL;
1353 thread_interp->thread_data->state |= THREAD_STATE_JOINED;
1354 UNLOCK(interpreter_array_mutex);
1355 JOIN(thread_interp->thread_data->thread, retval);
1356 LOCK(interpreter_array_mutex);
1359 UNLOCK(interpreter_array_mutex);
1360 return;
1365 =item C<static Parrot_Interp detach>
1367 Helper for detach and kill.
1369 Returns the interpreter, if it didn't finish yet.
1371 =cut
1375 static Parrot_Interp
1376 detach(UINTVAL tid)
1378 Parrot_Interp interp;
1380 LOCK(interpreter_array_mutex);
1381 interp = pt_check_tid(tid, "detach");
1383 * if interpreter is joinable, we detach em
1385 if (interp->thread_data->state == THREAD_STATE_JOINABLE ||
1386 interp->thread_data->state == THREAD_STATE_FINISHED) {
1387 DETACH(interp->thread_data->thread);
1388 interp->thread_data->state |= THREAD_STATE_DETACHED;
1390 if (interp->thread_data->state & THREAD_STATE_FINISHED) {
1391 interpreter_array[tid] = NULL;
1392 DEBUG_ONLY(fprintf(stderr, "destroying an interpreter [detach]\n"));
1393 Parrot_really_destroy(interp, 0, NULL);
1394 interp = NULL;
1397 UNLOCK(interpreter_array_mutex);
1398 return interp;
1403 =item C<void pt_thread_detach>
1405 Detaches (make non-joinable) the thread.
1407 =cut
1411 void
1412 pt_thread_detach(UINTVAL tid)
1414 (void) detach(tid);
1419 =item C<void pt_thread_kill>
1421 Kills the thread.
1423 =cut
1427 void
1428 pt_thread_kill(UINTVAL tid)
1430 PARROT_INTERP = detach(tid);
1432 /* schedule a terminate event for that interpreter */
1433 if (interp)
1434 Parrot_cx_runloop_end(interp);
1439 =back
1441 =head2 Threaded interpreter book-keeping
1443 =over 4
1445 =item C<void pt_add_to_interpreters>
1447 All threaded interpreters are stored in an array. Assumes that caller
1448 holds LOCK.
1450 =cut
1454 void
1455 pt_add_to_interpreters(PARROT_INTERP, ARGIN_NULLOK(Parrot_Interp new_interp))
1457 size_t i;
1458 DEBUG_ONLY(fprintf(stderr, "interp = %p\n", interp));
1460 if (!new_interp) {
1462 * Create an entry for the very first interpreter, event
1463 * handling needs it
1465 PARROT_ASSERT(!interpreter_array);
1466 PARROT_ASSERT(n_interpreters == 0);
1468 interpreter_array = mem_allocate_typed(Interp *);
1469 interpreter_array[0] = interp;
1470 n_interpreters = 1;
1472 shared_gc_info = (Shared_gc_info *)mem_sys_allocate_zeroed(sizeof (*shared_gc_info));
1473 COND_INIT(shared_gc_info->gc_cond);
1474 PARROT_ATOMIC_INT_INIT(shared_gc_info->gc_block_level);
1475 PARROT_ATOMIC_INT_SET(shared_gc_info->gc_block_level, 0);
1477 /* XXX try to defer this until later */
1478 PARROT_ASSERT(interp == interpreter_array[0]);
1479 interp->thread_data = mem_allocate_zeroed_typed(Thread_data);
1480 INTERPRETER_LOCK_INIT(interp);
1481 interp->thread_data->tid = 0;
1483 return;
1487 new_interp->thread_data = mem_allocate_zeroed_typed(Thread_data);
1488 INTERPRETER_LOCK_INIT(new_interp);
1489 running_threads++;
1490 if (Interp_debug_TEST(interp, PARROT_THREAD_DEBUG_FLAG))
1491 fprintf(stderr, "running threads %d\n", running_threads);
1493 * look for an empty slot
1495 for (i = 0; i < n_interpreters; ++i) {
1496 if (interpreter_array[i] == NULL) {
1497 interpreter_array[i] = new_interp;
1498 new_interp->thread_data->tid = i;
1499 new_interp->thread_data->state = THREAD_STATE_NOT_STARTED;
1500 return;
1504 /* need to resize */
1505 interpreter_array = (Interp **)mem_sys_realloc(interpreter_array,
1506 (n_interpreters + 1) * sizeof (Interp *));
1508 interpreter_array[n_interpreters] = new_interp;
1509 new_interp->thread_data->tid = n_interpreters;
1510 new_interp->thread_data->state = THREAD_STATE_NOT_STARTED;
1512 ++n_interpreters;
1517 =back
1519 =head2 DOD Synchronization Functions
1521 =over 4
1523 =item C<void pt_DOD_start_mark>
1525 DOD is going to start the mark phase. In the presence of shared PMCs, we can only
1526 run one DOD run at a time because C<< PMC->next_for_GC >> may be changed.
1528 C<flags> are the DOD flags. We check if we need to collect shared objects or
1529 not.
1531 TODO - Have a count of shared PMCs and check it during DOD.
1533 TODO - Evaluate if a interpreter lock is cheaper when C<dod_mark_ptr> is
1534 updated.
1536 =cut
1540 void
1541 pt_DOD_start_mark(PARROT_INTERP)
1543 Shared_gc_info *info;
1544 int block_level;
1546 DEBUG_ONLY(fprintf(stderr, "%p: pt_DOD_start_mark\n", interp));
1547 /* if no other threads are running, we are safe */
1548 if (!running_threads)
1549 return;
1551 info = get_pool(interp);
1552 PARROT_ATOMIC_INT_GET(block_level, info->gc_block_level);
1554 DEBUG_ONLY(fprintf(stderr, "start threaded mark\n"));
1556 * TODO now check, if we are the owner of a shared memory pool
1557 * if yes:
1558 * - suspend all other threads by sending them a suspend event
1559 * (or put a LOCK around updating the mark pointers)
1560 * - return and continue the mark phase
1561 * - then s. comments below
1563 LOCK(interpreter_array_mutex);
1564 if (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
1565 PARROT_ASSERT(!(interp->thread_data->state &
1566 THREAD_STATE_SUSPEND_GC_REQUESTED));
1567 DEBUG_ONLY(fprintf(stderr, "already suspended...\n"));
1568 UNLOCK(interpreter_array_mutex);
1570 else if (block_level) {
1571 /* unthreaded collection */
1572 DEBUG_ONLY(fprintf(stderr, "... but blocked\n"));
1574 /* holding the lock */
1575 return;
1577 else if (interp->thread_data->state &
1578 THREAD_STATE_SUSPEND_GC_REQUESTED) {
1579 while (!PMC_IS_NULL(Parrot_cx_delete_suspend_for_gc(interp)));
1581 interp->thread_data->state &= ~THREAD_STATE_SUSPEND_GC_REQUESTED;
1582 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1584 DEBUG_ONLY(fprintf(stderr, "%p: detected request\n", interp));
1585 UNLOCK(interpreter_array_mutex);
1587 else {
1588 /* we need to stop the world */
1589 DEBUG_ONLY(fprintf(stderr, "stop the world\n"));
1590 UNLOCK(interpreter_array_mutex);
1592 pt_suspend_all_for_gc(interp);
1595 DEBUG_ONLY(fprintf(stderr, "%p: wait for stage\n", interp));
1596 pt_gc_wait_for_stage(interp, THREAD_GC_STAGE_NONE, THREAD_GC_STAGE_MARK);
1598 DEBUG_ONLY(fprintf(stderr, "actually mark\n"));
1600 * we can't allow parallel running DODs both would mess with shared PMCs
1601 * next_for_GC pointers
1603 LOCK(interpreter_array_mutex);
1604 DEBUG_ONLY(fprintf(stderr, "got marking lock\n"));
1609 =item C<void pt_DOD_mark_root_finished>
1611 DOD is finished for the root set.
1613 =cut
1617 void
1618 pt_DOD_mark_root_finished(PARROT_INTERP)
1620 if (!running_threads)
1621 return;
1623 * TODO now check, if we are the owner of a shared memory pool
1624 * if yes:
1625 * - now run DOD_mark on all members of our pool
1626 * - if all shared PMCs are marked by all threads then
1627 * - we can continue to free unused objects
1633 =item C<void pt_DOD_stop_mark>
1635 DOD's mark phase is done.
1637 =cut
1641 void
1642 pt_DOD_stop_mark(PARROT_INTERP)
1644 if (!running_threads)
1645 return;
1647 * normal operation can continue now
1648 * - other threads may or not free unused objects then,
1649 * depending on their resource statistics
1651 if (!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC)) {
1652 UNLOCK(interpreter_array_mutex);
1653 return;
1656 PARROT_ASSERT(!(interp->thread_data->state &
1657 THREAD_STATE_SUSPEND_GC_REQUESTED));
1658 interp->thread_data->state &= ~THREAD_STATE_SUSPENDED_GC;
1660 while (!PMC_IS_NULL(Parrot_cx_delete_suspend_for_gc(interp))) {
1661 /* XXX FIXME make this message never trigger */
1662 fprintf(stderr, "%p: extraneous suspend_gc event\n", (void *)interp);
1665 DEBUG_ONLY(fprintf(stderr, "%p: unlock\n", interp));
1666 UNLOCK(interpreter_array_mutex);
1667 DEBUG_ONLY(fprintf(stderr, "wait to sweep\n"));
1669 pt_gc_wait_for_stage(interp, THREAD_GC_STAGE_MARK, THREAD_GC_STAGE_SWEEP);
1674 =item C<void Parrot_shared_DOD_block>
1676 Block stop-the-world DOD runs.
1678 =cut
1682 PARROT_API
1683 void
1684 Parrot_shared_DOD_block(PARROT_INTERP)
1686 Shared_gc_info * const info = get_pool(interp);
1688 if (info) {
1689 int level;
1690 PARROT_ATOMIC_INT_INC(level, info->gc_block_level);
1691 PARROT_ASSERT(level > 0);
1697 =item C<void Parrot_shared_DOD_unblock>
1699 Unblock stop-the-world DOD runs.
1701 =cut
1705 PARROT_API
1706 void
1707 Parrot_shared_DOD_unblock(PARROT_INTERP)
1709 Shared_gc_info * const info = get_pool(interp);
1710 if (info) {
1711 int level;
1712 PARROT_ATOMIC_INT_DEC(level, info->gc_block_level);
1713 PARROT_ASSERT(level >= 0);
1718 * Local variables:
1719 * c-file-style: "parrot"
1720 * End:
1721 * vim: expandtab shiftwidth=4: