int dthread_signal_set(dthread_t* thr) { #ifdef __WIN32__ DEBUGF("dthread_signal_set: handle=%d", DTHREAD_EVENT(thr->iq_signal[0])); SetEvent(DTHREAD_EVENT(thr->iq_signal[0])); return 1; #else DEBUGF("dthread_signal_set: fd=%d", DTHREAD_EVENT(thr->iq_signal[1])); return write(DTHREAD_EVENT(thr->iq_signal[1]), "!", 1); #endif }
// consume wakeup token int dthread_signal_reset(dthread_t* thr) { #ifdef __WIN32__ DEBUGF("dthread_signal_reset: handle=%d", DTHREAD_EVENT(thr->iq_signal[0])); ResetEvent(DTHREAD_EVENT(thr->iq_signal[0])); return 0; #else { char buf[1]; DEBUGF("dthread_signal_reset: fd=%d", DTHREAD_EVENT(thr->iq_signal[0])); return read(DTHREAD_EVENT(thr->iq_signal[0]), buf, 1); } #endif }
// NOTE: when SMP is enabled the messages go straight to the caller // This code is here to allow non SMP emulator with the same code base. static void uart_drv_ready_input(ErlDrvData d, ErlDrvEvent e) { drv_ctx_t* ctx = (drv_ctx_t*) d; DEBUGF("uart_drv: ready_input called"); if (ctx->self.iq_signal[0] == e) { // got input ! dmessage_t* mp; DEBUGF("uart_drv: ready_input handle=%d", DTHREAD_EVENT(ctx->self.iq_signal[0])); if ((mp = dthread_recv(&ctx->self, NULL)) == NULL) { DEBUGF("uart_drv: ready_input signaled with no event! handle=%d", DTHREAD_EVENT(ctx->self.iq_signal[0])); return; } switch(mp->cmd) { case DTHREAD_OUTPUT_TERM: DEBUGF("uart_drv: ready_input (OUTPUT_TERM)"); driver_output_term(ctx->self.port, (ErlDrvTermData*) mp->buffer, mp->used / sizeof(ErlDrvTermData)); break; case DTHREAD_SEND_TERM: DEBUGF("uart_drv: ready_input (SEND_TERM)"); // dterm_dump(stderr, (ErlDrvTermData*) mp->buffer, // mp->used / sizeof(ErlDrvTermData)); driver_send_term(ctx->self.port, mp->to, /* orignal from ! */ (ErlDrvTermData*) mp->buffer, mp->used / sizeof(ErlDrvTermData)); break; case DTHREAD_OUTPUT: DEBUGF("uart_drv: ready_input (OUTPUT)"); driver_output(ctx->self.port, mp->buffer, mp->used); break; default: DEBUGF("uart_drv: read_input cmd=%d not matched", mp->cmd); break; } dmessage_free(mp); } else { DEBUGF("uart_drv: ready_input (NO MATCH)"); } }
void dthread_signal_select(dthread_t* thr, int on) { DEBUGF("dthread_signal_select: fd=%d", DTHREAD_EVENT(thr->iq_signal[0])); #ifdef __WIN32__ driver_select(thr->port,thr->iq_signal[0],ERL_DRV_READ,on); #else driver_select(thr->port,thr->iq_signal[0],ERL_DRV_READ,on); #endif }
void dthread_signal_finish(dthread_t* thr, int and_close) { if (thr->iq_signal[0] != (ErlDrvEvent)DTHREAD_INVALID_EVENT) { if (and_close) { DEBUGF("dthread_signal_finish: close iq_signal[0]=%d", DTHREAD_EVENT(thr->iq_signal[0])); DTHREAD_CLOSE_EVENT(thr->iq_signal[0]); } thr->iq_signal[0] = (ErlDrvEvent)DTHREAD_INVALID_EVENT; } if (thr->iq_signal[1] != (ErlDrvEvent)DTHREAD_INVALID_EVENT) { if (and_close) { DEBUGF("dthread_signal_finish: iq_signal[1]=%d", DTHREAD_EVENT(thr->iq_signal[1])); DTHREAD_CLOSE_EVENT(thr->iq_signal[1]); } thr->iq_signal[1] = (ErlDrvEvent)DTHREAD_INVALID_EVENT; } }
// 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 void uart_drv_stop_select(ErlDrvEvent event, void* arg) { (void) arg; DEBUGF("uart_drv: stop_select event=%d", DTHREAD_EVENT(event)); dthread_event_close(event); }
int dthread_poll(dthread_t* thr, dthread_poll_event_t* events, size_t* nevents, int timeout) { struct timeval tm; struct timeval* tp; fd_set readfds; fd_set writefds; fd_set errorfds; int fd,nfds = 0; int ready; int i,n,iq_len=0; if (timeout < 0) tp = NULL; else { tm.tv_sec = timeout / 1000; tm.tv_usec = (timeout - tm.tv_sec*1000) * 1000; tp = &tm; } FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&errorfds); if ((fd = DTHREAD_EVENT(thr->iq_signal[0])) >= 0) { FD_SET(fd, &readfds); FD_SET(fd, &errorfds); DEBUGF("FD_SET: iq_signal[0] = %d", fd); if (fd > nfds) nfds = fd; } if (events && nevents && *nevents) { n = (int) (*nevents); for (i = 0; i < n; i++) { events[i].revents = 0; // clear here in case of timeout etc if (events[i].events) { fd = DTHREAD_EVENT(events[i].event); if (events[i].events & ERL_DRV_READ) { FD_SET(fd, &readfds); FD_SET(fd, &errorfds); } if (events[i].events & ERL_DRV_WRITE) FD_SET(fd, &writefds); if (fd > nfds) nfds = fd; } } } DEBUGF("select nfds=%d, tp=%p", nfds, tp); ready = select(nfds+1, &readfds, &writefds, &errorfds, tp); DEBUGF("select result r=%d", ready); if (ready <= 0) { if (nevents) *nevents = 0; return ready; } // check queue ! fd = DTHREAD_EVENT(thr->iq_signal[0]); if (FD_ISSET(fd, &readfds)) { erl_drv_mutex_lock(thr->iq_mtx); iq_len = thr->iq_len; erl_drv_mutex_unlock(thr->iq_mtx); ready--; } // check io events if (ready && events && nevents && *nevents) { size_t nready = 0; n = (int) (*nevents); for (i = 0; ready && (i < n); i++) { size_t fd_ready = 0; fd = DTHREAD_EVENT(events[i].event); if (FD_ISSET(fd, &readfds) || FD_ISSET(fd, &errorfds)) { events[i].revents |= ERL_DRV_READ; if (FD_ISSET(fd, &errorfds)) events[i].revents |= ERL_DRV_EXCEP; fd_ready = 1; } if (FD_ISSET(fd, &writefds)) { events[i].revents |= ERL_DRV_WRITE; fd_ready = 1; } nready += fd_ready; ready--; } *nevents = nready; } return iq_len; }
int dthread_poll(dthread_t* thr, dthread_poll_event_t* events, size_t* nevents, int timeout) { HANDLE handles[MAXIMUM_WAIT_OBJECTS]; int eindex[MAXIMUM_WAIT_OBJECTS]; DWORD nCount = 0; DWORD iq_len = 0; DWORD nready = 0; DWORD dwMilliseconds; DWORD res; int i,n; if (timeout < 0) dwMilliseconds = INFINITE; else dwMilliseconds = (DWORD) timeout; // install handles to wait for if (DTHREAD_EVENT(thr->iq_signal[0]) != DTHREAD_INVALID_EVENT) { eindex[nCount] = -1; // -1 == signal queue event handles[nCount] = DTHREAD_EVENT(thr->iq_signal[0]); nCount++; } if (events && nevents && *nevents) { n = (DWORD) (*nevents); for (i = 0; (nCount < MAXIMUM_WAIT_OBJECTS) && (i < n); i++) { events[i].revents = 0; // clear here in case of timeout etc if (events[i].events) { eindex[nCount] = i; // index in event array handles[nCount] = DTHREAD_EVENT(events[i].event); nCount++; } } } DEBUGF("WaitForMultipleObjects nCount=%d, timeout=%d", nCount, dwMilliseconds); // wait for first event that is signaled res = WaitForMultipleObjects(nCount, handles, FALSE, dwMilliseconds); DEBUGF("WaitForMultipleObjects result=%d", res); if (res == WAIT_TIMEOUT) return 0; else if (res == WAIT_FAILED) return -1; else if ((res >= WAIT_OBJECT_0) && (res < (WAIT_OBJECT_0+nCount))) { DWORD j = res - WAIT_OBJECT_0; if ((i = eindex[j]) < 0) { erl_drv_mutex_lock(thr->iq_mtx); iq_len = thr->iq_len; erl_drv_mutex_unlock(thr->iq_mtx); } else if (events != NULL) { events[i].revents |= ERL_DRV_READ; // event is ready nready++; } j++; // must scan rest of the events as well, else starvation may occure while (j < nCount) { if (WaitForSingleObject(handles[j], 0) == WAIT_OBJECT_0) { if ((i = eindex[j]) < 0) { erl_drv_mutex_lock(thr->iq_mtx); iq_len = thr->iq_len; erl_drv_mutex_unlock(thr->iq_mtx); } else if (events != NULL) { events[i].revents |= ERL_DRV_READ; // event is ready nready++; } } j++; } } if (nevents) *nevents = nready; return iq_len; }
extern void dthread_event_close(ErlDrvEvent event) { INFOF("event_close: %d", DTHREAD_EVENT(event)); DTHREAD_CLOSE_EVENT(event); }