fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / src / thread.c
blob98851199adad6e30a0fb09e27dc0435abbd811b8
1 /*
2 Copyright (C) 2001-2010, Parrot 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/extend.h"
23 #include "parrot/atomic.h"
24 #include "parrot/runcore_api.h"
25 #include "pmc/pmc_sub.h"
26 #include "pmc/pmc_parrotinterpreter.h"
28 /* HEADERIZER HFILE: include/parrot/thread.h */
30 /* HEADERIZER BEGIN: static */
31 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
33 static Parrot_Interp detach(UINTVAL tid);
34 PARROT_CAN_RETURN_NULL
35 static Shared_gc_info * get_pool(void);
37 PARROT_WARN_UNUSED_RESULT
38 static int is_suspended_for_gc(PARROT_INTERP)
39 __attribute__nonnull__(1);
41 PARROT_CAN_RETURN_NULL
42 static PMC * make_local_args_copy(PARROT_INTERP,
43 ARGIN(Parrot_Interp old_interp),
44 ARGIN_NULLOK(PMC *args))
45 __attribute__nonnull__(1)
46 __attribute__nonnull__(2);
48 PARROT_CAN_RETURN_NULL
49 static PMC * make_local_copy(PARROT_INTERP,
50 ARGIN(Parrot_Interp from),
51 ARGIN(PMC *arg))
52 __attribute__nonnull__(1)
53 __attribute__nonnull__(2)
54 __attribute__nonnull__(3);
56 static void mutex_unlock(ARGMOD(void *arg))
57 __attribute__nonnull__(1)
58 FUNC_MODIFIES(*arg);
60 static Parrot_Interp pt_check_tid(UINTVAL tid, ARGIN(const char *from))
61 __attribute__nonnull__(2);
63 static int pt_gc_count_threads(void);
64 static void pt_gc_wait_for_stage(PARROT_INTERP,
65 thread_gc_stage_enum from_stage,
66 thread_gc_stage_enum to_stage)
67 __attribute__nonnull__(1);
69 static void pt_gc_wakeup_check(void);
70 static void pt_ns_clone(PARROT_INTERP,
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 __attribute__nonnull__(5)
80 FUNC_MODIFIES(d)
81 FUNC_MODIFIES(*dest_ns);
83 static void pt_suspend_all_for_gc(PARROT_INTERP)
84 __attribute__nonnull__(1);
86 static void pt_suspend_one_for_gc(PARROT_INTERP)
87 __attribute__nonnull__(1);
89 static void pt_thread_signal(ARGIN(Parrot_Interp self), PARROT_INTERP)
90 __attribute__nonnull__(1)
91 __attribute__nonnull__(2);
93 static void pt_thread_wait(PARROT_INTERP)
94 __attribute__nonnull__(1);
96 PARROT_CAN_RETURN_NULL
97 static void* thread_func(ARGIN_NULLOK(void *arg));
99 #define ASSERT_ARGS_detach __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
100 #define ASSERT_ARGS_get_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
101 #define ASSERT_ARGS_is_suspended_for_gc __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
102 PARROT_ASSERT_ARG(interp))
103 #define ASSERT_ARGS_make_local_args_copy __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
104 PARROT_ASSERT_ARG(interp) \
105 , PARROT_ASSERT_ARG(old_interp))
106 #define ASSERT_ARGS_make_local_copy __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
107 PARROT_ASSERT_ARG(interp) \
108 , PARROT_ASSERT_ARG(from) \
109 , PARROT_ASSERT_ARG(arg))
110 #define ASSERT_ARGS_mutex_unlock __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
111 PARROT_ASSERT_ARG(arg))
112 #define ASSERT_ARGS_pt_check_tid __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
113 PARROT_ASSERT_ARG(from))
114 #define ASSERT_ARGS_pt_gc_count_threads __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
115 #define ASSERT_ARGS_pt_gc_wait_for_stage __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
116 PARROT_ASSERT_ARG(interp))
117 #define ASSERT_ARGS_pt_gc_wakeup_check __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
118 #define ASSERT_ARGS_pt_ns_clone __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
119 PARROT_ASSERT_ARG(interp) \
120 , PARROT_ASSERT_ARG(d) \
121 , PARROT_ASSERT_ARG(dest_ns) \
122 , PARROT_ASSERT_ARG(s) \
123 , PARROT_ASSERT_ARG(source_ns))
124 #define ASSERT_ARGS_pt_suspend_all_for_gc __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
125 PARROT_ASSERT_ARG(interp))
126 #define ASSERT_ARGS_pt_suspend_one_for_gc __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
127 PARROT_ASSERT_ARG(interp))
128 #define ASSERT_ARGS_pt_thread_signal __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
129 PARROT_ASSERT_ARG(self) \
130 , PARROT_ASSERT_ARG(interp))
131 #define ASSERT_ARGS_pt_thread_wait __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
132 PARROT_ASSERT_ARG(interp))
133 #define ASSERT_ARGS_thread_func __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
134 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
135 /* HEADERIZER END: static */
137 #if defined THREAD_DEBUG && THREAD_DEBUG
138 # define DEBUG_ONLY(x) (x)
139 #else
140 # define DEBUG_ONLY(x)
141 #endif
143 static int running_threads;
145 void Parrot_really_destroy(PARROT_INTERP, int exit_code, void *arg);
149 =item C<static PMC * make_local_copy(PARROT_INTERP, Parrot_Interp from, PMC
150 *arg)>
152 Creates a local copy of the PMC if necessary. (No copy is made if it is marked
153 shared.) This includes workarounds for Parrot_clone() not doing the Right Thing
154 with subroutines (specifically, code segments aren't preserved and it is
155 difficult to do so as long as Parrot_clone() depends on freezing).
157 =cut
161 PARROT_CAN_RETURN_NULL
162 static PMC *
163 make_local_copy(PARROT_INTERP, ARGIN(Parrot_Interp from), ARGIN(PMC *arg))
165 ASSERT_ARGS(make_local_copy)
166 PMC *ret_val;
167 STRING * const _sub = interp->vtables[enum_class_Sub]->whoami;
168 STRING * const _multi_sub = interp->vtables[enum_class_MultiSub]->whoami;
170 if (PMC_IS_NULL(arg)) {
171 ret_val = PMCNULL;
173 else if (PObj_is_PMC_shared_TEST(arg)) {
174 ret_val = arg;
176 else if (VTABLE_isa(from, arg, _multi_sub)) {
177 INTVAL i = 0;
178 const INTVAL n = VTABLE_elements(from, arg);
179 ret_val = Parrot_pmc_new(interp, enum_class_MultiSub);
181 for (i = 0; i < n; ++i) {
182 PMC *const orig = VTABLE_get_pmc_keyed_int(from, arg, i);
183 PMC *const copy = make_local_copy(interp, from, orig);
184 VTABLE_push_pmc(interp, ret_val, copy);
187 else if (VTABLE_isa(from, arg, _sub)) {
188 /* this is a workaround for cloning subroutines not actually
189 * working as one might expect mainly because the segment is
190 * not correctly copied
192 Parrot_Sub_attributes *ret_val_sub, *arg_sub;
194 ret_val = Parrot_clone(interp, arg);
195 PMC_get_sub(interp, ret_val, ret_val_sub);
196 PMC_get_sub(interp, arg, arg_sub);
197 ret_val_sub->seg = arg_sub->seg;
198 /* Skip vtable overrides and methods. */
199 if (ret_val_sub->vtable_index == -1
200 && !(ret_val_sub->comp_flags & SUB_COMP_FLAG_METHOD)) {
201 Parrot_ns_store_sub(interp, ret_val);
204 else {
205 ret_val = Parrot_clone(interp, arg);
207 return ret_val;
212 =item C<static Shared_gc_info * get_pool(void)>
214 Gets the shared gc information. For now this is global data; ideally it will
215 become something other than a static variable. If everything uses this
216 function, it will be easier to change.
218 =cut
222 PARROT_CAN_RETURN_NULL
223 static Shared_gc_info *
224 get_pool(void)
226 ASSERT_ARGS(get_pool)
227 return shared_gc_info;
232 =item C<void pt_free_pool(PARROT_INTERP)>
234 Frees the shared GC information. This clears any global data when joining all
235 threads at parent interpreter destruction.
237 =cut
241 void
242 pt_free_pool(PARROT_INTERP)
244 ASSERT_ARGS(pt_free_pool)
245 if (shared_gc_info) {
246 COND_DESTROY(shared_gc_info->gc_cond);
247 PARROT_ATOMIC_INT_DESTROY(shared_gc_info->gc_block_level);
248 mem_internal_free(shared_gc_info);
249 shared_gc_info = NULL;
255 =item C<static PMC * make_local_args_copy(PARROT_INTERP, Parrot_Interp
256 old_interp, PMC *args)>
258 Make a local copy of the corresponding array of arguments.
260 =cut
264 PARROT_CAN_RETURN_NULL
265 static PMC *
266 make_local_args_copy(PARROT_INTERP, ARGIN(Parrot_Interp old_interp), ARGIN_NULLOK(PMC *args))
268 ASSERT_ARGS(make_local_args_copy)
269 PMC *ret_val;
270 INTVAL old_size;
271 INTVAL i;
273 if (PMC_IS_NULL(args))
274 return PMCNULL;
276 old_size = VTABLE_get_integer(old_interp, args);
278 /* XXX should this be a different type? */
279 ret_val = Parrot_pmc_new(interp, enum_class_FixedPMCArray);
280 VTABLE_set_integer_native(interp, ret_val, old_size);
282 for (i = 0; i < old_size; ++i) {
283 PMC * const copy = make_local_copy(interp, old_interp,
284 VTABLE_get_pmc_keyed_int(old_interp, args, i));
286 VTABLE_set_pmc_keyed_int(interp, ret_val, i, copy);
289 return ret_val;
294 =item C<PMC * pt_shared_fixup(PARROT_INTERP, PMC *pmc)>
296 Modifies a PMC to be sharable. Right now, reassigns the vtable to one
297 owned by some master interpreter, so the PMC can be safely reused
298 after thread death.
300 In the future the PMC returned might be different than the one
301 passed, e.g., if we need to reallocate the PMC in a different
302 interpreter.
304 =cut
308 PARROT_CAN_RETURN_NULL
309 PMC *
310 pt_shared_fixup(PARROT_INTERP, ARGMOD(PMC *pmc))
312 ASSERT_ARGS(pt_shared_fixup)
313 /* TODO this will need to change for thread pools
314 * XXX should we have a separate interpreter for this?
316 INTVAL type_num;
317 Parrot_Interp master = interpreter_array[0];
318 const int is_ro = pmc->vtable->flags & VTABLE_IS_READONLY_FLAG;
320 /* This lock is paired with one in objects.c. It is necessary to protect
321 * against the master interpreter adding classes and consequently
322 * resizing its classname->type_id hashtable and/or expanding its vtable
323 * array.
324 * TODO investigate if a read-write lock results in substantially
325 * better performance.
327 LOCK_INTERPRETER(master);
328 type_num = pmc->vtable->base_type;
330 if (type_num == enum_type_undef) {
331 UNLOCK_INTERPRETER(master);
332 Parrot_ex_throw_from_c_args(interp, NULL, 1,
333 "pt_shared_fixup: unsharable type");
336 pmc->vtable = master->vtables[type_num];
338 UNLOCK_INTERPRETER(master);
340 if (is_ro)
341 pmc->vtable = pmc->vtable->ro_variant_vtable;
343 PObj_is_PMC_shared_SET(pmc);
345 /* make sure metadata doesn't go away unexpectedly */
346 if (PMC_metadata(pmc))
347 PMC_metadata(pmc) = pt_shared_fixup(interp, PMC_metadata(pmc));
349 return pmc;
354 =item C<static void pt_thread_signal(Parrot_Interp self, PARROT_INTERP)>
356 Wakes up an C<interp> which should have called pt_thread_wait().
358 =cut
362 static void
363 pt_thread_signal(ARGIN(Parrot_Interp self), PARROT_INTERP)
365 ASSERT_ARGS(pt_thread_signal)
366 COND_SIGNAL(interp->thread_data->interp_cond);
371 =item C<void pt_thread_wait_with(PARROT_INTERP, Parrot_mutex *mutex)>
373 Waits for this interpreter to be signalled through its condition variable,
374 dealing properly with GC issues. C<*mutex> is assumed locked on entry and
375 will be locked on exit from this function. If a GC run occurs in the middle of
376 this function, then a spurious wakeup may occur.
378 =cut
382 void
383 pt_thread_wait_with(PARROT_INTERP, ARGMOD(Parrot_mutex *mutex))
385 ASSERT_ARGS(pt_thread_wait_with)
386 LOCK(interpreter_array_mutex);
387 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
388 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
389 UNLOCK(interpreter_array_mutex);
390 UNLOCK(*mutex);
392 pt_suspend_self_for_gc(interp);
394 LOCK(*mutex);
395 /* since we unlocked the mutex something bad may have occured */
396 return;
399 interp->thread_data->state |= THREAD_STATE_GC_WAKEUP;
401 UNLOCK(interpreter_array_mutex);
402 COND_WAIT(interp->thread_data->interp_cond, *mutex);
403 LOCK(interpreter_array_mutex);
405 interp->thread_data->state &= ~THREAD_STATE_GC_WAKEUP;
407 if (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
408 UNLOCK(*mutex);
409 /* XXX loop needed? */
410 do {
411 UNLOCK(interpreter_array_mutex);
412 /* fprintf(stderr, "%p: woken up, doing GC run\n", interp); */
413 pt_suspend_self_for_gc(interp);
414 LOCK(interpreter_array_mutex);
415 } while (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC);
417 UNLOCK(interpreter_array_mutex);
418 LOCK(*mutex);
420 else {
421 UNLOCK(interpreter_array_mutex);
427 =item C<static void pt_thread_wait(PARROT_INTERP)>
429 Waits for a signal, handling GC matters correctly. C<interpreter_array_mutex>
430 is assumed held. Spurious wakeups may occur.
432 =cut
436 static void
437 pt_thread_wait(PARROT_INTERP)
439 ASSERT_ARGS(pt_thread_wait)
440 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
441 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
442 /* fprintf(stderr, "%p: pt_thread_wait, before sleep, doing GC run\n",
443 * interp); */
445 UNLOCK(interpreter_array_mutex);
446 pt_suspend_self_for_gc(interp);
447 LOCK(interpreter_array_mutex);
449 /* while we were GCing, whatever we were waiting on might have
450 * changed */
451 return;
454 interp->thread_data->state |= THREAD_STATE_GC_WAKEUP;
456 COND_WAIT(interp->thread_data->interp_cond, interpreter_array_mutex);
458 interp->thread_data->state &= ~THREAD_STATE_GC_WAKEUP;
460 while (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
461 UNLOCK(interpreter_array_mutex);
462 /* fprintf(stderr, "%p: woken up, doing GC run\n", interp); */
463 pt_suspend_self_for_gc(interp);
464 LOCK(interpreter_array_mutex);
471 =item C<static void* thread_func(void *arg)>
473 The actual thread function.
475 =cut
479 PARROT_CAN_RETURN_NULL
480 static void*
481 thread_func(ARGIN_NULLOK(void *arg))
483 ASSERT_ARGS(thread_func)
484 Parrot_runloop jump_point;
485 int lo_var_ptr;
486 UINTVAL tid;
487 PMC * volatile sub_pmc;
488 PMC *sub_arg;
489 PMC * const self = (PMC*) arg;
490 PMC *ret_val = PMCNULL;
491 Parrot_Interp interp =
492 (Parrot_Interp)((Parrot_ParrotInterpreter_attributes *)PMC_data(self))->interp;
494 Parrot_block_GC_mark(interp);
495 Parrot_block_GC_sweep(interp);
497 /* need to set it here because argument passing can trigger GC */
498 interp->lo_var_ptr = &lo_var_ptr;
499 GETATTR_ParrotInterpreter_sub(interp, self, sub_pmc);
500 sub_arg = VTABLE_get_pmc(interp, self);
502 if (setjmp(jump_point.resume)) {
503 /* caught exception */
504 /* XXX what should we really do here */
505 /* PMC *exception = Parrot_cx_peek_task(interp);
506 Parrot_io_eprintf(interp,
507 "Unhandled exception in thread with tid %d "
508 "(message=%Ss, number=%d)\n",
509 interp->thread_data->tid,
510 VTABLE_get_string(interp, exception),
511 VTABLE_get_integer_keyed_str(interp, exception,
512 Parrot_str_new_constant(interp, "type"))); */
514 else {
515 /* run normally */
516 Parrot_ex_add_c_handler(interp, &jump_point);
517 Parrot_unblock_GC_mark(interp);
518 Parrot_unblock_GC_sweep(interp);
519 Parrot_ext_call(interp, sub_pmc, "Pf->P", sub_arg, &ret_val);
522 /* thread is finito */
523 LOCK(interpreter_array_mutex);
524 DEBUG_ONLY(fprintf(stderr, "marking an thread as finished\n"));
526 interp->thread_data->state |= THREAD_STATE_FINISHED;
527 tid = interp->thread_data->tid;
529 if (interp != interpreter_array[tid]) {
530 UNLOCK(interpreter_array_mutex);
531 PANIC(interp, "thread finished: interpreter mismatch");
533 if (interp->thread_data->state & THREAD_STATE_DETACHED) {
534 interpreter_array[tid] = NULL;
535 DEBUG_ONLY(fprintf(stderr,
536 "really destroying an interpreter [exit while detached]\n"));
537 Parrot_really_destroy(interp, 0, NULL);
539 else if (interp->thread_data->state & THREAD_STATE_JOINED) {
540 pt_thread_signal(interp, interp->thread_data->joiner);
543 /* make sure we don't block a GC run */
544 pt_gc_wakeup_check();
545 PARROT_ASSERT(interp->thread_data->state & THREAD_STATE_FINISHED);
547 UNLOCK(interpreter_array_mutex);
549 return ret_val;
554 =back
556 =head2 Helper functions used also for running plain interpreters
558 =over 4
560 =item C<void pt_clone_code(Parrot_Interp d, Parrot_Interp s)>
562 Copies/clones the packfile/code from interpreter C<s> to C<d>. All
563 resources are created in C<d>.
565 =cut
569 void
570 pt_clone_code(Parrot_Interp d, Parrot_Interp s)
572 ASSERT_ARGS(pt_clone_code)
573 Parrot_block_GC_mark(d);
574 Interp_flags_SET(d, PARROT_EXTERN_CODE_FLAG);
575 d->code = NULL;
576 Parrot_switch_to_cs(d, s->code, 1);
577 Parrot_unblock_GC_mark(d);
582 =item C<static void pt_ns_clone(PARROT_INTERP, Parrot_Interp d, PMC *dest_ns,
583 Parrot_Interp s, PMC *source_ns)>
585 Clones all globals from C<s> to C<d>.
587 =cut
591 static void
592 pt_ns_clone(PARROT_INTERP, ARGOUT(Parrot_Interp d), ARGOUT(PMC *dest_ns),
593 ARGIN(Parrot_Interp s), ARGIN(PMC *source_ns))
595 ASSERT_ARGS(pt_ns_clone)
596 PMC * const iter = VTABLE_get_iter(s, source_ns);
597 const INTVAL n = VTABLE_elements(s, source_ns);
598 INTVAL i;
600 for (i = 0; i < n; ++i) {
601 /* XXX what if 'key' is a non-constant-pool string? */
602 STRING * const key = VTABLE_shift_string(s, iter);
603 PMC * const val = VTABLE_get_pmc_keyed_str(s, source_ns, key);
605 if (val->vtable->base_type == enum_class_NameSpace) {
606 PMC *sub_ns = VTABLE_get_pmc_keyed_str(d, dest_ns, key);
607 if (PMC_IS_NULL(sub_ns) || sub_ns->vtable->base_type !=
608 enum_class_NameSpace) {
609 sub_ns = Parrot_pmc_new(d, enum_class_NameSpace);
610 VTABLE_set_pmc_keyed_str(d, dest_ns, key, sub_ns);
612 pt_ns_clone(s, d, sub_ns, s, val);
614 else {
615 PMC * const dval = VTABLE_get_pmc_keyed_str(d, dest_ns, key);
617 if (PMC_IS_NULL(dval)) {
618 PMC * const copy = make_local_copy(d, s, val);
619 Parrot_Sub_attributes *val_sub;
621 if (val->vtable->base_type == enum_class_Sub)
622 PMC_get_sub(interp, val, val_sub);
624 /* Vtable overrides and methods were already cloned, so don't reclone them. */
625 if (! (val->vtable->base_type == enum_class_Sub
626 && (val_sub->vtable_index != -1
627 || val_sub->comp_flags & SUB_COMP_FLAG_METHOD))) {
628 VTABLE_set_pmc_keyed_str(d, dest_ns, key, copy);
637 =item C<void pt_clone_globals(Parrot_Interp d, Parrot_Interp s)>
639 Copies the global namespace when cloning a new interpreter.
641 =cut
645 void
646 pt_clone_globals(Parrot_Interp d, Parrot_Interp s)
648 ASSERT_ARGS(pt_clone_globals)
649 Parrot_block_GC_mark(d);
650 pt_ns_clone(s, d, d->root_namespace, s, s->root_namespace);
651 Parrot_unblock_GC_mark(d);
656 =item C<void pt_thread_prepare_for_run(Parrot_Interp d, Parrot_Interp s)>
658 Sets up a new thread to run.
660 =cut
664 void
665 pt_thread_prepare_for_run(Parrot_Interp d, SHIM(Parrot_Interp s))
667 ASSERT_ARGS(pt_thread_prepare_for_run)
672 =back
674 =head2 ParrotThread methods
676 =over 4
678 =cut
684 =item C<PMC * pt_transfer_sub(Parrot_Interp d, Parrot_Interp s, PMC *sub)>
686 Clones the sub so that it's suitable for the other interpreter.
688 =cut
692 PARROT_CAN_RETURN_NULL
693 PMC *
694 pt_transfer_sub(ARGOUT(Parrot_Interp d), ARGIN(Parrot_Interp s), ARGIN(PMC *sub))
696 ASSERT_ARGS(pt_transfer_sub)
697 #if defined THREAD_DEBUG && THREAD_DEBUG
698 Parrot_io_eprintf(s, "copying over subroutine [%Ss]\n",
699 Parrot_full_sub_name(s, sub));
700 #endif
701 return make_local_copy(d, s, sub);
706 =item C<PMC * pt_thread_create(PARROT_INTERP, INTVAL type, INTVAL clone_flags)>
708 create a pt_thread
710 =cut
714 PARROT_EXPORT
715 PARROT_CANNOT_RETURN_NULL
716 PARROT_WARN_UNUSED_RESULT
717 PMC *
718 pt_thread_create(PARROT_INTERP, INTVAL type, INTVAL clone_flags)
720 ASSERT_ARGS(pt_thread_create)
721 PMC * const new_interp_pmc = pmc_new(interp, type);
722 Interp * const new_interp = (Interp *)VTABLE_get_pointer(interp, new_interp_pmc);
724 clone_interpreter(new_interp, interp, clone_flags);
725 pt_thread_prepare_for_run(new_interp, interp);
727 return new_interp_pmc;
732 =item C<int pt_thread_run(PARROT_INTERP, PMC *thread_interp_pmc, PMC *sub, PMC
733 *arg)>
735 run a pt_thread
737 =cut
742 pt_thread_run(PARROT_INTERP, ARGMOD(PMC *thread_interp_pmc), ARGIN(PMC *sub),
743 ARGIN_NULLOK(PMC *arg))
745 ASSERT_ARGS(pt_thread_run)
746 Interp * const thread_interp = (Interp *)VTABLE_get_pointer(interp, thread_interp_pmc);
748 SETATTR_ParrotInterpreter_sub(interp,
749 thread_interp_pmc, pt_transfer_sub(thread_interp, interp, sub));
750 VTABLE_set_pmc(interp, thread_interp_pmc, make_local_args_copy(thread_interp, interp, arg));
751 thread_interp->thread_data->state = THREAD_STATE_JOINABLE;
753 THREAD_CREATE_JOINABLE(thread_interp->thread_data->thread, thread_func, thread_interp_pmc);
755 /* check for pending GC */
757 * can't do multi-threaded GC yet
758 * XXX a quick hack to pass the few tests
760 LOCK(interpreter_array_mutex);
761 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED)
762 pt_suspend_one_for_gc(new_interp);
763 UNLOCK(interpreter_array_mutex);
767 return thread_interp->thread_data->tid;
772 =item C<int pt_thread_create_run(PARROT_INTERP, INTVAL type, INTVAL clone_flags,
773 PMC *sub, PMC *arg)>
775 create a pt_thread run
777 =cut
782 pt_thread_create_run(PARROT_INTERP,
783 INTVAL type, INTVAL clone_flags, ARGIN(PMC *sub), ARGIN_NULLOK(PMC *arg))
785 ASSERT_ARGS(pt_thread_create_run)
786 PMC *thread_interp_pmc = pt_thread_create(interp, type, clone_flags);
787 return pt_thread_run(interp, thread_interp_pmc, sub, arg);
793 =item C<void pt_thread_yield(void)>
795 Relinquishes hold on the processor.
797 =cut
801 void
802 pt_thread_yield(void)
804 ASSERT_ARGS(pt_thread_yield)
805 YIELD;
810 =item C<static Parrot_Interp pt_check_tid(UINTVAL tid, const char *from)>
812 Helper function. Checks if the given thread ID is valid. The caller holds the
813 mutex. Returns the interpreter for C<tid>.
815 =cut
819 static Parrot_Interp
820 pt_check_tid(UINTVAL tid, ARGIN(const char *from))
822 ASSERT_ARGS(pt_check_tid)
823 if (tid >= n_interpreters) {
824 UNLOCK(interpreter_array_mutex);
825 exit_fatal(1, "%s: illegal thread tid %d", from, tid);
827 if (tid == 0) {
828 UNLOCK(interpreter_array_mutex);
829 exit_fatal(1, "%s: illegal thread tid %d (main)", from, tid);
831 if (!interpreter_array[tid]) {
832 UNLOCK(interpreter_array_mutex);
833 exit_fatal(1, "%s: illegal thread tid %d - empty", from, tid);
835 return interpreter_array[tid];
840 =item C<static void mutex_unlock(void *arg)>
842 Unlocks the mutex C<*arg>.
844 =cut
848 static void
849 mutex_unlock(ARGMOD(void *arg))
851 ASSERT_ARGS(mutex_unlock)
852 UNLOCK(*(Parrot_mutex *) arg);
857 =item C<static int is_suspended_for_gc(PARROT_INTERP)>
859 Returns true iff C<interp> is suspended for a global GC run. Be sure to hold
860 C<interpreter_array_mutex>.
862 =cut
866 PARROT_WARN_UNUSED_RESULT
867 static int
868 is_suspended_for_gc(PARROT_INTERP)
870 ASSERT_ARGS(is_suspended_for_gc)
871 if (!interp)
872 return 1;
873 else if (interp->thread_data->wants_shared_gc)
874 return 1;
875 else if (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC)
876 return 1;
877 else if ((interp->thread_data->state & THREAD_STATE_FINISHED) ||
878 (interp->thread_data->state & THREAD_STATE_NOT_STARTED))
879 return 1;
880 else
881 return 0;
886 =item C<static int pt_gc_count_threads(void)>
888 Returns the number of active threads in the system (running or suspended). Be
889 sure to hold C<interpreter_array_mutex>.
891 =cut
895 static int
896 pt_gc_count_threads(void)
898 ASSERT_ARGS(pt_gc_count_threads)
899 UINTVAL i;
900 int count = 0;
902 for (i = 0; i < n_interpreters; ++i) {
903 Parrot_Interp cur;
904 cur = interpreter_array[i];
905 if (!cur)
906 continue;
907 if (cur->thread_data->state & (THREAD_STATE_NOT_STARTED |
908 THREAD_STATE_FINISHED))
909 continue;
910 ++count;
912 DEBUG_ONLY(fprintf(stderr, "found %d threads\n", count));
913 return count;
918 =item C<static void pt_gc_wait_for_stage(PARROT_INTERP, thread_gc_stage_enum
919 from_stage, thread_gc_stage_enum to_stage)>
921 Waits until all threads have reached the desired stage. Takes an interpreter,
922 starting stage and ending stage as arguments. Updates the thread information.
923 Used in C<pt_gc_start_mark> and C<pt_gc_stop_mark>.
925 =cut
929 static void
930 pt_gc_wait_for_stage(PARROT_INTERP, thread_gc_stage_enum from_stage,
931 thread_gc_stage_enum to_stage)
933 ASSERT_ARGS(pt_gc_wait_for_stage)
934 Shared_gc_info * const info = shared_gc_info;
935 int thread_count;
937 DEBUG_ONLY(fprintf(stderr, "%p: gc_wait_for_stage: %d->%d\n", interp, from_stage, to_stage));
939 /* XXX well-timed thread death can mess this up */
940 LOCK(interpreter_array_mutex);
942 DEBUG_ONLY(fprintf(stderr, "%p: got lock\n", interp));
943 thread_count = pt_gc_count_threads();
945 PARROT_ASSERT(info->gc_stage == from_stage);
946 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_NOT_STARTED));
947 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_FINISHED));
949 if (from_stage == 0)
950 PARROT_ASSERT(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC);
951 else
952 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC));
954 ++info->num_reached;
956 DEBUG_ONLY(fprintf(stderr, "%p: gc_wait_for_stage: got %d\n", interp, info->num_reached));
958 if (info->num_reached == thread_count) {
959 info->gc_stage = to_stage;
960 info->num_reached = 0;
961 COND_BROADCAST(info->gc_cond);
963 else {
964 do {
965 COND_WAIT(info->gc_cond, interpreter_array_mutex);
966 } while (info->gc_stage != to_stage);
968 UNLOCK(interpreter_array_mutex);
974 =item C<static void pt_gc_wakeup_check(void)>
976 Checks if it's necessary to wake threads to perform garbage collection. This
977 is called after thread death. Be sure to hold C<interpreter_array_mutex>.
979 =cut
983 static void
984 pt_gc_wakeup_check(void)
986 ASSERT_ARGS(pt_gc_wakeup_check)
987 Shared_gc_info * const info = shared_gc_info;
988 int thread_count;
990 if (!info)
991 return;
993 thread_count = pt_gc_count_threads();
995 if (info->num_reached == thread_count) {
996 PARROT_ASSERT(info->gc_stage == THREAD_GC_STAGE_NONE);
997 info->gc_stage = THREAD_GC_STAGE_MARK;
998 info->num_reached = 0;
999 COND_BROADCAST(info->gc_cond);
1005 =item C<static void pt_suspend_one_for_gc(PARROT_INTERP)>
1007 Suspends a single interpreter for GC. Be sure to hold
1008 C<interpreter_array_mutex>.
1010 =cut
1014 static void
1015 pt_suspend_one_for_gc(PARROT_INTERP)
1017 ASSERT_ARGS(pt_suspend_one_for_gc)
1018 DEBUG_ONLY(fprintf(stderr, "suspend one: %p\n", interp));
1019 if (is_suspended_for_gc(interp)) {
1020 DEBUG_ONLY(fprintf(stderr, "ignoring already suspended\n"));
1021 return;
1024 if (interp->thread_data->state & THREAD_STATE_GC_WAKEUP) {
1025 DEBUG_ONLY(fprintf(stderr, "just waking it up\n"));
1026 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1027 COND_SIGNAL(interp->thread_data->interp_cond);
1029 else {
1030 DEBUG_ONLY(fprintf(stderr, "queuing event\n"));
1031 interp->thread_data->state |= THREAD_STATE_SUSPEND_GC_REQUESTED;
1032 Parrot_cx_request_suspend_for_gc(interp);
1038 =item C<static void pt_suspend_all_for_gc(PARROT_INTERP)>
1040 Notifies all threads to perform a GC run.
1042 =cut
1046 static void
1047 pt_suspend_all_for_gc(PARROT_INTERP)
1049 ASSERT_ARGS(pt_suspend_all_for_gc)
1050 UINTVAL i;
1052 DEBUG_ONLY(fprintf(stderr, "suspend_all_for_gc [interp=%p]\n", interp));
1054 LOCK(interpreter_array_mutex);
1055 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1057 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
1058 DEBUG_ONLY(fprintf(stderr, "found while suspending all\n"));
1059 Parrot_cx_delete_suspend_for_gc(interp);
1060 interp->thread_data->state &= ~THREAD_STATE_SUSPEND_GC_REQUESTED;
1061 UNLOCK(interpreter_array_mutex);
1062 return;
1065 /* now send all the non-suspended threads to suspend for GC */
1066 for (i = 0; i < n_interpreters; ++i) {
1067 Parrot_Interp other_interp = interpreter_array[i];
1069 if (interp == other_interp)
1070 continue;
1072 if (is_suspended_for_gc(other_interp))
1073 continue;
1075 pt_suspend_one_for_gc(other_interp);
1077 UNLOCK(interpreter_array_mutex);
1082 =item C<void pt_suspend_self_for_gc(PARROT_INTERP)>
1084 Suspends this thread for a full GC run.
1086 XXX FIXME -- if GC is blocked, we need to do a GC run as soon
1087 as it becomes unblocked.
1089 =cut
1093 void
1094 pt_suspend_self_for_gc(PARROT_INTERP)
1096 ASSERT_ARGS(pt_suspend_self_for_gc)
1097 PARROT_ASSERT(interp);
1098 PARROT_ASSERT(!Parrot_is_blocked_GC_mark(interp));
1099 DEBUG_ONLY(fprintf(stderr, "%p: suspend_self_for_gc\n", interp));
1100 /* since we are modifying our own state, we need to lock
1101 * the interpreter_array_mutex.
1103 LOCK(interpreter_array_mutex);
1104 DEBUG_ONLY(fprintf(stderr, "%p: got lock\n", interp));
1106 PARROT_ASSERT(interp->thread_data->state &
1107 (THREAD_STATE_SUSPEND_GC_REQUESTED | THREAD_STATE_SUSPENDED_GC));
1109 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
1110 DEBUG_ONLY(fprintf(stderr, "remove queued request\n"));
1111 while (!PMC_IS_NULL(Parrot_cx_delete_suspend_for_gc(interp))) {/*Empty body*/};
1112 DEBUG_ONLY(fprintf(stderr, "removed all queued requests\n"));
1113 interp->thread_data->state &= ~THREAD_STATE_SUSPEND_GC_REQUESTED;
1115 if (!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC)) {
1116 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1118 else {
1119 DEBUG_ONLY(fprintf(stderr, "no need to set suspended\n"));
1121 UNLOCK(interpreter_array_mutex);
1123 /* mark and sweep our world -- later callbacks will keep
1124 * it sync'd
1126 Parrot_gc_mark_and_sweep(interp, GC_trace_stack_FLAG);
1128 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC));
1133 =item C<PMC* pt_thread_join(Parrot_Interp parent, UINTVAL tid)>
1135 Joins (by waiting for) a joinable thread.
1137 =cut
1141 PARROT_CAN_RETURN_NULL
1142 PMC*
1143 pt_thread_join(ARGIN(Parrot_Interp parent), UINTVAL tid)
1145 ASSERT_ARGS(pt_thread_join)
1146 int state;
1147 Parrot_Interp interp;
1149 LOCK(interpreter_array_mutex);
1151 interp = pt_check_tid(tid, "join");
1153 if (interp == parent)
1154 do_panic(parent, "Can't join self", __FILE__, __LINE__);
1156 if ((!(interp->thread_data->state & (THREAD_STATE_DETACHED
1157 | THREAD_STATE_JOINED)) &&
1158 !(interp->thread_data->state & THREAD_STATE_NOT_STARTED)) ||
1159 interp->thread_data->state == THREAD_STATE_FINISHED) {
1160 void *raw_retval = NULL;
1161 PMC *retval;
1163 interp->thread_data->state |= THREAD_STATE_JOINED;
1165 while (!(interp->thread_data->state & THREAD_STATE_FINISHED)) {
1166 interp->thread_data->joiner = parent;
1167 pt_thread_wait(parent);
1170 UNLOCK(interpreter_array_mutex);
1171 JOIN(interp->thread_data->thread, raw_retval);
1173 retval = (PMC *)raw_retval;
1175 * we need to push a cleanup handler here: if cloning
1176 * of the retval fails (e.g. it's a NULLPMC) this lock
1177 * isn't released until eternity or someone hits ^C
1179 * TODO This is needed for all places holding a lock for
1180 * non-trivial tasks
1181 * -leo
1182 * TODO remove that and replace it with proper exception
1183 * handling, so that a failing clone in the parent
1184 * just stops that thread
1185 * -leo
1187 LOCK(interpreter_array_mutex);
1188 CLEANUP_PUSH(mutex_unlock, &interpreter_array_mutex);
1190 if (retval) {
1191 PMC *parent_ret;
1193 * clone the PMC into caller, if it's not a shared PMC
1194 * the PMC is not in the parents root set nor in the
1195 * stack so block GC during clone
1196 * XXX should probably acquire the parent's interpreter mutex
1198 Parrot_block_GC_mark(parent);
1199 parent_ret = make_local_copy(parent, interp, retval);
1201 /* this PMC is living only in the stack of this currently
1202 * dying interpreter, so register it in parent's GC registry
1203 * XXX is this still needed?
1205 Parrot_pmc_gc_register(parent, parent_ret);
1206 Parrot_unblock_GC_mark(parent);
1207 retval = parent_ret;
1209 else {
1210 retval = PMCNULL;
1212 interpreter_array[tid] = NULL;
1213 --running_threads;
1215 DEBUG_ONLY(fprintf(stderr, "destroying an interpreter [join]\n"));
1216 if (Interp_debug_TEST(parent, PARROT_THREAD_DEBUG_FLAG))
1217 fprintf(stderr, "running threads %d\n", running_threads);
1219 /* reparent it so memory pool merging works */
1220 interp->parent_interpreter = parent;
1221 Parrot_really_destroy(interp, 0, NULL);
1223 CLEANUP_POP(1);
1225 * interpreter destruction is done - unregister the return
1226 * value, caller gets it now
1228 if (retval)
1229 Parrot_pmc_gc_unregister(parent, retval);
1231 return retval;
1234 * when here thread was in wrong state
1236 state = interp->thread_data->state;
1237 UNLOCK(interpreter_array_mutex);
1238 Parrot_ex_throw_from_c_args(interp, NULL, 1,
1239 "join: illegal thread state %d tid %d", state, tid);
1244 =item C<void pt_join_threads(PARROT_INTERP)>
1246 Possibly waits for other running threads. This is called when destroying
1247 C<interp>.
1249 =cut
1253 void
1254 pt_join_threads(PARROT_INTERP)
1256 ASSERT_ARGS(pt_join_threads)
1257 size_t i;
1258 pt_free_pool(interp);
1260 /* if no threads were started - fine */
1261 LOCK(interpreter_array_mutex);
1262 if (n_interpreters <= 1) {
1263 n_interpreters = 0;
1264 UNLOCK(interpreter_array_mutex);
1265 return;
1268 /* only the first interpreter waits for other threads */
1269 if (interp != interpreter_array[0]) {
1270 UNLOCK(interpreter_array_mutex);
1271 return;
1274 for (i = 1; i < n_interpreters; ++i) {
1275 Parrot_Interp thread_interp = interpreter_array[i];
1276 if (thread_interp == NULL)
1277 continue;
1278 if (thread_interp->thread_data->state == THREAD_STATE_JOINABLE ||
1279 (thread_interp->thread_data->state & THREAD_STATE_FINISHED)) {
1281 void *retval = NULL;
1282 thread_interp->thread_data->state |= THREAD_STATE_JOINED;
1283 UNLOCK(interpreter_array_mutex);
1284 JOIN(thread_interp->thread_data->thread, retval);
1285 LOCK(interpreter_array_mutex);
1288 UNLOCK(interpreter_array_mutex);
1289 return;
1294 =item C<static Parrot_Interp detach(UINTVAL tid)>
1296 Helper for detach and kill.
1298 Returns the interpreter, if it didn't finish yet.
1300 =cut
1304 static Parrot_Interp
1305 detach(UINTVAL tid)
1307 ASSERT_ARGS(detach)
1308 Parrot_Interp interp;
1310 LOCK(interpreter_array_mutex);
1311 interp = pt_check_tid(tid, "detach");
1313 * if interpreter is joinable, we detach em
1315 if (interp->thread_data->state == THREAD_STATE_JOINABLE ||
1316 interp->thread_data->state == THREAD_STATE_FINISHED) {
1317 DETACH(interp->thread_data->thread);
1318 interp->thread_data->state |= THREAD_STATE_DETACHED;
1320 if (interp->thread_data->state & THREAD_STATE_FINISHED) {
1321 interpreter_array[tid] = NULL;
1322 DEBUG_ONLY(fprintf(stderr, "destroying an interpreter [detach]\n"));
1323 Parrot_really_destroy(interp, 0, NULL);
1324 interp = NULL;
1327 UNLOCK(interpreter_array_mutex);
1328 return interp;
1333 =item C<void pt_thread_detach(UINTVAL tid)>
1335 Detaches the thread, making it non-joinable.
1337 =cut
1341 void
1342 pt_thread_detach(UINTVAL tid)
1344 ASSERT_ARGS(pt_thread_detach)
1345 (void) detach(tid);
1350 =item C<void pt_thread_kill(UINTVAL tid)>
1352 Kills the thread.
1354 =cut
1358 void
1359 pt_thread_kill(UINTVAL tid)
1361 ASSERT_ARGS(pt_thread_kill)
1362 PARROT_INTERP = detach(tid);
1364 /* schedule a terminate event for that interpreter */
1365 if (interp)
1366 Parrot_cx_runloop_end(interp);
1371 =back
1373 =head2 Threaded interpreter book-keeping
1375 =over 4
1377 =item C<void pt_add_to_interpreters(PARROT_INTERP, Parrot_Interp new_interp)>
1379 Stores the given interpreter in the array of all interpreters. Be sure to hold
1380 C<interpreter_array_mutex>.
1382 =cut
1386 void
1387 pt_add_to_interpreters(PARROT_INTERP, ARGIN_NULLOK(Parrot_Interp new_interp))
1389 ASSERT_ARGS(pt_add_to_interpreters)
1390 size_t i;
1391 DEBUG_ONLY(fprintf(stderr, "interp = %p\n", interp));
1393 if (!new_interp) {
1395 * Create an entry for the very first interpreter, event
1396 * handling needs it
1398 PARROT_ASSERT(!interpreter_array);
1399 PARROT_ASSERT(n_interpreters == 0);
1401 interpreter_array = mem_internal_allocate_typed(Interp *);
1402 interpreter_array[0] = interp;
1403 n_interpreters = 1;
1405 shared_gc_info = (Shared_gc_info *)mem_internal_allocate_zeroed(sizeof (*shared_gc_info));
1406 COND_INIT(shared_gc_info->gc_cond);
1407 PARROT_ATOMIC_INT_INIT(shared_gc_info->gc_block_level);
1408 PARROT_ATOMIC_INT_SET(shared_gc_info->gc_block_level, 0);
1410 /* XXX try to defer this until later */
1411 PARROT_ASSERT(interp == interpreter_array[0]);
1412 interp->thread_data = mem_internal_allocate_zeroed_typed(Thread_data);
1413 INTERPRETER_LOCK_INIT(interp);
1414 interp->thread_data->tid = 0;
1416 return;
1420 new_interp->thread_data = mem_internal_allocate_zeroed_typed(Thread_data);
1421 INTERPRETER_LOCK_INIT(new_interp);
1422 ++running_threads;
1423 if (Interp_debug_TEST(interp, PARROT_THREAD_DEBUG_FLAG))
1424 fprintf(stderr, "running threads %d\n", running_threads);
1426 /* look for an empty slot */
1427 for (i = 0; i < n_interpreters; ++i) {
1428 if (interpreter_array[i] == NULL) {
1429 interpreter_array[i] = new_interp;
1430 new_interp->thread_data->tid = i;
1431 new_interp->thread_data->state = THREAD_STATE_NOT_STARTED;
1432 return;
1436 /* need to resize */
1437 interpreter_array = (Interp **)mem_internal_realloc(interpreter_array,
1438 (n_interpreters + 1) * sizeof (Interp *));
1440 interpreter_array[n_interpreters] = new_interp;
1441 new_interp->thread_data->tid = n_interpreters;
1442 new_interp->thread_data->state = THREAD_STATE_NOT_STARTED;
1444 ++n_interpreters;
1449 =back
1451 =head2 GC Synchronization Functions
1453 =over 4
1455 =item C<void pt_gc_start_mark(PARROT_INTERP)>
1457 Record that the mark phase of GC is about to begin. In the presence of shared
1458 PMCs, we can only run one GC run at a time.
1460 C<flags> are the GC flags. We check if we need to collect shared objects or
1461 not.
1463 TODO - Have a count of shared PMCs and check it during GC.
1465 TODO - Evaluate if a interpreter lock is cheaper when C<gc_mark_ptr> is
1466 updated.
1468 =cut
1472 void
1473 pt_gc_start_mark(PARROT_INTERP)
1475 ASSERT_ARGS(pt_gc_start_mark)
1476 Shared_gc_info *info;
1477 int block_level;
1479 DEBUG_ONLY(fprintf(stderr, "%p: pt_gc_start_mark\n", interp));
1480 /* if no other threads are running, we are safe */
1481 if (!running_threads)
1482 return;
1484 info = get_pool();
1485 PARROT_ATOMIC_INT_GET(block_level, info->gc_block_level);
1487 DEBUG_ONLY(fprintf(stderr, "start threaded mark\n"));
1489 * TODO now check, if we are the owner of a shared memory pool
1490 * if yes:
1491 * - suspend all other threads by sending them a suspend event
1492 * (or put a LOCK around updating the mark pointers)
1493 * - return and continue the mark phase
1494 * - then s. comments below
1496 LOCK(interpreter_array_mutex);
1497 if (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
1498 PARROT_ASSERT(!(interp->thread_data->state &
1499 THREAD_STATE_SUSPEND_GC_REQUESTED));
1500 DEBUG_ONLY(fprintf(stderr, "already suspended...\n"));
1501 UNLOCK(interpreter_array_mutex);
1503 else if (block_level) {
1504 /* unthreaded collection */
1505 DEBUG_ONLY(fprintf(stderr, "... but blocked\n"));
1507 /* holding the lock */
1508 return;
1510 else if (interp->thread_data->state &
1511 THREAD_STATE_SUSPEND_GC_REQUESTED) {
1512 while (!PMC_IS_NULL(Parrot_cx_delete_suspend_for_gc(interp))) {/*Empty body*/};
1514 interp->thread_data->state &= ~THREAD_STATE_SUSPEND_GC_REQUESTED;
1515 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1517 DEBUG_ONLY(fprintf(stderr, "%p: detected request\n", interp));
1518 UNLOCK(interpreter_array_mutex);
1520 else {
1521 /* we need to stop the world */
1522 DEBUG_ONLY(fprintf(stderr, "stop the world\n"));
1523 UNLOCK(interpreter_array_mutex);
1525 pt_suspend_all_for_gc(interp);
1528 DEBUG_ONLY(fprintf(stderr, "%p: wait for stage\n", interp));
1529 pt_gc_wait_for_stage(interp, THREAD_GC_STAGE_NONE, THREAD_GC_STAGE_MARK);
1531 DEBUG_ONLY(fprintf(stderr, "actually mark\n"));
1533 * We can't allow parallel running GCs.
1535 LOCK(interpreter_array_mutex);
1536 DEBUG_ONLY(fprintf(stderr, "got marking lock\n"));
1541 =item C<void pt_gc_mark_root_finished(PARROT_INTERP)>
1543 Records that GC has finished for the root set. EXCEPTION_UNIMPLEMENTED
1545 =cut
1549 void
1550 pt_gc_mark_root_finished(PARROT_INTERP)
1552 ASSERT_ARGS(pt_gc_mark_root_finished)
1553 if (!running_threads)
1554 return;
1556 * TODO now check, if we are the owner of a shared memory pool
1557 * if yes:
1558 * - now mark all members of our pool
1559 * - if all shared PMCs are marked by all threads then
1560 * - we can continue to free unused objects
1566 =item C<void pt_gc_stop_mark(PARROT_INTERP)>
1568 Records that the mark phase of GC has completed.
1570 =cut
1574 void
1575 pt_gc_stop_mark(PARROT_INTERP)
1577 ASSERT_ARGS(pt_gc_stop_mark)
1578 if (!running_threads)
1579 return;
1581 * normal operation can continue now
1582 * - other threads may or not free unused objects then,
1583 * depending on their resource statistics
1585 if (!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC)) {
1586 UNLOCK(interpreter_array_mutex);
1587 return;
1590 PARROT_ASSERT(!(interp->thread_data->state &
1591 THREAD_STATE_SUSPEND_GC_REQUESTED));
1592 interp->thread_data->state &= ~THREAD_STATE_SUSPENDED_GC;
1594 while (!PMC_IS_NULL(Parrot_cx_delete_suspend_for_gc(interp))) {
1595 /* XXX FIXME make this message never trigger */
1596 fprintf(stderr, "%p: extraneous suspend_gc event\n", (void *)interp);
1599 DEBUG_ONLY(fprintf(stderr, "%p: unlock\n", interp));
1600 UNLOCK(interpreter_array_mutex);
1601 DEBUG_ONLY(fprintf(stderr, "wait to sweep\n"));
1603 pt_gc_wait_for_stage(interp, THREAD_GC_STAGE_MARK, THREAD_GC_STAGE_SWEEP);
1608 =item C<void Parrot_shared_gc_block(PARROT_INTERP)>
1610 Blocks stop-the-world GC runs.
1612 =cut
1616 PARROT_EXPORT
1617 void
1618 Parrot_shared_gc_block(PARROT_INTERP)
1620 ASSERT_ARGS(Parrot_shared_gc_block)
1621 Shared_gc_info * const info = get_pool();
1623 if (info) {
1624 int level;
1625 PARROT_ATOMIC_INT_INC(level, info->gc_block_level);
1626 PARROT_ASSERT(level > 0);
1632 =item C<void Parrot_shared_gc_unblock(PARROT_INTERP)>
1634 Unblocks stop-the-world GC runs.
1636 =cut
1640 PARROT_EXPORT
1641 void
1642 Parrot_shared_gc_unblock(PARROT_INTERP)
1644 ASSERT_ARGS(Parrot_shared_gc_unblock)
1645 Shared_gc_info * const info = get_pool();
1646 if (info) {
1647 int level;
1648 PARROT_ATOMIC_INT_DEC(level, info->gc_block_level);
1649 PARROT_ASSERT(level >= 0);
1654 * Local variables:
1655 * c-file-style: "parrot"
1656 * End:
1657 * vim: expandtab shiftwidth=4: