static void zmqdrv_ready_input(ErlDrvData handle, ErlDrvEvent event) { zmq_drv_t *drv = (zmq_drv_t *)handle; // Get 0MQ sockets managed by application thread's signaler // identified by "event" fd. zmq_fd_sockets_map_t::iterator it = drv->zmq_fd_sockets.find((long)event); zmqdrv_fprintf("input ready on [idx=%ld]\r\n", (long)event); assert(it != drv->zmq_fd_sockets.end()); zmq_sock_set_t::iterator si = it->second.begin(); assert(si != it->second.end()); for (; si != it->second.end(); ++si) { zmq_socket_t s = (*si)->socket; uint32_t idx = (*si)->idx; ErlDrvTermData owner = (*si)->owner; int rc = 0; uint32_t events; size_t events_size = sizeof(events); zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); while (((*si)->active_mode || (*si)->in_caller) && (events & ZMQ_POLLIN)) { msg_t msg; rc = zmq_recv(s, &msg, ZMQ_NOBLOCK); ErlDrvTermData pid = (*si)->active_mode ? owner : (*si)->in_caller; if (rc == -1) { if (zmq_errno() != EAGAIN) { ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zmq, ERL_DRV_PORT, driver_mk_port(drv->port), ERL_DRV_UINT, idx, ERL_DRV_TUPLE, 2, ERL_DRV_ATOM, am_error, ERL_DRV_ATOM, error_atom(zmq_errno()), ERL_DRV_TUPLE, 2, ERL_DRV_TUPLE, 3}; driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0])); (*si)->in_caller = 0; } break; } if ((*si)->active_mode == 1) { // Send message {zmq, Socket, binary()} to the owner pid ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zmq, ERL_DRV_PORT, driver_mk_port(drv->port), ERL_DRV_UINT, idx, ERL_DRV_TUPLE, 2, ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg), ERL_DRV_TUPLE, 3}; driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0])); } else if ((*si)->active_mode == 2) { // Send message {zmq, Socket, [binary()]} to the owner pid // where binary() is each message part std::vector<ErlDrvTermData> specv; specv.reserve(100); std::vector<msg_t*> parts; specv.push_back(ERL_DRV_ATOM); specv.push_back(am_zmq); specv.push_back(ERL_DRV_PORT); specv.push_back(driver_mk_port(drv->port)); specv.push_back(ERL_DRV_UINT); specv.push_back(idx); specv.push_back(ERL_DRV_TUPLE); specv.push_back(2); specv.push_back(ERL_DRV_BUF2BINARY); specv.push_back((ErlDrvTermData)zmq_msg_data(&msg)); specv.push_back(zmq_msg_size(&msg)); int64_t more; size_t more_size = sizeof(more); int next_count = 0; while (true) { zmq_getsockopt(s, ZMQ_RCVMORE, &more, &more_size); if (!more) break; next_count += 1; msg_t *next_part = new msg_t; parts.push_back(next_part); rc = zmq_recv(s, next_part, ZMQ_NOBLOCK); assert (!rc); // FIXME specv.push_back(ERL_DRV_BUF2BINARY); specv.push_back((ErlDrvTermData)zmq_msg_data(next_part)); specv.push_back(zmq_msg_size(next_part)); } specv.push_back(ERL_DRV_NIL); specv.push_back(ERL_DRV_LIST); specv.push_back(next_count + 2); specv.push_back(ERL_DRV_TUPLE); specv.push_back(3); driver_send_term(drv->port, owner, specv.data(), specv.size()); for (uint i = 0; i < parts.size(); i++) { delete parts[i]; } } else { // Return result {ok, binary()} to the waiting caller's pid ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zok, ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg), ERL_DRV_TUPLE, 2}; driver_send_term(drv->port, pid, spec, sizeof(spec)/sizeof(spec[0])); (*si)->in_caller = 0; } // FIXME: add error handling zmqdrv_fprintf("received %ld byte message relayed to pid %ld\r\n", zmq_msg_size(&msg), pid); zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); } zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); if ((*si)->out_caller != 0 && (events & ZMQ_POLLOUT)) { // There was a pending unwritten message on this socket. // Try to write it. If the write succeeds/fails clear the ZMQ_POLLOUT // flag and notify the waiting caller of completion of operation. rc = zmq_send(s, &(*si)->out_msg, (*si)->out_flags | ZMQ_NOBLOCK); zmqdrv_fprintf("resending message %p (size=%ld) on socket %p (ret=%d)\r\n", zmq_msg_data(&(*si)->out_msg), zmq_msg_size(&(*si)->out_msg), s, rc); if (rc == 0) { zmq_msg_close(&(*si)->out_msg); // Unblock the waiting caller's pid by returning result zmqdrv_ok(drv, (*si)->out_caller); (*si)->out_caller = 0; } else if (zmq_errno() != EAGAIN) { // Unblock the waiting caller's pid by returning result zmq_msg_close(&(*si)->out_msg); zmqdrv_socket_error(drv, (*si)->out_caller, idx, zmq_errno()); (*si)->out_caller = 0; } } zmqdrv_fprintf("--> socket %p events=%d\r\n", s, events); } }
static void zmqdrv_ready_input(ErlDrvData handle, ErlDrvEvent event) { zmq_drv_t *drv = (zmq_drv_t *)handle; // Get 0MQ sockets managed by application thread's signaler // identified by "event" fd. zmq_fd_sockets_map_t::iterator it = drv->zmq_fd_sockets.find((long)event); zmqdrv_fprintf("input ready on [idx=%ld]\r\n", (long)event); assert(it != drv->zmq_fd_sockets.end()); zmq_sock_set_t::iterator si = it->second.begin(); assert(si != it->second.end()); for (; si != it->second.end(); ++si) { zmq_socket_t s = (*si)->socket; uint32_t idx = (*si)->idx; ErlDrvTermData owner = (*si)->owner; int rc = 0; uint32_t events; size_t events_size = sizeof(events); zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); while (((*si)->active_mode || (*si)->in_caller) && (events & ZMQ_POLLIN)) { msg_t msg; rc = zmq_recv(s, &msg, ZMQ_NOBLOCK); ErlDrvTermData pid = (*si)->active_mode ? owner : (*si)->in_caller; if (rc == -1) { if (zmq_errno() != EAGAIN) { ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zmq, ERL_DRV_UINT, idx, ERL_DRV_ATOM, error_atom(zmq_errno()), ERL_DRV_TUPLE, 2, ERL_DRV_TUPLE, 3}; driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0])); (*si)->in_caller = 0; } break; } if ((*si)->active_mode) { // Send message {zmq, Socket, binary()} to the owner pid ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zmq, ERL_DRV_UINT, idx, ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg), ERL_DRV_TUPLE, 3}; driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0])); } else { // Return result {ok, binary()} to the waiting caller's pid ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zok, ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg), ERL_DRV_TUPLE, 2}; driver_send_term(drv->port, pid, spec, sizeof(spec)/sizeof(spec[0])); (*si)->in_caller = 0; } // FIXME: add error handling zmqdrv_fprintf("received %ld byte message relayed to pid %ld\r\n", zmq_msg_size(&msg), pid); zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); } zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); if ((*si)->out_caller != 0 && (events & ZMQ_POLLOUT)) { // There was a pending unwritten message on this socket. // Try to write it. If the write succeeds/fails clear the ZMQ_POLLOUT // flag and notify the waiting caller of completion of operation. rc = zmq_send(s, &(*si)->out_msg, (*si)->out_flags | ZMQ_NOBLOCK); zmqdrv_fprintf("resending message %p (size=%ld) on socket %p (ret=%d)\r\n", zmq_msg_data(&(*si)->out_msg), zmq_msg_size(&(*si)->out_msg), s, rc); if (rc == 0) { zmq_msg_close(&(*si)->out_msg); // Unblock the waiting caller's pid by returning result zmqdrv_ok(drv, (*si)->out_caller); (*si)->out_caller = 0; } else if (zmq_errno() != EAGAIN) { // Unblock the waiting caller's pid by returning result zmq_msg_close(&(*si)->out_msg); zmqdrv_socket_error(drv, (*si)->out_caller, idx, zmq_errno()); (*si)->out_caller = 0; } } zmqdrv_fprintf("--> socket %p events=%d\r\n", s, events); } }