Example #1
0
static void
zmqdrv_send(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary*  bin   = ev->binv[1];
    char*          bytes = bin->orig_bytes;
    uint32_t       idx   = ntohl(*(uint32_t*)(bytes+1));
    zmq_sock_info* si    = drv->get_socket_info(idx);
    uint32_t       flags = ntohl(*(uint32_t*)(bytes+5));
    void*          data  = (void *)(bytes + 9);
    size_t         size  = bin->orig_size - 9;

    if (idx > drv->zmq_socket_count || !si) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

#ifdef ZMQDRV_DEBUG
    uint32_t events;
    size_t events_size = sizeof(events);
    zmq_getsockopt(si->socket, ZMQ_EVENTS, &events, &events_size);
    zmqdrv_fprintf("sending %p [idx=%d] %lu bytes (events=%d)\r\n", si->socket, idx, size, events);
#endif

    if (si->out_caller != 0) {
        // There's still an unwritten message pending
        zmqdrv_error_code(drv, EBUSY);
        return;
    }

    // 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, size, &zmq_free_binary, bin)) {
        zmqdrv_error_code(drv, zmq_errno());
        driver_binary_dec_refc(bin);
        return;
    }

    if (zmq_send(si->socket, &si->out_msg, flags | ZMQ_NOBLOCK) == 0) {
        zmqdrv_ok(drv);
        zmqdrv_ready_input((ErlDrvData)drv, (ErlDrvEvent)si->fd);
    } else {
        int e = zmq_errno();
        if (e == EAGAIN) {
            // No msg returned to caller - make him wait until async
            // send succeeds
            si->out_caller = driver_caller(drv->port);
            return;
        }
        zmqdrv_error_code(drv, e);
    }
    zmq_msg_close(&si->out_msg);
}
Example #2
0
static void 
zmqdrv_setsockopt(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary*  bin   = ev->binv[1];
    char*          bytes = bin->orig_bytes;
    uint32_t       idx   = ntohl(*(uint32_t*)(bytes+1));
    zmq_sock_info* si    = drv->get_socket_info(idx);
    uint8_t        n     = *(uint8_t*)(bytes+sizeof(idx)+1);
    char*          p     = bytes + 1 + sizeof(idx) + 1;

    if (idx > drv->zmq_socket_count || !si) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

    zmqdrv_fprintf("setsockopt %p (setting %d options)\r\n", si->socket, (int)n);

    for (uint8_t j=0; j < n; ++j) {
        unsigned char option = *p++;
        uint64_t optvallen   = *p++;
        void*    optval      = p;

        switch (option) {
            case ZMQ_HWM:           assert(optvallen == 8);  break;
            case ZMQ_SWAP:          assert(optvallen == 8);  break;
            case ZMQ_AFFINITY:      assert(optvallen == 8);  break;
            case ZMQ_IDENTITY:      assert(optvallen < 256); break;
            case ZMQ_SUBSCRIBE:     assert(optvallen < 256); break;
            case ZMQ_UNSUBSCRIBE:   assert(optvallen < 256); break;
            case ZMQ_RATE:          assert(optvallen == 8);  break;
            case ZMQ_RECOVERY_IVL:  assert(optvallen == 8);  break;
            case ZMQ_MCAST_LOOP:    assert(optvallen == 8);  break;
            case ZMQ_SNDBUF:        assert(optvallen == 8);  break;
            case ZMQ_RCVBUF:        assert(optvallen == 8);  break;
            case ZMQ_ACTIVE:        assert(optvallen == 1);  break;
        }

        zmqdrv_fprintf("setsockopt %p (%d)\r\n", si->socket, option);

        if (option == ZMQ_ACTIVE)
            si->active_mode = *(char*)optval;
        else if (zmq_setsockopt(si->socket, option, optval, optvallen) < 0) {
            zmqdrv_error_code(drv, zmq_errno());
            return;
        }

        p += optvallen;
    }

    zmqdrv_ok(drv);
}
Example #3
0
static void
zmqdrv_term(zmq_drv_t *drv, ErlIOVec *ev)
{
    if (!drv->zmq_context) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

    zmqdrv_fprintf("calling zmq_term(context) ...\r\n");
    int rc = zmq_term(drv->zmq_context);
    zmqdrv_fprintf("terminated zmq context\r\n");

    if (rc < 0) {
        zmqdrv_error_code(drv, zmq_errno());
        return;
    }

    zmqdrv_ok(drv);
    drv->zmq_context = NULL;
}
Example #4
0
static void
zmqdrv_init(zmq_drv_t *drv, ErlIOVec *ev)
{
    /* 
     * FIXME 
     * Use ei_decode_* to decode input from erlang VM.
     * This stuff is not documented anywhere, for now 
     * binary ErlIOVec is decoded by poking in iov struct.
     * 
     * Serge: Dhammika, ei_decode can only be used to decode
     * external binary format in the "output" callback function.
     * It's not suitable for using inside "outputv" body that
     * operates on I/O vectors unless you use term_to_binary/1
     * call to explicitely convert a term to external binary format.
     */

    uint32_t io_threads; 

    ErlDrvBinary* input = ev->binv[1];
    char* bytes = input->orig_bytes;
    io_threads  = ntohl(*(uint32_t *)(bytes + 1));

    zmqdrv_fprintf("iothreads = %u\r\n", io_threads);

    if (drv->zmq_context) {
        zmqdrv_error_code(drv, EBUSY);
        return;
    }
    
    drv->zmq_context = (void *)zmq_init(io_threads);

    if (!drv->zmq_context) {
        zmqdrv_error_code(drv, zmq_errno());
        return;
    }

    zmqdrv_ok(drv);
}
Example #5
0
static void
zmqdrv_close(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary* bin   = ev->binv[1];
    char*         bytes = bin->orig_bytes;
    uint32_t      idx   = ntohl(*(uint32_t*)(bytes+1));

    if (idx > drv->zmq_socket_count) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

    int ret = drv->del_socket(idx);

    zmqdrv_fprintf("close [idx=%d] -> %d\r\n", idx, ret);

    if (ret < 0) {
        zmqdrv_error_code(drv, zmq_errno());
        return;
    }
    
    zmqdrv_ok(drv);
}
Example #6
0
static void
zmqdrv_connect(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary* bin   = ev->binv[1];
    char*         bytes = bin->orig_bytes;
    uint32_t      idx   = ntohl(*(uint32_t*)(bytes+1));
    void*         s     = drv->get_zmq_socket(idx);
    uint16_t      size  = bin->orig_size - 5;
    char          addr[512];

    if (idx > drv->zmq_socket_count || !s) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

    if (size > sizeof(addr) - 1) {
        zmqdrv_error_code(drv, E2BIG);
        return;
    }

    memcpy(addr, bytes + 5, size);
    addr[size] = '\0';

    zmqdrv_fprintf("connect %s\r\n", addr);

    if (!addr[0]) {
        zmqdrv_error_code(drv, EINVAL);
        return;
    }

    if (zmq_connect(s, addr) < 0) {
        zmqdrv_error_code(drv, zmq_errno());
        return;
    }

    zmqdrv_ok(drv);
}
Example #7
0
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);
    }
}
Example #8
0
static void
zmqdrv_ok(zmq_drv_t *drv)
{
    zmqdrv_ok(drv, driver_caller(drv->port));
}
Example #9
0
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);
    }
}