Release 2.5.0
[parrot.git] / src / thread.c
blob030eb2627f119263214b0ceee576d4f0e0b81901
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/atomic.h"
23 #include "parrot/runcore_api.h"
24 #include "pmc/pmc_sub.h"
25 #include "pmc/pmc_parrotinterpreter.h"
27 /* HEADERIZER HFILE: include/parrot/thread.h */
29 /* HEADERIZER BEGIN: static */
30 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
32 static Parrot_Interp detach(UINTVAL tid);
33 PARROT_CAN_RETURN_NULL
34 static Shared_gc_info * get_pool(void);
36 PARROT_WARN_UNUSED_RESULT
37 static int is_suspended_for_gc(PARROT_INTERP)
38 __attribute__nonnull__(1);
40 PARROT_CAN_RETURN_NULL
41 static PMC * make_local_args_copy(PARROT_INTERP,
42 ARGIN(Parrot_Interp old_interp),
43 ARGIN_NULLOK(PMC *args))
44 __attribute__nonnull__(1)
45 __attribute__nonnull__(2);
47 PARROT_CAN_RETURN_NULL
48 static PMC * make_local_copy(PARROT_INTERP,
49 ARGIN(Parrot_Interp from),
50 ARGIN(PMC *arg))
51 __attribute__nonnull__(1)
52 __attribute__nonnull__(2)
53 __attribute__nonnull__(3);
55 static void mutex_unlock(ARGMOD(void *arg))
56 __attribute__nonnull__(1)
57 FUNC_MODIFIES(*arg);
59 static Parrot_Interp pt_check_tid(UINTVAL tid, ARGIN(const char *from))
60 __attribute__nonnull__(2);
62 static int pt_gc_count_threads(void);
63 static void pt_gc_wait_for_stage(PARROT_INTERP,
64 thread_gc_stage_enum from_stage,
65 thread_gc_stage_enum to_stage)
66 __attribute__nonnull__(1);
68 static void pt_gc_wakeup_check(void);
69 static void pt_ns_clone(PARROT_INTERP,
70 ARGOUT(Parrot_Interp d),
71 ARGOUT(PMC *dest_ns),
72 ARGIN(Parrot_Interp s),
73 ARGIN(PMC *source_ns))
74 __attribute__nonnull__(1)
75 __attribute__nonnull__(2)
76 __attribute__nonnull__(3)
77 __attribute__nonnull__(4)
78 __attribute__nonnull__(5)
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(ARGIN(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 void* thread_func(ARGIN_NULLOK(void *arg));
98 #define ASSERT_ARGS_detach __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
99 #define ASSERT_ARGS_get_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
100 #define ASSERT_ARGS_is_suspended_for_gc __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
101 PARROT_ASSERT_ARG(interp))
102 #define ASSERT_ARGS_make_local_args_copy __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
103 PARROT_ASSERT_ARG(interp) \
104 , PARROT_ASSERT_ARG(old_interp))
105 #define ASSERT_ARGS_make_local_copy __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
106 PARROT_ASSERT_ARG(interp) \
107 , PARROT_ASSERT_ARG(from) \
108 , PARROT_ASSERT_ARG(arg))
109 #define ASSERT_ARGS_mutex_unlock __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
110 PARROT_ASSERT_ARG(arg))
111 #define ASSERT_ARGS_pt_check_tid __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
112 PARROT_ASSERT_ARG(from))
113 #define ASSERT_ARGS_pt_gc_count_threads __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
114 #define ASSERT_ARGS_pt_gc_wait_for_stage __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
115 PARROT_ASSERT_ARG(interp))
116 #define ASSERT_ARGS_pt_gc_wakeup_check __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
117 #define ASSERT_ARGS_pt_ns_clone __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
118 PARROT_ASSERT_ARG(interp) \
119 , PARROT_ASSERT_ARG(d) \
120 , PARROT_ASSERT_ARG(dest_ns) \
121 , PARROT_ASSERT_ARG(s) \
122 , PARROT_ASSERT_ARG(source_ns))
123 #define ASSERT_ARGS_pt_suspend_all_for_gc __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
124 PARROT_ASSERT_ARG(interp))
125 #define ASSERT_ARGS_pt_suspend_one_for_gc __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
126 PARROT_ASSERT_ARG(interp))
127 #define ASSERT_ARGS_pt_thread_signal __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
128 PARROT_ASSERT_ARG(self) \
129 , PARROT_ASSERT_ARG(interp))
130 #define ASSERT_ARGS_pt_thread_wait __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
131 PARROT_ASSERT_ARG(interp))
132 #define ASSERT_ARGS_thread_func __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
133 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
134 /* HEADERIZER END: static */
136 #if defined THREAD_DEBUG && THREAD_DEBUG
137 # define DEBUG_ONLY(x) (x)
138 #else
139 # define DEBUG_ONLY(x)
140 #endif
142 static int running_threads;
144 void Parrot_really_destroy(PARROT_INTERP, int exit_code, void *arg);
148 =item C<static PMC * make_local_copy(PARROT_INTERP, Parrot_Interp from, PMC
149 *arg)>
151 Creates a local copy of the PMC if necessary. (No copy is made if it is marked
152 shared.) This includes workarounds for Parrot_clone() not doing the Right Thing
153 with subroutines (specifically, code segments aren't preserved and it is
154 difficult to do so as long as Parrot_clone() depends on freezing).
156 =cut
160 PARROT_CAN_RETURN_NULL
161 static PMC *
162 make_local_copy(PARROT_INTERP, ARGIN(Parrot_Interp from), ARGIN(PMC *arg))
164 ASSERT_ARGS(make_local_copy)
165 PMC *ret_val;
166 STRING * const _sub = interp->vtables[enum_class_Sub]->whoami;
167 STRING * const _multi_sub = interp->vtables[enum_class_MultiSub]->whoami;
169 if (PMC_IS_NULL(arg)) {
170 ret_val = PMCNULL;
172 else if (PObj_is_PMC_shared_TEST(arg)) {
173 ret_val = arg;
175 else if (VTABLE_isa(from, arg, _multi_sub)) {
176 INTVAL i = 0;
177 const INTVAL n = VTABLE_elements(from, arg);
178 ret_val = Parrot_pmc_new(interp, enum_class_MultiSub);
180 for (i = 0; i < n; ++i) {
181 PMC *const orig = VTABLE_get_pmc_keyed_int(from, arg, i);
182 PMC *const copy = make_local_copy(interp, from, orig);
183 VTABLE_push_pmc(interp, ret_val, copy);
186 else if (VTABLE_isa(from, arg, _sub)) {
187 /* this is a workaround for cloning subroutines not actually
188 * working as one might expect mainly because the segment is
189 * not correctly copied
191 Parrot_Sub_attributes *ret_val_sub, *arg_sub;
193 ret_val = Parrot_clone(interp, arg);
194 PMC_get_sub(interp, ret_val, ret_val_sub);
195 PMC_get_sub(interp, arg, arg_sub);
196 ret_val_sub->seg = arg_sub->seg;
197 /* Skip vtable overrides and methods. */
198 if (ret_val_sub->vtable_index == -1
199 && !(ret_val_sub->comp_flags & SUB_COMP_FLAG_METHOD)) {
200 Parrot_store_sub_in_namespace(interp, ret_val);
203 else {
204 ret_val = Parrot_clone(interp, arg);
206 return ret_val;
211 =item C<static Shared_gc_info * get_pool(void)>
213 Gets the shared gc information. For now this is global data; ideally it will
214 become something other than a static variable. If everything uses this
215 function, it will be easier to change.
217 =cut
221 PARROT_CAN_RETURN_NULL
222 static Shared_gc_info *
223 get_pool(void)
225 ASSERT_ARGS(get_pool)
226 return shared_gc_info;
231 =item C<void pt_free_pool(PARROT_INTERP)>
233 Frees the shared GC information. This clears any global data when joining all
234 threads at parent interpreter destruction.
236 =cut
240 void
241 pt_free_pool(PARROT_INTERP)
243 ASSERT_ARGS(pt_free_pool)
244 if (shared_gc_info) {
245 COND_DESTROY(shared_gc_info->gc_cond);
246 PARROT_ATOMIC_INT_DESTROY(shared_gc_info->gc_block_level);
247 mem_internal_free(shared_gc_info);
248 shared_gc_info = NULL;
254 =item C<static PMC * make_local_args_copy(PARROT_INTERP, Parrot_Interp
255 old_interp, PMC *args)>
257 Make a local copy of the corresponding array of arguments.
259 =cut
263 PARROT_CAN_RETURN_NULL
264 static PMC *
265 make_local_args_copy(PARROT_INTERP, ARGIN(Parrot_Interp old_interp), ARGIN_NULLOK(PMC *args))
267 ASSERT_ARGS(make_local_args_copy)
268 PMC *ret_val;
269 INTVAL old_size;
270 INTVAL i;
272 if (PMC_IS_NULL(args))
273 return PMCNULL;
275 old_size = VTABLE_get_integer(old_interp, args);
277 /* XXX should this be a different type? */
278 ret_val = Parrot_pmc_new(interp, enum_class_FixedPMCArray);
279 VTABLE_set_integer_native(interp, ret_val, old_size);
281 for (i = 0; i < old_size; ++i) {
282 PMC * const copy = make_local_copy(interp, old_interp,
283 VTABLE_get_pmc_keyed_int(old_interp, args, i));
285 VTABLE_set_pmc_keyed_int(interp, ret_val, i, copy);
288 return ret_val;
293 =item C<PMC * pt_shared_fixup(PARROT_INTERP, PMC *pmc)>
295 Modifies a PMC to be sharable. Right now, reassigns the vtable to one
296 owned by some master interpreter, so the PMC can be safely reused
297 after thread death.
299 In the future the PMC returned might be different than the one
300 passed, e.g., if we need to reallocate the PMC in a different
301 interpreter.
303 =cut
307 PARROT_CAN_RETURN_NULL
308 PMC *
309 pt_shared_fixup(PARROT_INTERP, ARGMOD(PMC *pmc))
311 ASSERT_ARGS(pt_shared_fixup)
312 /* TODO this will need to change for thread pools
313 * XXX should we have a separate interpreter for this?
315 INTVAL type_num;
316 Parrot_Interp master = interpreter_array[0];
317 const int is_ro = pmc->vtable->flags & VTABLE_IS_READONLY_FLAG;
319 /* This lock is paired with one in objects.c. It is necessary to protect
320 * against the master interpreter adding classes and consequently
321 * resizing its classname->type_id hashtable and/or expanding its vtable
322 * array.
323 * TODO investigate if a read-write lock results in substantially
324 * better performance.
326 LOCK_INTERPRETER(master);
327 type_num = pmc->vtable->base_type;
329 if (type_num == enum_type_undef) {
330 UNLOCK_INTERPRETER(master);
331 Parrot_ex_throw_from_c_args(interp, NULL, 1,
332 "pt_shared_fixup: unsharable type");
335 pmc->vtable = master->vtables[type_num];
337 UNLOCK_INTERPRETER(master);
339 if (is_ro)
340 pmc->vtable = pmc->vtable->ro_variant_vtable;
342 PObj_is_PMC_shared_SET(pmc);
344 /* make sure metadata doesn't go away unexpectedly */
345 if (PMC_metadata(pmc))
346 PMC_metadata(pmc) = pt_shared_fixup(interp, PMC_metadata(pmc));
348 return pmc;
353 =item C<static void pt_thread_signal(Parrot_Interp self, PARROT_INTERP)>
355 Wakes up an C<interp> which should have called pt_thread_wait().
357 =cut
361 static void
362 pt_thread_signal(ARGIN(Parrot_Interp self), PARROT_INTERP)
364 ASSERT_ARGS(pt_thread_signal)
365 COND_SIGNAL(interp->thread_data->interp_cond);
370 =item C<void pt_thread_wait_with(PARROT_INTERP, Parrot_mutex *mutex)>
372 Waits for this interpreter to be signalled through its condition variable,
373 dealing properly with GC issues. C<*mutex> is assumed locked on entry and
374 will be locked on exit from this function. If a GC run occurs in the middle of
375 this function, then a spurious wakeup may occur.
377 =cut
381 void
382 pt_thread_wait_with(PARROT_INTERP, ARGMOD(Parrot_mutex *mutex))
384 ASSERT_ARGS(pt_thread_wait_with)
385 LOCK(interpreter_array_mutex);
386 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
387 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
388 UNLOCK(interpreter_array_mutex);
389 UNLOCK(*mutex);
391 pt_suspend_self_for_gc(interp);
393 LOCK(*mutex);
394 /* since we unlocked the mutex something bad may have occured */
395 return;
398 interp->thread_data->state |= THREAD_STATE_GC_WAKEUP;
400 UNLOCK(interpreter_array_mutex);
401 COND_WAIT(interp->thread_data->interp_cond, *mutex);
402 LOCK(interpreter_array_mutex);
404 interp->thread_data->state &= ~THREAD_STATE_GC_WAKEUP;
406 if (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
407 UNLOCK(*mutex);
408 /* XXX loop needed? */
409 do {
410 UNLOCK(interpreter_array_mutex);
411 /* fprintf(stderr, "%p: woken up, doing GC run\n", interp); */
412 pt_suspend_self_for_gc(interp);
413 LOCK(interpreter_array_mutex);
414 } while (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC);
416 UNLOCK(interpreter_array_mutex);
417 LOCK(*mutex);
419 else {
420 UNLOCK(interpreter_array_mutex);
426 =item C<static void pt_thread_wait(PARROT_INTERP)>
428 Waits for a signal, handling GC matters correctly. C<interpreter_array_mutex>
429 is assumed held. Spurious wakeups may occur.
431 =cut
435 static void
436 pt_thread_wait(PARROT_INTERP)
438 ASSERT_ARGS(pt_thread_wait)
439 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
440 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
441 /* fprintf(stderr, "%p: pt_thread_wait, before sleep, doing GC run\n",
442 * interp); */
444 UNLOCK(interpreter_array_mutex);
445 pt_suspend_self_for_gc(interp);
446 LOCK(interpreter_array_mutex);
448 /* while we were GCing, whatever we were waiting on might have
449 * changed */
450 return;
453 interp->thread_data->state |= THREAD_STATE_GC_WAKEUP;
455 COND_WAIT(interp->thread_data->interp_cond, interpreter_array_mutex);
457 interp->thread_data->state &= ~THREAD_STATE_GC_WAKEUP;
459 while (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
460 UNLOCK(interpreter_array_mutex);
461 /* fprintf(stderr, "%p: woken up, doing GC run\n", interp); */
462 pt_suspend_self_for_gc(interp);
463 LOCK(interpreter_array_mutex);
470 =item C<static void* thread_func(void *arg)>
472 The actual thread function.
474 =cut
478 PARROT_CAN_RETURN_NULL
479 static void*
480 thread_func(ARGIN_NULLOK(void *arg))
482 ASSERT_ARGS(thread_func)
483 Parrot_runloop jump_point;
484 int lo_var_ptr;
485 UINTVAL tid;
486 PMC * volatile sub_pmc;
487 PMC *sub_arg;
488 PMC * const self = (PMC*) arg;
489 PMC *ret_val = PMCNULL;
490 Parrot_Interp interp =
491 (Parrot_Interp)((Parrot_ParrotInterpreter_attributes *)PMC_data(self))->interp;
493 Parrot_block_GC_mark(interp);
494 Parrot_block_GC_sweep(interp);
496 /* need to set it here because argument passing can trigger GC */
497 interp->lo_var_ptr = &lo_var_ptr;
498 GETATTR_ParrotInterpreter_sub(interp, self, sub_pmc);
499 sub_arg = VTABLE_get_pmc(interp, self);
501 if (setjmp(jump_point.resume)) {
502 /* caught exception */
503 /* XXX what should we really do here */
504 /* PMC *exception = Parrot_cx_peek_task(interp);
505 Parrot_io_eprintf(interp,
506 "Unhandled exception in thread with tid %d "
507 "(message=%Ss, number=%d)\n",
508 interp->thread_data->tid,
509 VTABLE_get_string(interp, exception),
510 VTABLE_get_integer_keyed_str(interp, exception,
511 Parrot_str_new_constant(interp, "type"))); */
513 else {
514 /* run normally */
515 Parrot_ex_add_c_handler(interp, &jump_point);
516 Parrot_unblock_GC_mark(interp);
517 Parrot_unblock_GC_sweep(interp);
518 Parrot_pcc_invoke_sub_from_c_args(interp, sub_pmc, "Pf->P", sub_arg, &ret_val);
521 /* thread is finito */
522 LOCK(interpreter_array_mutex);
523 DEBUG_ONLY(fprintf(stderr, "marking an thread as finished\n"));
525 interp->thread_data->state |= THREAD_STATE_FINISHED;
526 tid = interp->thread_data->tid;
528 if (interp != interpreter_array[tid]) {
529 UNLOCK(interpreter_array_mutex);
530 PANIC(interp, "thread finished: interpreter mismatch");
532 if (interp->thread_data->state & THREAD_STATE_DETACHED) {
533 interpreter_array[tid] = NULL;
534 DEBUG_ONLY(fprintf(stderr,
535 "really destroying an interpreter [exit while detached]\n"));
536 Parrot_really_destroy(interp, 0, NULL);
538 else if (interp->thread_data->state & THREAD_STATE_JOINED) {
539 pt_thread_signal(interp, interp->thread_data->joiner);
542 /* make sure we don't block a GC run */
543 pt_gc_wakeup_check();
544 PARROT_ASSERT(interp->thread_data->state & THREAD_STATE_FINISHED);
546 UNLOCK(interpreter_array_mutex);
548 return ret_val;
553 =back
555 =head2 Helper functions used also for running plain interpreters
557 =over 4
559 =item C<void pt_clone_code(Parrot_Interp d, Parrot_Interp s)>
561 Copies/clones the packfile/code from interpreter C<s> to C<d>. All
562 resources are created in C<d>.
564 =cut
568 void
569 pt_clone_code(Parrot_Interp d, Parrot_Interp s)
571 ASSERT_ARGS(pt_clone_code)
572 Parrot_block_GC_mark(d);
573 Interp_flags_SET(d, PARROT_EXTERN_CODE_FLAG);
574 d->code = NULL;
575 Parrot_switch_to_cs(d, s->code, 1);
576 Parrot_unblock_GC_mark(d);
581 =item C<static void pt_ns_clone(PARROT_INTERP, Parrot_Interp d, PMC *dest_ns,
582 Parrot_Interp s, PMC *source_ns)>
584 Clones all globals from C<s> to C<d>.
586 =cut
590 static void
591 pt_ns_clone(PARROT_INTERP, ARGOUT(Parrot_Interp d), ARGOUT(PMC *dest_ns),
592 ARGIN(Parrot_Interp s), ARGIN(PMC *source_ns))
594 ASSERT_ARGS(pt_ns_clone)
595 PMC * const iter = VTABLE_get_iter(s, source_ns);
596 const INTVAL n = VTABLE_elements(s, source_ns);
597 INTVAL i;
599 for (i = 0; i < n; ++i) {
600 /* XXX what if 'key' is a non-constant-pool string? */
601 STRING * const key = VTABLE_shift_string(s, iter);
602 PMC * const val = VTABLE_get_pmc_keyed_str(s, source_ns, key);
604 if (val->vtable->base_type == enum_class_NameSpace) {
605 PMC *sub_ns = VTABLE_get_pmc_keyed_str(d, dest_ns, key);
606 if (PMC_IS_NULL(sub_ns) || sub_ns->vtable->base_type !=
607 enum_class_NameSpace) {
608 sub_ns = Parrot_pmc_new(d, enum_class_NameSpace);
609 VTABLE_set_pmc_keyed_str(d, dest_ns, key, sub_ns);
611 pt_ns_clone(s, d, sub_ns, s, val);
613 else {
614 PMC * const dval = VTABLE_get_pmc_keyed_str(d, dest_ns, key);
616 if (PMC_IS_NULL(dval)) {
617 PMC * const copy = make_local_copy(d, s, val);
618 Parrot_Sub_attributes *val_sub;
620 if (val->vtable->base_type == enum_class_Sub)
621 PMC_get_sub(interp, val, val_sub);
623 /* Vtable overrides and methods were already cloned, so don't reclone them. */
624 if (! (val->vtable->base_type == enum_class_Sub
625 && (val_sub->vtable_index != -1
626 || val_sub->comp_flags & SUB_COMP_FLAG_METHOD))) {
627 VTABLE_set_pmc_keyed_str(d, dest_ns, key, copy);
636 =item C<void pt_clone_globals(Parrot_Interp d, Parrot_Interp s)>
638 Copies the global namespace when cloning a new interpreter.
640 =cut
644 void
645 pt_clone_globals(Parrot_Interp d, Parrot_Interp s)
647 ASSERT_ARGS(pt_clone_globals)
648 Parrot_block_GC_mark(d);
649 pt_ns_clone(s, d, d->root_namespace, s, s->root_namespace);
650 Parrot_unblock_GC_mark(d);
655 =item C<void pt_thread_prepare_for_run(Parrot_Interp d, Parrot_Interp s)>
657 Sets up a new thread to run.
659 =cut
663 void
664 pt_thread_prepare_for_run(Parrot_Interp d, SHIM(Parrot_Interp s))
666 ASSERT_ARGS(pt_thread_prepare_for_run)
667 Parrot_setup_event_func_ptrs(d);
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: