static ErlDrvData start(ErlDrvPort port, char *command) { mcd_data_t *mcd = driver_alloc(sizeof(mcd_data_t)); if (!mcd) goto error; mcd->ofd = -1; mcd->ifd = -1; #ifdef UNIX mcd->ofd = open("/dev/null", O_WRONLY); if (mcd->ofd < 0) goto error; if (driver_select(port, (ErlDrvEvent) (long) mcd->ofd, DO_WRITE, 1) != 0) goto error; mcd->ifd = open("/dev/zero", O_RDONLY); if (mcd->ifd < 0) goto error; if (driver_select(port, (ErlDrvEvent) (long) mcd->ifd, DO_READ, 1) != 0) goto error; #endif driver_set_timer(port, 0); return (ErlDrvData) mcd; error: stop((ErlDrvData) mcd); return ERL_DRV_ERROR_GENERAL; }
int enm_write_select(EnmData* d, int start) { ErlDrvEvent event; int rc; size_t optlen = sizeof d->sfd; if (start) { if (d->b.write_poll) return 0; if (d->sfd == -1) { if (d->protocol == NN_PULL || d->protocol == NN_SUB) { errno = EINVAL; return -1; } rc = nn_getsockopt(d->fd, NN_SOL_SOCKET, NN_SNDFD, &d->sfd, &optlen); if (rc < 0) return -1; enm_sockets[d->sfd] = d; } event = (ErlDrvEvent)(long)d->sfd; driver_select(d->port, event, ERL_DRV_WRITE|ERL_DRV_USE, 1); d->b.write_poll = 1; } else { if (!d->b.write_poll) return 0; assert(d->sfd != -1); event = (ErlDrvEvent)(long)d->sfd; driver_select(d->port, event, ERL_DRV_WRITE|ERL_DRV_USE, 0); d->b.write_poll = 0; } return 0; }
void dthread_signal_use(dthread_t* thr, int on) { #ifdef __WIN32__ driver_select(thr->port,thr->iq_signal[0],ERL_DRV_USE,on); #else driver_select(thr->port,thr->iq_signal[1],ERL_DRV_USE,on); driver_select(thr->port,thr->iq_signal[0],ERL_DRV_USE,on); #endif }
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 }
static void sendfile_drv_ready_output(ErlDrvData handle, ErlDrvEvent ev) { Desc* d = (Desc*)handle; ssize_t result; off_t cur_offset; Transfer* xfer; SocketFd* sfd = (SocketFd*)&ev; xfer = (Transfer*)hashtable_search(d->xfer_table, sfd->hashkey); if (xfer == NULL) { /* fatal error, something is very wrong */ driver_failure_atom(d->port, "socket_fd_not_found"); return; } cur_offset = xfer->offset; result = sendfile_call(sfd->socket_fd, xfer->file_fd, &xfer->offset, xfer->count); if (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == EALREADY)) { if (xfer->offset != cur_offset) { off_t written = xfer->offset - cur_offset; xfer->count -= written; xfer->total += written; } } else { int save_errno = errno; ErlDrvSizeT out_buflen; char buf[36]; Buffer b; b.buffer = buf; memset(buf, 0, sizeof buf); #ifdef ERL_DRV_WRITE driver_select(d->port, ev, ERL_DRV_WRITE, 0); #else driver_select(d->port, ev, DO_WRITE, 0); #endif close(xfer->file_fd); if (result < 0) { out_buflen = set_error_buffer(&b, sfd->socket_fd, save_errno); } else { uint64_t total = xfer->total + result; put_int64(total, &(b.result->count.count)); put_int32(sfd->socket_fd, &(b.result->out_fd)); b.result->success = 1; b.result->errno_string[0] = '\0'; out_buflen = sizeof(*b.result); } xfer->file_fd = -1; xfer->offset = xfer->count = xfer->total = 0; driver_output(d->port, buf, out_buflen); } }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void wrap_zmq_close(zmq_drv_t *drv) { ErlDrvTermData caller = driver_caller(drv->port); zmq_sock_info* si = drv->get_socket_info(caller); if (!si) { reply_error(drv->port, caller, ENODEV); return; } zmqdrv_fprintf("close %p\r\n", si->socket); driver_demonitor_process(drv->port, &si->monitor); if (si->busy) { // Remove socket from vm polling driver_select(drv->port, si->fd, ERL_DRV_READ, 0); } drv->zmq_pid_socket.erase(caller); drv->zmq_fd_socket.erase(si->fd); //zmq_close(Socket) is called in ~zmq_sock_info delete si; reply_ok(drv->port, caller); }
zmq_drv_t::~zmq_drv_t() { for (zmq_pid_sockets_map_t::iterator it = zmq_pid_sockets.begin(); it != zmq_pid_sockets.end(); ++it) driver_demonitor_process(port, &it->second.monitor); for (zmq_fd_sockets_map_t::iterator it = zmq_fd_sockets.begin(); it != zmq_fd_sockets.end(); ++it) driver_select(port, (ErlDrvEvent)it->first, ERL_DRV_READ, 0); for (zmq_sock_info *it=zmq_sock_infos, *next=(it ? it->next : NULL); it; it = next) { next = it->next; delete (&*it); } zmq_sockets.clear(); zmq_idxs.clear(); zmq_pid_sockets.clear(); zmq_fd_sockets.clear(); if (zmq_context) { zmqdrv_fprintf("calling zmq_term(context) ...\r\n"); zmq_term(zmq_context); zmqdrv_fprintf("terminated zmq context\r\n"); } }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void zmqdrv_process_exit(ErlDrvData handle, ErlDrvMonitor* monitor) { zmq_drv_t* drv = reinterpret_cast<zmq_drv_t*>(handle); ErlDrvTermData pid = driver_get_monitored_process(drv->port, monitor); zmqdrv_fprintf("detected death of %lu process\r\n", pid); zmq_sock_info* si = drv->get_socket_info(pid); assert(NULL != si); zmqdrv_fprintf("force close %p\r\n", si->socket); driver_demonitor_process(drv->port, &si->monitor); if (si->busy) { // Remove socket from vm polling driver_select(drv->port, si->fd, ERL_DRV_READ, 0); } drv->zmq_pid_socket.erase(pid); drv->zmq_fd_socket.erase(si->fd); //zmq_close(Socket) is called in ~zmq_sock_info delete si; }
static void uvc_drv_stop(ErlDrvData handle) { Uvc* d = (Uvc *)handle; driver_select(d->port, (ErlDrvEvent)d->fd, DO_READ|DO_WRITE, 0); int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(d->fd, VIDIOC_STREAMOFF, &type); int i; for(i = 0; i < d->nbufs; i++) { munmap(d->buffers[i].mem, d->buffers[i].size); } struct v4l2_requestbuffers rb; memset(&rb, 0, sizeof rb); rb.count = 0; rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; rb.memory = V4L2_MEMORY_MMAP; ioctl(d->fd, VIDIOC_REQBUFS, &rb); free(d->buffers); close(d->fd); driver_free((char*)handle); }
static void sendfile_drv_output(ErlDrvData handle, char* buf, ErlDrvSizeT buflen) { int fd, socket_fd; Desc* d = (Desc*)handle; Buffer b; b.buffer = buf; socket_fd = get_int32(&(b.args->out_fd)); fd = open(b.args->filename, O_RDONLY | O_NONBLOCK); if (fd < 0) { ErlDrvSizeT out_buflen = set_error_buffer(&b, socket_fd, errno); driver_output(d->port, buf, out_buflen); } else { Transfer* xfer; SocketFd sfd; sfd.socket_fd = socket_fd; xfer = (Transfer*)hashtable_search(d->xfer_table, sfd.hashkey); if (xfer == NULL) { /* Transfer objects are intentionally not freed until the driver stops, or if an insertion error occurs below. */ xfer = (Transfer*)malloc(sizeof(Transfer)); if (xfer == NULL) { ErlDrvSizeT out_buflen = set_error_buffer(&b, socket_fd, ENOMEM); driver_output(d->port, buf, out_buflen); return; } if (!hashtable_insert(d->xfer_table, sfd.hashkey, xfer)) { ErlDrvSizeT out_buflen = set_error_buffer(&b, socket_fd, ENOMEM); driver_output(d->port, buf, out_buflen); free(xfer); return; } } xfer->file_fd = fd; xfer->offset = get_int64(&(b.args->offset.offset)); xfer->count = get_int64(&(b.args->count.size)); xfer->total = 0; #if defined(ERL_DRV_USE) && defined(ERL_DRV_WRITE) driver_select(d->port, sfd.ev_data, ERL_DRV_USE|ERL_DRV_WRITE, 1); #else driver_select(d->port, sfd.ev_data, DO_WRITE, 1); #endif } }
void do_initscr(state *st) { st->win[0] = (WINDOW *)initscr(); driver_select(st->drv_port, (ErlDrvEvent)(size_t)fileno(stdin), DO_READ, 1); if (st->win[0] == NULL) { encode_ok_reply(st, -1); } else { encode_ok_reply(st, 0); } }
/* * Called when the Erlang port is closed. */ static void stop(ErlDrvData drv_data) { /* make sure we stop Erlang from selecting on fdsrv */ driver_select(instance, fdsrv, DO_READ, 0); /* cleanup */ close((int)(long)fdsrv); instance = (ErlDrvPort) -1; }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void wrap_zmq_recv(zmq_drv_t *drv, const uint8_t* bytes, size_t size) { int flags = *bytes; assert(sizeof(uint8_t) == size); ErlDrvTermData caller = driver_caller(drv->port); if (drv->terminating) { reply_error(drv->port, caller, ETERM); return; } zmq_sock_info* si = drv->get_socket_info(caller); if (!si) { reply_error(drv->port, caller, ENODEV); return; } assert(0 == si->in_caller); zmqdrv_fprintf("recv %p (flags: %d)\r\n", si->socket, flags); zmq_msg_t msg; zmq_msg_init(&msg); if (0 == zmq_recv(si->socket, &msg, flags|ZMQ_NOBLOCK)) { reply_ok_binary(drv->port, caller, zmq_msg_data(&msg), zmq_msg_size(&msg)); } else if (ZMQ_NOBLOCK != (ZMQ_NOBLOCK & flags) && EAGAIN == zmq_errno()) { // Caller requested blocking recv // No input available. Make the caller wait by not returning result zmqdrv_fprintf("recv %p blocking\r\n", si->socket); si->in_flags = flags; si->in_caller = caller; if (!si->busy) { driver_select(drv->port, si->fd, ERL_DRV_READ, 1); si->busy = true; } } else { reply_error(drv->port, caller, zmq_errno()); } zmq_msg_close(&msg); }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void wrap_zmq_term(zmq_drv_t *drv) { zmqdrv_fprintf("term %p\r\n", drv->zmq_context); if (0 < drv->zmq_pid_socket.size()) { for (zmq_pid_socket_map_t::iterator it = drv->zmq_pid_socket.begin(); it != drv->zmq_pid_socket.end(); ++it) { zmq_sock_info* si = it->second; if (si->busy) { // Remove socket from erlang vm polling driver_select(drv->port, si->fd, ERL_DRV_READ, 0); if (si->out_caller) { reply_error(drv->port, si->out_caller, ETERM); si->out_caller = 0; zmq_msg_close(&si->out_msg); } if (si->in_caller) { reply_error(drv->port, si->in_caller, ETERM); si->in_caller = 0; } if (si->poll_caller) { send_events(drv->port, si->poll_caller, (uint32_t)ZMQ_POLLERR); si->poll_caller = 0; } si->busy = false; } } // TODO: Remove if zeromq itself ever gets fixed. As zmq_term() is a // blocking call, and will not return until all sockets are closed, // so do not allow it to be called while there are open sockets. drv->terminating = true; reply_error(drv->port, driver_caller(drv->port), EAGAIN); return; } // cross fingers and hope zmq_term() doesn't block, else we hardlock. if (0 != zmq_term(drv->zmq_context)) { reply_error(drv->port, driver_caller(drv->port), zmq_errno()); return; } drv->zmq_context = NULL; reply_ok(drv->port, driver_caller(drv->port)); }
/* * Called when the Erlang port is closed. */ static void stop(ErlDrvData drv_data) { /* make sure we stop Erlang from selecting on fdsrv */ driver_select(instance, fdsrv, DO_READ, 0); /* cleanup */ close(fdsrv); instance = -1; return; }
static void stop(ErlDrvData edd) { dnssd_drv_t* dd = (dnssd_drv_t*) edd; #ifdef __WIN32__ if (dd->event) { driver_select(dd->erl_port, dd->event, DO_READ, 0); WSAEventSelect(DNSServiceRefSockFD(dd->sd_ref), NULL, 0); } if (dd->sd_ref) { DNSServiceRefDeallocate(dd->sd_ref); } #else if (dd->sd_ref) { driver_select(dd->erl_port, (ErlDrvEvent)(size_t) DNSServiceRefSockFD(dd->sd_ref), DO_READ, 0); DNSServiceRefDeallocate(dd->sd_ref); } #endif driver_free(dd); }
/* * Called when the Erlang port is closed. */ static void log_stop() { #if 0 /* make sure we stop Erlang from selecting on fdsrv */ driver_select(instance, fdsrv, DO_READ, 0); instance = -1; #endif closelog(); /* cleanup */ close(log_port); return; }
static int do_disconnect(our_data_t* data) { ei_x_buff x; if (data->socket == 0) return 0; driver_select(data->port, (ErlDrvEvent)data->socket, DO_READ, 0); data->socket = 0; PQfinish(data->conn); data->conn = NULL; ei_x_new_with_version(&x); encode_ok(&x); driver_output(data->port, x.buff, x.index); ei_x_free(&x); return 0; }
static void io_ready_exit_drv_stop(ErlDrvData drv_data) { IOReadyExitDrvData *oeddp = (IOReadyExitDrvData *) drv_data; #ifdef UNIX if (oeddp->fds[0] >= 0) { driver_select(oeddp->port, (ErlDrvEvent) oeddp->fds[0], DO_READ|DO_WRITE, 0); close(oeddp->fds[0]); } if (oeddp->fds[1] >= 0) close(oeddp->fds[1]); #endif driver_free((void *) oeddp); }
/* pending getkey request */ sl_ready_input(int port, int fd) { unsigned int key; driver_select(port, 0, DO_READ, 0); switch (wait_for) { case GETKEY: { key = SLang_getkey (); return ret_int(port, key); } case KP_GETKEY: { key = SLkp_getkey (); return ret_int(port, key); } return 0; } }
int zmq_drv_t::del_socket(uint32_t idx) { zmq_sock_info* s; int ret = -1; zmq_idx_socket_map_t::iterator it = zmq_sockets.find(idx); if (it == zmq_sockets.end()) { zmqdrv_fprintf("warning: socket info not found for idx %d\r\n", idx); return ret; } s = it->second; s->unlink(); if (s == zmq_sock_infos) zmq_sock_infos = s->next; zmq_sockets.erase(idx); zmq_idxs.erase(s->socket); { // Remove the socket from a list of sockets owned by pid. // If this was the last socket, demonitor pid. zmq_pid_sockets_map_t::iterator it = zmq_pid_sockets.find(s->owner); if (it != zmq_pid_sockets.end()) { it->second.sockets.erase(s); if (it->second.sockets.empty()) { driver_demonitor_process(port, &it->second.monitor); zmq_pid_sockets.erase(it); } } } { zmq_fd_sockets_map_t::iterator it = zmq_fd_sockets.find(s->fd); if (it != zmq_fd_sockets.end()) { it->second.erase(s); if (it->second.empty()) { zmq_fd_sockets.erase(it->first); driver_select(port, (ErlDrvEvent)it->first, ERL_DRV_READ, 0); zmqdrv_fprintf("unregistered sig_fd(%d) with VM\r\n", it->first); } } } delete s; return 0; }
static int do_connect(const char *s, our_data_t* data) { ei_x_buff x; PGconn* conn = PQconnectdb(s); ei_x_new_with_version(&x); if (PQstatus(conn) != CONNECTION_OK) { encode_error(&x, conn); PQfinish(conn); conn = NULL; } else { encode_ok(&x); data->socket = PQsocket(conn); driver_select(data->port, (ErlDrvEvent)data->socket, DO_READ, 1); } driver_output(data->port, x.buff, x.index); ei_x_free(&x); data->conn = conn; return 0; }
/* * Called when erlang side does "open_port()". */ static ErlDrvData log_start(ErlDrvPort port, char * buf) { log_port = port; #if 0 int len; char *path; struct sockaddr_un addr; /* only allow one copy at the time */ if (instance != -1) return -1; if (strlen(buf) > (sizeof(addr.sun_path) + 1)) return -1; instance = port; /* Figure out the path to the named socket */ if ((path = strrchr(buf, (int)' ')) == NULL) path = buf; else path++; fdsrv = socket(AF_UNIX, SOCK_STREAM, 0); bzero((char *)&addr, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, path); len = strlen(path) + sizeof(addr.sun_family) + 1; if (connect(fdsrv, (struct sockaddr *)&addr, len) < 0) { perror("connect"); instance = -1; return -1; } /* Have Erlang select on fdsrv */ driver_select(port, fdsrv, DO_READ, 1); #endif /* Open syslog */ openlog("Eddie", 0, LOG_DAEMON); return port; }
/* * Called when select triggers. Which is when there is an incomming fd. */ static void fd_is_ready(ErlDrvData drv_data, ErlDrvEvent event) { ErlDrvPort port = (ErlDrvPort) drv_data; int received_fd; if (port != instance) return; if (event != fdsrv) return; /* receive the file descriptor */ recv_fd(port, &received_fd, fdsrv); if (received_fd < 0) { close((int)(long)fdsrv); driver_select(port, fdsrv, DO_READ, 0); reply_err(port); } else reply_int(port, received_fd); }
static int io_ready_exit_drv_control(ErlDrvData drv_data, unsigned int command, char *buf, int len, char **rbuf, int rlen) { char *abuf; char *res_str; int res_len; IOReadyExitDrvData *oeddp = (IOReadyExitDrvData *) drv_data; #ifndef UNIX res_str = "nyiftos"; #else if (pipe(oeddp->fds) < 0) { res_str = "pipe failed"; } else { res_str = "ok"; write(oeddp->fds[1], "!", 1); driver_select(oeddp->port, (ErlDrvEvent) oeddp->fds[0], DO_READ|DO_WRITE, 1); } #endif res_len = strlen(res_str); if (res_len > rlen) { abuf = driver_alloc(sizeof(char)*res_len); if (!abuf) return 0; *rbuf = abuf; } memcpy((void *) *rbuf, (void *) res_str, res_len); return res_len; }
void zmq_drv_t::add_socket(zmq_sock_info* s) { // Insert the new socket info to the head of the list if (zmq_sock_infos) zmq_sock_infos->prev = s; s->next = zmq_sock_infos; zmq_sock_infos = s; // Update map: idx -> socket zmq_sockets[s->idx] = s; // Update map: socket -> idx zmq_idxs[s->socket] = s; { // Update map: pid -> sockets zmq_pid_sockets_map_t::iterator it = zmq_pid_sockets.find(s->owner); if (it != zmq_pid_sockets.end()) it->second.sockets.insert(s); else { monitor_sockets_t ms; driver_monitor_process(port, s->owner, &ms.monitor); ms.sockets.insert(s); zmq_pid_sockets[s->owner] = ms; } } { // Update map: fd -> sockets zmq_fd_sockets_map_t::iterator it = zmq_fd_sockets.find(s->fd); if (it != zmq_fd_sockets.end()) it->second.insert(s); else { zmq_sock_set_t set; set.insert(s); zmq_fd_sockets[s->fd] = set; driver_select(port, (ErlDrvEvent)s->fd, ERL_DRV_READ, 1); zmqdrv_fprintf("registered sig_fd(%d) with VM\r\n", s->fd); } } }
/* * Called when erlang side does "open_port()". */ static ErlDrvData start(ErlDrvPort port, char *buf) { int len; char *path; struct sockaddr_un addr; int s; if (instance != (ErlDrvPort) -1) /* only allow one copy at the time */ return (ErlDrvData) -1; if (strlen(buf) > (sizeof(addr.sun_path) + 1)) return (ErlDrvData) -1; instance = port; /* Figure out the path to the named socket */ if ((path = strrchr(buf, (int)' ')) == NULL) path = buf; else path++; s = socket(AF_UNIX, SOCK_STREAM, 0); bzero((char *)&addr, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, path); len = strlen(path) + sizeof(addr.sun_family) + 1; if (connect(s, (struct sockaddr *)&addr, len) < 0) { perror("connect"); instance = (ErlDrvPort) -1; return (ErlDrvData) -1; } fdsrv = (ErlDrvEvent)(long)s; /* Have Erlang select on fdsrv */ driver_select(port, fdsrv, DO_READ, 1); return (ErlDrvData) port; }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void zmqdrv_ready_input(ErlDrvData handle, ErlDrvEvent event) { zmq_drv_t *drv = reinterpret_cast<zmq_drv_t*>(handle); zmq_sock_info *si = drv->get_socket_info(event); // I'm not sure if a race condition could develop here or not // So let's assert and see if we ever hit it. Hopefully not. assert(!drv->terminating); assert(NULL != si); assert(si->busy); // unregister event with erlang vm while we work with the socket driver_select(drv->port, si->fd, ERL_DRV_READ, 0); // Finish blocking recv request if input is ready if (si->in_caller) { zmq_msg_t msg; zmq_msg_init(&msg); if (0 == zmq_recv(si->socket, &msg, si->in_flags|ZMQ_NOBLOCK)) { // Unblock the waiting caller's pid by returning result reply_ok_binary(drv->port, si->in_caller, zmq_msg_data(&msg), zmq_msg_size(&msg)); si->in_caller = 0; si->in_flags = 0; } else if (zmq_errno() != EAGAIN) { // Unblock the waiting caller's pid by returning error reply_error(drv->port, si->in_caller, zmq_errno()); si->in_caller = 0; si->in_flags = 0; } // else no input was ready, continue waiting zmq_msg_close(&msg); } // Finish blocking send request if able if (si->out_caller) { if (0 == zmq_send(si->socket, &si->out_msg, si->out_flags|ZMQ_NOBLOCK)) { // Unblock the waiting caller's pid by returning result reply_ok(drv->port, si->out_caller); si->out_caller = 0; si->out_flags = 0; zmq_msg_close(&si->out_msg); } else if (zmq_errno() != EAGAIN) { // Unblock the waiting caller's pid by returning error reply_error(drv->port, si->out_caller, zmq_errno()); si->out_caller = 0; si->out_flags = 0; zmq_msg_close(&si->out_msg); } // else not able to send, continue waiting } // Finish poll request if events available if (si->poll_caller) { uint32_t revents = 0; size_t revents_size = sizeof(revents); if (0 == zmq_getsockopt(si->socket, ZMQ_EVENTS, &revents, &revents_size)) { revents &= si->poll_events; if (0 != revents) { send_events(drv->port, si->poll_caller, revents); si->poll_caller = 0; si->poll_events = 0; } // else no requested events pending, continue waiting } else { // EINVAL will only occur if our getsockopt call was invalid assert(EINVAL != zmq_errno()); // send out of band event error notification send_events(drv->port, si->poll_caller, (uint32_t)ZMQ_POLLERR); si->poll_caller = 0; si->poll_events = 0; } } // reregister event with erlang vm if any pending operations exist if (si->poll_caller || si->in_caller || si->out_caller) { driver_select(drv->port, si->fd, ERL_DRV_READ, 1); } else { si->busy = false; } }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void wrap_zmq_poll(zmq_drv_t *drv, const uint8_t* bytes, size_t size) { uint32_t events = *bytes; assert(sizeof(uint8_t) == size); ErlDrvTermData caller = driver_caller(drv->port); if (drv->terminating) { reply_error(drv->port, caller, ETERM); return; } zmq_sock_info* si = drv->get_socket_info(caller); if (!si) { reply_error(drv->port, caller, ENODEV); return; } zmqdrv_fprintf("poll %p (events: %u)\r\n", si->socket, events); if (0 == events) { si->poll_events = 0; si->poll_caller = 0; if (si->busy && 0 == si->in_caller && 0 == si->out_caller) { driver_select(drv->port, si->fd, ERL_DRV_READ, 0); si->busy = false; } reply_ok(drv->port, caller); return; } if (si->busy) { reply_error(drv->port, caller, EBUSY); return; } assert((ZMQ_POLLIN|ZMQ_POLLOUT) & events); uint32_t revents; size_t revents_size = sizeof(revents); if (0 == zmq_getsockopt(si->socket, ZMQ_EVENTS, &revents, &revents_size)) { // reply immediately; poll event notification happens out of band. reply_ok(drv->port, caller); revents &= events; if (0 != revents) { send_events(drv->port, caller, revents); } else { // No matching pending event, wait for one. zmqdrv_fprintf("poll %p blocking\r\n", si->socket); si->poll_events = events; si->poll_caller = caller; si->busy = true; driver_select(drv->port, si->fd, ERL_DRV_READ, 1); } } else { // EINVAL will only occur if our getsockopt call was invalid assert(EINVAL != zmq_errno()); // else the problem was with the context/socket, and should be returned reply_error(drv->port, caller, zmq_errno()); } }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void wrap_zmq_send(zmq_drv_t *drv, const uint8_t* bytes, size_t size, ErlDrvBinary* bin) { int flags = *bytes; void* data = (void *)(bytes+sizeof(uint8_t)); size_t data_size = size - sizeof(uint8_t); assert(sizeof(uint8_t) <= size); ErlDrvTermData caller = driver_caller(drv->port); if (drv->terminating) { reply_error(drv->port, caller, ETERM); return; } zmq_sock_info* si = drv->get_socket_info(caller); if (!si) { reply_error(drv->port, caller, ENODEV); return; } assert(0 == si->out_caller); zmqdrv_fprintf("send %p (flags: %d bytes: %u)\r\n", si->socket, flags, data_size); // Increment the reference count on binary so that zmq can take ownership of it. driver_binary_inc_refc(bin); if (zmq_msg_init_data(&si->out_msg, data, data_size, &zmqcb_free_binary, bin)) { reply_error(drv->port, caller, zmq_errno()); driver_binary_dec_refc(bin); return; } if (0 == zmq_send(si->socket, &si->out_msg, flags|ZMQ_NOBLOCK)) { reply_ok(drv->port, caller); zmq_msg_close(&si->out_msg); } else if (ZMQ_NOBLOCK != (ZMQ_NOBLOCK & flags) && EAGAIN == zmq_errno()) { // Caller requested blocking send // Can't send right now. Make the caller wait by not returning result zmqdrv_fprintf("send %p blocking\r\n", si->socket); si->out_flags = flags; si->out_caller = caller; if (!si->busy) { driver_select(drv->port, si->fd, ERL_DRV_READ, 1); si->busy = true; } } else { reply_error(drv->port, caller, zmq_errno()); zmq_msg_close(&si->out_msg); } }