PageRenderTime 92ms CodeModel.GetById 3ms app.highlight 79ms RepoModel.GetById 2ms app.codeStats 0ms

/c_src/erlzmq_nif.c

https://github.com/betawaffle/erlzmq2
C | 1283 lines | 1118 code | 115 blank | 50 comment | 155 complexity | 82b242fe104cab7952b74c8ddbeb6207 MD5 | raw file
   1// -*- coding:utf-8;Mode:C;tab-width:2;c-basic-offset:2;indent-tabs-mode:nil -*-
   2// ex: set softtabstop=2 tabstop=2 shiftwidth=2 expandtab fileencoding=utf-8:
   3//
   4// Copyright (c) 2011 Yurii Rashkovskii, Evax Software and Michael Truog
   5// 
   6// Permission is hereby granted, free of charge, to any person obtaining a copy
   7// of this software and associated documentation files (the "Software"), to deal
   8// in the Software without restriction, including without limitation the rights
   9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10// copies of the Software, and to permit persons to whom the Software is
  11// furnished to do so, subject to the following conditions:
  12// 
  13// The above copyright notice and this permission notice shall be included in
  14// all copies or substantial portions of the Software.
  15// 
  16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22// THE SOFTWARE.
  23
  24#include "zmq.h"
  25#include "erl_nif.h"
  26#include "vector.h"
  27#include <string.h>
  28#include <stdio.h>
  29#include <assert.h>
  30
  31#define ERLZMQ_MAX_CONCURRENT_REQUESTS 16384
  32
  33static ErlNifResourceType* erlzmq_nif_resource_context;
  34static ErlNifResourceType* erlzmq_nif_resource_socket;
  35
  36typedef struct erlzmq_context {
  37  void * context_zmq;
  38  void * thread_socket;
  39  char * thread_socket_name;
  40  int64_t socket_index;
  41  ErlNifTid polling_tid;
  42  ErlNifMutex * mutex;
  43} erlzmq_context_t;
  44
  45#define ERLZMQ_SOCKET_ACTIVE_OFF        0
  46#define ERLZMQ_SOCKET_ACTIVE_PENDING    1
  47#define ERLZMQ_SOCKET_ACTIVE_ON         2
  48
  49typedef struct erlzmq_socket {
  50  erlzmq_context_t * context;
  51  int64_t socket_index;
  52  void * socket_zmq;
  53  int active;
  54  ErlNifMutex * mutex;
  55} erlzmq_socket_t;
  56
  57#define ERLZMQ_THREAD_REQUEST_SEND      1
  58#define ERLZMQ_THREAD_REQUEST_RECV      2
  59#define ERLZMQ_THREAD_REQUEST_CLOSE     3
  60#define ERLZMQ_THREAD_REQUEST_TERM      4
  61
  62typedef struct {
  63  int type;
  64  union {
  65    struct {
  66      erlzmq_socket_t * socket;
  67      ErlNifEnv * env;
  68      ERL_NIF_TERM ref;
  69      int flags;
  70      zmq_msg_t msg;
  71      ErlNifPid pid;
  72    } send;
  73    struct {
  74      erlzmq_socket_t * socket;
  75      ErlNifEnv * env;
  76      ERL_NIF_TERM ref;
  77      int flags;
  78      ErlNifPid pid;
  79    } recv;
  80    struct {
  81      erlzmq_socket_t * socket;
  82      ErlNifEnv * env;
  83      ERL_NIF_TERM ref;
  84      ErlNifPid pid;
  85    } close;
  86    struct {
  87      ErlNifEnv * env;
  88      ERL_NIF_TERM ref;
  89      ErlNifPid pid;
  90    } term;
  91  } data;
  92} erlzmq_thread_request_t;
  93
  94// Prototypes
  95#define NIF(name) \
  96  ERL_NIF_TERM name(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  97
  98NIF(erlzmq_nif_context);
  99NIF(erlzmq_nif_socket);
 100NIF(erlzmq_nif_bind);
 101NIF(erlzmq_nif_connect);
 102NIF(erlzmq_nif_setsockopt);
 103NIF(erlzmq_nif_getsockopt);
 104NIF(erlzmq_nif_send);
 105NIF(erlzmq_nif_recv);
 106NIF(erlzmq_nif_close);
 107NIF(erlzmq_nif_term);
 108NIF(erlzmq_nif_version);
 109
 110static void * polling_thread(void * handle);
 111static ERL_NIF_TERM add_active_req(ErlNifEnv* env, erlzmq_socket_t * socket);
 112static ERL_NIF_TERM return_zmq_errno(ErlNifEnv* env, int const value);
 113
 114static ErlNifFunc nif_funcs[] =
 115{
 116  {"context", 1, erlzmq_nif_context},
 117  {"socket", 3, erlzmq_nif_socket},
 118  {"bind", 2, erlzmq_nif_bind},
 119  {"connect", 2, erlzmq_nif_connect},
 120  {"setsockopt", 3, erlzmq_nif_setsockopt},
 121  {"getsockopt", 2, erlzmq_nif_getsockopt},
 122  {"send", 3, erlzmq_nif_send},
 123  {"recv", 2, erlzmq_nif_recv},
 124  {"close", 1, erlzmq_nif_close},
 125  {"term", 1, erlzmq_nif_term},
 126  {"version", 0, erlzmq_nif_version}
 127};
 128
 129NIF(erlzmq_nif_context)
 130{
 131  int thread_count;
 132
 133  if (! enif_get_int(env, argv[0], &thread_count)) {
 134    return enif_make_badarg(env);
 135  }
 136
 137  erlzmq_context_t * context = enif_alloc_resource(erlzmq_nif_resource_context,
 138                                                   sizeof(erlzmq_context_t));
 139  assert(context);
 140  context->context_zmq = zmq_init(thread_count);
 141  if (!context->context_zmq) {
 142    return return_zmq_errno(env, zmq_errno());
 143  }
 144
 145  char thread_socket_id[64];
 146  sprintf(thread_socket_id, "inproc://erlzmq-%ld", (long int) context);
 147  context->thread_socket = zmq_socket(context->context_zmq, ZMQ_PUSH);
 148  assert(context->thread_socket);
 149  context->mutex = enif_mutex_create("erlzmq_context_t_mutex");
 150  assert(context->mutex);
 151  if (zmq_bind(context->thread_socket, thread_socket_id)) {
 152    zmq_close(context->thread_socket);
 153    enif_mutex_destroy(context->mutex);
 154    zmq_term(context->context_zmq);
 155    enif_release_resource(context);
 156    return return_zmq_errno(env, zmq_errno());
 157  }
 158  context->thread_socket_name = strdup(thread_socket_id);
 159  assert(context->thread_socket_name);
 160  context->socket_index = 1;
 161
 162  int const value_errno = enif_thread_create("erlzmq_polling_thread",
 163                                             &context->polling_tid,
 164                                             polling_thread, context, NULL);
 165  if (value_errno) {
 166    free(context->thread_socket_name);
 167    zmq_close(context->thread_socket);
 168    zmq_term(context->context_zmq);
 169    enif_release_resource(context);
 170    return return_zmq_errno(env, value_errno);
 171  }
 172
 173  return enif_make_tuple2(env, enif_make_atom(env, "ok"),
 174                          enif_make_resource(env, context));
 175}
 176
 177NIF(erlzmq_nif_socket)
 178{
 179  erlzmq_context_t * context;
 180  int socket_type;
 181  int active;
 182
 183  if (! enif_get_resource(env, argv[0], erlzmq_nif_resource_context,
 184                          (void **) &context)) {
 185    return enif_make_badarg(env);
 186  }
 187
 188  if (! enif_get_int(env, argv[1], &socket_type)) {
 189    return enif_make_badarg(env);
 190  }
 191
 192  if (! enif_get_int(env, argv[2], &active)) {
 193    return enif_make_badarg(env);
 194  }
 195  
 196  erlzmq_socket_t * socket = enif_alloc_resource(erlzmq_nif_resource_socket,
 197                                                 sizeof(erlzmq_socket_t));
 198  assert(socket);
 199  socket->context = context;
 200  socket->socket_index = context->socket_index++;
 201  socket->socket_zmq = zmq_socket(context->context_zmq, socket_type);
 202  if (!socket->socket_zmq) {
 203    return return_zmq_errno(env, zmq_errno());
 204  }
 205  socket->active = active;
 206  socket->mutex = enif_mutex_create("erlzmq_socket_t_mutex");
 207  assert(socket->mutex);
 208
 209  return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_tuple2(env,
 210                          enif_make_uint64(env, socket->socket_index),
 211                          enif_make_resource(env, socket)));
 212}
 213
 214NIF(erlzmq_nif_bind)
 215{
 216  erlzmq_socket_t * socket;
 217  unsigned endpoint_length;
 218
 219  if (! enif_get_resource(env, argv[0], erlzmq_nif_resource_socket,
 220                          (void **) &socket)) {
 221    return enif_make_badarg(env);
 222  }
 223
 224  if (! enif_get_list_length(env, argv[1], &endpoint_length)) {
 225    return enif_make_badarg(env);
 226  }
 227
 228  char * endpoint = (char *) malloc(endpoint_length + 1);
 229  if (! enif_get_string(env, argv[1], endpoint, endpoint_length + 1,
 230                        ERL_NIF_LATIN1)) {
 231    return enif_make_badarg(env);
 232  }
 233
 234  enif_mutex_lock(socket->mutex);
 235  if (zmq_bind(socket->socket_zmq, endpoint)) {
 236    enif_mutex_unlock(socket->mutex);
 237    free(endpoint);
 238    return return_zmq_errno(env, zmq_errno());
 239  }
 240  else {
 241    enif_mutex_unlock(socket->mutex);
 242    free(endpoint);
 243    if (socket->active == ERLZMQ_SOCKET_ACTIVE_PENDING) {
 244      return add_active_req(env, socket);
 245    }
 246    else {
 247      return enif_make_atom(env, "ok");
 248    }
 249  }
 250}
 251
 252NIF(erlzmq_nif_connect)
 253{
 254  erlzmq_socket_t * socket;
 255  unsigned endpoint_length;
 256
 257  if (! enif_get_resource(env, argv[0], erlzmq_nif_resource_socket,
 258                          (void **) &socket)) {
 259    return enif_make_badarg(env);
 260  }
 261
 262  if (! enif_get_list_length(env, argv[1], &endpoint_length)) {
 263    return enif_make_badarg(env);
 264  }
 265
 266  char * endpoint = (char *) malloc(endpoint_length + 1);
 267  if (! enif_get_string(env, argv[1], endpoint, endpoint_length + 1,
 268                        ERL_NIF_LATIN1)) {
 269    return enif_make_badarg(env);
 270  }
 271
 272  enif_mutex_lock(socket->mutex);
 273  if (zmq_connect(socket->socket_zmq, endpoint)) {
 274    enif_mutex_unlock(socket->mutex);
 275    free(endpoint);
 276    return return_zmq_errno(env, zmq_errno());
 277  }
 278  else {
 279    enif_mutex_unlock(socket->mutex);
 280    free(endpoint);
 281    if (socket->active == ERLZMQ_SOCKET_ACTIVE_PENDING) {
 282      return add_active_req(env, socket);
 283    }
 284    else {
 285      return enif_make_atom(env, "ok");
 286    }
 287  }
 288}
 289
 290NIF(erlzmq_nif_setsockopt)
 291{
 292  erlzmq_socket_t * socket;
 293  int option_name;
 294
 295  if (! enif_get_resource(env, argv[0], erlzmq_nif_resource_socket,
 296                          (void **) &socket)) {
 297    return enif_make_badarg(env);
 298  }
 299
 300  if (! enif_get_int(env, argv[1], &option_name)) {
 301    return enif_make_badarg(env);
 302  }
 303
 304  ErlNifUInt64 value_uint64;
 305  ErlNifSInt64 value_int64;
 306  ErlNifBinary value_binary;
 307  int value_int;
 308  void *option_value;
 309  size_t option_len;
 310  switch (option_name) {
 311    // uint64_t
 312    case ZMQ_HWM:
 313    case ZMQ_AFFINITY:
 314    case ZMQ_SNDBUF:
 315    case ZMQ_RCVBUF:
 316      if (! enif_get_uint64(env, argv[2], &value_uint64)) {
 317        return enif_make_badarg(env);
 318      }
 319      option_value = &value_uint64;
 320      option_len = sizeof(int64_t);
 321      break;
 322    // int64_t
 323    case ZMQ_SWAP:
 324    case ZMQ_RATE:
 325    case ZMQ_RECOVERY_IVL:
 326    case ZMQ_MCAST_LOOP:
 327      if (! enif_get_int64(env, argv[2], &value_int64)) {
 328        return enif_make_badarg(env);
 329      }
 330      option_value = &value_int64;
 331      option_len = sizeof(int64_t);
 332      break;
 333    // binary
 334    case ZMQ_IDENTITY:
 335    case ZMQ_SUBSCRIBE:
 336    case ZMQ_UNSUBSCRIBE:
 337      if (! enif_inspect_iolist_as_binary(env, argv[2], &value_binary)) {
 338        return enif_make_badarg(env);
 339      }
 340      option_value = value_binary.data;
 341      option_len = value_binary.size;
 342      break;
 343    // int
 344    case ZMQ_LINGER:
 345    case ZMQ_RECONNECT_IVL:
 346    case ZMQ_BACKLOG:
 347      if (! enif_get_int(env, argv[2], &value_int)) {
 348        return enif_make_badarg(env);
 349      }
 350      option_value = &value_int;
 351      option_len = sizeof(int);
 352      break;
 353    default:
 354      return enif_make_badarg(env);
 355  }
 356
 357  enif_mutex_lock(socket->mutex);
 358  if (zmq_setsockopt(socket->socket_zmq, option_name,
 359                     option_value, option_len)) {
 360    enif_mutex_unlock(socket->mutex);
 361    return return_zmq_errno(env, zmq_errno());
 362  }
 363  else {
 364    enif_mutex_unlock(socket->mutex);
 365    return enif_make_atom(env, "ok");
 366  }
 367}
 368
 369NIF(erlzmq_nif_getsockopt)
 370{
 371  erlzmq_socket_t * socket;
 372  int option_name;
 373
 374  if (! enif_get_resource(env, argv[0], erlzmq_nif_resource_socket,
 375                          (void **) &socket)) {
 376    return enif_make_badarg(env);
 377  }
 378
 379  if (! enif_get_int(env, argv[1], &option_name)) {
 380    return enif_make_badarg(env);
 381  }
 382
 383  ErlNifBinary value_binary;
 384  int64_t value_int64;
 385  int64_t value_uint64;
 386  char option_value[256];
 387  int value_int;
 388  size_t option_len;
 389  switch(option_name) {
 390    // int64_t
 391    case ZMQ_RCVMORE:
 392    case ZMQ_SWAP:
 393    case ZMQ_RATE:
 394    case ZMQ_RECOVERY_IVL:
 395    case ZMQ_RECOVERY_IVL_MSEC:
 396    case ZMQ_MCAST_LOOP:
 397      option_len = sizeof(value_int64);
 398      enif_mutex_lock(socket->mutex);
 399      if (zmq_getsockopt(socket->socket_zmq, option_name,
 400                         &value_int64, &option_len)) {
 401        enif_mutex_unlock(socket->mutex);
 402        return return_zmq_errno(env, zmq_errno());
 403      }
 404      enif_mutex_unlock(socket->mutex);
 405      return enif_make_tuple2(env, enif_make_atom(env, "ok"),
 406                              enif_make_int64(env, value_int64));
 407    // uint64_t
 408    case ZMQ_HWM:
 409    case ZMQ_AFFINITY:
 410    case ZMQ_SNDBUF:
 411    case ZMQ_RCVBUF:
 412      option_len = sizeof(value_uint64);
 413      enif_mutex_lock(socket->mutex);
 414      if (zmq_getsockopt(socket->socket_zmq, option_name,
 415                         &value_uint64, &option_len)) {
 416        enif_mutex_unlock(socket->mutex);
 417        return return_zmq_errno(env, zmq_errno());
 418      }
 419      enif_mutex_unlock(socket->mutex);
 420      return enif_make_tuple2(env, enif_make_atom(env, "ok"),
 421                              enif_make_uint64(env, value_uint64));
 422    // binary
 423    case ZMQ_IDENTITY:
 424      option_len = sizeof(option_value);
 425      enif_mutex_lock(socket->mutex);
 426      if (zmq_getsockopt(socket->socket_zmq, option_name,
 427                         option_value, &option_len)) {
 428        enif_mutex_unlock(socket->mutex);
 429        return return_zmq_errno(env, zmq_errno());
 430      }
 431      enif_mutex_unlock(socket->mutex);
 432      enif_alloc_binary(option_len, &value_binary);
 433      memcpy(value_binary.data, option_value, option_len);
 434      return enif_make_tuple2(env, enif_make_atom(env, "ok"),
 435                              enif_make_binary(env, &value_binary));
 436    // int
 437    case ZMQ_TYPE:
 438    case ZMQ_LINGER:
 439    case ZMQ_RECONNECT_IVL:
 440    case ZMQ_RECONNECT_IVL_MAX:
 441    case ZMQ_BACKLOG:
 442    case ZMQ_FD:   // FIXME: ZMQ_FD returns SOCKET on Windows
 443      option_len = sizeof(value_int);
 444      enif_mutex_lock(socket->mutex);
 445      if (zmq_getsockopt(socket->socket_zmq, option_name,
 446                         &value_int, &option_len)) {
 447        enif_mutex_unlock(socket->mutex);
 448        return return_zmq_errno(env, zmq_errno());
 449      }
 450      enif_mutex_unlock(socket->mutex);
 451      return enif_make_tuple2(env, enif_make_atom(env, "ok"),
 452                              enif_make_int(env, value_int));
 453    default:
 454      return enif_make_badarg(env);
 455  }
 456}
 457
 458NIF(erlzmq_nif_send)
 459{
 460  erlzmq_thread_request_t req;
 461  erlzmq_socket_t * socket;
 462  ErlNifBinary binary;
 463
 464  if (! enif_get_resource(env, argv[0], erlzmq_nif_resource_socket,
 465                          (void **) &socket)) {
 466    return enif_make_badarg(env);
 467  }
 468
 469  if (! enif_inspect_iolist_as_binary(env, argv[1], &binary)) {
 470    return enif_make_badarg(env);
 471  }
 472
 473  if (! enif_get_int(env, argv[2], &req.data.send.flags)) {
 474    return enif_make_badarg(env);
 475  }
 476
 477  if (zmq_msg_init_size(&req.data.send.msg, binary.size)) {
 478    return return_zmq_errno(env, zmq_errno());
 479  }
 480
 481  memcpy(zmq_msg_data(&req.data.send.msg), binary.data, binary.size);
 482
 483  int polling_thread_send = 1;
 484  if (! socket->active) {
 485    enif_mutex_lock(socket->mutex);
 486    if (zmq_send(socket->socket_zmq, &req.data.send.msg,
 487                 req.data.send.flags | ZMQ_NOBLOCK)) {
 488      enif_mutex_unlock(socket->mutex);
 489      int const error = zmq_errno();
 490      if (error != EAGAIN ||
 491          (error == EAGAIN && (req.data.send.flags & ZMQ_NOBLOCK))) {
 492        zmq_msg_close(&req.data.send.msg);
 493        return return_zmq_errno(env, error);
 494      }
 495    }
 496    else {
 497      enif_mutex_unlock(socket->mutex);
 498      polling_thread_send = 0;
 499    }
 500  }
 501  
 502  if (polling_thread_send) {
 503    req.type = ERLZMQ_THREAD_REQUEST_SEND;
 504    req.data.send.env = enif_alloc_env();
 505    req.data.send.ref = enif_make_ref(req.data.send.env);
 506    enif_self(env, &req.data.send.pid);
 507    req.data.send.socket = socket;
 508
 509    zmq_msg_t msg;
 510    if (zmq_msg_init_size(&msg, sizeof(erlzmq_thread_request_t))) {
 511      zmq_msg_close(&req.data.send.msg);
 512      enif_free_env(req.data.send.env);
 513      return return_zmq_errno(env, zmq_errno());
 514    }
 515
 516    memcpy(zmq_msg_data(&msg), &req, sizeof(erlzmq_thread_request_t));
 517
 518    enif_mutex_lock(socket->context->mutex);
 519    if (socket->context->thread_socket_name == NULL) {
 520      enif_mutex_unlock(socket->context->mutex);
 521      return return_zmq_errno(env, ETERM);
 522    }
 523    if (zmq_send(socket->context->thread_socket, &msg, 0)) {
 524      enif_mutex_unlock(socket->context->mutex);
 525
 526      zmq_msg_close(&msg);
 527      zmq_msg_close(&req.data.send.msg);
 528      enif_free_env(req.data.send.env);
 529      return return_zmq_errno(env, zmq_errno());
 530    }
 531    else {
 532      enif_mutex_unlock(socket->context->mutex);
 533
 534      zmq_msg_close(&msg);
 535      // each pointer to the socket in a request increments the reference
 536      enif_keep_resource(socket);
 537  
 538      return enif_make_copy(env, req.data.send.ref);
 539    }
 540  }
 541  else {
 542    zmq_msg_close(&req.data.send.msg);
 543
 544    return enif_make_atom(env, "ok");
 545  }
 546}
 547
 548NIF(erlzmq_nif_recv)
 549{
 550  erlzmq_thread_request_t req;
 551  erlzmq_socket_t * socket;
 552
 553  if (! enif_get_resource(env, argv[0], erlzmq_nif_resource_socket,
 554                          (void **) &socket)) {
 555    return enif_make_badarg(env);
 556  }
 557
 558  if (! enif_get_int(env, argv[1], &req.data.recv.flags)) {
 559    return enif_make_badarg(env);
 560  }
 561
 562  if (socket->active) {
 563    return enif_make_tuple2(env, enif_make_atom(env, "error"),
 564                            enif_make_atom(env, "active"));
 565  }
 566
 567  zmq_msg_t msg;
 568  if (zmq_msg_init(&msg)) {
 569    return return_zmq_errno(env, zmq_errno());
 570  }
 571
 572  // try recv with noblock
 573  enif_mutex_lock(socket->mutex);
 574  if (zmq_recv(socket->socket_zmq, &msg, ZMQ_NOBLOCK)) {
 575    enif_mutex_unlock(socket->mutex);
 576    zmq_msg_close(&msg);
 577
 578    int const error = zmq_errno();
 579    if (error != EAGAIN ||
 580        (error == EAGAIN && (req.data.recv.flags & ZMQ_NOBLOCK))) {
 581      return return_zmq_errno(env, error);
 582    }
 583    req.type = ERLZMQ_THREAD_REQUEST_RECV;
 584    req.data.recv.env = enif_alloc_env();
 585    req.data.recv.ref = enif_make_ref(req.data.recv.env);
 586    enif_self(env, &req.data.recv.pid);
 587    req.data.recv.socket = socket;
 588
 589    if (zmq_msg_init_size(&msg, sizeof(erlzmq_thread_request_t))) {
 590      enif_free_env(req.data.recv.env);
 591      return return_zmq_errno(env, zmq_errno());
 592    }
 593
 594    memcpy(zmq_msg_data(&msg), &req, sizeof(erlzmq_thread_request_t));
 595
 596    enif_mutex_lock(socket->context->mutex);
 597    if (socket->context->thread_socket_name == NULL) {
 598      enif_mutex_unlock(socket->context->mutex);
 599      return return_zmq_errno(env, ETERM);
 600    }
 601    if (zmq_send(socket->context->thread_socket, &msg, 0)) {
 602      enif_mutex_unlock(socket->context->mutex);
 603
 604      zmq_msg_close(&msg);
 605      enif_free_env(req.data.recv.env);
 606      return return_zmq_errno(env, zmq_errno());
 607    }
 608    else {
 609      enif_mutex_unlock(socket->context->mutex);
 610
 611      zmq_msg_close(&msg);
 612      // each pointer to the socket in a request increments the reference
 613      enif_keep_resource(socket);
 614  
 615      return enif_make_copy(env, req.data.recv.ref);
 616    }
 617  }
 618  else {
 619    enif_mutex_unlock(socket->mutex);
 620    
 621    ErlNifBinary binary;
 622    enif_alloc_binary(zmq_msg_size(&msg), &binary);
 623    memcpy(binary.data, zmq_msg_data(&msg), zmq_msg_size(&msg));
 624  
 625    zmq_msg_close(&msg);
 626  
 627    return enif_make_tuple2(env, enif_make_atom(env, "ok"),
 628                            enif_make_binary(env, &binary));
 629  }
 630}
 631
 632NIF(erlzmq_nif_close)
 633{
 634  erlzmq_socket_t * socket;
 635
 636  if (! enif_get_resource(env, argv[0], erlzmq_nif_resource_socket,
 637                          (void **) &socket)) {
 638    return enif_make_badarg(env);
 639  }
 640
 641  erlzmq_thread_request_t req;
 642  req.type = ERLZMQ_THREAD_REQUEST_CLOSE;
 643  req.data.close.env = enif_alloc_env();
 644  req.data.close.ref = enif_make_ref(req.data.close.env);
 645  enif_self(env, &req.data.close.pid);
 646  req.data.close.socket = socket;
 647
 648  zmq_msg_t msg;
 649  if (zmq_msg_init_size(&msg, sizeof(erlzmq_thread_request_t))) {
 650    enif_free_env(req.data.close.env);
 651    return return_zmq_errno(env, zmq_errno());
 652  }
 653
 654  memcpy(zmq_msg_data(&msg), &req, sizeof(erlzmq_thread_request_t));
 655
 656  enif_mutex_lock(socket->context->mutex);
 657  if (socket->context->thread_socket_name == NULL) {
 658    // context is gone
 659    enif_mutex_lock(socket->mutex);
 660    zmq_msg_close(&msg);
 661    zmq_close(socket->socket_zmq);
 662    enif_mutex_unlock(socket->mutex);
 663    enif_mutex_destroy(socket->mutex);
 664    enif_release_resource(socket);
 665    enif_mutex_unlock(socket->context->mutex);
 666    return enif_make_atom(env, "ok");
 667  }
 668  if (zmq_send(socket->context->thread_socket, &msg, 0)) {
 669    enif_mutex_unlock(socket->context->mutex);
 670    zmq_msg_close(&msg);
 671    enif_free_env(req.data.close.env);
 672    return return_zmq_errno(env, zmq_errno());
 673  }
 674  else {
 675    enif_mutex_unlock(socket->context->mutex);
 676    zmq_msg_close(&msg);
 677    // each pointer to the socket in a request increments the reference
 678    enif_keep_resource(socket);
 679    return enif_make_copy(env, req.data.close.ref);
 680  }
 681}
 682
 683NIF(erlzmq_nif_term)
 684{
 685  erlzmq_context_t * context;
 686
 687  if (!enif_get_resource(env, argv[0], erlzmq_nif_resource_context,
 688                         (void **) &context)) {
 689    return enif_make_badarg(env);
 690  }
 691
 692  erlzmq_thread_request_t req;
 693  req.type = ERLZMQ_THREAD_REQUEST_TERM;
 694  req.data.term.env = enif_alloc_env();
 695  req.data.term.ref = enif_make_ref(req.data.term.env);
 696  enif_self(env, &req.data.term.pid);
 697
 698  zmq_msg_t msg;
 699  if (zmq_msg_init_size(&msg, sizeof(erlzmq_thread_request_t))) {
 700    enif_free_env(req.data.term.env);
 701    return return_zmq_errno(env, zmq_errno());
 702  }
 703
 704  memcpy(zmq_msg_data(&msg), &req, sizeof(erlzmq_thread_request_t));
 705
 706  enif_mutex_lock(context->mutex);
 707  if (zmq_send(context->thread_socket, &msg, 0)) {
 708    enif_mutex_unlock(context->mutex);
 709    zmq_msg_close(&msg);
 710    enif_free_env(req.data.term.env);
 711    return return_zmq_errno(env, zmq_errno());
 712  }
 713  else {
 714    enif_mutex_unlock(context->mutex);
 715    zmq_msg_close(&msg);
 716    // thread has a reference to the context, decrement here
 717    enif_release_resource(context);
 718    return enif_make_copy(env, req.data.term.ref);
 719  }
 720}
 721
 722NIF(erlzmq_nif_version)
 723{
 724  int major, minor, patch;
 725  zmq_version(&major, &minor, &patch);
 726  return enif_make_tuple3(env, enif_make_int(env, major),
 727                          enif_make_int(env, minor),
 728                          enif_make_int(env, patch));
 729}
 730
 731static void * polling_thread(void * handle)
 732{
 733  erlzmq_context_t * context = (erlzmq_context_t *) handle;
 734  enif_keep_resource(context);
 735
 736  void * thread_socket = zmq_socket(context->context_zmq, ZMQ_PULL);
 737  assert(thread_socket);
 738  int status = zmq_connect(thread_socket, context->thread_socket_name);
 739  assert(status == 0);
 740
 741  vector_t items_zmq;
 742  status = vector_initialize_pow2(zmq_pollitem_t, &items_zmq, 1,
 743                                  ERLZMQ_MAX_CONCURRENT_REQUESTS);
 744  assert(status == 0);
 745  zmq_pollitem_t thread_socket_poll_zmq = {thread_socket, 0, ZMQ_POLLIN, 0};
 746  status = vector_append(zmq_pollitem_t, &items_zmq, &thread_socket_poll_zmq);
 747  assert(status == 0);
 748
 749  vector_t requests;
 750  status = vector_initialize_pow2(erlzmq_thread_request_t, &requests, 1,
 751                                  ERLZMQ_MAX_CONCURRENT_REQUESTS);
 752  assert(status == 0);
 753  erlzmq_thread_request_t request_empty;
 754  memset(&request_empty, 0, sizeof(erlzmq_thread_request_t));
 755  status = vector_append(erlzmq_thread_request_t, &requests, &request_empty);
 756  assert(status == 0);
 757
 758  int i;
 759  for (;;) {
 760    int count = zmq_poll(vector_p(zmq_pollitem_t, &items_zmq),
 761                         vector_count(&items_zmq), -1);
 762    assert(count != -1);
 763    if (vector_get(zmq_pollitem_t, &items_zmq, 0)->revents & ZMQ_POLLIN) {
 764      --count;
 765    }
 766    for (i = 1; i < vector_count(&items_zmq); ++i) {
 767      zmq_pollitem_t * item = vector_get(zmq_pollitem_t, &items_zmq, i);
 768      erlzmq_thread_request_t * r = vector_get(erlzmq_thread_request_t,
 769                                               &requests, i);
 770      if (item->revents & ZMQ_POLLIN) {
 771        size_t value_len = sizeof(int64_t);
 772        int64_t flag_value = 0;
 773        
 774        assert(r->type == ERLZMQ_THREAD_REQUEST_RECV);
 775        --count;
 776
 777        zmq_msg_t msg;
 778        zmq_msg_init(&msg);
 779        enif_mutex_lock(r->data.recv.socket->mutex);
 780        if (zmq_recv(r->data.recv.socket->socket_zmq, &msg,
 781                     r->data.recv.flags) ||
 782            (r->data.recv.socket->active == ERLZMQ_SOCKET_ACTIVE_ON && 
 783            zmq_getsockopt(r->data.recv.socket->socket_zmq, 
 784                    ZMQ_RCVMORE, &flag_value, &value_len)) )
 785        {
 786          enif_mutex_unlock(r->data.recv.socket->mutex);
 787          if (r->data.recv.socket->active == ERLZMQ_SOCKET_ACTIVE_ON) {
 788            enif_send(NULL, &r->data.recv.pid, r->data.recv.env,
 789              enif_make_tuple3(r->data.recv.env,
 790                enif_make_atom(r->data.recv.env, "zmq"),
 791                enif_make_tuple2(r->data.recv.env,
 792                  enif_make_uint64(r->data.recv.env,
 793                                   r->data.recv.socket->socket_index),
 794                  enif_make_resource(r->data.recv.env, r->data.recv.socket)),
 795                return_zmq_errno(r->data.recv.env, zmq_errno())));
 796            enif_free_env(r->data.recv.env);
 797            r->data.recv.env = enif_alloc_env();
 798            item->revents = 0;
 799          }
 800          else {
 801            assert(0);
 802          }
 803        }
 804        else {
 805          enif_mutex_unlock(r->data.recv.socket->mutex);
 806        }
 807
 808        ErlNifBinary binary;
 809        enif_alloc_binary(zmq_msg_size(&msg), &binary);
 810        memcpy(binary.data, zmq_msg_data(&msg), zmq_msg_size(&msg));
 811        zmq_msg_close(&msg);
 812
 813        if (r->data.recv.socket->active == ERLZMQ_SOCKET_ACTIVE_ON) {
 814          ERL_NIF_TERM flags_list;
 815          
 816          // Should we send the multipart flag
 817          if(flag_value == 1) {
 818            flags_list = enif_make_list1(r->data.recv.env, enif_make_atom(r->data.recv.env, "rcvmore"));
 819          } else {
 820            flags_list = enif_make_list(r->data.recv.env, 0);
 821          }
 822          
 823          enif_send(NULL, &r->data.recv.pid, r->data.recv.env,
 824            enif_make_tuple4(r->data.recv.env,
 825              enif_make_atom(r->data.recv.env, "zmq"),
 826              enif_make_tuple2(r->data.recv.env,
 827                enif_make_uint64(r->data.recv.env,
 828                                 r->data.recv.socket->socket_index),
 829                enif_make_resource(r->data.recv.env, r->data.recv.socket)),
 830              enif_make_binary(r->data.recv.env, &binary),
 831              flags_list));
 832          enif_free_env(r->data.recv.env);
 833          r->data.recv.env = enif_alloc_env();
 834          item->revents = 0;
 835        }
 836        else {
 837          enif_send(NULL, &r->data.recv.pid, r->data.recv.env,
 838            enif_make_tuple2(r->data.recv.env,
 839              enif_make_copy(r->data.recv.env, r->data.recv.ref),
 840              enif_make_binary(r->data.recv.env, &binary)));
 841
 842          enif_free_env(r->data.recv.env);
 843          enif_release_resource(r->data.recv.socket);
 844
 845          status = vector_remove(&items_zmq, i);
 846          assert(status == 0);
 847          status = vector_remove(&requests, i);
 848          assert(status == 0);
 849          --i;
 850        }
 851      }
 852      else if (item->revents & ZMQ_POLLOUT) {
 853        assert(r->type == ERLZMQ_THREAD_REQUEST_SEND);
 854        --count;
 855
 856        enif_mutex_lock(r->data.send.socket->mutex);
 857        if (zmq_send(r->data.send.socket->socket_zmq,
 858                     &r->data.send.msg, r->data.send.flags)) {
 859          enif_mutex_unlock(r->data.send.socket->mutex);
 860          enif_send(NULL, &r->data.send.pid, r->data.send.env,
 861            enif_make_tuple2(r->data.send.env,
 862              enif_make_copy(r->data.send.env, r->data.send.ref),
 863              return_zmq_errno(r->data.send.env, zmq_errno())));
 864        } else {
 865          enif_mutex_unlock(r->data.send.socket->mutex);
 866          enif_send(NULL, &r->data.send.pid, r->data.send.env,
 867            enif_make_tuple2(r->data.send.env,
 868              enif_make_copy(r->data.send.env, r->data.send.ref),
 869              enif_make_atom(r->data.send.env, "ok")));
 870        }
 871        zmq_msg_close(&r->data.send.msg);
 872        enif_free_env(r->data.send.env);
 873        enif_release_resource(r->data.send.socket);
 874
 875        status = vector_remove(&items_zmq, i);
 876        assert(status == 0);
 877        status = vector_remove(&requests, i);
 878        assert(status == 0);
 879        --i;
 880      }
 881    }
 882
 883    if (vector_get(zmq_pollitem_t, &items_zmq, 0)->revents & ZMQ_POLLIN) {
 884      vector_get(zmq_pollitem_t, &items_zmq, 0)->revents = 0;
 885      zmq_msg_t msg;
 886      zmq_msg_init(&msg);
 887      enif_mutex_lock(context->mutex);
 888      status = zmq_recv(thread_socket, &msg, 0);
 889      enif_mutex_unlock(context->mutex);
 890      assert(status == 0);
 891
 892      assert(zmq_msg_size(&msg) == sizeof(erlzmq_thread_request_t));
 893
 894      erlzmq_thread_request_t * r =
 895        (erlzmq_thread_request_t *) zmq_msg_data(&msg);
 896      if (r->type == ERLZMQ_THREAD_REQUEST_SEND) {
 897        zmq_pollitem_t item_zmq = {r->data.send.socket->socket_zmq,
 898                                   0, ZMQ_POLLOUT, 0};
 899        status = vector_append(zmq_pollitem_t, &items_zmq, &item_zmq);
 900        assert(status == 0);
 901        status = vector_append(erlzmq_thread_request_t, &requests, r);
 902        assert(status == 0);
 903        zmq_msg_close(&msg);
 904      }
 905      else if (r->type == ERLZMQ_THREAD_REQUEST_RECV) {
 906        zmq_pollitem_t item_zmq = {r->data.recv.socket->socket_zmq,
 907                                   0, ZMQ_POLLIN, 0};
 908        status = vector_append(zmq_pollitem_t, &items_zmq, &item_zmq);
 909        assert(status == 0);
 910        status = vector_append(erlzmq_thread_request_t, &requests, r);
 911        assert(status == 0);
 912        zmq_msg_close(&msg);
 913      }
 914      else if (r->type == ERLZMQ_THREAD_REQUEST_CLOSE) {
 915        // remove all entries with this socket
 916        for (i = vector_count(&items_zmq) - 1; i > 0; --i) {
 917          zmq_pollitem_t * item = vector_get(zmq_pollitem_t, &items_zmq, i);
 918          if (item->socket == r->data.close.socket->socket_zmq) {
 919            erlzmq_thread_request_t * r_old =
 920              vector_get(erlzmq_thread_request_t, &requests, i);
 921            if (r_old->type == ERLZMQ_THREAD_REQUEST_RECV) {
 922              enif_clear_env(r_old->data.recv.env);
 923              // FIXME
 924              // causes crash on R14B01, works fine on R14B02
 925              // (repeated enif_send with active receive broken on R14B01)
 926              //enif_free_env(r_old->data.recv.env);
 927              enif_release_resource(r_old->data.recv.socket);
 928            }
 929            else if (r_old->type == ERLZMQ_THREAD_REQUEST_SEND) {
 930              zmq_msg_close(&(r_old->data.send.msg));
 931              enif_free_env(r_old->data.send.env);
 932              enif_release_resource(r_old->data.send.socket);
 933            }
 934            else {
 935              assert(0);
 936            }
 937            status = vector_remove(&items_zmq, i);
 938            assert(status == 0);
 939            status = vector_remove(&requests, i);
 940            assert(status == 0);
 941          }
 942        }
 943        // close the socket
 944        enif_mutex_lock(r->data.close.socket->mutex);
 945        zmq_close(r->data.close.socket->socket_zmq);
 946        enif_mutex_unlock(r->data.close.socket->mutex);
 947        enif_mutex_destroy(r->data.close.socket->mutex);
 948        enif_release_resource(r->data.close.socket);
 949        // notify the waiting request
 950        enif_send(NULL, &r->data.close.pid, r->data.close.env,
 951          enif_make_tuple2(r->data.close.env,
 952            enif_make_copy(r->data.close.env, r->data.close.ref),
 953            enif_make_atom(r->data.close.env, "ok")));
 954        enif_free_env(r->data.close.env);
 955        zmq_msg_close(&msg);
 956      }
 957      else if (r->type == ERLZMQ_THREAD_REQUEST_TERM) {
 958        enif_mutex_lock(context->mutex);
 959        free(context->thread_socket_name);
 960        // use this to flag context is over
 961        context->thread_socket_name = NULL;
 962        enif_mutex_unlock(context->mutex);
 963        // cleanup pending requests
 964        for (i = 1; i < vector_count(&requests); ++i) {
 965          erlzmq_thread_request_t * r_old = vector_get(erlzmq_thread_request_t,
 966                                                       &requests, i);
 967          if (r_old->type == ERLZMQ_THREAD_REQUEST_RECV) {
 968            enif_free_env(r_old->data.recv.env);
 969            enif_release_resource(r_old->data.recv.socket);
 970            zmq_close(r_old->data.recv.socket->socket_zmq);
 971          }
 972          else if (r_old->type == ERLZMQ_THREAD_REQUEST_SEND) {
 973            zmq_msg_close(&r_old->data.send.msg);
 974            enif_free_env(r_old->data.send.env);
 975            enif_release_resource(r_old->data.send.socket);
 976            zmq_close(r_old->data.send.socket->socket_zmq);
 977          }
 978        }
 979        // terminate the context
 980        enif_mutex_lock(context->mutex);
 981        zmq_close(thread_socket);
 982        zmq_close(context->thread_socket);
 983        enif_mutex_unlock(context->mutex);
 984        zmq_term(context->context_zmq);
 985        enif_mutex_lock(context->mutex);
 986        enif_mutex_unlock(context->mutex);
 987        enif_mutex_destroy(context->mutex);
 988        enif_release_resource(context);
 989        // notify the waiting request
 990        enif_send(NULL, &r->data.term.pid, r->data.term.env,
 991          enif_make_tuple2(r->data.term.env,
 992            enif_make_copy(r->data.term.env, r->data.term.ref),
 993            enif_make_atom(r->data.term.env, "ok")));
 994        enif_free_env(r->data.term.env);
 995        zmq_msg_close(&msg);
 996        vector_destroy(&items_zmq);
 997        vector_destroy(&requests);
 998        return NULL;
 999      }
1000      else {
1001        assert(0);
1002      }
1003    }
1004  }
1005  return NULL;
1006}
1007
1008static ERL_NIF_TERM add_active_req(ErlNifEnv* env, erlzmq_socket_t * socket)
1009{
1010  socket->active = ERLZMQ_SOCKET_ACTIVE_ON;
1011
1012  erlzmq_thread_request_t req;
1013  req.type = ERLZMQ_THREAD_REQUEST_RECV;
1014  req.data.recv.env = enif_alloc_env();
1015  req.data.recv.flags = 0;
1016  enif_self(env, &req.data.recv.pid);
1017  req.data.recv.socket = socket;
1018
1019  zmq_msg_t msg;
1020  if (zmq_msg_init_size(&msg, sizeof(erlzmq_thread_request_t))) {
1021    enif_free_env(req.data.recv.env);
1022    return return_zmq_errno(env, zmq_errno());
1023  }
1024
1025  memcpy(zmq_msg_data(&msg), &req, sizeof(erlzmq_thread_request_t));
1026
1027  if (zmq_send(socket->context->thread_socket, &msg, 0)) {
1028    zmq_msg_close(&msg);
1029    enif_free_env(req.data.recv.env);
1030    return return_zmq_errno(env, zmq_errno());
1031  }
1032  else {
1033    zmq_msg_close(&msg);
1034    // each pointer to the socket in a request increments the reference
1035    enif_keep_resource(socket);
1036    return enif_make_atom(env, "ok");
1037  }
1038}
1039
1040static ERL_NIF_TERM return_zmq_errno(ErlNifEnv* env, int const value)
1041{
1042  switch (value) {
1043    case EPERM:
1044      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1045                              enif_make_atom(env, "eperm"));
1046    case ENOENT:
1047      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1048                              enif_make_atom(env, "enoent"));
1049    case ESRCH:
1050      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1051                              enif_make_atom(env, "esrch"));
1052    case EINTR:
1053      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1054                              enif_make_atom(env, "eintr"));
1055    case EIO:
1056      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1057                              enif_make_atom(env, "eio"));
1058    case ENXIO:
1059      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1060                              enif_make_atom(env, "enxio"));
1061    case ENOEXEC:
1062      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1063                              enif_make_atom(env, "enoexec"));
1064    case EBADF:
1065      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1066                              enif_make_atom(env, "ebadf"));
1067    case ECHILD:
1068      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1069                              enif_make_atom(env, "echild"));
1070    case EDEADLK:
1071      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1072                              enif_make_atom(env, "edeadlk"));
1073    case ENOMEM:
1074      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1075                              enif_make_atom(env, "enomem"));
1076    case EACCES:
1077      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1078                              enif_make_atom(env, "eacces"));
1079    case EFAULT:
1080      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1081                              enif_make_atom(env, "efault"));
1082    case ENOTBLK:
1083      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1084                              enif_make_atom(env, "enotblk"));
1085    case EBUSY:
1086      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1087                              enif_make_atom(env, "ebusy"));
1088    case EEXIST:
1089      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1090                              enif_make_atom(env, "eexist"));
1091    case EXDEV:
1092      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1093                              enif_make_atom(env, "exdev"));
1094    case ENODEV:
1095      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1096                              enif_make_atom(env, "enodev"));
1097    case ENOTDIR:
1098      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1099                              enif_make_atom(env, "enotdir"));
1100    case EISDIR:
1101      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1102                              enif_make_atom(env, "eisdir"));
1103    case EINVAL:
1104      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1105                              enif_make_atom(env, "einval"));
1106    case ENFILE:
1107      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1108                              enif_make_atom(env, "enfile"));
1109    case EMFILE:
1110      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1111                              enif_make_atom(env, "emfile"));
1112    case ETXTBSY:
1113      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1114                              enif_make_atom(env, "etxtbsy"));
1115    case EFBIG:
1116      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1117                              enif_make_atom(env, "efbig"));
1118    case ENOSPC:
1119      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1120                              enif_make_atom(env, "enospc"));
1121    case ESPIPE:
1122      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1123                              enif_make_atom(env, "espipe"));
1124    case EROFS:
1125      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1126                              enif_make_atom(env, "erofs"));
1127    case EMLINK:
1128      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1129                              enif_make_atom(env, "emlink"));
1130    case EPIPE:
1131      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1132                              enif_make_atom(env, "epipe"));
1133    case EAGAIN:
1134      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1135                              enif_make_atom(env, "eagain"));
1136    case EINPROGRESS:
1137      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1138                              enif_make_atom(env, "einprogress"));
1139    case EALREADY:
1140      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1141                              enif_make_atom(env, "ealready"));
1142    case ENOTSOCK:
1143      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1144                              enif_make_atom(env, "enotsock"));
1145    case EDESTADDRREQ:
1146      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1147                              enif_make_atom(env, "edestaddrreq"));
1148    case EMSGSIZE:
1149      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1150                              enif_make_atom(env, "emsgsize"));
1151    case EPROTOTYPE:
1152      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1153                              enif_make_atom(env, "eprototype"));
1154    case ENOPROTOOPT:
1155      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1156                              enif_make_atom(env, "eprotoopt"));
1157    case EPROTONOSUPPORT:
1158      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1159                              enif_make_atom(env, "eprotonosupport"));
1160    case ESOCKTNOSUPPORT:
1161      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1162                              enif_make_atom(env, "esocktnosupport"));
1163    case ENOTSUP:
1164      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1165                              enif_make_atom(env, "enotsup"));
1166    case EPFNOSUPPORT:
1167      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1168                              enif_make_atom(env, "epfnosupport"));
1169    case EAFNOSUPPORT:
1170      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1171                              enif_make_atom(env, "eafnosupport"));
1172    case EADDRINUSE:
1173      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1174                              enif_make_atom(env, "eaddrinuse"));
1175    case EADDRNOTAVAIL:
1176      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1177                              enif_make_atom(env, "eaddrnotavail"));
1178    case ENETDOWN:
1179      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1180                              enif_make_atom(env, "enetdown"));
1181    case ENETUNREACH:
1182      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1183                              enif_make_atom(env, "enetunreach"));
1184    case ENETRESET:
1185      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1186                              enif_make_atom(env, "enetreset"));
1187    case ECONNABORTED:
1188      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1189                              enif_make_atom(env, "econnaborted"));
1190    case ECONNRESET:
1191      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1192                              enif_make_atom(env, "econnreset"));
1193    case ENOBUFS:
1194      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1195                              enif_make_atom(env, "enobufs"));
1196    case EISCONN:
1197      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1198                              enif_make_atom(env, "eisconn"));
1199    case ENOTCONN:
1200      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1201                              enif_make_atom(env, "enotconn"));
1202    case ESHUTDOWN:
1203      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1204                              enif_make_atom(env, "eshutdown"));
1205    case ETOOMANYREFS:
1206      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1207                              enif_make_atom(env, "etoomanyrefs"));
1208    case ETIMEDOUT:
1209      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1210                              enif_make_atom(env, "etimedout"));
1211    case ECONNREFUSED:
1212      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1213                              enif_make_atom(env, "econnrefused"));
1214    case ELOOP:
1215      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1216                              enif_make_atom(env, "eloop"));
1217    case ENAMETOOLONG:
1218      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1219                              enif_make_atom(env, "enametoolong"));
1220    case (ZMQ_HAUSNUMERO +  1):
1221      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1222                              enif_make_atom(env, "enotsup"));
1223    case (ZMQ_HAUSNUMERO +  2):
1224      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1225                              enif_make_atom(env, "eprotonosupport"));
1226    case (ZMQ_HAUSNUMERO +  3):
1227      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1228                              enif_make_atom(env, "enobufs"));
1229    case (ZMQ_HAUSNUMERO +  4):
1230      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1231                              enif_make_atom(env, "enetdown"));
1232    case (ZMQ_HAUSNUMERO +  5):
1233      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1234                              enif_make_atom(env, "eaddrinuse"));
1235    case (ZMQ_HAUSNUMERO +  6):
1236      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1237                              enif_make_atom(env, "eaddrnotavail"));
1238    case (ZMQ_HAUSNUMERO +  7):
1239      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1240                              enif_make_atom(env, "econnrefused"));
1241    case (ZMQ_HAUSNUMERO +  8):
1242      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1243                              enif_make_atom(env, "einprogress"));
1244    case (ZMQ_HAUSNUMERO + 51):
1245      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1246                              enif_make_atom(env, "efsm"));
1247    case (ZMQ_HAUSNUMERO + 52):
1248      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1249                              enif_make_atom(env, "enocompatproto"));
1250    case (ZMQ_HAUSNUMERO + 53):
1251      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1252                              enif_make_atom(env, "eterm"));
1253    case (ZMQ_HAUSNUMERO + 54):
1254      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1255                              enif_make_atom(env, "emthread"));
1256    default:
1257      return enif_make_tuple2(env, enif_make_atom(env, "error"),
1258                              enif_make_int(env, value));
1259  }
1260}
1261
1262static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
1263{
1264  erlzmq_nif_resource_context =
1265    enif_open_resource_type(env, "erlzmq_nif",
1266                            "erlzmq_nif_resource_context",
1267                            NULL,
1268                            ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER,
1269                            0);
1270  erlzmq_nif_resource_socket =
1271    enif_open_resource_type(env, "erlzmq_nif",
1272                            "erlzmq_nif_resource_socket",
1273                            NULL,
1274                            ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER,
1275                            0);
1276  return 0;
1277}
1278
1279static void on_unload(ErlNifEnv* env, void* priv_data) {
1280}
1281
1282ERL_NIF_INIT(erlzmq_nif, nif_funcs, &on_load, NULL, NULL, &on_unload);
1283