#include "moar.h" /* Allocates a dispatcher table. */ static MVMDispRegistryTable * allocate_table(MVMThreadContext *tc, MVMuint32 num_entries) { MVMDispRegistryTable *table = MVM_calloc(1, sizeof(MVMDispRegistryTable)); table->num_dispatchers = 0; table->alloc_dispatchers = num_entries; table->dispatchers = MVM_calloc(table->alloc_dispatchers, sizeof(MVMDispDefinition *)); return table; } /* Hashes and adds an entry to the registry. */ static void add_to_table(MVMThreadContext *tc, MVMDispRegistryTable *table, MVMDispDefinition *def) { size_t slot = (size_t)(MVM_string_hash_code(tc, def->id) % table->alloc_dispatchers); while (table->dispatchers[slot] != NULL) slot = (slot + 1) % table->alloc_dispatchers; table->dispatchers[slot] = def; table->num_dispatchers++; } /* We keep the registry at maximum 75% load to avoid collisions. */ static void grow_registry_if_needed(MVMThreadContext *tc) { MVMDispRegistry *reg = &(tc->instance->disp_registry); MVMDispRegistryTable *current_table = reg->table; if ((double)current_table->num_dispatchers / (double)current_table->alloc_dispatchers >= 0.75) { /* Copy entries to new table. */ MVMDispRegistryTable *new_table = allocate_table(tc, current_table->alloc_dispatchers * 2); MVMuint32 i; for (i = 0; i < current_table->alloc_dispatchers; i++) if (current_table->dispatchers[i]) add_to_table(tc, new_table, current_table->dispatchers[i]); /* Install the new table. */ reg->table = new_table; /* Free the previous table at the next safepoint. */ MVM_free_at_safepoint(tc, current_table->dispatchers); MVM_free_at_safepoint(tc, current_table); } } /* The core part of registering a new dispatcher. Assumes we are either in * the setup phase *or* we hold the mutex for registering dispatchers if it's * a user-defined one. Also that the REPR of the dispatch (and, if non-null, * resume) dispatcher is MVMCFunction or MVMCode. */ static void register_internal(MVMThreadContext *tc, MVMString *id, MVMObject *dispatch, MVMObject *resume) { MVMDispRegistry *reg = &(tc->instance->disp_registry); /* Allocate and populate the dispatch definition. */ MVMDispDefinition *def = MVM_malloc(sizeof(MVMDispDefinition)); def->id = id; def->dispatch = dispatch; def->resume = resume != NULL && IS_CONCRETE(resume) ? resume : NULL; /* Insert into the registry. */ grow_registry_if_needed(tc); add_to_table(tc, reg->table, def); } /* Registers a boot dispatcher (that is, one provided by the VM). */ static void register_boot_dispatcher(MVMThreadContext *tc, const char *id, MVMObject *dispatch) { MVMString *id_str = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, id); register_internal(tc, id_str, dispatch, NULL); } /* Initialize the dispatcher registry and add all of the boot dispatchers. */ void MVM_disp_registry_init(MVMThreadContext *tc) { MVMDispRegistry *reg = &(tc->instance->disp_registry); /* Set up dispatchers table with hopefully enough slots we don't tend to * need to expand it. */ reg->table = allocate_table(tc, 32); /* Set up mutex. */ int init_stat; if ((init_stat = uv_mutex_init(&(reg->mutex_update))) < 0) { fprintf(stderr, "MoarVM: Initialization of dispatch registry mutex failed\n %s\n", uv_strerror(init_stat)); exit(1); } /* Add each of the boot dispatchers. */ MVM_gc_allocate_gen2_default_set(tc); register_boot_dispatcher(tc, "boot-constant", MVM_disp_boot_constant_dispatch(tc)); register_boot_dispatcher(tc, "boot-value", MVM_disp_boot_value_dispatch(tc)); register_boot_dispatcher(tc, "boot-code-constant", MVM_disp_boot_code_constant_dispatch(tc)); register_boot_dispatcher(tc, "boot-code", MVM_disp_boot_code_dispatch(tc)); register_boot_dispatcher(tc, "boot-foreign-code", MVM_disp_boot_foreign_code_dispatch(tc)); register_boot_dispatcher(tc, "boot-syscall", MVM_disp_boot_syscall_dispatch(tc)); register_boot_dispatcher(tc, "boot-resume", MVM_disp_boot_resume_dispatch(tc)); register_boot_dispatcher(tc, "boot-resume-caller", MVM_disp_boot_resume_caller_dispatch(tc)); register_boot_dispatcher(tc, "lang-call", MVM_disp_lang_call_dispatch(tc)); register_boot_dispatcher(tc, "lang-meth-call", MVM_disp_lang_meth_call_dispatch(tc)); register_boot_dispatcher(tc, "lang-find-meth", MVM_disp_lang_find_meth_dispatch(tc)); register_boot_dispatcher(tc, "lang-meth-not-found", MVM_disp_lang_meth_not_found_dispatch(tc)); register_boot_dispatcher(tc, "boot-boolify", MVM_disp_boot_boolify_dispatch(tc)); register_boot_dispatcher(tc, "lang-hllize", MVM_disp_lang_hllize_dispatch(tc)); register_boot_dispatcher(tc, "lang-isinvokable", MVM_disp_lang_isinvokable_dispatch(tc)); MVM_gc_allocate_gen2_default_clear(tc); } /* Register a new dispatcher. */ void MVM_disp_registry_register(MVMThreadContext *tc, MVMString *id, MVMObject *dispatch, MVMObject *resume) { MVMDispRegistry *reg = &(tc->instance->disp_registry); if (!MVM_code_iscode(tc, dispatch)) MVM_exception_throw_adhoc(tc, "dispatch callback be an instance with repr MVMCode"); if (resume && !MVM_code_iscode(tc, resume)) MVM_exception_throw_adhoc(tc, "resume callback be an instance with repr MVMCode"); uv_mutex_lock(&(reg->mutex_update)); register_internal(tc, id, dispatch, resume); uv_mutex_unlock(&(reg->mutex_update)); } /* Find a dispatcher. Throws if there isn't one. */ MVMDispDefinition * MVM_disp_registry_find(MVMThreadContext *tc, MVMString *id) { MVMDispRegistry *reg = &(tc->instance->disp_registry); MVMDispRegistryTable *table = reg->table; size_t start_slot = (size_t)(MVM_string_hash_code(tc, id) % table->alloc_dispatchers); size_t cur_slot = start_slot; while (1) { MVMDispDefinition *disp = table->dispatchers[cur_slot]; if (disp && MVM_string_equal(tc, disp->id, id)) return disp; cur_slot = (cur_slot + 1) % table->alloc_dispatchers; if (cur_slot == start_slot) // Wrapped all the way around. break; } { char *c_id = MVM_string_utf8_encode_C_string(tc, id); char *waste[] = { c_id, NULL }; MVM_exception_throw_adhoc_free(tc, waste, "No dispatcher registered with ID '%s'", c_id); } } /* Mark the dispatch registry. */ void MVM_disp_registry_mark(MVMThreadContext *tc, MVMGCWorklist *worklist) { MVMDispRegistry *reg = &(tc->instance->disp_registry); MVMDispRegistryTable *table = reg->table; size_t i; for (i = 0; i < table->alloc_dispatchers; i++) { MVMDispDefinition *disp = table->dispatchers[i]; if (disp) { MVM_gc_worklist_add(tc, worklist, &(disp->id)); MVM_gc_worklist_add(tc, worklist, &(disp->dispatch)); MVM_gc_worklist_add(tc, worklist, &(disp->resume)); } } } void MVM_disp_registry_describe(MVMThreadContext *tc, MVMHeapSnapshotState *ss) { MVMDispRegistry *reg = &(tc->instance->disp_registry); MVMDispRegistryTable *table = reg->table; size_t i; for (i = 0; i < table->alloc_dispatchers; i++) { MVMDispDefinition *disp = table->dispatchers[i]; if (disp) { MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss, (MVMCollectable *)disp->id, "Dispatch Program ID"); MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss, (MVMCollectable *)disp->dispatch, "Dispatch Program dispatch"); MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss, (MVMCollectable *)disp->resume, "Dispatch Program resume"); } } } /* Tear down the dispatcher registry, freeing all memory associated with it. */ void MVM_disp_registry_destroy(MVMThreadContext *tc) { MVMDispRegistry *reg = &(tc->instance->disp_registry); MVMDispRegistryTable *table = reg->table; MVMuint32 i; for (i = 0; i < table->alloc_dispatchers; i++) if (table->dispatchers[i]) MVM_free(table->dispatchers[i]); MVM_free(table->dispatchers); MVM_free(table); uv_mutex_destroy(®->mutex_update); }