dthread_t* dthread_start(ErlDrvPort port, void* (*func)(void* arg), void* arg, int stack_size) { ErlDrvThreadOpts* opts = NULL; dthread_t* thr = NULL; if (!(thr = DALLOC(sizeof(dthread_t)))) return 0; if (dthread_init(thr, port) < 0) goto error; if (!(opts = erl_drv_thread_opts_create("dthread_opts"))) goto error; opts->suggested_stack_size = stack_size; thr->arg = arg; if (erl_drv_thread_create("dthread", &thr->tid, func, thr, opts) < 0) goto error; erl_drv_thread_opts_destroy(opts); return thr; error: dthread_finish(thr); if (opts) erl_drv_thread_opts_destroy(opts); dthread_finish(thr); DFREE(thr); return 0; }
// Initialize thread structure int dthread_init(dthread_t* thr, ErlDrvPort port) { ErlDrvSysInfo sys_info; memset(thr, 0, sizeof(dthread_t)); dthread_signal_init(thr); driver_system_info(&sys_info, sizeof(ErlDrvSysInfo)); // smp_support is used for message passing from thread to // calling process. if SMP is supported the message will go // directly to sender, otherwise it must be sent to port thr->smp_support = sys_info.smp_support; thr->port = port; thr->dport = driver_mk_port(port); thr->owner = driver_connected(port); if (!(thr->iq_mtx = erl_drv_mutex_create("iq_mtx"))) return -1; #ifdef __WIN32__ // create a manual reset event if (!(thr->iq_signal[0] = (ErlDrvEvent) CreateEvent(NULL, TRUE, FALSE, NULL))) { dthread_finish(thr); return -1; } DEBUGF("dthread_init: handle=%d", DTHREAD_EVENT(thr->iq_signal[0])); #else { int pfd[2]; if (pipe(pfd) < 0) { dthread_finish(thr); return -1; } DEBUGF("dthread_init: pipe[0]=%d,pidp[1]=%d", pfd[0], pfd[1]); thr->iq_signal[0] = (ErlDrvEvent) ((long)pfd[0]); thr->iq_signal[1] = (ErlDrvEvent) ((long)pfd[1]); INFOF("pipe: %d,%d", pfd[0], pfd[1]); } #endif return 0; }
static ErlDrvData uart_drv_start(ErlDrvPort port, char* command) { (void) command; drv_ctx_t* ctx = NULL; INFOF("memory allocated: %ld", dlib_allocated()); INFOF("total memory allocated: %ld", dlib_total_allocated()); ctx = DZALLOC(sizeof(drv_ctx_t)); dthread_init(&ctx->self, port); if (strncmp(command, "uart_drv", 8) == 0) command += 8; if (*command == ' ') command++; DEBUGF("uart_drv: start (%s)", command); if (strcmp(command, "ftdi") == 0) { #ifdef HAVE_FTDI ctx->other = dthread_start(port, uart_ftdi_main, &ctx->self, 4096); DEBUGF("uart_drv: ftdi thread = %p", ctx->other); #endif } else { #ifdef __WIN32__ if ((*command == '\0') || (strcmp(command, "win32") == 0)) { ctx->other = dthread_start(port, uart_win32_main, &ctx->self, 4096); DEBUGF("uart_drv: win32 thread = %p", ctx->other); } #else if ((*command == '\0') || (strcmp(command, "unix") == 0)) { ctx->other = dthread_start(port, uart_unix_main, &ctx->self, 4096); DEBUGF("uart_drv: unix thread = %p", ctx->other); } #endif } if (ctx->other == NULL) { dthread_finish(&ctx->self); DFREE(ctx); return ERL_DRV_ERROR_BADARG; } dthread_signal_use(&ctx->self, 1); dthread_signal_select(&ctx->self, 1); set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); return (ErlDrvData) ctx; }
static void uart_drv_stop(ErlDrvData d) { drv_ctx_t* ctx = (drv_ctx_t*) d; void* value; DEBUGF("uart_drv_stop: called"); dthread_stop(ctx->other, &ctx->self, &value); DEBUGF("uart_drv_stop: signal_use=0"); dthread_signal_use(&ctx->self, 0); DEBUGF("uart_drv_stop: dthread_finish"); dthread_finish(&ctx->self); DFREE(ctx); INFOF("memory allocated: %ld", dlib_allocated()); INFOF("total memory allocated: %ld", dlib_total_allocated()); }
int dthread_stop(dthread_t* target, dthread_t* source, void** exit_value) { dmessage_t* mp; int r; if (!(mp = dmessage_create(DTHREAD_STOP, NULL, 0))) return -1; dthread_send(target, source, mp); DEBUGF("dthread_stop: wait to join"); r = erl_drv_thread_join(target->tid, exit_value); DEBUGF("dthread_stop: thread_join: return=%d, exit_value=%p", r, *exit_value); dthread_signal_finish(target, 1); dthread_finish(target); DFREE(target); return 0; }