[TT #871] Add rand as a dynop, with tests
[parrot.git] / src / thread.c
blobb143a3041a7b7875223ca6a91cf84708a3885344
1 /*
2 Copyright (C) 2001-2009, 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 "pmc/pmc_sub.h"
24 #include "pmc/pmc_parrotinterpreter.h"
26 /* HEADERIZER HFILE: include/parrot/thread.h */
28 /* HEADERIZER BEGIN: static */
29 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
31 static Parrot_Interp detach(UINTVAL tid);
32 PARROT_CAN_RETURN_NULL
33 static Shared_gc_info * get_pool(PARROT_INTERP)
34 __attribute__nonnull__(1);
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(PARROT_INTERP)
63 __attribute__nonnull__(1);
65 static void pt_gc_wait_for_stage(PARROT_INTERP,
66 thread_gc_stage_enum from_stage,
67 thread_gc_stage_enum to_stage)
68 __attribute__nonnull__(1);
70 static void pt_gc_wakeup_check(PARROT_INTERP)
71 __attribute__nonnull__(1);
73 static void pt_ns_clone(PARROT_INTERP,
74 ARGOUT(Parrot_Interp d),
75 ARGOUT(PMC *dest_ns),
76 ARGIN(Parrot_Interp s),
77 ARGIN(PMC *source_ns))
78 __attribute__nonnull__(1)
79 __attribute__nonnull__(2)
80 __attribute__nonnull__(3)
81 __attribute__nonnull__(4)
82 __attribute__nonnull__(5)
83 FUNC_MODIFIES(d)
84 FUNC_MODIFIES(*dest_ns);
86 static void pt_suspend_all_for_gc(PARROT_INTERP)
87 __attribute__nonnull__(1);
89 static void pt_suspend_one_for_gc(PARROT_INTERP)
90 __attribute__nonnull__(1);
92 static void pt_thread_signal(NOTNULL(Parrot_Interp self), PARROT_INTERP)
93 __attribute__nonnull__(1)
94 __attribute__nonnull__(2);
96 static void pt_thread_wait(PARROT_INTERP)
97 __attribute__nonnull__(1);
99 PARROT_CAN_RETURN_NULL
100 static QUEUE_ENTRY * remove_queued_suspend_gc(PARROT_INTERP)
101 __attribute__nonnull__(1);
103 PARROT_CAN_RETURN_NULL
104 static void* thread_func(ARGIN_NULLOK(void *arg));
106 #define ASSERT_ARGS_detach __attribute__unused__ int _ASSERT_ARGS_CHECK = 0
107 #define ASSERT_ARGS_get_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = \
108 PARROT_ASSERT_ARG(interp)
109 #define ASSERT_ARGS_is_suspended_for_gc __attribute__unused__ int _ASSERT_ARGS_CHECK = \
110 PARROT_ASSERT_ARG(interp)
111 #define ASSERT_ARGS_make_local_args_copy __attribute__unused__ int _ASSERT_ARGS_CHECK = \
112 PARROT_ASSERT_ARG(interp) \
113 || PARROT_ASSERT_ARG(old_interp)
114 #define ASSERT_ARGS_make_local_copy __attribute__unused__ int _ASSERT_ARGS_CHECK = \
115 PARROT_ASSERT_ARG(interp) \
116 || PARROT_ASSERT_ARG(from) \
117 || PARROT_ASSERT_ARG(arg)
118 #define ASSERT_ARGS_mutex_unlock __attribute__unused__ int _ASSERT_ARGS_CHECK = \
119 PARROT_ASSERT_ARG(arg)
120 #define ASSERT_ARGS_pt_check_tid __attribute__unused__ int _ASSERT_ARGS_CHECK = \
121 PARROT_ASSERT_ARG(from)
122 #define ASSERT_ARGS_pt_gc_count_threads __attribute__unused__ int _ASSERT_ARGS_CHECK = \
123 PARROT_ASSERT_ARG(interp)
124 #define ASSERT_ARGS_pt_gc_wait_for_stage __attribute__unused__ int _ASSERT_ARGS_CHECK = \
125 PARROT_ASSERT_ARG(interp)
126 #define ASSERT_ARGS_pt_gc_wakeup_check __attribute__unused__ int _ASSERT_ARGS_CHECK = \
127 PARROT_ASSERT_ARG(interp)
128 #define ASSERT_ARGS_pt_ns_clone __attribute__unused__ int _ASSERT_ARGS_CHECK = \
129 PARROT_ASSERT_ARG(interp) \
130 || PARROT_ASSERT_ARG(d) \
131 || PARROT_ASSERT_ARG(dest_ns) \
132 || PARROT_ASSERT_ARG(s) \
133 || PARROT_ASSERT_ARG(source_ns)
134 #define ASSERT_ARGS_pt_suspend_all_for_gc __attribute__unused__ int _ASSERT_ARGS_CHECK = \
135 PARROT_ASSERT_ARG(interp)
136 #define ASSERT_ARGS_pt_suspend_one_for_gc __attribute__unused__ int _ASSERT_ARGS_CHECK = \
137 PARROT_ASSERT_ARG(interp)
138 #define ASSERT_ARGS_pt_thread_signal __attribute__unused__ int _ASSERT_ARGS_CHECK = \
139 PARROT_ASSERT_ARG(self) \
140 || PARROT_ASSERT_ARG(interp)
141 #define ASSERT_ARGS_pt_thread_wait __attribute__unused__ int _ASSERT_ARGS_CHECK = \
142 PARROT_ASSERT_ARG(interp)
143 #define ASSERT_ARGS_remove_queued_suspend_gc __attribute__unused__ int _ASSERT_ARGS_CHECK = \
144 PARROT_ASSERT_ARG(interp)
145 #define ASSERT_ARGS_thread_func __attribute__unused__ int _ASSERT_ARGS_CHECK = 0
146 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
147 /* HEADERIZER END: static */
149 #if defined THREAD_DEBUG && THREAD_DEBUG
150 # define DEBUG_ONLY(x) (x)
151 #else
152 # define DEBUG_ONLY(x)
153 #endif
155 static int running_threads;
157 void Parrot_really_destroy(PARROT_INTERP, int exit_code, void *arg);
161 =item C<static PMC * make_local_copy(PARROT_INTERP, Parrot_Interp from, PMC
162 *arg)>
164 Creates a local copy of the PMC if necessary. (No copy is made if it is marked
165 shared.) This includes workarounds for Parrot_clone() not doing the Right Thing
166 with subroutines (specifically, code segments aren't preserved and it is
167 difficult to do so as long as Parrot_clone() depends on freezing).
169 =cut
173 PARROT_CAN_RETURN_NULL
174 static PMC *
175 make_local_copy(PARROT_INTERP, ARGIN(Parrot_Interp from), ARGIN(PMC *arg))
177 ASSERT_ARGS(make_local_copy)
178 PMC *ret_val;
179 STRING * const _sub = interp->vtables[enum_class_Sub]->whoami;
180 STRING * const _multi_sub = interp->vtables[enum_class_MultiSub]->whoami;
182 if (PMC_IS_NULL(arg)) {
183 ret_val = PMCNULL;
185 else if (PObj_is_PMC_shared_TEST(arg)) {
186 ret_val = arg;
188 else if (VTABLE_isa(from, arg, _multi_sub)) {
189 INTVAL i = 0;
190 const INTVAL n = VTABLE_elements(from, arg);
191 ret_val = pmc_new(interp, enum_class_MultiSub);
193 for (i = 0; i < n; ++i) {
194 PMC *const orig = VTABLE_get_pmc_keyed_int(from, arg, i);
195 PMC *const copy = make_local_copy(interp, from, orig);
196 VTABLE_push_pmc(interp, ret_val, copy);
199 else if (VTABLE_isa(from, arg, _sub)) {
200 /* this is a workaround for cloning subroutines not actually
201 * working as one might expect mainly because the segment is
202 * not correctly copied
204 Parrot_sub *ret_val_sub, *arg_sub;
206 ret_val = Parrot_clone(interp, arg);
207 PMC_get_sub(interp, ret_val, ret_val_sub);
208 PMC_get_sub(interp, arg, arg_sub);
209 ret_val_sub->seg = arg_sub->seg;
210 /* Skip vtable overrides and methods. */
211 if (ret_val_sub->vtable_index == -1
212 && !(ret_val_sub->comp_flags & SUB_COMP_FLAG_METHOD)) {
213 Parrot_store_sub_in_namespace(interp, ret_val);
216 else {
217 ret_val = Parrot_clone(interp, arg);
219 return ret_val;
224 =item C<static Shared_gc_info * get_pool(PARROT_INTERP)>
226 Gets the shared gc information. For now this is global data; ideally it will
227 become something other than a static variable. If everything uses this
228 function, it will be easier to change.
230 =cut
234 PARROT_CAN_RETURN_NULL
235 static Shared_gc_info *
236 get_pool(PARROT_INTERP)
238 ASSERT_ARGS(get_pool)
239 return shared_gc_info;
244 =item C<void pt_free_pool(PARROT_INTERP)>
246 Frees the shared GC information. This clears any global data when joining all
247 threads at parent interpreter destruction.
249 =cut
253 void
254 pt_free_pool(PARROT_INTERP)
256 ASSERT_ARGS(pt_free_pool)
257 if (shared_gc_info) {
258 COND_DESTROY(shared_gc_info->gc_cond);
259 PARROT_ATOMIC_INT_DESTROY(shared_gc_info->gc_block_level);
260 mem_sys_free(shared_gc_info);
261 shared_gc_info = NULL;
267 =item C<static PMC * make_local_args_copy(PARROT_INTERP, Parrot_Interp
268 old_interp, PMC *args)>
270 Make a local copy of the corresponding array of arguments.
272 =cut
276 PARROT_CAN_RETURN_NULL
277 static PMC *
278 make_local_args_copy(PARROT_INTERP, ARGIN(Parrot_Interp old_interp), ARGIN_NULLOK(PMC *args))
280 ASSERT_ARGS(make_local_args_copy)
281 PMC *ret_val;
282 INTVAL old_size;
283 INTVAL i;
285 if (PMC_IS_NULL(args))
286 return PMCNULL;
288 old_size = VTABLE_get_integer(old_interp, args);
290 /* XXX should this be a different type? */
291 ret_val = pmc_new(interp, enum_class_FixedPMCArray);
292 VTABLE_set_integer_native(interp, ret_val, old_size);
294 for (i = 0; i < old_size; ++i) {
295 PMC * const copy = make_local_copy(interp, old_interp,
296 VTABLE_get_pmc_keyed_int(old_interp, args, i));
298 VTABLE_set_pmc_keyed_int(interp, ret_val, i, copy);
301 return ret_val;
306 =item C<PMC * pt_shared_fixup(PARROT_INTERP, PMC *pmc)>
308 Modifies a PMC to be sharable. Right now, reassigns the vtable to one
309 owned by some master interpreter, so the PMC can be safely reused
310 after thread death.
312 In the future the PMC returned might be different than the one
313 passed, e.g., if we need to reallocate the PMC in a different
314 interpreter.
316 =cut
320 PARROT_CAN_RETURN_NULL
321 PMC *
322 pt_shared_fixup(PARROT_INTERP, ARGMOD(PMC *pmc))
324 ASSERT_ARGS(pt_shared_fixup)
325 /* TODO this will need to change for thread pools
326 * XXX should we have a separate interpreter for this?
328 INTVAL type_num;
329 Parrot_Interp master = interpreter_array[0];
330 const int is_ro = pmc->vtable->flags & VTABLE_IS_READONLY_FLAG;
332 /* This lock is paired with one in objects.c. It is necessary to protect
333 * against the master interpreter adding classes and consequently
334 * resizing its classname->type_id hashtable and/or expanding its vtable
335 * array.
336 * TODO investigate if a read-write lock results in substantially
337 * better performance.
339 LOCK_INTERPRETER(master);
340 type_num = pmc->vtable->base_type;
342 if (type_num == enum_type_undef) {
343 UNLOCK_INTERPRETER(master);
344 Parrot_ex_throw_from_c_args(interp, NULL, 1,
345 "pt_shared_fixup: unsharable type");
348 pmc->vtable = master->vtables[type_num];
350 UNLOCK_INTERPRETER(master);
352 if (is_ro)
353 pmc->vtable = pmc->vtable->ro_variant_vtable;
355 Parrot_gc_add_pmc_sync(interp, pmc);
357 PObj_is_PMC_shared_SET(pmc);
359 /* make sure metadata doesn't go away unexpectedly */
360 if (PMC_metadata(pmc))
361 PMC_metadata(pmc) = pt_shared_fixup(interp, PMC_metadata(pmc));
363 return pmc;
368 =item C<static void pt_thread_signal(Parrot_Interp self, PARROT_INTERP)>
370 Wakes up an C<interp> which should have called pt_thread_wait().
372 =cut
376 static void
377 pt_thread_signal(NOTNULL(Parrot_Interp self), PARROT_INTERP)
379 ASSERT_ARGS(pt_thread_signal)
380 COND_SIGNAL(interp->thread_data->interp_cond);
385 =item C<void pt_thread_wait_with(PARROT_INTERP, Parrot_mutex *mutex)>
387 Waits for this interpreter to be signalled through its condition variable,
388 dealing properly with GC issues. C<*mutex> is assumed locked on entry and
389 will be locked on exit from this function. If a GC run occurs in the middle of
390 this function, then a spurious wakeup may occur.
392 =cut
396 void
397 pt_thread_wait_with(PARROT_INTERP, ARGMOD(Parrot_mutex *mutex))
399 ASSERT_ARGS(pt_thread_wait_with)
400 LOCK(interpreter_array_mutex);
401 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
402 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
403 UNLOCK(interpreter_array_mutex);
404 UNLOCK(*mutex);
406 pt_suspend_self_for_gc(interp);
408 LOCK(*mutex);
409 /* since we unlocked the mutex something bad may have occured */
410 return;
413 interp->thread_data->state |= THREAD_STATE_GC_WAKEUP;
415 UNLOCK(interpreter_array_mutex);
416 COND_WAIT(interp->thread_data->interp_cond, *mutex);
417 LOCK(interpreter_array_mutex);
419 interp->thread_data->state &= ~THREAD_STATE_GC_WAKEUP;
421 if (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
422 UNLOCK(*mutex);
423 /* XXX loop needed? */
424 do {
425 UNLOCK(interpreter_array_mutex);
426 /* fprintf(stderr, "%p: woken up, doing GC run\n", interp); */
427 pt_suspend_self_for_gc(interp);
428 LOCK(interpreter_array_mutex);
429 } while (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC);
431 UNLOCK(interpreter_array_mutex);
432 LOCK(*mutex);
434 else {
435 UNLOCK(interpreter_array_mutex);
441 =item C<static void pt_thread_wait(PARROT_INTERP)>
443 Waits for a signal, handling GC matters correctly. C<interpreter_array_mutex>
444 is assumed held. Spurious wakeups may occur.
446 =cut
450 static void
451 pt_thread_wait(PARROT_INTERP)
453 ASSERT_ARGS(pt_thread_wait)
454 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
455 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
456 /* fprintf(stderr, "%p: pt_thread_wait, before sleep, doing GC run\n",
457 * interp); */
459 UNLOCK(interpreter_array_mutex);
460 pt_suspend_self_for_gc(interp);
461 LOCK(interpreter_array_mutex);
463 /* while we were GCing, whatever we were waiting on might have
464 * changed */
465 return;
468 interp->thread_data->state |= THREAD_STATE_GC_WAKEUP;
470 COND_WAIT(interp->thread_data->interp_cond, interpreter_array_mutex);
472 interp->thread_data->state &= ~THREAD_STATE_GC_WAKEUP;
474 while (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
475 UNLOCK(interpreter_array_mutex);
476 /* fprintf(stderr, "%p: woken up, doing GC run\n", interp); */
477 pt_suspend_self_for_gc(interp);
478 LOCK(interpreter_array_mutex);
485 =item C<static void* thread_func(void *arg)>
487 The actual thread function.
489 =cut
493 PARROT_CAN_RETURN_NULL
494 static void*
495 thread_func(ARGIN_NULLOK(void *arg))
497 ASSERT_ARGS(thread_func)
498 Parrot_runloop jump_point;
499 int lo_var_ptr;
500 UINTVAL tid;
501 PMC * volatile sub_pmc;
502 PMC *sub_arg;
503 PMC * const self = (PMC*) arg;
504 PMC *ret_val = NULL;
505 Parrot_Interp interp =
506 (Parrot_Interp)((Parrot_ParrotInterpreter_attributes *)PMC_data(self))->interp;
508 Parrot_block_GC_mark(interp);
509 Parrot_block_GC_sweep(interp);
511 /* need to set it here because argument passing can trigger GC */
512 interp->lo_var_ptr = &lo_var_ptr;
513 GETATTR_ParrotInterpreter_sub(interp, self, sub_pmc);
514 sub_arg = VTABLE_get_pmc(interp, self);
516 if (setjmp(jump_point.resume)) {
517 /* caught exception */
518 /* XXX what should we really do here */
519 /* PMC *exception = Parrot_cx_peek_task(interp);
520 Parrot_io_eprintf(interp,
521 "Unhandled exception in thread with tid %d "
522 "(message=%Ss, number=%d)\n",
523 interp->thread_data->tid,
524 VTABLE_get_string(interp, exception),
525 VTABLE_get_integer_keyed_str(interp, exception,
526 Parrot_str_new_constant(interp, "type"))); */
528 ret_val = PMCNULL;
530 else {
531 /* run normally */
532 Parrot_ex_add_c_handler(interp, &jump_point);
533 Parrot_unblock_GC_mark(interp);
534 Parrot_unblock_GC_sweep(interp);
535 ret_val = Parrot_runops_fromc_args(interp, sub_pmc, "PF", sub_arg);
538 /* thread is finito */
539 LOCK(interpreter_array_mutex);
540 DEBUG_ONLY(fprintf(stderr, "marking an thread as finished\n"));
542 interp->thread_data->state |= THREAD_STATE_FINISHED;
543 tid = interp->thread_data->tid;
545 if (interp != interpreter_array[tid]) {
546 UNLOCK(interpreter_array_mutex);
547 PANIC(interp, "thread finished: interpreter mismatch");
549 if (interp->thread_data->state & THREAD_STATE_DETACHED) {
550 interpreter_array[tid] = NULL;
551 DEBUG_ONLY(fprintf(stderr,
552 "really destroying an interpreter [exit while detached]\n"));
553 Parrot_really_destroy(interp, 0, NULL);
555 else if (interp->thread_data->state & THREAD_STATE_JOINED) {
556 pt_thread_signal(interp, interp->thread_data->joiner);
559 /* make sure we don't block a GC run */
560 pt_gc_wakeup_check(interp);
561 PARROT_ASSERT(interp->thread_data->state & THREAD_STATE_FINISHED);
563 UNLOCK(interpreter_array_mutex);
565 return ret_val;
570 =back
572 =head2 Helper functions used also for running plain interpreters
574 =over 4
576 =item C<void pt_clone_code(Parrot_Interp d, Parrot_Interp s)>
578 Copies/clones the packfile/code from interpreter C<s> to C<d>. All
579 resources are created in C<d>.
581 =cut
585 void
586 pt_clone_code(Parrot_Interp d, Parrot_Interp s)
588 ASSERT_ARGS(pt_clone_code)
589 Parrot_block_GC_mark(d);
590 Interp_flags_SET(d, PARROT_EXTERN_CODE_FLAG);
591 d->code = NULL;
592 Parrot_switch_to_cs(d, s->code, 1);
593 Parrot_unblock_GC_mark(d);
598 =item C<static void pt_ns_clone(PARROT_INTERP, Parrot_Interp d, PMC *dest_ns,
599 Parrot_Interp s, PMC *source_ns)>
601 Clones all globals from C<s> to C<d>.
603 =cut
607 static void
608 pt_ns_clone(PARROT_INTERP, ARGOUT(Parrot_Interp d), ARGOUT(PMC *dest_ns),
609 ARGIN(Parrot_Interp s), ARGIN(PMC *source_ns))
611 ASSERT_ARGS(pt_ns_clone)
612 PMC * const iter = VTABLE_get_iter(s, source_ns);
613 const INTVAL n = VTABLE_elements(s, source_ns);
614 INTVAL i;
616 for (i = 0; i < n; ++i) {
617 /* XXX what if 'key' is a non-constant-pool string? */
618 STRING * const key = VTABLE_shift_string(s, iter);
619 PMC * const val = VTABLE_get_pmc_keyed_str(s, source_ns, key);
621 if (val->vtable->base_type == enum_class_NameSpace) {
622 PMC *sub_ns = VTABLE_get_pmc_keyed_str(d, dest_ns, key);
623 if (PMC_IS_NULL(sub_ns) || sub_ns->vtable->base_type !=
624 enum_class_NameSpace) {
625 sub_ns = pmc_new(d, enum_class_NameSpace);
626 VTABLE_set_pmc_keyed_str(d, dest_ns, key, sub_ns);
628 pt_ns_clone(s, d, sub_ns, s, val);
630 else {
631 PMC * const dval = VTABLE_get_pmc_keyed_str(d, dest_ns, key);
633 if (PMC_IS_NULL(dval)) {
634 PMC * const copy = make_local_copy(d, s, val);
635 Parrot_sub *val_sub;
637 if (val->vtable->base_type == enum_class_Sub)
638 PMC_get_sub(interp, val, val_sub);
640 /* Vtable overrides and methods were already cloned, so don't reclone them. */
641 if (! (val->vtable->base_type == enum_class_Sub
642 && (val_sub->vtable_index != -1
643 || val_sub->comp_flags & SUB_COMP_FLAG_METHOD))) {
644 VTABLE_set_pmc_keyed_str(d, dest_ns, key, copy);
653 =item C<void pt_clone_globals(Parrot_Interp d, Parrot_Interp s)>
655 Copies the global namespace when cloning a new interpreter.
657 =cut
661 void
662 pt_clone_globals(Parrot_Interp d, Parrot_Interp s)
664 ASSERT_ARGS(pt_clone_globals)
665 Parrot_block_GC_mark(d);
666 pt_ns_clone(s, d, d->root_namespace, s, s->root_namespace);
667 Parrot_unblock_GC_mark(d);
672 =item C<void pt_thread_prepare_for_run(Parrot_Interp d, Parrot_Interp s)>
674 Sets up a new thread to run.
676 =cut
680 void
681 pt_thread_prepare_for_run(Parrot_Interp d, SHIM(Parrot_Interp s))
683 ASSERT_ARGS(pt_thread_prepare_for_run)
684 Parrot_setup_event_func_ptrs(d);
689 =back
691 =head2 ParrotThread methods
693 =over 4
695 =cut
701 =item C<PMC * pt_transfer_sub(Parrot_Interp d, Parrot_Interp s, PMC *sub)>
703 Clones the sub so that it's suitable for the other interpreter.
705 =cut
709 PARROT_CAN_RETURN_NULL
710 PMC *
711 pt_transfer_sub(ARGOUT(Parrot_Interp d), ARGIN(Parrot_Interp s), ARGIN(PMC *sub))
713 ASSERT_ARGS(pt_transfer_sub)
714 #if defined THREAD_DEBUG && THREAD_DEBUG
715 Parrot_io_eprintf(s, "copying over subroutine [%Ss]\n",
716 Parrot_full_sub_name(s, sub));
717 #endif
718 return make_local_copy(d, s, sub);
723 =item C<int pt_thread_run(PARROT_INTERP, PMC *dest_interp, PMC *sub, PMC *arg)>
725 Runs the C<*sub> PMC in a separate thread using the interpreter in
726 C<*dest_interp>.
728 C<arg> should be an array of arguments for the subroutine.
730 =cut
735 pt_thread_run(PARROT_INTERP, ARGOUT(PMC *dest_interp), ARGIN(PMC *sub), ARGIN_NULLOK(PMC *arg))
737 ASSERT_ARGS(pt_thread_run)
738 PMC *old_dest_interp;
739 PMC *parent;
740 Interp * const interpreter = (Parrot_Interp)VTABLE_get_pointer(interp,
741 dest_interp);
743 Parrot_block_GC_sweep(interpreter);
744 Parrot_block_GC_mark(interpreter);
745 Parrot_block_GC_sweep(interp);
746 Parrot_block_GC_mark(interp);
748 /* make a copy of the ParrotThread PMC so we can use it
749 * to hold parameters to the new thread without it being
750 * garbage collected or otherwise changed by the parent thread.
751 * Also so the new thread's getinterp doesn't return an object
752 * owned by the wrong interpreter -- which would be very bad
753 * if the parent is destroyed before the child.
754 * XXX FIXME move this elsewhere? at least the set_pmc_keyed_int
756 old_dest_interp = dest_interp;
757 dest_interp = pmc_new_noinit(interpreter, enum_class_ParrotThread);
759 /* so it's not accidentally deleted */
760 VTABLE_set_pointer(interp, old_dest_interp, NULL);
761 VTABLE_set_pointer(interp, dest_interp, interpreter);
763 VTABLE_set_pmc_keyed_int(interpreter, interpreter->iglobals,
764 (INTVAL) IGLOBALS_INTERPRETER, dest_interp);
766 parent = VTABLE_get_pmc_keyed_int(interp, interp->iglobals,
767 IGLOBALS_INTERPRETER);
770 * TODO check if thread flags are consistent
772 if (interp->flags & PARROT_THR_COPY_INTERP)
773 clone_interpreter(interpreter,
774 (Parrot_Interp)VTABLE_get_pointer(interp, parent),
775 PARROT_CLONE_DEFAULT);
777 * TODO thread pools
780 pt_thread_prepare_for_run(interpreter, interp);
782 SETATTR_ParrotInterpreter_sub(interp, dest_interp,
783 pt_transfer_sub(interpreter, interp, sub));
784 VTABLE_set_pmc(interp, dest_interp,
785 make_local_args_copy(interpreter, interp, arg));
788 * set regs according to pdd03
790 interpreter->current_object = dest_interp;
792 * create a joinable thread
794 interpreter->thread_data->state = THREAD_STATE_JOINABLE;
796 Parrot_unblock_GC_mark(interpreter);
797 Parrot_unblock_GC_sweep(interpreter);
798 Parrot_unblock_GC_mark(interp);
799 Parrot_unblock_GC_sweep(interp);
801 THREAD_CREATE_JOINABLE(interpreter->thread_data->thread,
802 thread_func, dest_interp);
804 /* check for pending GC */
805 LOCK(interpreter_array_mutex);
806 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED)
807 pt_suspend_one_for_gc(interpreter);
809 UNLOCK(interpreter_array_mutex);
810 return 0;
815 =item C<int pt_thread_run_1(PARROT_INTERP, PMC* dest_interp, PMC* sub, PMC
816 *arg)>
818 Runs a thread that shares nothing and does not communicate with the other
819 interpreter.
821 =cut
826 pt_thread_run_1(PARROT_INTERP, ARGOUT(PMC* dest_interp), ARGIN(PMC* sub), ARGIN(PMC *arg))
828 ASSERT_ARGS(pt_thread_run_1)
829 interp->flags |= PARROT_THR_TYPE_1;
830 return pt_thread_run(interp, dest_interp, sub, arg);
835 =item C<int pt_thread_run_2(PARROT_INTERP, PMC* dest_interp, PMC* sub, PMC
836 *arg)>
838 Runs an interpreter in a thread with no shared variables, but which
839 communicates by sending messages.
841 =cut
846 pt_thread_run_2(PARROT_INTERP, ARGOUT(PMC* dest_interp), ARGIN(PMC* sub), ARGIN(PMC *arg))
848 ASSERT_ARGS(pt_thread_run_2)
849 interp->flags |= PARROT_THR_TYPE_2;
850 return pt_thread_run(interp, dest_interp, sub, arg);
855 =item C<int pt_thread_run_3(PARROT_INTERP, PMC* dest_interp, PMC* sub, PMC
856 *arg)>
858 Runs an interpreter in a thread, allowing shared variables and using a thread
859 pool.
861 =cut
866 pt_thread_run_3(PARROT_INTERP, ARGOUT(PMC* dest_interp), ARGIN(PMC* sub), ARGIN(PMC *arg))
868 ASSERT_ARGS(pt_thread_run_3)
869 interp->flags |= PARROT_THR_TYPE_3;
870 return pt_thread_run(interp, dest_interp, sub, arg);
875 =item C<void pt_thread_yield(void)>
877 Relinquishes hold on the processor.
879 =cut
883 void
884 pt_thread_yield(void)
886 ASSERT_ARGS(pt_thread_yield)
887 YIELD;
892 =item C<static Parrot_Interp pt_check_tid(UINTVAL tid, const char *from)>
894 Helper function. Checks if the given thread ID is valid. The caller holds the
895 mutex. Returns the interpreter for C<tid>.
897 =cut
901 static Parrot_Interp
902 pt_check_tid(UINTVAL tid, ARGIN(const char *from))
904 ASSERT_ARGS(pt_check_tid)
905 if (tid >= n_interpreters) {
906 UNLOCK(interpreter_array_mutex);
907 exit_fatal(1, "%s: illegal thread tid %d", from, tid);
909 if (tid == 0) {
910 UNLOCK(interpreter_array_mutex);
911 exit_fatal(1, "%s: illegal thread tid %d (main)", from, tid);
913 if (!interpreter_array[tid]) {
914 UNLOCK(interpreter_array_mutex);
915 exit_fatal(1, "%s: illegal thread tid %d - empty", from, tid);
917 return interpreter_array[tid];
922 =item C<static void mutex_unlock(void *arg)>
924 Unlocks the mutex C<*arg>.
926 =cut
930 static void
931 mutex_unlock(ARGMOD(void *arg))
933 ASSERT_ARGS(mutex_unlock)
934 UNLOCK(*(Parrot_mutex *) arg);
939 =item C<static int is_suspended_for_gc(PARROT_INTERP)>
941 Returns true iff C<interp> is suspended for a global GC run. Be sure to hold
942 C<interpreter_array_mutex>.
944 =cut
948 PARROT_WARN_UNUSED_RESULT
949 static int
950 is_suspended_for_gc(PARROT_INTERP)
952 ASSERT_ARGS(is_suspended_for_gc)
953 if (!interp)
954 return 1;
955 else if (interp->thread_data->wants_shared_gc)
956 return 1;
957 else if (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC)
958 return 1;
959 else if ((interp->thread_data->state & THREAD_STATE_FINISHED) ||
960 (interp->thread_data->state & THREAD_STATE_NOT_STARTED))
961 return 1;
962 else
963 return 0;
968 =item C<static QUEUE_ENTRY * remove_queued_suspend_gc(PARROT_INTERP)>
970 Removes an event requesting that the interpreter suspend itself for a
971 garbage-collection run from the event queue.
973 =cut
977 PARROT_CAN_RETURN_NULL
978 static QUEUE_ENTRY *
979 remove_queued_suspend_gc(PARROT_INTERP)
981 ASSERT_ARGS(remove_queued_suspend_gc)
982 parrot_event *ev = NULL;
983 QUEUE * const queue = interp->task_queue;
984 QUEUE_ENTRY *prev = NULL;
985 QUEUE_ENTRY *cur;
987 queue_lock(queue);
988 cur = queue->head;
990 while (cur) {
991 ev = (parrot_event *)cur->data;
993 if (ev->type == EVENT_TYPE_SUSPEND_FOR_GC)
994 break;
996 prev = cur;
997 cur = cur->next;
1000 if (cur) {
1001 if (prev)
1002 prev->next = cur->next;
1003 else
1004 queue->head = cur->next;
1006 if (cur == queue->tail)
1007 queue->tail = prev;
1009 if (cur == queue->head)
1010 queue->head = cur->next;
1012 mem_sys_free(ev);
1013 mem_sys_free(cur);
1014 cur = NULL;
1015 DEBUG_ONLY(fprintf(stderr, "%p: remove_queued_suspend_gc: got one\n", interp));
1018 queue_unlock(queue);
1019 return cur;
1024 =item C<static int pt_gc_count_threads(PARROT_INTERP)>
1026 Returns the number of active threads in the system (running or suspended). Be
1027 sure to hold C<interpreter_array_mutex>.
1029 =cut
1033 static int
1034 pt_gc_count_threads(PARROT_INTERP)
1036 ASSERT_ARGS(pt_gc_count_threads)
1037 UINTVAL i;
1038 int count = 0;
1040 for (i = 0; i < n_interpreters; ++i) {
1041 Parrot_Interp cur;
1042 cur = interpreter_array[i];
1043 if (!cur)
1044 continue;
1045 if (cur->thread_data->state & (THREAD_STATE_NOT_STARTED |
1046 THREAD_STATE_FINISHED))
1047 continue;
1048 ++count;
1050 DEBUG_ONLY(fprintf(stderr, "found %d threads\n", count));
1051 return count;
1056 =item C<static void pt_gc_wait_for_stage(PARROT_INTERP, thread_gc_stage_enum
1057 from_stage, thread_gc_stage_enum to_stage)>
1059 Waits until all threads have reached the desired stage. Takes an interpreter,
1060 starting stage and ending stage as arguments. Updates the thread information.
1061 Used in C<pt_gc_start_mark> and C<pt_gc_stop_mark>.
1063 =cut
1067 static void
1068 pt_gc_wait_for_stage(PARROT_INTERP, thread_gc_stage_enum from_stage,
1069 thread_gc_stage_enum to_stage)
1071 ASSERT_ARGS(pt_gc_wait_for_stage)
1072 Shared_gc_info * const info = shared_gc_info;
1073 int thread_count;
1075 DEBUG_ONLY(fprintf(stderr, "%p: gc_wait_for_stage: %d->%d\n", interp, from_stage, to_stage));
1077 /* XXX well-timed thread death can mess this up */
1078 LOCK(interpreter_array_mutex);
1080 DEBUG_ONLY(fprintf(stderr, "%p: got lock\n", interp));
1081 thread_count = pt_gc_count_threads(interp);
1083 PARROT_ASSERT(info->gc_stage == from_stage);
1084 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_NOT_STARTED));
1085 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_FINISHED));
1087 if (from_stage == 0)
1088 PARROT_ASSERT(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC);
1089 else
1090 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC));
1092 ++info->num_reached;
1094 DEBUG_ONLY(fprintf(stderr, "%p: gc_wait_for_stage: got %d\n", interp, info->num_reached));
1096 if (info->num_reached == thread_count) {
1097 info->gc_stage = to_stage;
1098 info->num_reached = 0;
1099 COND_BROADCAST(info->gc_cond);
1101 else {
1102 do {
1103 COND_WAIT(info->gc_cond, interpreter_array_mutex);
1104 } while (info->gc_stage != to_stage);
1106 UNLOCK(interpreter_array_mutex);
1112 =item C<static void pt_gc_wakeup_check(PARROT_INTERP)>
1114 Checks if it's necessary to wake threads to perform garbage collection. This
1115 is called after thread death. Be sure to hold C<interpreter_array_mutex>.
1117 =cut
1121 static void
1122 pt_gc_wakeup_check(PARROT_INTERP)
1124 ASSERT_ARGS(pt_gc_wakeup_check)
1125 Shared_gc_info * const info = shared_gc_info;
1126 int thread_count;
1128 /* XXX: maybe a little hack; see RT #49532 */
1129 if (!info)
1130 return;
1132 thread_count = pt_gc_count_threads(interp);
1134 if (info->num_reached == thread_count) {
1135 PARROT_ASSERT(info->gc_stage == THREAD_GC_STAGE_NONE);
1136 info->gc_stage = THREAD_GC_STAGE_MARK;
1137 info->num_reached = 0;
1138 COND_BROADCAST(info->gc_cond);
1144 =item C<static void pt_suspend_one_for_gc(PARROT_INTERP)>
1146 Suspends a single interpreter for GC. Be sure to hold
1147 C<interpreter_array_mutex>.
1149 =cut
1153 static void
1154 pt_suspend_one_for_gc(PARROT_INTERP)
1156 ASSERT_ARGS(pt_suspend_one_for_gc)
1157 DEBUG_ONLY(fprintf(stderr, "suspend one: %p\n", interp));
1158 if (is_suspended_for_gc(interp)) {
1159 DEBUG_ONLY(fprintf(stderr, "ignoring already suspended\n"));
1160 return;
1163 if (interp->thread_data->state & THREAD_STATE_GC_WAKEUP) {
1164 DEBUG_ONLY(fprintf(stderr, "just waking it up\n"));
1165 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1166 COND_SIGNAL(interp->thread_data->interp_cond);
1168 else {
1169 DEBUG_ONLY(fprintf(stderr, "queuing event\n"));
1170 interp->thread_data->state |= THREAD_STATE_SUSPEND_GC_REQUESTED;
1171 Parrot_cx_request_suspend_for_gc(interp);
1177 =item C<static void pt_suspend_all_for_gc(PARROT_INTERP)>
1179 Notifies all threads to perform a GC run.
1181 =cut
1185 static void
1186 pt_suspend_all_for_gc(PARROT_INTERP)
1188 ASSERT_ARGS(pt_suspend_all_for_gc)
1189 UINTVAL i;
1191 DEBUG_ONLY(fprintf(stderr, "suspend_all_for_gc [interp=%p]\n", interp));
1193 LOCK(interpreter_array_mutex);
1194 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1196 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
1197 DEBUG_ONLY(fprintf(stderr, "found while suspending all\n"));
1198 Parrot_cx_delete_suspend_for_gc(interp);
1199 interp->thread_data->state &= ~THREAD_STATE_SUSPEND_GC_REQUESTED;
1200 UNLOCK(interpreter_array_mutex);
1201 return;
1204 #if 0
1205 for (i = 0; i < n_interpreters; ++i) {
1206 Parrot_Interp other_interp;
1207 other_interp = interpreter_array[i];
1208 if (!other_interp)
1209 continue;
1211 if (is_suspended_for_gc(other_interp) &&
1212 other_interp != interp &&
1213 (other_interp->thread_data->state & THREAD_STATE_SUSPENDED_GC))
1215 PMC *successp;
1216 /* this means that someone else already got this far,
1217 * so we have a suspend event in our queue to ignore
1219 /* XXX still reachable? */
1220 DEBUG_ONLY(fprintf(stderr, "apparently someone else is doing it [%p]\n", other_interp));
1221 fprintf(stderr, "??? found later (%p)\n", other_interp);
1222 successp = Parrot_cx_delete_suspend_for_gc(interp);
1223 PARROT_ASSERT(successp);
1224 UNLOCK(interpreter_array_mutex);
1225 return;
1228 #endif
1230 /* now send all the non-suspended threads to suspend for GC */
1231 for (i = 0; i < n_interpreters; ++i) {
1232 Parrot_Interp other_interp = interpreter_array[i];
1234 if (interp == other_interp)
1235 continue;
1237 if (is_suspended_for_gc(other_interp))
1238 continue;
1240 pt_suspend_one_for_gc(other_interp);
1242 UNLOCK(interpreter_array_mutex);
1247 =item C<void pt_suspend_self_for_gc(PARROT_INTERP)>
1249 Suspends this thread for a full GC run.
1251 XXX FIXME -- if GC is blocked, we need to do a GC run as soon
1252 as it becomes unblocked.
1254 =cut
1258 void
1259 pt_suspend_self_for_gc(PARROT_INTERP)
1261 ASSERT_ARGS(pt_suspend_self_for_gc)
1262 PARROT_ASSERT(interp);
1263 PARROT_ASSERT(!Parrot_is_blocked_GC_mark(interp));
1264 DEBUG_ONLY(fprintf(stderr, "%p: suspend_self_for_gc\n", interp));
1265 /* since we are modifying our own state, we need to lock
1266 * the interpreter_array_mutex.
1268 LOCK(interpreter_array_mutex);
1269 DEBUG_ONLY(fprintf(stderr, "%p: got lock\n", interp));
1271 PARROT_ASSERT(interp->thread_data->state &
1272 (THREAD_STATE_SUSPEND_GC_REQUESTED | THREAD_STATE_SUSPENDED_GC));
1274 if (interp->thread_data->state & THREAD_STATE_SUSPEND_GC_REQUESTED) {
1275 DEBUG_ONLY(fprintf(stderr, "remove queued request\n"));
1276 while (!PMC_IS_NULL(Parrot_cx_delete_suspend_for_gc(interp))) {/*Empty body*/};
1277 DEBUG_ONLY(fprintf(stderr, "removed all queued requests\n"));
1278 interp->thread_data->state &= ~THREAD_STATE_SUSPEND_GC_REQUESTED;
1280 if (!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC)) {
1281 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1283 else {
1284 DEBUG_ONLY(fprintf(stderr, "no need to set suspended\n"));
1286 UNLOCK(interpreter_array_mutex);
1288 /* mark and sweep our world -- later callbacks will keep
1289 * it sync'd
1291 Parrot_gc_mark_and_sweep(interp, GC_trace_stack_FLAG);
1293 PARROT_ASSERT(!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC));
1298 =item C<PMC* pt_thread_join(Parrot_Interp parent, UINTVAL tid)>
1300 Joins (by waiting for) a joinable thread.
1302 =cut
1306 PARROT_CAN_RETURN_NULL
1307 PMC*
1308 pt_thread_join(NOTNULL(Parrot_Interp parent), UINTVAL tid)
1310 ASSERT_ARGS(pt_thread_join)
1311 int state;
1312 Parrot_Interp interp;
1314 LOCK(interpreter_array_mutex);
1316 interp = pt_check_tid(tid, "join");
1318 if (interp == parent)
1319 do_panic(parent, "Can't join self", __FILE__, __LINE__);
1321 if ((!(interp->thread_data->state & (THREAD_STATE_DETACHED
1322 | THREAD_STATE_JOINED)) &&
1323 !(interp->thread_data->state & THREAD_STATE_NOT_STARTED)) ||
1324 interp->thread_data->state == THREAD_STATE_FINISHED) {
1325 void *raw_retval = NULL;
1326 PMC *retval;
1328 interp->thread_data->state |= THREAD_STATE_JOINED;
1330 while (!(interp->thread_data->state & THREAD_STATE_FINISHED)) {
1331 interp->thread_data->joiner = parent;
1332 pt_thread_wait(parent);
1335 UNLOCK(interpreter_array_mutex);
1336 JOIN(interp->thread_data->thread, raw_retval);
1338 retval = (PMC *)raw_retval;
1340 * we need to push a cleanup handler here: if cloning
1341 * of the retval fails (e.g. it's a NULLPMC) this lock
1342 * isn't released until eternity or someone hits ^C
1344 * TODO This is needed for all places holding a lock for
1345 * non-trivial tasks
1346 * -leo
1347 * TODO remove that and replace it with proper exception
1348 * handling, so that a failing clone in the parent
1349 * just stops that thread
1350 * -leo
1352 LOCK(interpreter_array_mutex);
1353 CLEANUP_PUSH(mutex_unlock, &interpreter_array_mutex);
1355 if (retval) {
1356 PMC *parent_ret;
1358 * clone the PMC into caller, if it's not a shared PMC
1359 * the PMC is not in the parents root set nor in the
1360 * stack so block GC during clone
1361 * XXX should probably acquire the parent's interpreter mutex
1363 Parrot_block_GC_mark(parent);
1364 parent_ret = make_local_copy(parent, interp, retval);
1366 /* this PMC is living only in the stack of this currently
1367 * dying interpreter, so register it in parent's GC registry
1368 * XXX is this still needed?
1370 gc_register_pmc(parent, parent_ret);
1371 Parrot_unblock_GC_mark(parent);
1372 retval = parent_ret;
1374 else {
1375 retval = PMCNULL;
1377 interpreter_array[tid] = NULL;
1378 running_threads--;
1380 DEBUG_ONLY(fprintf(stderr, "destroying an interpreter [join]\n"));
1381 if (Interp_debug_TEST(parent, PARROT_THREAD_DEBUG_FLAG))
1382 fprintf(stderr, "running threads %d\n", running_threads);
1384 /* reparent it so memory pool merging works */
1385 interp->parent_interpreter = parent;
1386 Parrot_really_destroy(interp, 0, NULL);
1388 CLEANUP_POP(1);
1390 * interpreter destruction is done - unregister the return
1391 * value, caller gets it now
1393 if (retval)
1394 gc_unregister_pmc(parent, retval);
1396 return retval;
1399 * when here thread was in wrong state
1401 state = interp->thread_data->state;
1402 UNLOCK(interpreter_array_mutex);
1403 Parrot_ex_throw_from_c_args(interp, NULL, 1,
1404 "join: illegal thread state %d tid %d", state, tid);
1409 =item C<void pt_join_threads(PARROT_INTERP)>
1411 Possibly waits for other running threads. This is called when destroying
1412 C<interp>.
1414 =cut
1418 void
1419 pt_join_threads(PARROT_INTERP)
1421 ASSERT_ARGS(pt_join_threads)
1422 size_t i;
1423 pt_free_pool(interp);
1425 /* if no threads were started - fine */
1426 LOCK(interpreter_array_mutex);
1427 if (n_interpreters <= 1) {
1428 n_interpreters = 0;
1429 UNLOCK(interpreter_array_mutex);
1430 return;
1433 /* only the first interpreter waits for other threads */
1434 if (interp != interpreter_array[0]) {
1435 UNLOCK(interpreter_array_mutex);
1436 return;
1439 for (i = 1; i < n_interpreters; ++i) {
1440 Parrot_Interp thread_interp = interpreter_array[i];
1441 if (thread_interp == NULL)
1442 continue;
1443 if (thread_interp->thread_data->state == THREAD_STATE_JOINABLE ||
1444 (thread_interp->thread_data->state & THREAD_STATE_FINISHED)) {
1446 void *retval = NULL;
1447 thread_interp->thread_data->state |= THREAD_STATE_JOINED;
1448 UNLOCK(interpreter_array_mutex);
1449 JOIN(thread_interp->thread_data->thread, retval);
1450 LOCK(interpreter_array_mutex);
1453 UNLOCK(interpreter_array_mutex);
1454 return;
1459 =item C<static Parrot_Interp detach(UINTVAL tid)>
1461 Helper for detach and kill.
1463 Returns the interpreter, if it didn't finish yet.
1465 =cut
1469 static Parrot_Interp
1470 detach(UINTVAL tid)
1472 ASSERT_ARGS(detach)
1473 Parrot_Interp interp;
1475 LOCK(interpreter_array_mutex);
1476 interp = pt_check_tid(tid, "detach");
1478 * if interpreter is joinable, we detach em
1480 if (interp->thread_data->state == THREAD_STATE_JOINABLE ||
1481 interp->thread_data->state == THREAD_STATE_FINISHED) {
1482 DETACH(interp->thread_data->thread);
1483 interp->thread_data->state |= THREAD_STATE_DETACHED;
1485 if (interp->thread_data->state & THREAD_STATE_FINISHED) {
1486 interpreter_array[tid] = NULL;
1487 DEBUG_ONLY(fprintf(stderr, "destroying an interpreter [detach]\n"));
1488 Parrot_really_destroy(interp, 0, NULL);
1489 interp = NULL;
1492 UNLOCK(interpreter_array_mutex);
1493 return interp;
1498 =item C<void pt_thread_detach(UINTVAL tid)>
1500 Detaches the thread, making it non-joinable.
1502 =cut
1506 void
1507 pt_thread_detach(UINTVAL tid)
1509 ASSERT_ARGS(pt_thread_detach)
1510 (void) detach(tid);
1515 =item C<void pt_thread_kill(UINTVAL tid)>
1517 Kills the thread.
1519 =cut
1523 void
1524 pt_thread_kill(UINTVAL tid)
1526 ASSERT_ARGS(pt_thread_kill)
1527 PARROT_INTERP = detach(tid);
1529 /* schedule a terminate event for that interpreter */
1530 if (interp)
1531 Parrot_cx_runloop_end(interp);
1536 =back
1538 =head2 Threaded interpreter book-keeping
1540 =over 4
1542 =item C<void pt_add_to_interpreters(PARROT_INTERP, Parrot_Interp new_interp)>
1544 Stores the given interpreter in the array of all interpreters. Be sure to hold
1545 C<interpreter_array_mutex>.
1547 =cut
1551 void
1552 pt_add_to_interpreters(PARROT_INTERP, ARGIN_NULLOK(Parrot_Interp new_interp))
1554 ASSERT_ARGS(pt_add_to_interpreters)
1555 size_t i;
1556 DEBUG_ONLY(fprintf(stderr, "interp = %p\n", interp));
1558 if (!new_interp) {
1560 * Create an entry for the very first interpreter, event
1561 * handling needs it
1563 PARROT_ASSERT(!interpreter_array);
1564 PARROT_ASSERT(n_interpreters == 0);
1566 interpreter_array = mem_allocate_typed(Interp *);
1567 interpreter_array[0] = interp;
1568 n_interpreters = 1;
1570 shared_gc_info = (Shared_gc_info *)mem_sys_allocate_zeroed(sizeof (*shared_gc_info));
1571 COND_INIT(shared_gc_info->gc_cond);
1572 PARROT_ATOMIC_INT_INIT(shared_gc_info->gc_block_level);
1573 PARROT_ATOMIC_INT_SET(shared_gc_info->gc_block_level, 0);
1575 /* XXX try to defer this until later */
1576 PARROT_ASSERT(interp == interpreter_array[0]);
1577 interp->thread_data = mem_allocate_zeroed_typed(Thread_data);
1578 INTERPRETER_LOCK_INIT(interp);
1579 interp->thread_data->tid = 0;
1581 return;
1585 new_interp->thread_data = mem_allocate_zeroed_typed(Thread_data);
1586 INTERPRETER_LOCK_INIT(new_interp);
1587 running_threads++;
1588 if (Interp_debug_TEST(interp, PARROT_THREAD_DEBUG_FLAG))
1589 fprintf(stderr, "running threads %d\n", running_threads);
1591 /* look for an empty slot */
1592 for (i = 0; i < n_interpreters; ++i) {
1593 if (interpreter_array[i] == NULL) {
1594 interpreter_array[i] = new_interp;
1595 new_interp->thread_data->tid = i;
1596 new_interp->thread_data->state = THREAD_STATE_NOT_STARTED;
1597 return;
1601 /* need to resize */
1602 interpreter_array = (Interp **)mem_sys_realloc(interpreter_array,
1603 (n_interpreters + 1) * sizeof (Interp *));
1605 interpreter_array[n_interpreters] = new_interp;
1606 new_interp->thread_data->tid = n_interpreters;
1607 new_interp->thread_data->state = THREAD_STATE_NOT_STARTED;
1609 ++n_interpreters;
1614 =back
1616 =head2 GC Synchronization Functions
1618 =over 4
1620 =item C<void pt_gc_start_mark(PARROT_INTERP)>
1622 Record that the mark phase of GC is about to begin. In the presence of shared
1623 PMCs, we can only run one GC run at a time because C<< PMC->next_for_GC >> may
1624 be changed.
1626 C<flags> are the GC flags. We check if we need to collect shared objects or
1627 not.
1629 TODO - Have a count of shared PMCs and check it during GC.
1631 TODO - Evaluate if a interpreter lock is cheaper when C<gc_mark_ptr> is
1632 updated.
1634 =cut
1638 void
1639 pt_gc_start_mark(PARROT_INTERP)
1641 ASSERT_ARGS(pt_gc_start_mark)
1642 Shared_gc_info *info;
1643 int block_level;
1645 DEBUG_ONLY(fprintf(stderr, "%p: pt_gc_start_mark\n", interp));
1646 /* if no other threads are running, we are safe */
1647 if (!running_threads)
1648 return;
1650 info = get_pool(interp);
1651 PARROT_ATOMIC_INT_GET(block_level, info->gc_block_level);
1653 DEBUG_ONLY(fprintf(stderr, "start threaded mark\n"));
1655 * TODO now check, if we are the owner of a shared memory pool
1656 * if yes:
1657 * - suspend all other threads by sending them a suspend event
1658 * (or put a LOCK around updating the mark pointers)
1659 * - return and continue the mark phase
1660 * - then s. comments below
1662 LOCK(interpreter_array_mutex);
1663 if (interp->thread_data->state & THREAD_STATE_SUSPENDED_GC) {
1664 PARROT_ASSERT(!(interp->thread_data->state &
1665 THREAD_STATE_SUSPEND_GC_REQUESTED));
1666 DEBUG_ONLY(fprintf(stderr, "already suspended...\n"));
1667 UNLOCK(interpreter_array_mutex);
1669 else if (block_level) {
1670 /* unthreaded collection */
1671 DEBUG_ONLY(fprintf(stderr, "... but blocked\n"));
1673 /* holding the lock */
1674 return;
1676 else if (interp->thread_data->state &
1677 THREAD_STATE_SUSPEND_GC_REQUESTED) {
1678 while (!PMC_IS_NULL(Parrot_cx_delete_suspend_for_gc(interp))) {/*Empty body*/};
1680 interp->thread_data->state &= ~THREAD_STATE_SUSPEND_GC_REQUESTED;
1681 interp->thread_data->state |= THREAD_STATE_SUSPENDED_GC;
1683 DEBUG_ONLY(fprintf(stderr, "%p: detected request\n", interp));
1684 UNLOCK(interpreter_array_mutex);
1686 else {
1687 /* we need to stop the world */
1688 DEBUG_ONLY(fprintf(stderr, "stop the world\n"));
1689 UNLOCK(interpreter_array_mutex);
1691 pt_suspend_all_for_gc(interp);
1694 DEBUG_ONLY(fprintf(stderr, "%p: wait for stage\n", interp));
1695 pt_gc_wait_for_stage(interp, THREAD_GC_STAGE_NONE, THREAD_GC_STAGE_MARK);
1697 DEBUG_ONLY(fprintf(stderr, "actually mark\n"));
1699 * We can't allow parallel running GCs; both would mess with shared PMCs'
1700 * next_for_GC pointers
1702 LOCK(interpreter_array_mutex);
1703 DEBUG_ONLY(fprintf(stderr, "got marking lock\n"));
1708 =item C<void pt_gc_mark_root_finished(PARROT_INTERP)>
1710 Records that GC has finished for the root set. EXCEPTION_UNIMPLEMENTED
1712 =cut
1716 void
1717 pt_gc_mark_root_finished(PARROT_INTERP)
1719 ASSERT_ARGS(pt_gc_mark_root_finished)
1720 if (!running_threads)
1721 return;
1723 * TODO now check, if we are the owner of a shared memory pool
1724 * if yes:
1725 * - now mark all members of our pool
1726 * - if all shared PMCs are marked by all threads then
1727 * - we can continue to free unused objects
1733 =item C<void pt_gc_stop_mark(PARROT_INTERP)>
1735 Records that the mark phase of GC has completed.
1737 =cut
1741 void
1742 pt_gc_stop_mark(PARROT_INTERP)
1744 ASSERT_ARGS(pt_gc_stop_mark)
1745 if (!running_threads)
1746 return;
1748 * normal operation can continue now
1749 * - other threads may or not free unused objects then,
1750 * depending on their resource statistics
1752 if (!(interp->thread_data->state & THREAD_STATE_SUSPENDED_GC)) {
1753 UNLOCK(interpreter_array_mutex);
1754 return;
1757 PARROT_ASSERT(!(interp->thread_data->state &
1758 THREAD_STATE_SUSPEND_GC_REQUESTED));
1759 interp->thread_data->state &= ~THREAD_STATE_SUSPENDED_GC;
1761 while (!PMC_IS_NULL(Parrot_cx_delete_suspend_for_gc(interp))) {
1762 /* XXX FIXME make this message never trigger */
1763 fprintf(stderr, "%p: extraneous suspend_gc event\n", (void *)interp);
1766 DEBUG_ONLY(fprintf(stderr, "%p: unlock\n", interp));
1767 UNLOCK(interpreter_array_mutex);
1768 DEBUG_ONLY(fprintf(stderr, "wait to sweep\n"));
1770 pt_gc_wait_for_stage(interp, THREAD_GC_STAGE_MARK, THREAD_GC_STAGE_SWEEP);
1775 =item C<void Parrot_shared_gc_block(PARROT_INTERP)>
1777 Blocks stop-the-world GC runs.
1779 =cut
1783 PARROT_EXPORT
1784 void
1785 Parrot_shared_gc_block(PARROT_INTERP)
1787 ASSERT_ARGS(Parrot_shared_gc_block)
1788 Shared_gc_info * const info = get_pool(interp);
1790 if (info) {
1791 int level;
1792 PARROT_ATOMIC_INT_INC(level, info->gc_block_level);
1793 PARROT_ASSERT(level > 0);
1799 =item C<void Parrot_shared_gc_unblock(PARROT_INTERP)>
1801 Unblocks stop-the-world GC runs.
1803 =cut
1807 PARROT_EXPORT
1808 void
1809 Parrot_shared_gc_unblock(PARROT_INTERP)
1811 ASSERT_ARGS(Parrot_shared_gc_unblock)
1812 Shared_gc_info * const info = get_pool(interp);
1813 if (info) {
1814 int level;
1815 PARROT_ATOMIC_INT_DEC(level, info->gc_block_level);
1816 PARROT_ASSERT(level >= 0);
1821 * Local variables:
1822 * c-file-style: "parrot"
1823 * End:
1824 * vim: expandtab shiftwidth=4: