mrb_value cfunc_rubyvm_dispatch(mrb_state *mrb, mrb_value self) { struct cfunc_rubyvm_data *data = mrb_get_datatype(mrb, self, &cfunc_rubyvm_data_type); mrb_value name_obj, *args; int args_len; mrb_get_args(mrb, "o*", &name_obj, &args, &args_len); struct queue_task *task = malloc(sizeof(struct queue_task)); task->refcount = 2; task->result = NULL; task->status = queue_task_queued; pthread_mutex_init(&task->sync_mutex, NULL); pthread_cond_init(&task->sync_cond, NULL); const char* name = mrb_string_value_ptr(mrb, name_obj); int name_len = strlen(name); task->name = malloc(name_len+1); strncpy(task->name, name, name_len+1); task->args_len = args_len; task->args = malloc(sizeof(struct task_arg) * task->args_len); for(int i=0; i<args_len; ++i) { task->args[i] = mrb_value_to_task_arg(mrb, args[i]); } pthread_mutex_lock(&data->queue_mutex); vector_enqueue(data->queue, task); pthread_cond_signal(&data->queue_cond); pthread_mutex_unlock(&data->queue_mutex); return mrb_obj_value((struct RObject *)Data_Wrap_Struct(mrb, cfunc_state(mrb)->rubyvm_task_class, &cfunc_rubyvm_task_data_type, task)); }
struct task_arg* mrb_value_to_task_arg(mrb_state *mrb, mrb_value v) { struct task_arg *arg = mrb_malloc(mrb, sizeof(struct task_arg)); arg->tt = mrb_type(v); switch (mrb_type(v)) { case MRB_TT_FALSE: case MRB_TT_TRUE: case MRB_TT_FIXNUM: arg->value.i = v.value.i; break; case MRB_TT_FLOAT: arg->value.f = v.value.f; break; case MRB_TT_SYMBOL: { size_t len; const char* name = mrb_sym2name_len(mrb, v.value.sym, &len); arg->value.string.len = len; arg->value.string.ptr = mrb_malloc(mrb, len + 1); memcpy(arg->value.string.ptr, name, len + 1); } break; case MRB_TT_STRING: { struct RString *str = mrb_str_ptr(v); arg->value.string.len = str->len; arg->value.string.ptr = mrb_malloc(mrb, arg->value.string.len+1); memcpy(arg->value.string.ptr, str->ptr, arg->value.string.len+1); } break; case MRB_TT_ARRAY: { struct RArray *ary = mrb_ary_ptr(v); arg->value.array.len = ary->len; arg->value.array.ptr = mrb_malloc(mrb, ary->len * sizeof(struct task_arg)); int i; for(i=0; i<ary->len; i++) { arg->value.array.ptr[i] = mrb_value_to_task_arg(mrb, ary->ptr[i]); } } break; default: mrb_free(mrb, arg); mrb_raise(mrb, E_TYPE_ERROR, "cannot pass to other RubyVM"); break; } return arg; }
void* cfunc_rubyvm_open(void *args) { struct cfunc_rubyvm_data *data = args; mrb_state *mrb = mrb_open(); data->state = mrb; #ifdef DISABLE_GEMS init_cfunc_module(mrb); #endif int n = mrb_read_irep(mrb, data->mrb_data); mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[n]), mrb_top_self(mrb)); if (mrb->exc) { return NULL; } while(true) { pthread_mutex_lock(&data->queue_mutex); while(data->queue->length == 0) { pthread_cond_wait(&data->queue_cond, &data->queue_mutex); } struct queue_task *task = vector_dequeue(data->queue); task->status = queue_task_running; mrb_sym taskname = mrb_intern(mrb, task->name); int args_len = task->args_len; mrb_value *args = mrb_malloc(mrb, sizeof(struct task_arg) * task->args_len); int i; for(i=0; i<task->args_len; ++i) { args[i] = task_arg_to_mrb_value(data->state, task->args[i]); } pthread_mutex_unlock(&data->queue_mutex); mrb_value result = mrb_funcall_argv(mrb, mrb_top_self(data->state), taskname, args_len, args); task->result = mrb_value_to_task_arg(mrb, result); task->status = queue_task_finished; pthread_cond_signal(&task->sync_cond); mrb_free(mrb, args); free_queue_task(mrb, task); } return NULL; }