/* This callback handles starting execution of a thread. */ static void start_thread(void *data) { ThreadStart *ts = (ThreadStart *)data; MVMThreadContext *tc = ts->tc; /* Set the current frame in the thread to be the initial caller; * the ref count for this was incremented in the original thread. */ tc->cur_frame = ts->caller; /* wait for the GC to finish if it's not finished stealing us. */ MVM_gc_mark_thread_unblocked(tc); tc->thread_obj->body.stage = MVM_thread_stage_started; /* Enter the interpreter, to run code. */ MVM_interp_run(tc, &thread_initial_invoke, ts); /* mark as exited, so the GC will know to clear our stuff. */ tc->thread_obj->body.stage = MVM_thread_stage_exited; /* Now we're done, decrement the reference count of the caller. */ MVM_frame_dec_ref(tc, ts->caller); /* Mark ourselves as dying, so that another thread will take care * of GC-ing our objects and cleaning up our thread context. */ MVM_gc_mark_thread_blocked(tc); /* hopefully pop the ts->thread_obj temp */ MVM_gc_root_temp_pop(tc); free(ts); /* Exit the thread, now it's completed. */ MVM_platform_thread_exit(NULL); }
MVMString * MVM_string_repeat(MVMThreadContext *tc, MVMString *a, MVMint64 count) { MVMString *result; MVMint64 bkup_count = count; MVMStringIndex string_offset = 0, graphs = 0, rgraphs; if (!IS_CONCRETE((MVMObject *)a)) { MVM_exception_throw_adhoc(tc, "repeat needs a concrete string"); } if (count < 0) MVM_exception_throw_adhoc(tc, "repeat count (%lld) cannot be negative", count); if (count > (1<<30)) MVM_exception_throw_adhoc(tc, "repeat count > %lld arbitrarily unsupported...", (1<<30)); MVM_gc_root_temp_push(tc, (MVMCollectable **)&a); result = (MVMString *)REPR(a)->allocate(tc, STABLE(a)); MVM_gc_root_temp_pop(tc); if (IS_ONE_STRING_ROPE(a)) { string_offset = a->body.strands->string_offset; graphs = a->body.strands[1].graphs; a = a->body.strands->string; } else { graphs = NUM_GRAPHS(a); } rgraphs = graphs * count; /* XXX compute tradeoff here of space usage of the strands table vs real string */ if (rgraphs) { MVMStrand *strands = result->body.strands = calloc(sizeof(MVMStrand), count + 1); result->body.flags = MVM_STRING_TYPE_ROPE; while (count--) { strands[count].compare_offset = count * graphs; strands[count].string = a; strands[count].string_offset = string_offset; } strands[bkup_count].graphs = rgraphs; result->body.num_strands = bkup_count; _STRAND_DEPTH(result) = STRAND_DEPTH(a) + 1; } else { result->body.flags = MVM_STRING_TYPE_UINT8; /* leave graphs 0 and storage null */ } return result; }
/* XXX inline parent's strands if it's a rope too? */ MVMString * MVM_string_concatenate(MVMThreadContext *tc, MVMString *a, MVMString *b) { MVMString *result; MVMStrandIndex strand_count = 0; MVMStrand *strands; MVMStringIndex index = 0; MVMStrandIndex max_strand_depth = 0; MVMStringIndex agraphs = NUM_GRAPHS(a), bgraphs = NUM_GRAPHS(b), rgraphs; if (!IS_CONCRETE((MVMObject *)a) || !IS_CONCRETE((MVMObject *)b)) { MVM_exception_throw_adhoc(tc, "Concatenate needs concrete strings"); } MVM_gc_root_temp_push(tc, (MVMCollectable **)&a); result = (MVMString *)REPR(a)->allocate(tc, STABLE(a)); MVM_gc_root_temp_pop(tc); /* there could be unattached combining chars at the beginning of b, so, XXX TODO handle this */ result->body.flags = MVM_STRING_TYPE_ROPE; rgraphs = agraphs + bgraphs; if (agraphs) strand_count = 1; if (bgraphs) ++strand_count; strands = result->body.strands = calloc(sizeof(MVMStrand), strand_count + 1); strand_count = 0; if (agraphs) { strands->string = a; strands->string_offset = 0; strands->compare_offset = index; index = agraphs; strand_count = 1; max_strand_depth = STRAND_DEPTH(a); } if (bgraphs) { strands[strand_count].string = b; strands[strand_count].string_offset = 0; strands[strand_count].compare_offset = index; index += bgraphs; ++strand_count; if (STRAND_DEPTH(b) > max_strand_depth) max_strand_depth = STRAND_DEPTH(b); } strands[strand_count].graphs = index; result->body.num_strands = strand_count; result->body.flags = MVM_STRING_TYPE_ROPE; _STRAND_DEPTH(result) = max_strand_depth + 1; return result; }
/* Returns a substring of the given string */ MVMString * MVM_string_substring(MVMThreadContext *tc, MVMString *a, MVMint64 start, MVMint64 length) { MVMString *result; MVMStrand *strands; MVMStringIndex agraphs = NUM_GRAPHS(a); if (start < 0) { start += agraphs; if (start < 0) start = 0; } if (!IS_CONCRETE((MVMObject *)a)) { MVM_exception_throw_adhoc(tc, "Substring needs a concrete string"); } if (start > agraphs) start = agraphs; if (length == -1) /* -1 signifies go to the end of the string */ length = agraphs - start; if (length < 0) MVM_exception_throw_adhoc(tc, "Substring length (%lld) cannot be negative", length); if (start + length > agraphs) length = agraphs - start; MVM_gc_root_temp_push(tc, (MVMCollectable **)&a); result = (MVMString *)REPR(a)->allocate(tc, STABLE(a)); MVM_gc_root_temp_pop(tc); strands = result->body.strands = calloc(sizeof(MVMStrand), 2); /* if we're substringing a substring, substring the same one */ if (IS_ONE_STRING_ROPE(a)) { strands->string_offset = (MVMStringIndex)start + a->body.strands->string_offset; strands->string = a->body.strands->string; } else { strands->string_offset = (MVMStringIndex)start; strands->string = a; } /* result->body.codes = 0; /* Populate this lazily. */ result->body.flags = MVM_STRING_TYPE_ROPE; result->body.num_strands = 1; strands[1].graphs = length; _STRAND_DEPTH(result) = STRAND_DEPTH(strands->string) + 1; return result; }
void MVM_thread_join(MVMThreadContext *tc, MVMObject *thread_obj) { if (REPR(thread_obj)->ID == MVM_REPR_ID_MVMThread) { MVMThread *thread = (MVMThread *)thread_obj; int status; MVM_gc_root_temp_push(tc, (MVMCollectable **)&thread); MVM_gc_mark_thread_blocked(tc); if (((MVMThread *)thread_obj)->body.stage < MVM_thread_stage_exited) { status = uv_thread_join(&thread->body.thread); } else { /* the target already ended */ /* used to be APR_SUCCESS, but then we ditched APR */ status = 0; } MVM_gc_mark_thread_unblocked(tc); MVM_gc_root_temp_pop(tc); if (status < 0) MVM_panic(MVM_exitcode_compunit, "Could not join thread: errorcode %d", status); } else { MVM_exception_throw_adhoc(tc, "Thread handle passed to join must have representation MVMThread"); } }
MVMObject * MVM_thread_start(MVMThreadContext *tc, MVMObject *invokee, MVMObject *result_type) { int status; ThreadStart *ts; MVMObject *child_obj; /* Create a thread object to wrap it up in. */ MVM_gc_root_temp_push(tc, (MVMCollectable **)&invokee); child_obj = REPR(result_type)->allocate(tc, STABLE(result_type)); MVM_gc_root_temp_pop(tc); if (REPR(child_obj)->ID == MVM_REPR_ID_MVMThread) { MVMThread *child = (MVMThread *)child_obj; MVMThread * volatile *threads; /* Create a new thread context and set it up. */ MVMThreadContext *child_tc = MVM_tc_create(tc->instance); child->body.tc = child_tc; MVM_ASSIGN_REF(tc, child, child->body.invokee, invokee); child_tc->thread_obj = child; child_tc->thread_id = MVM_incr(&tc->instance->next_user_thread_id); /* Create the thread. Note that we take a reference to the current frame, * since it must survive to be the dynamic scope of where the thread was * started, and there's no promises that the thread won't start before * the code creating the thread returns. The count is decremented when * the thread is done. */ ts = malloc(sizeof(ThreadStart)); ts->tc = child_tc; ts->caller = MVM_frame_inc_ref(tc, tc->cur_frame); ts->thread_obj = child_obj; /* push this to the *child* tc's temp roots. */ MVM_gc_root_temp_push(child_tc, (MVMCollectable **)&ts->thread_obj); /* Signal to the GC we have a childbirth in progress. The GC * will null it for us. */ MVM_gc_mark_thread_blocked(child_tc); MVM_ASSIGN_REF(tc, tc->thread_obj, tc->thread_obj->body.new_child, child); /* push to starting threads list */ threads = &tc->instance->threads; do { MVMThread *curr = *threads; MVM_ASSIGN_REF(tc, child, child->body.next, curr); } while (MVM_casptr(threads, child->body.next, child) != child->body.next); status = uv_thread_create(&child->body.thread, &start_thread, ts); if (status < 0) { MVM_panic(MVM_exitcode_compunit, "Could not spawn thread: errorcode %d", status); } /* need to run the GC to clear our new_child field in case we try * try to launch another thread before the GC runs and before the * thread starts. */ GC_SYNC_POINT(tc); } else { MVM_exception_throw_adhoc(tc, "Thread result type must have representation MVMThread"); } return child_obj; }