summaryrefslogtreecommitdiff
path: root/erts
diff options
context:
space:
mode:
authorMicael Karlberg <bmk@erlang.org>2023-04-05 17:59:15 +0200
committerMicael Karlberg <bmk@erlang.org>2023-04-05 17:59:15 +0200
commit30a8a6cf56df07579aae6f47d4a890bc94fa689c (patch)
tree151bae9a754537e77f3cb041f53a672ae369f439 /erts
parent52b5b71607a0693fbc3a16267d7dc40834f66f9b (diff)
parent2bbcf3d17c19cb0883a75717849d531277dfe339 (diff)
downloaderlang-30a8a6cf56df07579aae6f47d4a890bc94fa689c.tar.gz
Merge branch 'bmk/erts/esock/20230331/win_async_io2/OTP-18029'
Diffstat (limited to 'erts')
-rw-r--r--erts/emulator/Makefile.in3
-rw-r--r--erts/emulator/nifs/common/prim_socket_int.h159
-rw-r--r--erts/emulator/nifs/common/prim_socket_nif.c6852
-rw-r--r--erts/emulator/nifs/common/socket_asyncio.h172
-rw-r--r--erts/emulator/nifs/common/socket_int.h72
-rw-r--r--erts/emulator/nifs/common/socket_io.h80
-rw-r--r--erts/emulator/nifs/common/socket_syncio.h55
-rw-r--r--erts/emulator/nifs/common/socket_util.c122
-rw-r--r--erts/emulator/nifs/common/socket_util.h30
-rw-r--r--erts/emulator/nifs/unix/unix_socket_syncio.c2714
-rw-r--r--erts/emulator/nifs/win32/win_socket_asyncio.c9600
-rw-r--r--erts/preloaded/ebin/prim_socket.beambin36920 -> 36524 bytes
-rw-r--r--erts/preloaded/src/prim_socket.erl10
13 files changed, 15305 insertions, 4564 deletions
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 0a488a010b..d911febb81 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -1150,7 +1150,8 @@ OS_OBJS = \
$(OBJDIR)/sys_interrupt.o \
$(OBJDIR)/sys_env.o \
$(OBJDIR)/dosmap.o \
- $(OBJDIR)/win_prim_file.o
+ $(OBJDIR)/win_prim_file.o \
+ $(OBJDIR)/win_socket_asyncio.o
else
OS_OBJS = \
diff --git a/erts/emulator/nifs/common/prim_socket_int.h b/erts/emulator/nifs/common/prim_socket_int.h
index 855011446b..90937d308e 100644
--- a/erts/emulator/nifs/common/prim_socket_int.h
+++ b/erts/emulator/nifs/common/prim_socket_int.h
@@ -27,6 +27,7 @@
#define PRIM_SOCKET_INT_H__
#include <erl_nif.h>
+#include <sys.h>
#include "socket_int.h"
#include "socket_dbg.h"
@@ -40,6 +41,7 @@
#if defined(__WIN32__)
#define INVALID_EVENT NULL
+#define SOCKET_FORMAT_STR "%lld"
#else
@@ -48,6 +50,7 @@ typedef int HANDLE;
#define INVALID_SOCKET (-1)
typedef int SOCKET; /* A subset of HANDLE */
#define INVALID_EVENT INVALID_HANDLE
+#define SOCKET_FORMAT_STR "%d"
#endif
@@ -77,12 +80,18 @@ typedef int SOCKET; /* A subset of HANDLE */
//
#define ESOCK_STATE_DTOR 0x8000
+#define IS_BOUND(st) \
+ (((st) & ESOCK_STATE_BOUND) != 0)
+
#define IS_CLOSED(st) \
(((st) & ESOCK_STATE_CLOSED) != 0)
#define IS_CLOSING(st) \
(((st) & ESOCK_STATE_CLOSING) != 0)
+#define IS_ACCEPTING(st) \
+ (((st) & ESOCK_STATE_ACCEPTING) != 0)
+
#define IS_OPEN(st) \
(((st) & (ESOCK_STATE_CLOSED | ESOCK_STATE_CLOSING)) == 0)
@@ -90,6 +99,9 @@ typedef int SOCKET; /* A subset of HANDLE */
((((d)->readState | (d)->writeState) & ESOCK_STATE_SELECTED) != 0)
+#define ESOCK_DESC_PATTERN_CREATED 0x03030303
+#define ESOCK_DESC_PATTERN_DTOR 0xC0C0C0C0
+
/* ==========================================================================
* The ESOCK_IS_ERROR macro below is used for portability reasons.
@@ -319,7 +331,12 @@ typedef struct {
typedef struct {
int flag;
ERL_NIF_TERM* name;
-} ESockMsgFlag;
+} ESockFlag;
+
+extern const ESockFlag esock_msg_flags[];
+extern const int esock_msg_flags_length;
+extern const ESockFlag esock_ioctl_flags[];
+extern const int esock_ioctl_flags_length;
/* ********************************************************************* *
@@ -335,7 +352,6 @@ typedef struct {
/* XXX Should be locked but too awkward and small gain */
BOOLEAN_T dbg;
BOOLEAN_T useReg;
- unsigned int ioNumThreads; // Set once and never changed
/* Registry stuff */
ErlNifPid regPid; /* Constant - not locked */
@@ -404,8 +420,21 @@ typedef struct {
ErlNifMutex* writeMtx;
/**/
unsigned int writeState; // For debugging
+#ifndef __WIN32__
+ /*
+ * On *none* Windows:
+ * This is intended for the *current* writer.
+ * The queue is intended for *waiting* writers.
+ *
+ * *On* Windows:
+ * We let the I/O Completion Ports handle the queue'ing
+ * so we do not need to keep track which request is active
+ * and which are waiting.
+ * We only use the *queue* as a database.
+ */
ESockRequestor currentWriter;
ESockRequestor* currentWriterP; // NULL or &currentWriter
+#endif
ESockRequestQueue writersQ;
ESockCounter writePkgCnt;
ESockCounter writePkgMax;
@@ -418,6 +447,7 @@ typedef struct {
HANDLE sendfileHandle;
ESockSendfileCounters* sendfileCountersP;
#endif
+
/* +++ Connector +++ */
ESockRequestor connector;
ESockRequestor* connectorP; // NULL or &connector
@@ -429,8 +459,21 @@ typedef struct {
ErlNifMutex* readMtx;
/**/
unsigned int readState; // For debugging
+#ifndef __WIN32__
+ /*
+ * On *none* Windows:
+ * This is intended for the *current* reader.
+ * The queue is intended for *waiting* readers.
+ *
+ * *On* Windows:
+ * We let the I/O Completion Ports handle the queue'ing
+ * so we do not need to keep track which request is active
+ * and which are waiting.
+ * We only use the *queue* as a database.
+ */
ESockRequestor currentReader;
ESockRequestor* currentReaderP; // NULL or &currentReader
+#endif
ESockRequestQueue readersQ;
ErlNifBinary rbuffer; // DO WE NEED THIS
Uint32 readCapacity; // DO WE NEED THIS
@@ -441,9 +484,23 @@ typedef struct {
ESockCounter readTries;
ESockCounter readWaits;
ESockCounter readFails;
+
/* +++ Accept stuff +++ */
+#ifndef __WIN32__
+ /*
+ * On *none* Windows:
+ * This is intended for the *current* acceptor.
+ * The queue is intended for *waiting* acceptors.
+ *
+ * *On* Windows:
+ * We let the I/O Completion Ports handle the queue'ing
+ * so we do not need to keep track which request is active
+ * and which are waiting.
+ * We only use the *queue* as a database.
+ */
ESockRequestor currentAcceptor;
ESockRequestor* currentAcceptorP; // NULL or &currentAcceptor
+#endif
ESockRequestQueue acceptorsQ;
ESockCounter accSuccess;
ESockCounter accTries;
@@ -456,10 +513,14 @@ typedef struct {
* If rNum is 0 (zero), then rNumCnt is not used and only *one* read will
* be done. Also, when get'ing the value of the option (rcvbuf) with
* getopt, the value will be reported as an integer. If the rNum has a
- * value greater then 0 (zero), then it will instead be reported as {N, BufSz}.
+ * value greater then 0 (zero), then it will instead be reported as
+ * {N, BufSz}.
+ * On Windows, rNum and rNumCnt is *not* used!
*/
+#ifndef __WIN32__
unsigned int rNum; // recv: Number of reads using rBufSz
unsigned int rNumCnt; // recv: Current number of reads (so far)
+#endif
size_t rCtrlSz; // Read control buffer size
/* Locked by readMtx and writeMtx combined for writing,
@@ -477,7 +538,6 @@ typedef struct {
ESockMonitor ctrlMon;
/* +++ The actual socket +++ */
SOCKET sock;
- ErlNifEvent event;
SOCKET origFD; // A 'socket' created from this FD
BOOLEAN_T closeOnClose; // Have we dup'ed or not
/* +++ The dbg flag for SSDBG +++ */
@@ -486,6 +546,11 @@ typedef struct {
/* Lock order: readMtx, writeMtx, cntMtx
*/
+
+#if defined(ESOCK_DESCRIPTOR_FILLER)
+ char filler[1024];
+#endif
+
} ESockDescriptor;
@@ -503,7 +568,17 @@ extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */
* ======================================================================== *
*/
-extern ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event);
+extern ESockDescriptor* esock_alloc_descriptor(SOCKET sock);
+extern void esock_dealloc_descriptor(ErlNifEnv* env,
+ ESockDescriptor* descP);
+
+extern BOOLEAN_T esock_open_is_debug(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ BOOLEAN_T def);
+extern BOOLEAN_T esock_open_use_registry(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ BOOLEAN_T def);
+extern BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto);
extern BOOLEAN_T esock_getopt_int(SOCKET sock,
int level,
@@ -512,17 +587,20 @@ extern BOOLEAN_T esock_getopt_int(SOCKET sock,
/* ** Socket Registry functions *** */
-#ifndef __WIN32__
extern void esock_send_reg_add_msg(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef);
extern void esock_send_reg_del_msg(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef);
-#endif // #ifndef __WIN32__
/* *** Message sending functions *** */
+extern void esock_send_simple_abort_msg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifPid* pid,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM reason);
extern void esock_send_abort_msg(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
@@ -581,9 +659,21 @@ extern int esock_select_cancel(ErlNifEnv* env,
ErlNifEvent event,
enum ErlNifSelectFlags mode,
void* obj);
+extern ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef);
+extern ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef);
+extern ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ int smode,
+ int rmode);
/* *** Request queue functions *** */
+extern void esock_free_request_queue(ESockRequestQueue* q);
extern BOOLEAN_T esock_requestor_pop(ESockRequestQueue* q,
ESockRequestor* reqP);
@@ -593,6 +683,7 @@ extern void esock_requestor_release(const char* slogan,
ESockDescriptor* descP,
ESockRequestor* reqP);
+
/* *** esock_activate_next_acceptor ***
* *** esock_activate_next_writer ***
* *** esock_activate_next_reader ***
@@ -637,7 +728,8 @@ ACTIVATE_NEXT_FUNCS_DEFS
extern void esock_##O##_push(ErlNifEnv* env, \
ESockDescriptor* descP, \
ErlNifPid pid, \
- ERL_NIF_TERM ref); \
+ ERL_NIF_TERM ref, \
+ void* dataP); \
extern BOOLEAN_T esock_##O##_pop(ErlNifEnv* env, \
ESockDescriptor* descP, \
ESockRequestor* reqP); \
@@ -652,6 +744,7 @@ ESOCK_OPERATOR_FUNCS_DEFS
/* *** Environment wrapper functions ***
* These hould really be inline, but for now...
*/
+extern void esock_clear_env(const char* slogan, ErlNifEnv* env);
extern void esock_free_env(const char* slogan, ErlNifEnv* env);
extern ErlNifEnv* esock_alloc_env(const char* slogan);
@@ -696,6 +789,30 @@ extern void esock_encode_msg_flags(ErlNifEnv* env,
#endif
+extern void esock_stop_handle_current(ErlNifEnv* env,
+ const char* role,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ESockRequestor* reqP);
+extern void esock_inform_waiting_procs(ErlNifEnv* env,
+ const char* role,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ESockRequestQueue* q,
+ ERL_NIF_TERM reason);
+
+
+/* *** Control Message 'stuff' ***
+ */
+extern void* esock_init_cmsghdr(struct cmsghdr* cmsgP,
+ size_t rem, // Remaining space
+ size_t size, // Size of data
+ size_t* usedP);
+extern ESockCmsgSpec* esock_lookup_cmsg_table(int level, size_t *num);
+extern ESockCmsgSpec* esock_lookup_cmsg_spec(ESockCmsgSpec* table,
+ size_t num,
+ ERL_NIF_TERM eType);
+
/* *** Sendfile 'stuff' ***
*/
#ifdef HAVE_SENDFILE
@@ -704,16 +821,32 @@ extern ESockSendfileCounters initESockSendfileCounters;
#endif
+/* *** message functions ****
+ */
+extern void esock_send_wrap_msg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM cnt);
+extern BOOLEAN_T esock_send_msg(ErlNifEnv* env,
+ ErlNifPid* pid,
+ ERL_NIF_TERM msg,
+ ErlNifEnv* msgEnv);
+extern ERL_NIF_TERM esock_mk_socket_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM tag,
+ ERL_NIF_TERM info);
+extern ERL_NIF_TERM esock_mk_socket(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef);
+#ifdef HAVE_SENDFILE
+extern void esock_send_sendfile_deferred_close_msg(ErlNifEnv* env,
+ ESockDescriptor* descP);
+#endif
+
/* *** 'close' functions ***
*/
-#ifndef __WIN32__
-extern BOOLEAN_T esock_do_stop(ErlNifEnv* env,
- ESockDescriptor* descP);
extern int esock_close_socket(ErlNifEnv* env,
ESockDescriptor* descP,
BOOLEAN_T unlock);
-#endif
-
#endif // PRIM_SOCKET_INT_H__
diff --git a/erts/emulator/nifs/common/prim_socket_nif.c b/erts/emulator/nifs/common/prim_socket_nif.c
index 8e0c4d1d6a..a46e44d53b 100644
--- a/erts/emulator/nifs/common/prim_socket_nif.c
+++ b/erts/emulator/nifs/common/prim_socket_nif.c
@@ -125,6 +125,10 @@ ERL_NIF_INIT(prim_socket, esock_funcs, on_load, NULL, NULL, NULL)
* *
* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
+#define ESOCK_CMSG_SPACE(l) WSA_CMSG_SPACE((l))
+#define ESOCK_CMSG_LEN(l) WSA_CMSG_LEN((l))
+#define ESOCK_CMSG_DATA(p) WSA_CMSG_DATA((p))
+
#define STRNCASECMP strncasecmp
#define INCL_WINSOCK_API_TYPEDEFS 1
@@ -167,6 +171,10 @@ ERL_NIF_INIT(prim_socket, esock_funcs, on_load, NULL, NULL, NULL)
* *
* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
+#define ESOCK_CMSG_SPACE(l) CMSG_SPACE((l))
+#define ESOCK_CMSG_LEN(l) CMSG_LEN((l))
+#define ESOCK_CMSG_DATA(p) CMSG_DATA((p))
+
#include <sys/time.h>
#ifdef NETDB_H_NEEDS_IN_H
@@ -387,6 +395,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
#include "socket_util.h"
#include "prim_socket_int.h"
#include "socket_io.h"
+#include "socket_asyncio.h"
#include "socket_syncio.h"
#include "prim_file_nif_dyncall.h"
@@ -447,9 +456,6 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
#define ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024
#define ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT 1024
-#define ESOCK_DESC_PATTERN_CREATED 0x03030303
-#define ESOCK_DESC_PATTERN_DTOR 0xC0C0C0C0
-
/*----------------------------------------------------------------------------
* Interface constants.
@@ -458,8 +464,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
* msg_flag() in socket.erl.
*/
-const ESockMsgFlag msg_flags[] = {
-
+const ESockFlag esock_msg_flags[] = {
{
#ifdef MSG_CMSG_CLOEXEC
MSG_CMSG_CLOEXEC,
@@ -548,13 +553,9 @@ const ESockMsgFlag msg_flags[] = {
#endif
&esock_atom_trunc}
};
+const int esock_msg_flags_length = NUM(esock_msg_flags);
-
-static const struct ioctl_flag {
- int flag;
- ERL_NIF_TERM *name;
-} ioctl_flags[] = {
-
+const ESockFlag esock_ioctl_flags[] = {
{
#ifdef IFF_UP
IFF_UP,
@@ -824,6 +825,7 @@ static const struct ioctl_flag {
#endif
&esock_atom_nogroup}
};
+const int esock_ioctl_flags_length = NUM(esock_ioctl_flags);
@@ -879,29 +881,29 @@ static const struct ioctl_flag {
/* *** Windows macros *** */
-#define sock_accept(s, addr, len) \
- make_noninheritable_handle(accept((s), (addr), (len)))
-#define sock_bind(s, addr, len) bind((s), (addr), (len))
+/* #define sock_accept(s, addr, len) \
+ make_noninheritable_handle(accept((s), (addr), (len))) */
+// #define sock_bind(s, addr, len) bind((s), (addr), (len))
#define sock_close(s) closesocket((s))
-#define sock_close_event(e) WSACloseEvent(e)
-#define sock_connect(s, addr, len) connect((s), (addr), (len))
-#define sock_create_event(s) WSACreateEvent()
+// #define sock_close_event(e) WSACloseEvent(e)
+// #define sock_connect(s, addr, len) connect((s), (addr), (len))
+// #define sock_create_event(s) WSACreateEvent()
#define sock_errno() WSAGetLastError()
#define sock_getopt(s,l,o,v,ln) getsockopt((s),(l),(o),(v),(ln))
-#define sock_htons(x) htons((x))
-#define sock_htonl(x) htonl((x))
+// #define sock_htons(x) htons((x))
+// #define sock_htonl(x) htonl((x))
#define sock_listen(s, b) listen((s), (b))
#define sock_name(s, addr, len) getsockname((s), (addr), (len))
-#define sock_ntohs(x) ntohs((x))
-#define sock_open(domain, type, proto) \
- make_noninheritable_handle(socket((domain), (type), (proto)))
+// #define sock_ntohs(x) ntohs((x))
+/* #define sock_open(domain, type, proto) \
+ make_noninheritable_handle(socket((domain), (type), (proto))) */
#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
-#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
+// #define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
#define sock_recvfrom(s,buf,blen,flag,addr,alen) \
recvfrom((s),(buf),(blen),(flag),(addr),(alen))
-#define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag))
-#define sock_sendto(s,buf,blen,flag,addr,alen) \
- sendto((s),(buf),(blen),(flag),(addr),(alen))
+/* #define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) */
+/* #define sock_sendto(s,buf,blen,flag,addr,alen) \
+ sendto((s),(buf),(blen),(flag),(addr),(alen)) */
#define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln))
#define sock_shutdown(s, how) shutdown((s), (how))
@@ -933,18 +935,18 @@ static unsigned long one_value = 1;
// #endif
// #define sock_bind(s, addr, len) bind((s), (addr), (len))
#define sock_close(s) close((s))
-#define sock_close_event(e) /* do nothing */
+// #define sock_close_event(e) /* do nothing */
// #define sock_connect(s, addr, len) connect((s), (addr), (len))
-#define sock_create_event(s) (s) /* return file descriptor */
+// #define sock_create_event(s) (s) /* return file descriptor */
#define sock_errno() errno
#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l))
-#define sock_htons(x) htons((x))
-#define sock_htonl(x) htonl((x))
-// #define sock_listen(s, b) listen((s), (b))
-// #define sock_name(s, addr, len) getsockname((s), (addr), (len))
-#define sock_ntohs(x) ntohs((x))
+// #define sock_htons(x) htons((x))
+// #define sock_htonl(x) htonl((x))
+#define sock_listen(s, b) listen((s), (b))
+#define sock_name(s, addr, len) getsockname((s), (addr), (len))
+// #define sock_ntohs(x) ntohs((x))
// #define sock_open(domain, type, proto) socket((domain), (type), (proto))
-// #define sock_peer(s, addr, len) getpeername((s), (addr), (len))
+#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
// #define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
/* #define sock_recvfrom(s,buf,blen,flag,addr,alen) \ */
/* recvfrom((s),(buf),(blen),(flag),(addr),(alen)) */
@@ -954,7 +956,7 @@ static unsigned long one_value = 1;
/* #define sock_sendto(s,buf,blen,flag,addr,alen) \ */
/* sendto((s),(buf),(blen),(flag),(addr),(alen)) */
#define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln))
-// #define sock_shutdown(s, how) shutdown((s), (how))
+#define sock_shutdown(s, how) shutdown((s), (how))
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *
@@ -1078,6 +1080,11 @@ typedef struct {
ESockIOInit init;
ESockIOFinish finish;
+ ESockIOInfo info;
+ ESockIOCommand cmd;
+ ESockIOSupports0 supports_0;
+ ESockIOSupports1 supports_1;
+
ESockIOOpenWithFd open_with_fd;
ESockIOOpenPlain open_plain;
ESockIOBind bind;
@@ -1103,69 +1110,87 @@ typedef struct {
ESockIOSockName sockname;
ESockIOPeerName peername;
+
+ /* The various cancel operations */
+ ESockIOCancelConnect cancel_connect;
+ ESockIOCancelAccept cancel_accept;
+ ESockIOCancelSend cancel_send;
+ ESockIOCancelRecv cancel_recv;
+
+ /* Socket option callback functions */
+ ESockIOSetopt setopt;
+ ESockIOSetoptNative setopt_native;
+ ESockIOSetoptOtp setopt_otp;
+ ESockIOGetopt getopt;
+ ESockIOGetoptNative getopt_native;
+ ESockIOGetoptOtp getopt_otp;
+
+ /* Socket ioctl callback functions */
+ ESockIOIoctl_2 ioctl_2;
+ ESockIOIoctl_3 ioctl_3;
+ ESockIOIoctl_4 ioctl_4;
+
+ /* (socket) NIF resource callback functions */
+ ESockIODTor dtor;
+ ESockIOStop stop;
+ ESockIODown down;
+
} ESockIoBackend;
+/* ------------------------------------------------------------------------
+ * Socket option(s) and table(s)
+ */
-#ifndef __WIN32__
-/* ---------------------------------------------------------------------- *
- * *
- * *
- * Start of non-__WIN32__ section a.k.a UNIX section *
- * *
- * *
- * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
+struct ESockOpt
+{
+ int opt; // Option number
-/* And here comes the functions that does the actual work (for the most part) */
+ // Function to set option
+ ERL_NIF_TERM (*setopt)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal);
-static ERL_NIF_TERM esock_command(ErlNifEnv* env,
- ERL_NIF_TERM command,
- ERL_NIF_TERM cdata);
-static ERL_NIF_TERM esock_command_debug(ErlNifEnv* env,
- ERL_NIF_TERM cdata);
-static ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env,
- ERL_NIF_TERM cdata);
-static ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env,
- ERL_NIF_TERM cdata);
+ // Function to get option
+ ERL_NIF_TERM (*getopt)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt);
-static ERL_NIF_TERM esock_global_info(ErlNifEnv* env);
-static ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
- ESockDescriptor* descP);
-static ERL_NIF_TERM esock_socket_info_domain(ErlNifEnv* env,
- ESockDescriptor* descP);
-static ERL_NIF_TERM esock_socket_info_type(ErlNifEnv* env,
- ESockDescriptor* descP);
-static ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env,
- ESockDescriptor* descP);
-static ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env,
- ESockDescriptor* descP);
-static ERL_NIF_TERM esock_socket_info_state(ErlNifEnv* env,
- unsigned int state);
-#define ESOCK_SOCKET_INFO_REQ_FUNCS \
- ESOCK_SOCKET_INFO_REQ_FUNC_DEF(readers); \
- ESOCK_SOCKET_INFO_REQ_FUNC_DEF(writers); \
- ESOCK_SOCKET_INFO_REQ_FUNC_DEF(acceptors);
+ ERL_NIF_TERM *nameP; // Pointer to option name atom
+};
-#define ESOCK_SOCKET_INFO_REQ_FUNC_DEF(F) \
- static ERL_NIF_TERM esock_socket_info_##F(ErlNifEnv* env, \
- ESockDescriptor* descP);
-ESOCK_SOCKET_INFO_REQ_FUNCS
-#undef ESOCK_SOCKET_INFO_REQ_FUNC_DEF
-static ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env,
- ESockDescriptor* descP,
- ESockRequestor* currentRequestorP,
- ESockRequestQueue* q);
+/* Option levels table*/
-static ERL_NIF_TERM esock_supports_0(ErlNifEnv* env);
-static ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key);
+struct ESockOptLevel
+{
+ int level; // Level number
+
+ size_t num; // Number of options
+
+ struct ESockOpt *opts; // Options table
+
+ ERL_NIF_TERM *nameP; // Pointer to level name atom
+};
-static ERL_NIF_TERM esock_supports_msg_flags(ErlNifEnv* env);
-static ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env);
-static ERL_NIF_TERM esock_supports_ioctl_requests(ErlNifEnv* env);
-static ERL_NIF_TERM esock_supports_ioctl_flags(ErlNifEnv* env);
-static ERL_NIF_TERM esock_supports_options(ErlNifEnv* env);
+
+/* First chunk of forwards...some of these are used in the options tables... */
+
+/* Set native options */
+static ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM valueSpec);
/* Set OTP level options */
static ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -1180,28 +1205,186 @@ static ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
* *** esock_setopt_otp_meta ***
* *** esock_setopt_otp_use_registry ***
*/
-#define ESOCK_SETOPT_OTP_FUNCS \
- ESOCK_SETOPT_OTP_FUNC_DEF(debug); \
- ESOCK_SETOPT_OTP_FUNC_DEF(iow); \
- ESOCK_SETOPT_OTP_FUNC_DEF(ctrl_proc); \
- ESOCK_SETOPT_OTP_FUNC_DEF(rcvbuf); \
- ESOCK_SETOPT_OTP_FUNC_DEF(rcvctrlbuf); \
- ESOCK_SETOPT_OTP_FUNC_DEF(sndctrlbuf); \
- ESOCK_SETOPT_OTP_FUNC_DEF(meta); \
+#define ESOCK_SETOPT_OTP_FUNCS \
+ ESOCK_SETOPT_OTP_FUNC_DEF(debug); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(iow); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(ctrl_proc); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(rcvbuf); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(rcvctrlbuf); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(sndctrlbuf); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(meta); \
ESOCK_SETOPT_OTP_FUNC_DEF(use_registry);
-#define ESOCK_SETOPT_OTP_FUNC_DEF(F) \
- static ERL_NIF_TERM esock_setopt_otp_##F(ErlNifEnv* env, \
- ESockDescriptor* descP, \
+#define ESOCK_SETOPT_OTP_FUNC_DEF(F) \
+ static ERL_NIF_TERM esock_setopt_otp_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
ERL_NIF_TERM eVal)
ESOCK_SETOPT_OTP_FUNCS
#undef ESOCK_SETOPT_OTP_FUNC_DEF
-/* Set native options */
-static ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt);
+/* *** esock_getopt_otp_debug ***
+ * *** esock_getopt_otp_iow ***
+ * *** esock_getopt_otp_ctrl_proc ***
+ * *** esock_getopt_otp_rcvbuf ***
+ * *** esock_getopt_otp_rcvctrlbuf ***
+ * *** esock_getopt_otp_sndctrlbuf ***
+ * *** esock_getopt_otp_fd ***
+ * *** esock_getopt_otp_meta ***
+ * *** esock_getopt_otp_use_registry ***
+ * *** esock_getopt_otp_domain ***
+ * *** //esock_getopt_otp_type ***
+ * *** //esock_getopt_otp_protocol ***
+ * *** //esock_getopt_otp_dtp ***
+ */
+#define ESOCK_GETOPT_OTP_FUNCS \
+ ESOCK_GETOPT_OTP_FUNC_DEF(debug); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(iow); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(ctrl_proc); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(rcvbuf); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(rcvctrlbuf); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(sndctrlbuf); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(fd); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(meta); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(use_registry); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(domain);
+#if 0
+ESOCK_GETOPT_OTP_FUNC_DEF(type); \
+ESOCK_GETOPT_OTP_FUNC_DEF(protocol); \
+ESOCK_GETOPT_OTP_FUNC_DEF(dtp);
+#endif
+#define ESOCK_GETOPT_OTP_FUNC_DEF(F) \
+ static ERL_NIF_TERM esock_getopt_otp_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP)
+ESOCK_GETOPT_OTP_FUNCS
+#undef ESOCK_GETOPT_OTP_FUNC_DEF
+
+static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ void* optVal,
+ socklen_t optLen);
+static ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt);
+static ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt);
+static ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ SOCKOPTLEN_T valueSz);
+static ERL_NIF_TERM esock_getopt_bin_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ErlNifBinary* binP);
+
+static int socket_setopt(int sock,
+ int level,
+ int opt,
+ const void* optVal,
+ const socklen_t optLen);
+
+static int cmpESockOpt(const void *vpa, const void *vpb);
+static int cmpESockOptLevel(const void *vpa, const void *vpb);
+static struct ESockOpt *lookupOpt(int level, int opt);
+
+
+static ERL_NIF_TERM esock_supports_0(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key);
+
+static ERL_NIF_TERM esock_supports_msg_flags(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_ioctl_requests(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_ioctl_flags(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_options(ErlNifEnv* env);
+
+#ifndef __WIN32__
+/* ---------------------------------------------------------------------- *
+ * *
+ * *
+ * Start of non-__WIN32__ section a.k.a UNIX section *
+ * *
+ * *
+ * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
+
+/* *** esock_activate_next_acceptor ***
+ * *** esock_activate_next_writer ***
+ * *** esock_activate_next_reader ***
+ *
+ * All the activate-next functions for acceptor, writer and reader
+ * have exactly the same API, so we apply some macro magic to simplify.
+ * They simply operates on dufferent data structures.
+ *
+ */
+
+#define ACTIVATE_NEXT_FUNCS_DEFS \
+ ACTIVATE_NEXT_FUNC_DEF(acceptor) \
+ ACTIVATE_NEXT_FUNC_DEF(writer) \
+ ACTIVATE_NEXT_FUNC_DEF(reader)
+
+#define ACTIVATE_NEXT_FUNC_DEF(F) \
+ extern BOOLEAN_T esock_activate_next_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM sockRef);
+ACTIVATE_NEXT_FUNCS_DEFS
+#undef ACTIVATE_NEXT_FUNC_DEF
+
+/* esock_acceptor_search4pid | esock_writer_search4pid | esock_reader_search4pid
+ * esock_acceptor_push | esock_writer_push | esock_reader_push
+ * esock_acceptor_pop | esock_writer_pop | esock_reader_pop
+ * esock_acceptor_unqueue | esock_writer_unqueue | esock_reader_unqueue
+ *
+ * All the queue operator functions (search4pid, push, pop
+ * and unqueue) for acceptor, writer and reader has exactly
+ * the same API, so we apply some macro magic to simplify.
+ */
+
+#define ESOCK_OPERATOR_FUNCS_DEFS \
+ ESOCK_OPERATOR_FUNCS_DEF(acceptor) \
+ ESOCK_OPERATOR_FUNCS_DEF(writer) \
+ ESOCK_OPERATOR_FUNCS_DEF(reader)
+
+#define ESOCK_OPERATOR_FUNCS_DEF(O) \
+ extern BOOLEAN_T esock_##O##_search4pid(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ErlNifPid* pid); \
+ extern void esock_##O##_push(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ErlNifPid pid, \
+ ERL_NIF_TERM ref, \
+ void* dataP); \
+ extern BOOLEAN_T esock_##O##_pop(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ESockRequestor* reqP); \
+ extern BOOLEAN_T esock_##O##_unqueue(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM* refP, \
+ const ErlNifPid* pidP);
+ESOCK_OPERATOR_FUNCS_DEFS
+#undef ESOCK_OPERATOR_FUNCS_DEF
+
+static ERL_NIF_TERM mk_select_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM selectRef);
+
+
+/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *
+ * *
+ * *
+ * End of non-__WIN32__ section a.k.a UNIX section *
+ * *
+ * *
+ * ---------------------------------------------------------------------- */
+#endif // #ifndef __WIN32__
+
+
static ERL_NIF_TERM esock_setopt(ErlNifEnv* env,
ESockDescriptor* descP,
int level,
@@ -1367,50 +1550,6 @@ static ERL_NIF_TERM esock_setopt_sctp_rtoinfo(ErlNifEnv* env,
#endif // defined(HAVE_SCTP)
-static ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt);
-/* *** esock_getopt_otp_debug ***
- * *** esock_getopt_otp_iow ***
- * *** esock_getopt_otp_ctrl_proc ***
- * *** esock_getopt_otp_rcvbuf ***
- * *** esock_getopt_otp_rcvctrlbuf ***
- * *** esock_getopt_otp_sndctrlbuf ***
- * *** esock_getopt_otp_fd ***
- * *** esock_getopt_otp_meta ***
- * *** esock_getopt_otp_use_registry ***
- * *** esock_getopt_otp_domain ***
- * *** //esock_getopt_otp_type ***
- * *** //esock_getopt_otp_protocol ***
- * *** //esock_getopt_otp_dtp ***
- */
-#define ESOCK_GETOPT_OTP_FUNCS \
- ESOCK_GETOPT_OTP_FUNC_DEF(debug); \
- ESOCK_GETOPT_OTP_FUNC_DEF(iow); \
- ESOCK_GETOPT_OTP_FUNC_DEF(ctrl_proc); \
- ESOCK_GETOPT_OTP_FUNC_DEF(rcvbuf); \
- ESOCK_GETOPT_OTP_FUNC_DEF(rcvctrlbuf); \
- ESOCK_GETOPT_OTP_FUNC_DEF(sndctrlbuf); \
- ESOCK_GETOPT_OTP_FUNC_DEF(fd); \
- ESOCK_GETOPT_OTP_FUNC_DEF(meta); \
- ESOCK_GETOPT_OTP_FUNC_DEF(use_registry); \
- ESOCK_GETOPT_OTP_FUNC_DEF(domain);
-#if 0
- ESOCK_GETOPT_OTP_FUNC_DEF(type); \
- ESOCK_GETOPT_OTP_FUNC_DEF(protocol); \
- ESOCK_GETOPT_OTP_FUNC_DEF(dtp);
-#endif
-#define ESOCK_GETOPT_OTP_FUNC_DEF(F) \
- static ERL_NIF_TERM esock_getopt_otp_##F(ErlNifEnv* env, \
- ESockDescriptor* descP)
-ESOCK_GETOPT_OTP_FUNCS
-#undef ESOCK_GETOPT_OTP_FUNC_DEF
-
-static ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- ERL_NIF_TERM valueSpec);
static ERL_NIF_TERM esock_getopt(ErlNifEnv* env,
ESockDescriptor* descP,
int level,
@@ -1447,9 +1586,9 @@ ERL_NIF_TERM esock_getopt_sock_protocol(ErlNifEnv* env,
#if defined(IP_MTU_DISCOVER)
static ERL_NIF_TERM esock_getopt_ip_mtu_discover(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt);
+ ESockDescriptor* descP,
+ int level,
+ int opt);
#endif
#if defined(IP_MULTICAST_IF)
static ERL_NIF_TERM esock_getopt_multicast_if(ErlNifEnv* env,
@@ -1515,295 +1654,6 @@ static ERL_NIF_TERM esock_getopt_sctp_rtoinfo(ErlNifEnv* env,
#endif // defined(HAVE_SCTP)
-static ERL_NIF_TERM esock_ioctl1(ErlNifEnv* env,
- ESockDescriptor* descP,
- unsigned long req);
-static ERL_NIF_TERM esock_ioctl2(ErlNifEnv* env,
- ESockDescriptor* descP,
- unsigned long req,
- ERL_NIF_TERM arg);
-static ERL_NIF_TERM esock_ioctl3(ErlNifEnv* env,
- ESockDescriptor* descP,
- unsigned long req,
- ERL_NIF_TERM ename,
- ERL_NIF_TERM eval);
-static ERL_NIF_TERM esock_ioctl_gifconf(ErlNifEnv* env,
- ESockDescriptor* descP);
-#if defined(SIOCGIFNAME)
-static ERL_NIF_TERM esock_ioctl_gifname(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eidx);
-#endif
-
-/* esock_ioctl_gifindex */
-#if defined(SIOCGIFINDEX)
-#define IOCTL_GIFINDEX_FUNC_DEF IOCTL_GET_FUNC_DEF(gifindex)
-#else
-#define IOCTL_GIFINDEX_FUNC_DEF
-#endif
-
-/* esock_ioctl_gifflags */
-#if defined(SIOCGIFFLAGS)
-#define IOCTL_GIFFLAGS_FUNC_DEF IOCTL_GET_FUNC_DEF(gifflags)
-#else
-#define IOCTL_GIFFLAGS_FUNC_DEF
-#endif
-
-/* esock_ioctl_gifaddr */
-#if defined(SIOCGIFADDR)
-#define IOCTL_GIFADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifaddr)
-#else
-#define IOCTL_GIFADDR_FUNC_DEF
-#endif
-
-/* esock_ioctl_gifdstaddr */
-#if defined(SIOCGIFDSTADDR)
-#define IOCTL_GIFDSTADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifdstaddr)
-#else
-#define IOCTL_GIFDSTADDR_FUNC_DEF
-#endif
-
-/* esock_ioctl_gifbrdaddr */
-#if defined(SIOCGIFBRDADDR)
-#define IOCTL_GIFBRDADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifbrdaddr)
-#else
-#define IOCTL_GIFBRDADDR_FUNC_DEF
-#endif
-
-/* esock_ioctl_gifnetmask */
-#if defined(SIOCGIFNETMASK)
-#define IOCTL_GIFNETMASK_FUNC_DEF IOCTL_GET_FUNC_DEF(gifnetmask)
-#else
-#define IOCTL_GIFNETMASK_FUNC_DEF
-#endif
-
-/* esock_ioctl_gifmtu */
-#if defined(SIOCGIFMTU)
-#define IOCTL_GIFMTU_FUNC_DEF IOCTL_GET_FUNC_DEF(gifmtu)
-#else
-#define IOCTL_GIFMTU_FUNC_DEF
-#endif
-
-/* esock_ioctl_gifhwaddr */
-#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
-#define IOCTL_GIFHWADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifhwaddr)
-#else
-#define IOCTL_GIFHWADDR_FUNC_DEF
-#endif
-
-/* esock_ioctl_gifmap */
-#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
-#define IOCTL_GIFMAP_FUNC_DEF IOCTL_GET_FUNC_DEF(gifmap)
-#else
-#define IOCTL_GIFMAP_FUNC_DEF
-#endif
-
-/* esock_ioctl_giftxqlen */
-#if defined(SIOCGIFTXQLEN)
-#define IOCTL_GIFTXQLEN_FUNC_DEF IOCTL_GET_FUNC_DEF(giftxqlen)
-#else
-#define IOCTL_GIFTXQLEN_FUNC_DEF
-#endif
-
-#define IOCTL_GET_FUNCS_DEF \
- IOCTL_GIFINDEX_FUNC_DEF; \
- IOCTL_GIFFLAGS_FUNC_DEF; \
- IOCTL_GIFADDR_FUNC_DEF; \
- IOCTL_GIFDSTADDR_FUNC_DEF; \
- IOCTL_GIFBRDADDR_FUNC_DEF; \
- IOCTL_GIFNETMASK_FUNC_DEF; \
- IOCTL_GIFMTU_FUNC_DEF; \
- IOCTL_GIFHWADDR_FUNC_DEF; \
- IOCTL_GIFMAP_FUNC_DEF; \
- IOCTL_GIFTXQLEN_FUNC_DEF;
-#define IOCTL_GET_FUNC_DEF(F) \
- static ERL_NIF_TERM esock_ioctl_##F(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ERL_NIF_TERM ename)
-IOCTL_GET_FUNCS_DEF
-#undef IOCTL_GET_FUNC_DEF
-
-/* esock_ioctl_sifflags */
-#if defined(SIOCSIFFLAGS)
-#define IOCTL_SIFFLAGS_FUNC_DEF IOCTL_SET_FUNC_DEF(sifflags)
-#else
-#define IOCTL_SIFFLAGS_FUNC_DEF
-#endif
-
-/* esock_ioctl_sifaddr */
-#if defined(SIOCSIFADDR)
-#define IOCTL_SIFADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifaddr)
-#else
-#define IOCTL_SIFADDR_FUNC_DEF
-#endif
-
-/* esock_ioctl_sifdstaddr */
-#if defined(SIOCSIFDSTADDR)
-#define IOCTL_SIFDSTADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifdstaddr)
-#else
-#define IOCTL_SIFDSTADDR_FUNC_DEF
-#endif
-
-/* esock_ioctl_sifbrdaddr */
-#if defined(SIOCSIFBRDADDR)
-#define IOCTL_SIFBRDADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifbrdaddr)
-#else
-#define IOCTL_SIFBRDADDR_FUNC_DEF
-#endif
-
-/* esock_ioctl_sifnetmask */
-#if defined(SIOCSIFNETMASK)
-#define IOCTL_SIFNETMASK_FUNC_DEF IOCTL_SET_FUNC_DEF(sifnetmask)
-#else
-#define IOCTL_SIFNETMASK_FUNC_DEF
-#endif
-
-/* esock_ioctl_sifmtu */
-#if defined(SIOCSIFMTU)
-#define IOCTL_SIFMTU_FUNC_DEF IOCTL_SET_FUNC_DEF(sifmtu)
-#else
-#define IOCTL_SIFMTU_FUNC_DEF
-#endif
-
-/* esock_ioctl_siftxqlen */
-#if defined(SIOCSIFTXQLEN)
-#define IOCTL_SIFTXQLEN_FUNC_DEF IOCTL_SET_FUNC_DEF(siftxqlen)
-#else
-#define IOCTL_SIFTXQLEN_FUNC_DEF
-#endif
-
-#define IOCTL_SET_FUNCS_DEF \
- IOCTL_SIFFLAGS_FUNC_DEF; \
- IOCTL_SIFADDR_FUNC_DEF; \
- IOCTL_SIFDSTADDR_FUNC_DEF; \
- IOCTL_SIFBRDADDR_FUNC_DEF; \
- IOCTL_SIFNETMASK_FUNC_DEF; \
- IOCTL_SIFMTU_FUNC_DEF; \
- IOCTL_SIFTXQLEN_FUNC_DEF;
-#define IOCTL_SET_FUNC_DEF(F) \
- static ERL_NIF_TERM esock_ioctl_##F(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ERL_NIF_TERM ename, \
- ERL_NIF_TERM evalue)
-IOCTL_SET_FUNCS_DEF
-#undef IOCTL_SET_FUNC_DEF
-
-
-static ERL_NIF_TERM encode_ioctl_ifconf(ErlNifEnv* env,
- ESockDescriptor* descP,
- struct ifconf* ifcP);
-static ERL_NIF_TERM encode_ioctl_ifconf_ifreq(ErlNifEnv* env,
- ESockDescriptor* descP,
- struct ifreq* ifrP);
-static ERL_NIF_TERM encode_ioctl_ifreq_name(ErlNifEnv* env,
- char* name);
-static ERL_NIF_TERM encode_ioctl_ifreq_sockaddr(ErlNifEnv* env,
- struct sockaddr* sa);
-static ERL_NIF_TERM make_ifreq(ErlNifEnv* env,
- ERL_NIF_TERM name,
- ERL_NIF_TERM key2,
- ERL_NIF_TERM val2);
-#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
-static ERL_NIF_TERM encode_ioctl_ifrmap(ErlNifEnv* env,
- ESockDescriptor* descP,
- struct ifmap* mapP);
-#endif
-#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
-static ERL_NIF_TERM encode_ioctl_hwaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- struct sockaddr* addrP);
-#endif
-static ERL_NIF_TERM encode_ioctl_ifraddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- struct sockaddr* addrP);
-static ERL_NIF_TERM encode_ioctl_flags(ErlNifEnv* env,
- ESockDescriptor* descP,
- short flags);
-#if defined(SIOCSIFFLAGS)
-static BOOLEAN_T decode_ioctl_flags(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eflags,
- short* flags);
-#endif
-static BOOLEAN_T decode_ioctl_sockaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eaddr,
- ESockAddress* addr);
-#if defined(SIOCSIFMTU)
-static BOOLEAN_T decode_ioctl_mtu(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM emtu,
- int* mtu);
-#endif
-#if defined(SIOCSIFTXQLEN)
-static BOOLEAN_T decode_ioctl_txqlen(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM etxqlen,
- int* txqlen);
-#endif
-#if defined(SIOCSIFTXQLEN)
-static BOOLEAN_T decode_ioctl_ivalue(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eivalue,
- int* ivalue);
-#endif
-static ERL_NIF_TERM encode_ioctl_ivalue(ErlNifEnv* env,
- ESockDescriptor* descP,
- int ivalue);
-
-static ERL_NIF_TERM esock_cancel(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM op,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM esock_cancel_connect(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM esock_cancel_accept(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM esock_cancel_accept_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM esock_cancel_accept_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef,
- const ErlNifPid* selfP);
-static ERL_NIF_TERM esock_cancel_send(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM esock_cancel_send_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM esock_cancel_send_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef,
- const ErlNifPid* selfP);
-static ERL_NIF_TERM esock_cancel_recv(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM esock_cancel_recv_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM esock_cancel_recv_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef,
- const ErlNifPid* selfP);
-static ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef,
- int smode,
- int rmode);
-
#if defined(USE_SETOPT_STR_OPT)
static ERL_NIF_TERM esock_setopt_str_opt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -1822,7 +1672,7 @@ static ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv* env,
int level,
int opt,
ERL_NIF_TERM eVal);
-#if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \
+#if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \
&& defined(ESOCK_USE_RCVSNDTIMEO)
static ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -1830,13 +1680,6 @@ static ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv* env,
int opt,
ERL_NIF_TERM eVal);
#endif
-static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- void* optVal,
- socklen_t optLen);
-
#if defined(USE_GETOPT_STR_OPT)
static ERL_NIF_TERM esock_getopt_str_opt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -1845,25 +1688,7 @@ static ERL_NIF_TERM esock_getopt_str_opt(ErlNifEnv* env,
int max,
BOOLEAN_T stripNUL);
#endif
-static ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt);
-static ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt);
-static ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- SOCKOPTLEN_T valueSz);
-static ERL_NIF_TERM esock_getopt_bin_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- ErlNifBinary* binP);
-#if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \
+#if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \
&& defined(ESOCK_USE_RCVSNDTIMEO)
static ERL_NIF_TERM esock_getopt_timeval_opt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -1873,36 +1698,946 @@ static ERL_NIF_TERM esock_getopt_timeval_opt(ErlNifEnv* env,
-/* ------------------------------------------------------------------------
- * Socket option tables and handling
+
+
+
+
+static ERL_NIF_TERM esock_shutdown(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int how);
+static ERL_NIF_TERM esock_sockname(ErlNifEnv* env,
+ ESockDescriptor* descP);
+static ERL_NIF_TERM esock_peername(ErlNifEnv* env,
+ ESockDescriptor* descP);
+
+static ERL_NIF_TERM esock_command(ErlNifEnv* env,
+ ERL_NIF_TERM command,
+ ERL_NIF_TERM cdata);
+static ERL_NIF_TERM esock_command_debug(ErlNifEnv* env,
+ ERL_NIF_TERM cdata);
+static ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env,
+ ERL_NIF_TERM cdata);
+static ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env,
+ ERL_NIF_TERM cdata);
+
+#define ESOCK_SOCKET_INFO_REQ_FUNCS \
+ ESOCK_SOCKET_INFO_REQ_FUNC_DEF(readers); \
+ ESOCK_SOCKET_INFO_REQ_FUNC_DEF(writers); \
+ ESOCK_SOCKET_INFO_REQ_FUNC_DEF(acceptors);
+
+#define ESOCK_SOCKET_INFO_REQ_FUNC_DEF(F) \
+ static ERL_NIF_TERM esock_socket_info_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP);
+ESOCK_SOCKET_INFO_REQ_FUNCS
+#undef ESOCK_SOCKET_INFO_REQ_FUNC_DEF
+
+static ERL_NIF_TERM esock_cancel(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM op,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+/*
+static ERL_NIF_TERM esock_cancel_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+*/
+static ERL_NIF_TERM esock_listen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int backlog);
+
+static ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env,
+ ESockDescriptor* descP,
+#ifndef __WIN32__
+ ESockRequestor* currentRequestorP,
+#endif
+ ESockRequestQueue* q);
+
+static ERL_NIF_TERM esock_global_info(ErlNifEnv* env);
+static ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
+ ESockDescriptor* descP);
+static ERL_NIF_TERM esock_socket_info_domain(ErlNifEnv* env,
+ ESockDescriptor* descP);
+static ERL_NIF_TERM esock_socket_info_type(ErlNifEnv* env,
+ ESockDescriptor* descP);
+static ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env,
+ ESockDescriptor* descP);
+static ERL_NIF_TERM esock_socket_info_state(ErlNifEnv* env,
+ unsigned int state);
+static ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env,
+ ESockDescriptor* descP);
+
+static ERL_NIF_TERM mk_close_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM closeRef);
+static ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env,
+ ERL_NIF_TERM tag,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM mk_simple_abort_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM reason);
+static ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef,
+ ERL_NIF_TERM reason);
+static ERL_NIF_TERM mk_wrap_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM cnt);
+static BOOLEAN_T qsearch4pid(ErlNifEnv* env,
+ ESockRequestQueue* q,
+ ErlNifPid* pid);
+static unsigned int qlength(ESockRequestQueue* q);
+static void qpush(ESockRequestQueue* q,
+ ESockRequestQueueElement* e);
+static ESockRequestQueueElement* qpop(ESockRequestQueue* q);
+static BOOLEAN_T qunqueue(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const char* slogan,
+ ESockRequestQueue* q,
+ ERL_NIF_TERM* refP,
+ const ErlNifPid* pidP);
+static ESockRequestQueueElement* qget(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const char* slogan,
+ ESockRequestQueue* q,
+ ERL_NIF_TERM* refP,
+ const ErlNifPid* pidP);
+
+static char* extract_debug_filename(ErlNifEnv* env,
+ ERL_NIF_TERM map);
+
+
+/* --------------------------------------------------------------------- */
+
+#if defined(IP_TOS)
+static BOOLEAN_T decode_ip_tos(ErlNifEnv* env,
+ ERL_NIF_TERM eVal,
+ int* val);
+#endif
+#if defined(IP_MTU_DISCOVER)
+static BOOLEAN_T decode_ip_pmtudisc(ErlNifEnv* env,
+ ERL_NIF_TERM eVal,
+ int* val);
+#endif
+#if defined(IP_MTU_DISCOVER)
+static void encode_ip_pmtudisc(ErlNifEnv* env,
+ int val,
+ ERL_NIF_TERM* eVal);
+#endif
+#if defined(IPV6_MTU_DISCOVER)
+static BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env,
+ ERL_NIF_TERM eVal,
+ int* val);
+#endif
+#if defined(IPV6_MTU_DISCOVER)
+static void encode_ipv6_pmtudisc(ErlNifEnv* env,
+ int val,
+ ERL_NIF_TERM* eVal);
+#endif
+
+static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val);
+
+#if defined(IPV6_MULTICAST_HOPS) || defined(IPV6_UNICAST_HOPS)
+static
+BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val);
+#endif
+
+#if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO)
+static BOOLEAN_T decode_sctp_assoc_t(ErlNifEnv* env,
+ ERL_NIF_TERM eVal,
+ sctp_assoc_t* val);
+static ERL_NIF_TERM encode_sctp_assoc_t(ErlNifEnv* env,
+ sctp_assoc_t val);
+#endif // #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO)
+
+
+static BOOLEAN_T ehow2how(ERL_NIF_TERM ehow, int* how);
+
+
+/*
+#if defined(HAS_AF_LOCAL) || defined(SO_BINDTODEVICE)
+static size_t my_strnlen(const char *s, size_t maxlen);
+#endif
+*/
+
+static void esock_dtor(ErlNifEnv* env, void* obj);
+static void esock_stop(ErlNifEnv* env,
+ void* obj,
+ ErlNifEvent fd,
+ int is_direct_call);
+static void esock_down(ErlNifEnv* env,
+ void* obj,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+
+static void esock_on_halt(void* priv_data);
+
+static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
+
+
+
+#if HAVE_IN6
+# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY
+# if HAVE_DECL_IN6ADDR_ANY_INIT
+static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } };
+# else
+static const struct in6_addr in6addr_any =
+ { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
+# endif /* HAVE_IN6ADDR_ANY_INIT */
+# endif /* ! HAVE_DECL_IN6ADDR_ANY */
+
+# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK
+# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT
+static const struct in6_addr in6addr_loopback =
+ { { IN6ADDR_LOOPBACK_INIT } };
+# else
+static const struct in6_addr in6addr_loopback =
+ { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } };
+# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */
+# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */
+#endif /* HAVE_IN6 */
+
+
+
+/* *** Global atoms ***
+ * Note that when an (global) atom is added here, it must also be added
+ * in the socket_int.h file!
*/
+#define GLOBAL_ATOMS \
+ GLOBAL_ATOM_DECL(abort); \
+ GLOBAL_ATOM_DECL(accept); \
+ GLOBAL_ATOM_DECL(acceptconn); \
+ GLOBAL_ATOM_DECL(acceptfilter); \
+ GLOBAL_ATOM_DECL(acc_success); \
+ GLOBAL_ATOM_DECL(acc_fails); \
+ GLOBAL_ATOM_DECL(acc_tries); \
+ GLOBAL_ATOM_DECL(acc_waits); \
+ GLOBAL_ATOM_DECL(adaption_layer); \
+ GLOBAL_ATOM_DECL(addr); \
+ GLOBAL_ATOM_DECL(addrform); \
+ GLOBAL_ATOM_DECL(add_membership); \
+ GLOBAL_ATOM_DECL(add_socket); \
+ GLOBAL_ATOM_DECL(add_source_membership); \
+ GLOBAL_ATOM_DECL(alen); \
+ GLOBAL_ATOM_DECL(allmulti); \
+ GLOBAL_ATOM_DECL(already); \
+ GLOBAL_ATOM_DECL(any); \
+ GLOBAL_ATOM_DECL(appletlk); \
+ GLOBAL_ATOM_DECL(arcnet); \
+ GLOBAL_ATOM_DECL(associnfo); \
+ GLOBAL_ATOM_DECL(atm); \
+ GLOBAL_ATOM_DECL(authhdr); \
+ GLOBAL_ATOM_DECL(auth_active_key); \
+ GLOBAL_ATOM_DECL(auth_asconf); \
+ GLOBAL_ATOM_DECL(auth_chunk); \
+ GLOBAL_ATOM_DECL(auth_delete_key); \
+ GLOBAL_ATOM_DECL(auth_key); \
+ GLOBAL_ATOM_DECL(auth_level); \
+ GLOBAL_ATOM_DECL(autoclose); \
+ GLOBAL_ATOM_DECL(automedia); \
+ GLOBAL_ATOM_DECL(ax25); \
+ GLOBAL_ATOM_DECL(bad_data); \
+ GLOBAL_ATOM_DECL(base_addr); \
+ GLOBAL_ATOM_DECL(bindtodevice); \
+ GLOBAL_ATOM_DECL(block_source); \
+ GLOBAL_ATOM_DECL(broadcast); \
+ GLOBAL_ATOM_DECL(busy_poll); \
+ GLOBAL_ATOM_DECL(cancel); \
+ GLOBAL_ATOM_DECL(cantconfig); \
+ GLOBAL_ATOM_DECL(chaos); \
+ GLOBAL_ATOM_DECL(checksum); \
+ GLOBAL_ATOM_DECL(close); \
+ GLOBAL_ATOM_DECL(closed); \
+ GLOBAL_ATOM_DECL(cmsg_cloexec); \
+ GLOBAL_ATOM_DECL(command); \
+ GLOBAL_ATOM_DECL(completion); \
+ GLOBAL_ATOM_DECL(confirm); \
+ GLOBAL_ATOM_DECL(congestion); \
+ GLOBAL_ATOM_DECL(connect); \
+ GLOBAL_ATOM_DECL(connected); \
+ GLOBAL_ATOM_DECL(connecting); \
+ GLOBAL_ATOM_DECL(context); \
+ GLOBAL_ATOM_DECL(cork); \
+ GLOBAL_ATOM_DECL(counters); \
+ GLOBAL_ATOM_DECL(credentials); \
+ GLOBAL_ATOM_DECL(ctrl); \
+ GLOBAL_ATOM_DECL(ctrunc); \
+ GLOBAL_ATOM_DECL(data); \
+ GLOBAL_ATOM_DECL(data_size); \
+ GLOBAL_ATOM_DECL(debug); \
+ GLOBAL_ATOM_DECL(default); \
+ GLOBAL_ATOM_DECL(default_send_params); \
+ GLOBAL_ATOM_DECL(delayed_ack_time); \
+ GLOBAL_ATOM_DECL(dgram); \
+ GLOBAL_ATOM_DECL(disable_fragments); \
+ GLOBAL_ATOM_DECL(dlci); \
+ GLOBAL_ATOM_DECL(dma); \
+ GLOBAL_ATOM_DECL(domain); \
+ GLOBAL_ATOM_DECL(dontfrag); \
+ GLOBAL_ATOM_DECL(dontroute); \
+ GLOBAL_ATOM_DECL(dormant); \
+ GLOBAL_ATOM_DECL(drop_membership); \
+ GLOBAL_ATOM_DECL(drop_source_membership); \
+ GLOBAL_ATOM_DECL(dstopts); \
+ GLOBAL_ATOM_DECL(dup); \
+ GLOBAL_ATOM_DECL(dying); \
+ GLOBAL_ATOM_DECL(dynamic); \
+ GLOBAL_ATOM_DECL(echo); \
+ GLOBAL_ATOM_DECL(eether); \
+ GLOBAL_ATOM_DECL(efile); \
+ GLOBAL_ATOM_DECL(egp); \
+ GLOBAL_ATOM_DECL(enotsup); \
+ GLOBAL_ATOM_DECL(eor); \
+ GLOBAL_ATOM_DECL(error); \
+ GLOBAL_ATOM_DECL(errqueue); \
+ GLOBAL_ATOM_DECL(esp_network_level); \
+ GLOBAL_ATOM_DECL(esp_trans_level); \
+ GLOBAL_ATOM_DECL(ether); \
+ GLOBAL_ATOM_DECL(eui64); \
+ GLOBAL_ATOM_DECL(events); \
+ GLOBAL_ATOM_DECL(explicit_eor); \
+ GLOBAL_ATOM_DECL(faith); \
+ GLOBAL_ATOM_DECL(false); \
+ GLOBAL_ATOM_DECL(family); \
+ GLOBAL_ATOM_DECL(fastroute); \
+ GLOBAL_ATOM_DECL(flags); \
+ GLOBAL_ATOM_DECL(flowinfo); \
+ GLOBAL_ATOM_DECL(fragment_interleave); \
+ GLOBAL_ATOM_DECL(freebind); \
+ GLOBAL_ATOM_DECL(frelay); \
+ GLOBAL_ATOM_DECL(get_overlapped_result); \
+ GLOBAL_ATOM_DECL(get_peer_addr_info); \
+ GLOBAL_ATOM_DECL(hatype); \
+ GLOBAL_ATOM_DECL(hdrincl); \
+ GLOBAL_ATOM_DECL(hmac_ident); \
+ GLOBAL_ATOM_DECL(hoplimit); \
+ GLOBAL_ATOM_DECL(hopopts); \
+ GLOBAL_ATOM_DECL(host); \
+ GLOBAL_ATOM_DECL(icmp); \
+ GLOBAL_ATOM_DECL(icmp6); \
+ GLOBAL_ATOM_DECL(ieee802); \
+ GLOBAL_ATOM_DECL(ieee1394); \
+ GLOBAL_ATOM_DECL(ifindex); \
+ GLOBAL_ATOM_DECL(igmp); \
+ GLOBAL_ATOM_DECL(implink); \
+ GLOBAL_ATOM_DECL(index); \
+ GLOBAL_ATOM_DECL(inet); \
+ GLOBAL_ATOM_DECL(inet6); \
+ GLOBAL_ATOM_DECL(infiniband); \
+ GLOBAL_ATOM_DECL(info); \
+ GLOBAL_ATOM_DECL(initmsg); \
+ GLOBAL_ATOM_DECL(invalid); \
+ GLOBAL_ATOM_DECL(integer_range); \
+ GLOBAL_ATOM_DECL(iov); \
+ GLOBAL_ATOM_DECL(ip); \
+ GLOBAL_ATOM_DECL(ipcomp_level); \
+ GLOBAL_ATOM_DECL(ipip); \
+ GLOBAL_ATOM_DECL(ipv6); \
+ GLOBAL_ATOM_DECL(irq); \
+ GLOBAL_ATOM_DECL(i_want_mapped_v4_addr); \
+ GLOBAL_ATOM_DECL(join_group); \
+ GLOBAL_ATOM_DECL(keepalive); \
+ GLOBAL_ATOM_DECL(keepcnt); \
+ GLOBAL_ATOM_DECL(keepidle); \
+ GLOBAL_ATOM_DECL(keepintvl); \
+ GLOBAL_ATOM_DECL(kernel); \
+ GLOBAL_ATOM_DECL(knowsepoch); \
+ GLOBAL_ATOM_DECL(leave_group); \
+ GLOBAL_ATOM_DECL(level); \
+ GLOBAL_ATOM_DECL(linger); \
+ GLOBAL_ATOM_DECL(link); \
+ GLOBAL_ATOM_DECL(link0); \
+ GLOBAL_ATOM_DECL(link1); \
+ GLOBAL_ATOM_DECL(link2); \
+ GLOBAL_ATOM_DECL(local); \
+ GLOBAL_ATOM_DECL(localtlk); \
+ GLOBAL_ATOM_DECL(local_auth_chunks); \
+ GLOBAL_ATOM_DECL(loopback); \
+ GLOBAL_ATOM_DECL(lowdelay); \
+ GLOBAL_ATOM_DECL(lower_up); \
+ GLOBAL_ATOM_DECL(mark); \
+ GLOBAL_ATOM_DECL(master); \
+ GLOBAL_ATOM_DECL(maxburst); \
+ GLOBAL_ATOM_DECL(maxseg); \
+ GLOBAL_ATOM_DECL(md5sig); \
+ GLOBAL_ATOM_DECL(mem_end); \
+ GLOBAL_ATOM_DECL(mem_start); \
+ GLOBAL_ATOM_DECL(metricom); \
+ GLOBAL_ATOM_DECL(mincost); \
+ GLOBAL_ATOM_DECL(minttl); \
+ GLOBAL_ATOM_DECL(monitor); \
+ GLOBAL_ATOM_DECL(more); \
+ GLOBAL_ATOM_DECL(msfilter); \
+ GLOBAL_ATOM_DECL(mtu); \
+ GLOBAL_ATOM_DECL(mtu_discover); \
+ GLOBAL_ATOM_DECL(multicast); \
+ GLOBAL_ATOM_DECL(multicast_all); \
+ GLOBAL_ATOM_DECL(multicast_hops); \
+ GLOBAL_ATOM_DECL(multicast_if); \
+ GLOBAL_ATOM_DECL(multicast_loop); \
+ GLOBAL_ATOM_DECL(multicast_ttl); \
+ GLOBAL_ATOM_DECL(name); \
+ GLOBAL_ATOM_DECL(netns); \
+ GLOBAL_ATOM_DECL(netrom); \
+ GLOBAL_ATOM_DECL(nlen); \
+ GLOBAL_ATOM_DECL(noarp); \
+ GLOBAL_ATOM_DECL(nodelay); \
+ GLOBAL_ATOM_DECL(nodefrag); \
+ GLOBAL_ATOM_DECL(nogroup); \
+ GLOBAL_ATOM_DECL(none); \
+ GLOBAL_ATOM_DECL(noopt); \
+ GLOBAL_ATOM_DECL(nopush); \
+ GLOBAL_ATOM_DECL(nosignal); \
+ GLOBAL_ATOM_DECL(notrailers); \
+ GLOBAL_ATOM_DECL(not_bound); \
+ GLOBAL_ATOM_DECL(not_found); \
+ GLOBAL_ATOM_DECL(num_general_errors); \
+ GLOBAL_ATOM_DECL(not_owner); \
+ GLOBAL_ATOM_DECL(num_threads); \
+ GLOBAL_ATOM_DECL(num_unexpected_accepts); \
+ GLOBAL_ATOM_DECL(num_unexpected_connects); \
+ GLOBAL_ATOM_DECL(num_unexpected_reads); \
+ GLOBAL_ATOM_DECL(num_unexpected_writes); \
+ GLOBAL_ATOM_DECL(num_unknown_cmds); \
+ GLOBAL_ATOM_DECL(oactive); \
+ GLOBAL_ATOM_DECL(ok); \
+ GLOBAL_ATOM_DECL(oob); \
+ GLOBAL_ATOM_DECL(oobinline); \
+ GLOBAL_ATOM_DECL(options); \
+ GLOBAL_ATOM_DECL(origdstaddr); \
+ GLOBAL_ATOM_DECL(otherhost); \
+ GLOBAL_ATOM_DECL(outgoing); \
+ GLOBAL_ATOM_DECL(packet); \
+ GLOBAL_ATOM_DECL(partial_delivery_point); \
+ GLOBAL_ATOM_DECL(passcred); \
+ GLOBAL_ATOM_DECL(path); \
+ GLOBAL_ATOM_DECL(peek); \
+ GLOBAL_ATOM_DECL(peek_off); \
+ GLOBAL_ATOM_DECL(peer_addr_params); \
+ GLOBAL_ATOM_DECL(peer_auth_chunks); \
+ GLOBAL_ATOM_DECL(peercred); \
+ GLOBAL_ATOM_DECL(pktinfo); \
+ GLOBAL_ATOM_DECL(pktoptions); \
+ GLOBAL_ATOM_DECL(pkttype); \
+ GLOBAL_ATOM_DECL(pointopoint); \
+ GLOBAL_ATOM_DECL(port); \
+ GLOBAL_ATOM_DECL(portrange); \
+ GLOBAL_ATOM_DECL(portsel); \
+ GLOBAL_ATOM_DECL(ppromisc); \
+ GLOBAL_ATOM_DECL(primary_addr); \
+ GLOBAL_ATOM_DECL(prim_file); \
+ GLOBAL_ATOM_DECL(priority); \
+ GLOBAL_ATOM_DECL(promisc); \
+ GLOBAL_ATOM_DECL(pronet); \
+ GLOBAL_ATOM_DECL(protocol); \
+ GLOBAL_ATOM_DECL(pup); \
+ GLOBAL_ATOM_DECL(raw); \
+ GLOBAL_ATOM_DECL(rcvbuf); \
+ GLOBAL_ATOM_DECL(rcvbufforce); \
+ GLOBAL_ATOM_DECL(rcvlowat); \
+ GLOBAL_ATOM_DECL(rcvtimeo); \
+ GLOBAL_ATOM_DECL(rdm); \
+ GLOBAL_ATOM_DECL(read_byte); \
+ GLOBAL_ATOM_DECL(read_fails); \
+ GLOBAL_ATOM_DECL(read_pkg); \
+ GLOBAL_ATOM_DECL(read_tries); \
+ GLOBAL_ATOM_DECL(read_waits); \
+ GLOBAL_ATOM_DECL(recv); \
+ GLOBAL_ATOM_DECL(recvdstaddr); \
+ GLOBAL_ATOM_DECL(recverr); \
+ GLOBAL_ATOM_DECL(recvfrom); \
+ GLOBAL_ATOM_DECL(recvhoplimit); \
+ GLOBAL_ATOM_DECL(recvif); \
+ GLOBAL_ATOM_DECL(recvmsg); \
+ GLOBAL_ATOM_DECL(recvopts); \
+ GLOBAL_ATOM_DECL(recvorigdstaddr); \
+ GLOBAL_ATOM_DECL(recvpktinfo); \
+ GLOBAL_ATOM_DECL(recvtclass); \
+ GLOBAL_ATOM_DECL(recvtos); \
+ GLOBAL_ATOM_DECL(recvttl); \
+ GLOBAL_ATOM_DECL(reliability); \
+ GLOBAL_ATOM_DECL(renaming); \
+ GLOBAL_ATOM_DECL(reset_streams); \
+ GLOBAL_ATOM_DECL(retopts); \
+ GLOBAL_ATOM_DECL(reuseaddr); \
+ GLOBAL_ATOM_DECL(reuseport); \
+ GLOBAL_ATOM_DECL(rights); \
+ GLOBAL_ATOM_DECL(router_alert); \
+ GLOBAL_ATOM_DECL(rthdr); \
+ GLOBAL_ATOM_DECL(rtoinfo); \
+ GLOBAL_ATOM_DECL(running); \
+ GLOBAL_ATOM_DECL(rxq_ovfl); \
+ GLOBAL_ATOM_DECL(scope_id); \
+ GLOBAL_ATOM_DECL(sctp); \
+ GLOBAL_ATOM_DECL(sec); \
+ GLOBAL_ATOM_DECL(select); \
+ GLOBAL_ATOM_DECL(select_failed); \
+ GLOBAL_ATOM_DECL(select_sent); \
+ GLOBAL_ATOM_DECL(send); \
+ GLOBAL_ATOM_DECL(sendfile); \
+ GLOBAL_ATOM_DECL(sendfile_byte); \
+ GLOBAL_ATOM_DECL(sendfile_deferred_close); \
+ GLOBAL_ATOM_DECL(sendfile_fails); \
+ GLOBAL_ATOM_DECL(sendfile_max); \
+ GLOBAL_ATOM_DECL(sendfile_pkg); \
+ GLOBAL_ATOM_DECL(sendfile_pkg_max); \
+ GLOBAL_ATOM_DECL(sendfile_tries); \
+ GLOBAL_ATOM_DECL(sendfile_waits); \
+ GLOBAL_ATOM_DECL(sendmsg); \
+ GLOBAL_ATOM_DECL(sendsrcaddr); \
+ GLOBAL_ATOM_DECL(sendto); \
+ GLOBAL_ATOM_DECL(seqpacket); \
+ GLOBAL_ATOM_DECL(setfib); \
+ GLOBAL_ATOM_DECL(set_peer_primary_addr); \
+ GLOBAL_ATOM_DECL(simplex); \
+ GLOBAL_ATOM_DECL(slave); \
+ GLOBAL_ATOM_DECL(slen); \
+ GLOBAL_ATOM_DECL(sndbuf); \
+ GLOBAL_ATOM_DECL(sndbufforce); \
+ GLOBAL_ATOM_DECL(sndlowat); \
+ GLOBAL_ATOM_DECL(sndtimeo); \
+ GLOBAL_ATOM_DECL(sockaddr); \
+ GLOBAL_ATOM_DECL(socket); \
+ GLOBAL_ATOM_DECL(spec_dst); \
+ GLOBAL_ATOM_DECL(staticarp); \
+ GLOBAL_ATOM_DECL(state); \
+ GLOBAL_ATOM_DECL(status); \
+ GLOBAL_ATOM_DECL(stream); \
+ GLOBAL_ATOM_DECL(syncnt); \
+ GLOBAL_ATOM_DECL(tclass); \
+ GLOBAL_ATOM_DECL(tcp); \
+ GLOBAL_ATOM_DECL(throughput); \
+ GLOBAL_ATOM_DECL(timestamp); \
+ GLOBAL_ATOM_DECL(tos); \
+ GLOBAL_ATOM_DECL(transparent); \
+ GLOBAL_ATOM_DECL(timeout); \
+ GLOBAL_ATOM_DECL(true); \
+ GLOBAL_ATOM_DECL(trunc); \
+ GLOBAL_ATOM_DECL(ttl); \
+ GLOBAL_ATOM_DECL(tunnel); \
+ GLOBAL_ATOM_DECL(tunnel6); \
+ GLOBAL_ATOM_DECL(txqlen); \
+ GLOBAL_ATOM_DECL(type); \
+ GLOBAL_ATOM_DECL(udp); \
+ GLOBAL_ATOM_DECL(unblock_source); \
+ GLOBAL_ATOM_DECL(undefined); \
+ GLOBAL_ATOM_DECL(unicast_hops); \
+ GLOBAL_ATOM_DECL(unknown); \
+ GLOBAL_ATOM_DECL(unspec); \
+ GLOBAL_ATOM_DECL(up); \
+ GLOBAL_ATOM_DECL(update_accept_context); \
+ GLOBAL_ATOM_DECL(update_connect_context); \
+ GLOBAL_ATOM_DECL(usec); \
+ GLOBAL_ATOM_DECL(user); \
+ GLOBAL_ATOM_DECL(user_timeout); \
+ GLOBAL_ATOM_DECL(use_ext_recvinfo); \
+ GLOBAL_ATOM_DECL(use_min_mtu); \
+ GLOBAL_ATOM_DECL(use_registry); \
+ GLOBAL_ATOM_DECL(value); \
+ GLOBAL_ATOM_DECL(void); \
+ GLOBAL_ATOM_DECL(v6only); \
+ GLOBAL_ATOM_DECL(write_byte); \
+ GLOBAL_ATOM_DECL(write_fails); \
+ GLOBAL_ATOM_DECL(write_pkg); \
+ GLOBAL_ATOM_DECL(write_tries); \
+ GLOBAL_ATOM_DECL(write_waits); \
+ GLOBAL_ATOM_DECL(zero)
-struct ESockOpt
-{
- int opt; // Option number
- // Function to set option
- ERL_NIF_TERM (*setopt)
- (ErlNifEnv *env, ESockDescriptor *descP,
- int level, int opt, ERL_NIF_TERM eVal);
- // Function to get option
- ERL_NIF_TERM (*getopt)
- (ErlNifEnv *env, ESockDescriptor *descP,
- int level, int opt);
+/* *** Global error reason atoms *** */
+#define GLOBAL_ERROR_REASON_ATOMS \
+ GLOBAL_ATOM_DECL(create_accept_socket); \
+ GLOBAL_ATOM_DECL(eagain); \
+ GLOBAL_ATOM_DECL(einval); \
+ GLOBAL_ATOM_DECL(select_read); \
+ GLOBAL_ATOM_DECL(select_write)
- ERL_NIF_TERM *nameP; // Pointer to option name atom
+
+#define GLOBAL_ATOM_DECL(A) ERL_NIF_TERM esock_atom_##A
+GLOBAL_ATOMS;
+GLOBAL_ERROR_REASON_ATOMS;
+#undef GLOBAL_ATOM_DECL
+ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
+
+/* *** Local atoms *** */
+#define LOCAL_ATOMS \
+ LOCAL_ATOM_DECL(accepting); \
+ LOCAL_ATOM_DECL(adaptation_layer); \
+ LOCAL_ATOM_DECL(add); \
+ LOCAL_ATOM_DECL(addr_unreach); \
+ LOCAL_ATOM_DECL(address); \
+ LOCAL_ATOM_DECL(adm_prohibited); \
+ LOCAL_ATOM_DECL(association); \
+ LOCAL_ATOM_DECL(assoc_id); \
+ LOCAL_ATOM_DECL(authentication); \
+ LOCAL_ATOM_DECL(boolean); \
+ LOCAL_ATOM_DECL(bound); \
+ LOCAL_ATOM_DECL(bufsz); \
+ LOCAL_ATOM_DECL(close); \
+ LOCAL_ATOM_DECL(closing); \
+ LOCAL_ATOM_DECL(code); \
+ LOCAL_ATOM_DECL(cookie_life); \
+ LOCAL_ATOM_DECL(counter_wrap); \
+ LOCAL_ATOM_DECL(ctype); \
+ LOCAL_ATOM_DECL(data_io); \
+ LOCAL_ATOM_DECL(debug_filename); \
+ LOCAL_ATOM_DECL(del); \
+ LOCAL_ATOM_DECL(dest_unreach); \
+ LOCAL_ATOM_DECL(do); \
+ LOCAL_ATOM_DECL(dont); \
+ LOCAL_ATOM_DECL(dtor); \
+ LOCAL_ATOM_DECL(exclude); \
+ LOCAL_ATOM_DECL(false); \
+ LOCAL_ATOM_DECL(frag_needed); \
+ LOCAL_ATOM_DECL(gifaddr); \
+ LOCAL_ATOM_DECL(gifbrdaddr); \
+ LOCAL_ATOM_DECL(gifconf); \
+ LOCAL_ATOM_DECL(gifdstaddr); \
+ LOCAL_ATOM_DECL(gifflags); \
+ LOCAL_ATOM_DECL(gifhwaddr); \
+ LOCAL_ATOM_DECL(gifindex); \
+ LOCAL_ATOM_DECL(gifmap); \
+ LOCAL_ATOM_DECL(gifmtu); \
+ LOCAL_ATOM_DECL(gifname); \
+ LOCAL_ATOM_DECL(gifnetmask); \
+ LOCAL_ATOM_DECL(giftxqlen); \
+ LOCAL_ATOM_DECL(host_unknown); \
+ LOCAL_ATOM_DECL(host_unreach); \
+ LOCAL_ATOM_DECL(how); \
+ LOCAL_ATOM_DECL(in4_sockaddr); \
+ LOCAL_ATOM_DECL(in6_sockaddr); \
+ LOCAL_ATOM_DECL(include); \
+ LOCAL_ATOM_DECL(initial); \
+ LOCAL_ATOM_DECL(interface); \
+ LOCAL_ATOM_DECL(integer); \
+ LOCAL_ATOM_DECL(ioctl_flags); \
+ LOCAL_ATOM_DECL(ioctl_requests); \
+ LOCAL_ATOM_DECL(iov_max); \
+ LOCAL_ATOM_DECL(iow); \
+ LOCAL_ATOM_DECL(io_backend); \
+ LOCAL_ATOM_DECL(io_num_threads); \
+ LOCAL_ATOM_DECL(listening); \
+ LOCAL_ATOM_DECL(local_rwnd); \
+ LOCAL_ATOM_DECL(map); \
+ LOCAL_ATOM_DECL(max); \
+ LOCAL_ATOM_DECL(max_attempts); \
+ LOCAL_ATOM_DECL(max_init_timeo); \
+ LOCAL_ATOM_DECL(max_instreams); \
+ LOCAL_ATOM_DECL(asocmaxrxt); \
+ LOCAL_ATOM_DECL(min); \
+ LOCAL_ATOM_DECL(missing); \
+ LOCAL_ATOM_DECL(mode); \
+ LOCAL_ATOM_DECL(msg); \
+ LOCAL_ATOM_DECL(msg_flags); \
+ LOCAL_ATOM_DECL(mtu); \
+ LOCAL_ATOM_DECL(multiaddr); \
+ LOCAL_ATOM_DECL(net_unknown); \
+ LOCAL_ATOM_DECL(net_unreach); \
+ LOCAL_ATOM_DECL(nogroup); \
+ LOCAL_ATOM_DECL(none); \
+ LOCAL_ATOM_DECL(noroute); \
+ LOCAL_ATOM_DECL(not_neighbour); \
+ LOCAL_ATOM_DECL(null); \
+ LOCAL_ATOM_DECL(num_acceptors); \
+ LOCAL_ATOM_DECL(num_cnt_bits); \
+ LOCAL_ATOM_DECL(num_dinet); \
+ LOCAL_ATOM_DECL(num_dinet6); \
+ LOCAL_ATOM_DECL(num_dlocal); \
+ LOCAL_ATOM_DECL(num_outstreams); \
+ LOCAL_ATOM_DECL(number_peer_destinations); \
+ LOCAL_ATOM_DECL(num_pip); \
+ LOCAL_ATOM_DECL(num_psctp); \
+ LOCAL_ATOM_DECL(num_ptcp); \
+ LOCAL_ATOM_DECL(num_pudp); \
+ LOCAL_ATOM_DECL(num_readers); \
+ LOCAL_ATOM_DECL(num_sockets); \
+ LOCAL_ATOM_DECL(num_tdgrams); \
+ LOCAL_ATOM_DECL(num_tseqpkgs); \
+ LOCAL_ATOM_DECL(num_tstreams); \
+ LOCAL_ATOM_DECL(num_writers); \
+ LOCAL_ATOM_DECL(offender); \
+ LOCAL_ATOM_DECL(onoff); \
+ LOCAL_ATOM_DECL(options); \
+ LOCAL_ATOM_DECL(origin); \
+ LOCAL_ATOM_DECL(otp); \
+ LOCAL_ATOM_DECL(otp_socket_option);\
+ LOCAL_ATOM_DECL(owner); \
+ LOCAL_ATOM_DECL(partial_delivery); \
+ LOCAL_ATOM_DECL(peer_error); \
+ LOCAL_ATOM_DECL(peer_rwnd); \
+ LOCAL_ATOM_DECL(pkt_toobig); \
+ LOCAL_ATOM_DECL(policy_fail); \
+ LOCAL_ATOM_DECL(port); \
+ LOCAL_ATOM_DECL(port_unreach); \
+ LOCAL_ATOM_DECL(probe); \
+ LOCAL_ATOM_DECL(protocols); \
+ LOCAL_ATOM_DECL(rcvctrlbuf); \
+ LOCAL_ATOM_DECL(read); \
+ LOCAL_ATOM_DECL(read_pkg_max); \
+ LOCAL_ATOM_DECL(read_waits); \
+ LOCAL_ATOM_DECL(read_write); \
+ LOCAL_ATOM_DECL(registry); \
+ LOCAL_ATOM_DECL(reject_route); \
+ LOCAL_ATOM_DECL(remote); \
+ LOCAL_ATOM_DECL(rstates); \
+ LOCAL_ATOM_DECL(selected); \
+ LOCAL_ATOM_DECL(sender_dry); \
+ LOCAL_ATOM_DECL(send_failure); \
+ LOCAL_ATOM_DECL(shutdown); \
+ LOCAL_ATOM_DECL(sifaddr); \
+ LOCAL_ATOM_DECL(sifbrdaddr); \
+ LOCAL_ATOM_DECL(sifdstaddr); \
+ LOCAL_ATOM_DECL(sifflags); \
+ LOCAL_ATOM_DECL(sifmtu); \
+ LOCAL_ATOM_DECL(sifnetmask); \
+ LOCAL_ATOM_DECL(siftxqlen); \
+ LOCAL_ATOM_DECL(slist); \
+ LOCAL_ATOM_DECL(sndctrlbuf); \
+ LOCAL_ATOM_DECL(socket_debug); \
+ LOCAL_ATOM_DECL(socket_level); \
+ LOCAL_ATOM_DECL(socket_option); \
+ LOCAL_ATOM_DECL(sourceaddr); \
+ LOCAL_ATOM_DECL(time_exceeded); \
+ LOCAL_ATOM_DECL(true); \
+ LOCAL_ATOM_DECL(txstatus); \
+ LOCAL_ATOM_DECL(txtime); \
+ LOCAL_ATOM_DECL(want); \
+ LOCAL_ATOM_DECL(write); \
+ LOCAL_ATOM_DECL(write_pkg_max); \
+ LOCAL_ATOM_DECL(wstates); \
+ LOCAL_ATOM_DECL(zerocopy)
+
+/* Local error reason atoms
+ * Keep this (commented) for future use...
+ */
+/*
+#define LOCAL_ERROR_REASON_ATOMS \
+ LOCAL_ATOM_DECL(select_read); \
+ LOCAL_ATOM_DECL(select_write)
+*/
+#define LOCAL_ATOM_DECL(LA) static ERL_NIF_TERM atom_##LA
+LOCAL_ATOMS;
+// LOCAL_ERROR_REASON_ATOMS;
+#undef LOCAL_ATOM_DECL
+
+/* *** Sockets *** */
+static ErlNifResourceType* esocks;
+static ErlNifResourceTypeInit esockInit = {
+ esock_dtor,
+ esock_stop,
+ (ErlNifResourceDown*) esock_down
};
-// qsort and bsearch helper
-static int cmpESockOpt(const void *vpa, const void *vpb) {
- struct ESockOpt *a, *b;
- a = (struct ESockOpt *) vpa;
- b = (struct ESockOpt *) vpb;
- return a->opt < b->opt ? -1 : (a->opt > b->opt ? 1 : 0);
-}
+// Initiated when the nif is loaded
+static ESockData data;
+
+/* Jump table for the I/O backend (async or sync) */
+static ESockIoBackend io_backend = {0};
+
+
+/* This, the test for NULL), is temporary until we have a win stub */
+#define ESOCK_IO_INIT(NUMT) \
+ ((io_backend.init != NULL) ? \
+ io_backend.init((NUMT), &data) : ESOCK_IO_ERR_UNSUPPORTED)
+#define ESOCK_IO_FIN() \
+ ((io_backend.finish != NULL) ? \
+ io_backend.finish() : ESOCK_IO_ERR_UNSUPPORTED)
+
+#define ESOCK_IO_INFO(ENV) \
+ ((io_backend.info != NULL) ? \
+ io_backend.info((ENV)) : MKEMA((ENV)))
+#define ESOCK_IO_CMD(ENV, CMD, CDATA) \
+ ((io_backend.cmd != NULL) ? \
+ io_backend.cmd((ENV), (CMD), (CDATA)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SUPPORTS_0(ENV) \
+ ((io_backend.supports_0 != NULL) ? \
+ io_backend.supports_0((ENV)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SUPPORTS_1(ENV, KEY) \
+ ((io_backend.supports_1 != NULL) ? \
+ io_backend.supports_1((ENV), (KEY)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+
+#define ESOCK_IO_OPEN_WITH_FD(ENV, FD, EOPTS) \
+ ((io_backend.open_with_fd != NULL) ? \
+ io_backend.open_with_fd((ENV), (FD), (EOPTS), &data) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_OPEN_PLAIN(ENV, D, T, P, EOPTS) \
+ ((io_backend.open_plain != NULL) ? \
+ io_backend.open_plain((ENV), (D), \
+ (T), (P), (EOPTS), &data) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_BIND(ENV, D, SAP, AL) \
+ ((io_backend.bind != NULL) ? \
+ io_backend.bind((ENV), (D), (SAP), (AL)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_CONNECT(ENV, D, SR, CR, AP, AL) \
+ ((io_backend.connect != NULL) ? \
+ io_backend.connect((ENV), (D), \
+ (SR), (CR), (AP), (AL)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_LISTEN(ENV, D, BL) \
+ ((io_backend.listen != NULL) ? \
+ io_backend.listen((ENV), (D), (BL)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_ACCEPT(ENV, D, SR, AR) \
+ ((io_backend.accept != NULL) ? \
+ io_backend.accept((ENV), (D), (SR), (AR)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SEND(ENV, D, SR, RF, L, F) \
+ ((io_backend.send != NULL) ? \
+ io_backend.send((ENV), (D), \
+ (SR), (RF), (L), (F)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SENDTO(ENV, D, \
+ SOCKR, SENDR, \
+ DP, F, TAP, TAL) \
+ ((io_backend.sendto != NULL) ? \
+ io_backend.sendto((ENV), (D), \
+ (SOCKR), (SENDR), \
+ (DP), (F), (TAP), (TAL)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SENDMSG(ENV, D, \
+ SOCKR, SENDR, EM, F, EIOV) \
+ ((io_backend.sendmsg != NULL) ? \
+ io_backend.sendmsg((ENV), (D), \
+ (SOCKR), (SENDR), \
+ (EM), (F), (EIOV), &data) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SENDFILE_START(ENV, D, \
+ SOR, SNR, \
+ O, CN, FR) \
+ ((io_backend.sendfile_start != NULL) ? \
+ io_backend.sendfile_start((ENV), (D), \
+ (SOR), (SNR), \
+ (O), (CN), (FR)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SENDFILE_CONT(ENV, D, \
+ SOR, SNR, \
+ O, CP) \
+ ((io_backend.sendfile_cont != NULL) ? \
+ io_backend.sendfile_cont((ENV), (D), \
+ (SOR), (SNR), \
+ (O), (CP)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SENDFILE_DC(ENV, D) \
+ ((io_backend.sendfile_dc != NULL) ? \
+ io_backend.sendfile_dc((ENV), (D)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_RECV(ENV, D, \
+ SR, RR, L, F) \
+ ((io_backend.recv != NULL) ? \
+ io_backend.recv((ENV), (D), \
+ (SR), (RR), (L), (F)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_RECVFROM(ENV, D, \
+ SR, RR, L, F) \
+ ((io_backend.recvfrom != NULL) ? \
+ io_backend.recvfrom((ENV), (D), \
+ (SR), (RR), (L), (F)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_RECVMSG(ENV, D, \
+ SR, RR, BL, CL, F) \
+ ((io_backend.recvmsg != NULL) ? \
+ io_backend.recvmsg((ENV), (D), \
+ (SR), (RR), \
+ (BL), (CL), (F)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_CLOSE(ENV, D) \
+ ((io_backend.close != NULL) ? \
+ io_backend.close((ENV), (D)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_FIN_CLOSE(ENV, D) \
+ ((io_backend.fin_close != NULL) ? \
+ io_backend.fin_close((ENV), (D)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SHUTDOWN(ENV, D, H) \
+ ((io_backend.shutdown != NULL) ? \
+ io_backend.shutdown((ENV), (D), (H)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SOCKNAME(ENV, D) \
+ ((io_backend.sockname != NULL) ? \
+ io_backend.sockname((ENV), (D)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_PEERNAME(ENV, D) \
+ ((io_backend.peername != NULL) ? \
+ io_backend.peername((ENV), (D)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_CANCEL_CONNECT(ENV, D, OR) \
+ ((io_backend.cancel_connect != NULL) ? \
+ io_backend.cancel_connect((ENV), (D), (OR)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_CANCEL_ACCEPT(ENV, D, SR, OR) \
+ ((io_backend.cancel_accept != NULL) ? \
+ io_backend.cancel_accept((ENV), (D), (SR), (OR)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_CANCEL_SEND(ENV, D, SR, OR) \
+ ((io_backend.cancel_send != NULL) ? \
+ io_backend.cancel_send((ENV), (D), (SR), (OR)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_CANCEL_RECV(ENV, D, SR, OR) \
+ ((io_backend.cancel_recv != NULL) ? \
+ io_backend.cancel_recv((ENV), (D), (SR), (OR)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SETOPT(ENV, D, L, O, EV) \
+ ((io_backend.setopt != NULL) ? \
+ io_backend.setopt((ENV), (D), (L), (O), (EV)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SETOPT_NATIVE(ENV, D, L, O, EV) \
+ ((io_backend.setopt_native != NULL) ? \
+ io_backend.setopt_native((ENV), (D), (L), (O), (EV)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_SETOPT_OTP(ENV, D, L, O) \
+ ((io_backend.setopt_otp != NULL) ? \
+ io_backend.setopt_otp((ENV), (D), (L), (O)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_GETOPT(ENV, D, L, O) \
+ ((io_backend.getopt != NULL) ? \
+ io_backend.getopt((ENV), (D), (L), (O)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_GETOPT_NATIVE(ENV, D, L, O, VS) \
+ ((io_backend.getopt_native != NULL) ? \
+ io_backend.getopt_native((ENV), (D), (L), (O), (VS)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_GETOPT_OTP(ENV, D, EO) \
+ ((io_backend.getopt_otp != NULL) ? \
+ io_backend.getopt_otp((ENV), (D), (EO)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_IOCTL_2(ENV, D, R) \
+ ((io_backend.ioctl_2 != NULL) ? \
+ io_backend.ioctl_2((ENV), (D), (R)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_IOCTL_3(ENV, D, R, A) \
+ ((io_backend.ioctl_3 != NULL) ? \
+ io_backend.ioctl_3((ENV), (D), (R), (A)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+#define ESOCK_IO_IOCTL_4(ENV, D, R, A1, A2) \
+ ((io_backend.ioctl_4 != NULL) ? \
+ io_backend.ioctl_4((ENV), (D), (R), (A1), (A2)) : \
+ enif_raise_exception((ENV), MKA((ENV), "notsup")))
+
+#define ESOCK_IO_DTOR(ENV, D) \
+ ((io_backend.dtor != NULL) ? \
+ io_backend.dtor((ENV), (D)) : ((void) (D)))
+#define ESOCK_IO_STOP(ENV, D) \
+ ((io_backend.stop != NULL) ? \
+ io_backend.stop((ENV), (D)) : ((void) (D)))
+#define ESOCK_IO_DOWN(ENV, D, PP, MP) \
+ ((io_backend.down != NULL) ? \
+ io_backend.down((ENV), (D), (PP), (MP)) : ((void) (D)))
+
+/* ------------------------------------------------------------------------
+ * Socket option tables and handling
+ */
+
/* SO_* options -------------------------------------------------------- */
static struct ESockOpt optLevelSocket[] =
@@ -2597,11 +3332,11 @@ static struct ESockOpt optLevelIPV6[] =
{
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO)
#if defined(IPV6_RECVPKTINFO)
- IPV6_RECVPKTINFO,
+ IPV6_RECVPKTINFO,
#else
- IPV6_PKTINFO,
+ IPV6_PKTINFO,
#endif
- esock_setopt_bool_opt, esock_getopt_bool_opt,
+ esock_setopt_bool_opt, esock_getopt_bool_opt,
#else
0, NULL, NULL,
#endif
@@ -2663,7 +3398,7 @@ static struct ESockOpt optLevelIPV6[] =
#endif
&esock_atom_v6only}
- };
+ };
#endif // #ifdef HAVE_IPV6
@@ -2852,60 +3587,53 @@ static struct ESockOpt optLevelUDP[] =
};
-/* Option levels table ------------------------------------------------- */
-struct ESockOptLevel
-{
- int level; // Level number
-
- size_t num; // Number of options
-
- struct ESockOpt *opts; // Options table
-};
+/* Option levels utility macro */
-// qsort and bsearch helper
-static int cmpESockOptLevel(const void *vpa, const void *vpb) {
- struct ESockOptLevel *a, *b;
- a = (struct ESockOptLevel*) vpa;
- b = (struct ESockOptLevel*) vpb;
- return a->level < b->level ? -1 : (a->level > b->level ? 1 : 0);
-}
+#define OPT_LEVEL(Level, Opts, Name) {(Level), NUM(Opts), (Opts), (Name)}
-#define OPT_LEVEL(Level, Opts) {(Level), NUM(Opts), (Opts)}
/* Table --------------------------------------------------------------- */
static struct ESockOptLevel optLevels[] =
{
- OPT_LEVEL(SOL_SOCKET, optLevelSocket),
+ OPT_LEVEL(SOL_SOCKET, optLevelSocket, &esock_atom_socket),
+#ifndef __WIN32__
#ifdef SOL_IP
- OPT_LEVEL(SOL_IP, optLevelIP),
+ OPT_LEVEL(SOL_IP, optLevelIP, &esock_atom_ip),
#else
- OPT_LEVEL(IPPROTO_IP, optLevelIP),
+ OPT_LEVEL(IPPROTO_IP, optLevelIP, &esock_atom_ip),
+#endif
+#else
+ OPT_LEVEL(IPPROTO_IP, optLevelIP, &esock_atom_ip),
#endif
#ifdef HAVE_IPV6
+#ifndef __WIN32__
#ifdef SOL_IPV6
- OPT_LEVEL(SOL_IPV6, optLevelIPV6),
+ OPT_LEVEL(SOL_IPV6, optLevelIPV6, &esock_atom_ipv6),
+#else
+ OPT_LEVEL(IPPROTO_IPV6, optLevelIPV6, &esock_atom_ipv6),
+#endif
#else
- OPT_LEVEL(IPPROTO_IPV6, optLevelIPV6),
+ OPT_LEVEL(IPPROTO_IPV6, optLevelIPV6, &esock_atom_ipv6),
#endif
#endif // #ifdef HAVE_IPV6
#ifdef HAVE_SCTP
- OPT_LEVEL(IPPROTO_SCTP, optLevelSCTP),
+ OPT_LEVEL(IPPROTO_SCTP, optLevelSCTP, &esock_atom_sctp),
#endif // #ifdef HAVE_SCTP
- OPT_LEVEL(IPPROTO_UDP, optLevelUDP),
- OPT_LEVEL(IPPROTO_TCP, optLevelTCP)
+ OPT_LEVEL(IPPROTO_UDP, optLevelUDP, &esock_atom_udp),
+ OPT_LEVEL(IPPROTO_TCP, optLevelTCP, &esock_atom_tcp)
};
#undef OPT_LEVEL
/* Tables init (sorting) ----------------------------------------------- */
-#define ESOCK_SORT_TABLE(Array, Cmp) \
+#define ESOCK_SORT_TABLE(Array, Cmp) \
qsort((Array), NUM(Array), sizeof(*(Array)), (Cmp))
static void initOpts(void) {
@@ -2922,945 +3650,74 @@ static void initOpts(void) {
ESOCK_SORT_TABLE(optLevels, cmpESockOptLevel);
}
+
+/* ------------------------------------------------------------------------
+ * Socket option tables and handling
+ */
+
+// qsort and bsearch helper(s)
+static
+int cmpESockOpt(const void *vpa, const void *vpb)
+{
+ struct ESockOpt *a, *b;
+ a = (struct ESockOpt *) vpa;
+ b = (struct ESockOpt *) vpb;
+ return a->opt < b->opt ? -1 : (a->opt > b->opt ? 1 : 0);
+}
+
+static
+int cmpESockOptLevel(const void *vpa, const void *vpb)
+{
+ struct ESockOptLevel *a, *b;
+ a = (struct ESockOptLevel*) vpa;
+ b = (struct ESockOptLevel*) vpb;
+ return a->level < b->level ? -1 : (a->level > b->level ? 1 : 0);
+}
+
/* Option lookup in tables --------------------------------------------- */
-static struct ESockOpt *lookupOpt(int level, int opt) {
+static
+struct ESockOpt *lookupOpt(int level, int opt)
+{
struct ESockOptLevel levelKey, *levelP;
struct ESockOpt optKey;
sys_memzero((char *) &levelKey, sizeof(levelKey));
levelKey.level = level;
- levelP =
- bsearch(&levelKey, optLevels, NUM(optLevels), sizeof(*optLevels),
- cmpESockOptLevel);
+ levelP = bsearch(&levelKey, optLevels, NUM(optLevels), sizeof(*optLevels),
+ cmpESockOptLevel);
if (levelP == NULL)
return NULL;
sys_memzero((char *) &optKey, sizeof(optKey));
optKey.opt = opt;
- return
- bsearch(&optKey, levelP->opts, levelP->num, sizeof(*levelP->opts),
- cmpESockOpt);
+ return bsearch(&optKey, levelP->opts, levelP->num, sizeof(*levelP->opts),
+ cmpESockOpt);
}
-/* --------------------------------------------------------------------- */
-
-#if defined(IP_TOS)
-static BOOLEAN_T decode_ip_tos(ErlNifEnv* env,
- ERL_NIF_TERM eVal,
- int* val);
-#endif
-#if defined(IP_MTU_DISCOVER)
-static BOOLEAN_T decode_ip_pmtudisc(ErlNifEnv* env,
- ERL_NIF_TERM eVal,
- int* val);
-#endif
-#if defined(IP_MTU_DISCOVER)
-static void encode_ip_pmtudisc(ErlNifEnv* env,
- int val,
- ERL_NIF_TERM* eVal);
-#endif
-#if defined(IPV6_MTU_DISCOVER)
-static BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env,
- ERL_NIF_TERM eVal,
- int* val);
-#endif
-#if defined(IPV6_MTU_DISCOVER)
-static void encode_ipv6_pmtudisc(ErlNifEnv* env,
- int val,
- ERL_NIF_TERM* eVal);
-#endif
-#if defined(IPV6_MULTICAST_HOPS) || defined(IPV6_UNICAST_HOPS)
-static
-BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val);
-#endif
-
-/*
-static BOOLEAN_T decode_bool(ErlNifEnv* env,
- ERL_NIF_TERM eVal,
- BOOLEAN_T* val);
-*/
-// static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal);
-static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val);
-
-#if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO)
-static BOOLEAN_T decode_sctp_assoc_t(ErlNifEnv* env,
- ERL_NIF_TERM eVal,
- sctp_assoc_t* val);
-static ERL_NIF_TERM encode_sctp_assoc_t(ErlNifEnv* env,
- sctp_assoc_t val);
-#endif // #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO)
-
-static void esock_stop_handle_current(ErlNifEnv* env,
- const char* role,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ESockRequestor* reqP);
-static void inform_waiting_procs(ErlNifEnv* env,
- const char* role,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ESockRequestQueue* q,
- ERL_NIF_TERM reason);
-
-static int socket_setopt(int sock,
- int level,
- int opt,
- const void* optVal,
- const socklen_t optLen);
-
-static BOOLEAN_T ehow2how(ERL_NIF_TERM ehow, int* how);
-
-/* *** esock_activate_next_acceptor ***
- * *** esock_activate_next_writer ***
- * *** esock_activate_next_reader ***
- *
- * All the activate-next functions for acceptor, writer and reader
- * have exactly the same API, so we apply some macro magic to simplify.
- * They simply operates on dufferent data structures.
- *
- */
-
-#define ACTIVATE_NEXT_FUNCS_DEFS \
- ACTIVATE_NEXT_FUNC_DEF(acceptor) \
- ACTIVATE_NEXT_FUNC_DEF(writer) \
- ACTIVATE_NEXT_FUNC_DEF(reader)
-
-#define ACTIVATE_NEXT_FUNC_DEF(F) \
- extern BOOLEAN_T esock_activate_next_##F(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ERL_NIF_TERM sockRef);
-ACTIVATE_NEXT_FUNCS_DEFS
-#undef ACTIVATE_NEXT_FUNC_DEF
-
-/* esock_acceptor_search4pid | esock_writer_search4pid | esock_reader_search4pid
- * esock_acceptor_push | esock_writer_push | esock_reader_push
- * esock_acceptor_pop | esock_writer_pop | esock_reader_pop
- * esock_acceptor_unqueue | esock_writer_unqueue | esock_reader_unqueue
- *
- * All the queue operator functions (search4pid, push, pop
- * and unqueue) for acceptor, writer and reader has exactly
- * the same API, so we apply some macro magic to simplify.
- */
-
-#define ESOCK_OPERATOR_FUNCS_DEFS \
- ESOCK_OPERATOR_FUNCS_DEF(acceptor) \
- ESOCK_OPERATOR_FUNCS_DEF(writer) \
- ESOCK_OPERATOR_FUNCS_DEF(reader)
-
-#define ESOCK_OPERATOR_FUNCS_DEF(O) \
- extern BOOLEAN_T esock_##O##_search4pid(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ErlNifPid* pid); \
- extern void esock_##O##_push(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ErlNifPid pid, \
- ERL_NIF_TERM ref); \
- extern BOOLEAN_T esock_##O##_pop(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ESockRequestor* reqP); \
- extern BOOLEAN_T esock_##O##_unqueue(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ERL_NIF_TERM* refP, \
- const ErlNifPid* pidP);
-ESOCK_OPERATOR_FUNCS_DEFS
-#undef ESOCK_OPERATOR_FUNCS_DEF
-
-static BOOLEAN_T qsearch4pid(ErlNifEnv* env,
- ESockRequestQueue* q,
- ErlNifPid* pid);
-static void qpush(ESockRequestQueue* q,
- ESockRequestQueueElement* e);
-static ESockRequestQueueElement* qpop(ESockRequestQueue* q);
-static BOOLEAN_T qunqueue(ErlNifEnv* env,
- ESockDescriptor* descP,
- const char* slogan,
- ESockRequestQueue* q,
- ERL_NIF_TERM* refP,
- const ErlNifPid* pidP);
-
-static void esock_down_ctrl(ErlNifEnv* env,
- ESockDescriptor* descP,
- const ErlNifPid* pidP);
-static void esock_down_acceptor(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- const ErlNifPid* pidP,
- const ErlNifMonitor* monP);
-static void esock_down_writer(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- const ErlNifPid* pidP,
- const ErlNifMonitor* monP);
-static void esock_down_reader(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- const ErlNifPid* pidP,
- const ErlNifMonitor* monP);
-
-#ifdef HAVE_SENDFILE
-static void
-esock_send_sendfile_deferred_close_msg(ErlNifEnv* env,
- ESockDescriptor* descP);
-#endif
-static BOOLEAN_T esock_send_msg(ErlNifEnv* env,
- ErlNifPid* pid,
- ERL_NIF_TERM msg,
- ErlNifEnv* msgEnv);
-
-static ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env,
- ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env,
- ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env,
- ERL_NIF_TERM tag,
- ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef,
- ERL_NIF_TERM reason);
-static ERL_NIF_TERM mk_wrap_msg(ErlNifEnv* env,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM cnt);
-static ERL_NIF_TERM mk_close_msg(ErlNifEnv* env,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM closeRef);
-static ERL_NIF_TERM mk_select_msg(ErlNifEnv* env,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM selectRef);
-static ERL_NIF_TERM mk_socket_msg(ErlNifEnv* env,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM tag,
- ERL_NIF_TERM info);
-static ERL_NIF_TERM mk_socket(ErlNifEnv* env,
- ERL_NIF_TERM sockRef);
-
-/*
-static int esock_select_read(ErlNifEnv* env,
- ErlNifEvent event,
- void* obj,
- const ErlNifPid* pidP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM selectRef);
-static int esock_select_write(ErlNifEnv* env,
- ErlNifEvent event,
- void* obj,
- const ErlNifPid* pidP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM selectRef);
-static int esock_select_stop(ErlNifEnv* env,
- ErlNifEvent event,
- void* obj);
-static int esock_select_cancel(ErlNifEnv* env,
- ErlNifEvent event,
- enum ErlNifSelectFlags mode,
- void* obj);
-*/
-
-static char* extract_debug_filename(ErlNifEnv* env,
- ERL_NIF_TERM map);
-
-
-/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *
- * *
- * *
- * End of non-__WIN32__ section a.k.a UNIX section *
- * *
- * *
- * ---------------------------------------------------------------------- */
-#endif // #ifndef __WIN32__
-
-
-/*
-#if defined(HAS_AF_LOCAL) || defined(SO_BINDTODEVICE)
-static size_t my_strnlen(const char *s, size_t maxlen);
-#endif
-*/
-
-static void esock_dtor(ErlNifEnv* env, void* obj);
-static void esock_stop(ErlNifEnv* env,
- void* obj,
- ErlNifEvent fd,
- int is_direct_call);
-static void esock_down(ErlNifEnv* env,
- void* obj,
- const ErlNifPid* pidP,
- const ErlNifMonitor* monP);
-
-static void esock_on_halt(void* priv_data);
-
-static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
-
-
-
-#if HAVE_IN6
-# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY
-# if HAVE_DECL_IN6ADDR_ANY_INIT
-static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } };
-# else
-static const struct in6_addr in6addr_any =
- { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
-# endif /* HAVE_IN6ADDR_ANY_INIT */
-# endif /* ! HAVE_DECL_IN6ADDR_ANY */
-
-# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK
-# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT
-static const struct in6_addr in6addr_loopback =
- { { IN6ADDR_LOOPBACK_INIT } };
-# else
-static const struct in6_addr in6addr_loopback =
- { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } };
-# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */
-# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */
-#endif /* HAVE_IN6 */
-
-
-
-/* *** Global atoms ***
- * Note that when an (global) atom is added here, it must also be added
- * in the socket_int.h file!
- */
-#define GLOBAL_ATOMS \
- GLOBAL_ATOM_DECL(abort); \
- GLOBAL_ATOM_DECL(accept); \
- GLOBAL_ATOM_DECL(acceptconn); \
- GLOBAL_ATOM_DECL(acceptfilter); \
- GLOBAL_ATOM_DECL(acc_success); \
- GLOBAL_ATOM_DECL(acc_fails); \
- GLOBAL_ATOM_DECL(acc_tries); \
- GLOBAL_ATOM_DECL(acc_waits); \
- GLOBAL_ATOM_DECL(adaption_layer); \
- GLOBAL_ATOM_DECL(addr); \
- GLOBAL_ATOM_DECL(addrform); \
- GLOBAL_ATOM_DECL(add_membership); \
- GLOBAL_ATOM_DECL(add_source_membership); \
- GLOBAL_ATOM_DECL(alen); \
- GLOBAL_ATOM_DECL(allmulti); \
- GLOBAL_ATOM_DECL(already); \
- GLOBAL_ATOM_DECL(any); \
- GLOBAL_ATOM_DECL(appletlk); \
- GLOBAL_ATOM_DECL(arcnet); \
- GLOBAL_ATOM_DECL(associnfo); \
- GLOBAL_ATOM_DECL(atm); \
- GLOBAL_ATOM_DECL(authhdr); \
- GLOBAL_ATOM_DECL(auth_active_key); \
- GLOBAL_ATOM_DECL(auth_asconf); \
- GLOBAL_ATOM_DECL(auth_chunk); \
- GLOBAL_ATOM_DECL(auth_delete_key); \
- GLOBAL_ATOM_DECL(auth_key); \
- GLOBAL_ATOM_DECL(auth_level); \
- GLOBAL_ATOM_DECL(autoclose); \
- GLOBAL_ATOM_DECL(automedia); \
- GLOBAL_ATOM_DECL(ax25); \
- GLOBAL_ATOM_DECL(bad_data); \
- GLOBAL_ATOM_DECL(bindtodevice); \
- GLOBAL_ATOM_DECL(block_source); \
- GLOBAL_ATOM_DECL(broadcast); \
- GLOBAL_ATOM_DECL(busy_poll); \
- GLOBAL_ATOM_DECL(cantconfig); \
- GLOBAL_ATOM_DECL(chaos); \
- GLOBAL_ATOM_DECL(checksum); \
- GLOBAL_ATOM_DECL(close); \
- GLOBAL_ATOM_DECL(closed); \
- GLOBAL_ATOM_DECL(cmsg_cloexec); \
- GLOBAL_ATOM_DECL(command); \
- GLOBAL_ATOM_DECL(confirm); \
- GLOBAL_ATOM_DECL(congestion); \
- GLOBAL_ATOM_DECL(connect); \
- GLOBAL_ATOM_DECL(connected); \
- GLOBAL_ATOM_DECL(connecting); \
- GLOBAL_ATOM_DECL(context); \
- GLOBAL_ATOM_DECL(cork); \
- GLOBAL_ATOM_DECL(credentials); \
- GLOBAL_ATOM_DECL(ctrl); \
- GLOBAL_ATOM_DECL(ctrunc); \
- GLOBAL_ATOM_DECL(data); \
- GLOBAL_ATOM_DECL(data_size); \
- GLOBAL_ATOM_DECL(debug); \
- GLOBAL_ATOM_DECL(default); \
- GLOBAL_ATOM_DECL(default_send_params); \
- GLOBAL_ATOM_DECL(delayed_ack_time); \
- GLOBAL_ATOM_DECL(dgram); \
- GLOBAL_ATOM_DECL(disable_fragments); \
- GLOBAL_ATOM_DECL(dlci); \
- GLOBAL_ATOM_DECL(domain); \
- GLOBAL_ATOM_DECL(dontfrag); \
- GLOBAL_ATOM_DECL(dontroute); \
- GLOBAL_ATOM_DECL(dormant); \
- GLOBAL_ATOM_DECL(drop_membership); \
- GLOBAL_ATOM_DECL(drop_source_membership); \
- GLOBAL_ATOM_DECL(dstopts); \
- GLOBAL_ATOM_DECL(dup); \
- GLOBAL_ATOM_DECL(dying); \
- GLOBAL_ATOM_DECL(dynamic); \
- GLOBAL_ATOM_DECL(echo); \
- GLOBAL_ATOM_DECL(eether); \
- GLOBAL_ATOM_DECL(efile); \
- GLOBAL_ATOM_DECL(egp); \
- GLOBAL_ATOM_DECL(enotsup); \
- GLOBAL_ATOM_DECL(eor); \
- GLOBAL_ATOM_DECL(error); \
- GLOBAL_ATOM_DECL(errqueue); \
- GLOBAL_ATOM_DECL(esp_network_level); \
- GLOBAL_ATOM_DECL(esp_trans_level); \
- GLOBAL_ATOM_DECL(ether); \
- GLOBAL_ATOM_DECL(eui64); \
- GLOBAL_ATOM_DECL(events); \
- GLOBAL_ATOM_DECL(explicit_eor); \
- GLOBAL_ATOM_DECL(faith); \
- GLOBAL_ATOM_DECL(false); \
- GLOBAL_ATOM_DECL(family); \
- GLOBAL_ATOM_DECL(fastroute); \
- GLOBAL_ATOM_DECL(flags); \
- GLOBAL_ATOM_DECL(flowinfo); \
- GLOBAL_ATOM_DECL(fragment_interleave); \
- GLOBAL_ATOM_DECL(freebind); \
- GLOBAL_ATOM_DECL(frelay); \
- GLOBAL_ATOM_DECL(get_peer_addr_info); \
- GLOBAL_ATOM_DECL(hatype); \
- GLOBAL_ATOM_DECL(hdrincl); \
- GLOBAL_ATOM_DECL(hmac_ident); \
- GLOBAL_ATOM_DECL(hoplimit); \
- GLOBAL_ATOM_DECL(hopopts); \
- GLOBAL_ATOM_DECL(host); \
- GLOBAL_ATOM_DECL(icmp); \
- GLOBAL_ATOM_DECL(icmp6); \
- GLOBAL_ATOM_DECL(ieee802); \
- GLOBAL_ATOM_DECL(ieee1394); \
- GLOBAL_ATOM_DECL(ifindex); \
- GLOBAL_ATOM_DECL(igmp); \
- GLOBAL_ATOM_DECL(implink); \
- GLOBAL_ATOM_DECL(index); \
- GLOBAL_ATOM_DECL(inet); \
- GLOBAL_ATOM_DECL(inet6); \
- GLOBAL_ATOM_DECL(infiniband); \
- GLOBAL_ATOM_DECL(info); \
- GLOBAL_ATOM_DECL(initmsg); \
- GLOBAL_ATOM_DECL(invalid); \
- GLOBAL_ATOM_DECL(integer_range); \
- GLOBAL_ATOM_DECL(iov); \
- GLOBAL_ATOM_DECL(ip); \
- GLOBAL_ATOM_DECL(ipcomp_level); \
- GLOBAL_ATOM_DECL(ipip); \
- GLOBAL_ATOM_DECL(ipv6); \
- GLOBAL_ATOM_DECL(i_want_mapped_v4_addr); \
- GLOBAL_ATOM_DECL(join_group); \
- GLOBAL_ATOM_DECL(keepalive); \
- GLOBAL_ATOM_DECL(keepcnt); \
- GLOBAL_ATOM_DECL(keepidle); \
- GLOBAL_ATOM_DECL(keepintvl); \
- GLOBAL_ATOM_DECL(kernel); \
- GLOBAL_ATOM_DECL(knowsepoch); \
- GLOBAL_ATOM_DECL(leave_group); \
- GLOBAL_ATOM_DECL(level); \
- GLOBAL_ATOM_DECL(linger); \
- GLOBAL_ATOM_DECL(link); \
- GLOBAL_ATOM_DECL(link0); \
- GLOBAL_ATOM_DECL(link1); \
- GLOBAL_ATOM_DECL(link2); \
- GLOBAL_ATOM_DECL(local); \
- GLOBAL_ATOM_DECL(localtlk); \
- GLOBAL_ATOM_DECL(local_auth_chunks); \
- GLOBAL_ATOM_DECL(loopback); \
- GLOBAL_ATOM_DECL(lowdelay); \
- GLOBAL_ATOM_DECL(lower_up); \
- GLOBAL_ATOM_DECL(mark); \
- GLOBAL_ATOM_DECL(master); \
- GLOBAL_ATOM_DECL(maxburst); \
- GLOBAL_ATOM_DECL(maxseg); \
- GLOBAL_ATOM_DECL(md5sig); \
- GLOBAL_ATOM_DECL(metricom); \
- GLOBAL_ATOM_DECL(mincost); \
- GLOBAL_ATOM_DECL(minttl); \
- GLOBAL_ATOM_DECL(monitor); \
- GLOBAL_ATOM_DECL(more); \
- GLOBAL_ATOM_DECL(msfilter); \
- GLOBAL_ATOM_DECL(mtu); \
- GLOBAL_ATOM_DECL(mtu_discover); \
- GLOBAL_ATOM_DECL(multicast); \
- GLOBAL_ATOM_DECL(multicast_all); \
- GLOBAL_ATOM_DECL(multicast_hops); \
- GLOBAL_ATOM_DECL(multicast_if); \
- GLOBAL_ATOM_DECL(multicast_loop); \
- GLOBAL_ATOM_DECL(multicast_ttl); \
- GLOBAL_ATOM_DECL(name); \
- GLOBAL_ATOM_DECL(netns); \
- GLOBAL_ATOM_DECL(netrom); \
- GLOBAL_ATOM_DECL(nlen); \
- GLOBAL_ATOM_DECL(noarp); \
- GLOBAL_ATOM_DECL(nodelay); \
- GLOBAL_ATOM_DECL(nodefrag); \
- GLOBAL_ATOM_DECL(nogroup); \
- GLOBAL_ATOM_DECL(none); \
- GLOBAL_ATOM_DECL(noopt); \
- GLOBAL_ATOM_DECL(nopush); \
- GLOBAL_ATOM_DECL(nosignal); \
- GLOBAL_ATOM_DECL(notrailers); \
- GLOBAL_ATOM_DECL(not_found); \
- GLOBAL_ATOM_DECL(not_owner); \
- GLOBAL_ATOM_DECL(oactive); \
- GLOBAL_ATOM_DECL(ok); \
- GLOBAL_ATOM_DECL(oob); \
- GLOBAL_ATOM_DECL(oobinline); \
- GLOBAL_ATOM_DECL(options); \
- GLOBAL_ATOM_DECL(origdstaddr); \
- GLOBAL_ATOM_DECL(otherhost); \
- GLOBAL_ATOM_DECL(outgoing); \
- GLOBAL_ATOM_DECL(packet); \
- GLOBAL_ATOM_DECL(partial_delivery_point); \
- GLOBAL_ATOM_DECL(passcred); \
- GLOBAL_ATOM_DECL(path); \
- GLOBAL_ATOM_DECL(peek); \
- GLOBAL_ATOM_DECL(peek_off); \
- GLOBAL_ATOM_DECL(peer_addr_params); \
- GLOBAL_ATOM_DECL(peer_auth_chunks); \
- GLOBAL_ATOM_DECL(peercred); \
- GLOBAL_ATOM_DECL(pktinfo); \
- GLOBAL_ATOM_DECL(pktoptions); \
- GLOBAL_ATOM_DECL(pkttype); \
- GLOBAL_ATOM_DECL(pointopoint); \
- GLOBAL_ATOM_DECL(port); \
- GLOBAL_ATOM_DECL(portrange); \
- GLOBAL_ATOM_DECL(portsel); \
- GLOBAL_ATOM_DECL(ppromisc); \
- GLOBAL_ATOM_DECL(primary_addr); \
- GLOBAL_ATOM_DECL(prim_file); \
- GLOBAL_ATOM_DECL(priority); \
- GLOBAL_ATOM_DECL(promisc); \
- GLOBAL_ATOM_DECL(pronet); \
- GLOBAL_ATOM_DECL(protocol); \
- GLOBAL_ATOM_DECL(pup); \
- GLOBAL_ATOM_DECL(raw); \
- GLOBAL_ATOM_DECL(rcvbuf); \
- GLOBAL_ATOM_DECL(rcvbufforce); \
- GLOBAL_ATOM_DECL(rcvlowat); \
- GLOBAL_ATOM_DECL(rcvtimeo); \
- GLOBAL_ATOM_DECL(rdm); \
- GLOBAL_ATOM_DECL(read_byte); \
- GLOBAL_ATOM_DECL(read_fails); \
- GLOBAL_ATOM_DECL(read_pkg); \
- GLOBAL_ATOM_DECL(read_tries); \
- GLOBAL_ATOM_DECL(recv); \
- GLOBAL_ATOM_DECL(recvdstaddr); \
- GLOBAL_ATOM_DECL(recverr); \
- GLOBAL_ATOM_DECL(recvfrom); \
- GLOBAL_ATOM_DECL(recvhoplimit); \
- GLOBAL_ATOM_DECL(recvif); \
- GLOBAL_ATOM_DECL(recvmsg); \
- GLOBAL_ATOM_DECL(recvopts); \
- GLOBAL_ATOM_DECL(recvorigdstaddr); \
- GLOBAL_ATOM_DECL(recvpktinfo); \
- GLOBAL_ATOM_DECL(recvtclass); \
- GLOBAL_ATOM_DECL(recvtos); \
- GLOBAL_ATOM_DECL(recvttl); \
- GLOBAL_ATOM_DECL(reliability); \
- GLOBAL_ATOM_DECL(renaming); \
- GLOBAL_ATOM_DECL(reset_streams); \
- GLOBAL_ATOM_DECL(retopts); \
- GLOBAL_ATOM_DECL(reuseaddr); \
- GLOBAL_ATOM_DECL(reuseport); \
- GLOBAL_ATOM_DECL(rights); \
- GLOBAL_ATOM_DECL(router_alert); \
- GLOBAL_ATOM_DECL(rthdr); \
- GLOBAL_ATOM_DECL(rtoinfo); \
- GLOBAL_ATOM_DECL(running); \
- GLOBAL_ATOM_DECL(rxq_ovfl); \
- GLOBAL_ATOM_DECL(scope_id); \
- GLOBAL_ATOM_DECL(sctp); \
- GLOBAL_ATOM_DECL(sec); \
- GLOBAL_ATOM_DECL(select); \
- GLOBAL_ATOM_DECL(select_failed); \
- GLOBAL_ATOM_DECL(select_sent); \
- GLOBAL_ATOM_DECL(send); \
- GLOBAL_ATOM_DECL(sendfile); \
- GLOBAL_ATOM_DECL(sendfile_byte); \
- GLOBAL_ATOM_DECL(sendfile_deferred_close); \
- GLOBAL_ATOM_DECL(sendfile_fails); \
- GLOBAL_ATOM_DECL(sendfile_max); \
- GLOBAL_ATOM_DECL(sendfile_pkg); \
- GLOBAL_ATOM_DECL(sendfile_pkg_max); \
- GLOBAL_ATOM_DECL(sendfile_tries); \
- GLOBAL_ATOM_DECL(sendfile_waits); \
- GLOBAL_ATOM_DECL(sendmsg); \
- GLOBAL_ATOM_DECL(sendsrcaddr); \
- GLOBAL_ATOM_DECL(sendto); \
- GLOBAL_ATOM_DECL(seqpacket); \
- GLOBAL_ATOM_DECL(setfib); \
- GLOBAL_ATOM_DECL(set_peer_primary_addr); \
- GLOBAL_ATOM_DECL(simplex); \
- GLOBAL_ATOM_DECL(slave); \
- GLOBAL_ATOM_DECL(slen); \
- GLOBAL_ATOM_DECL(sndbuf); \
- GLOBAL_ATOM_DECL(sndbufforce); \
- GLOBAL_ATOM_DECL(sndlowat); \
- GLOBAL_ATOM_DECL(sndtimeo); \
- GLOBAL_ATOM_DECL(socket); \
- GLOBAL_ATOM_DECL(spec_dst); \
- GLOBAL_ATOM_DECL(staticarp); \
- GLOBAL_ATOM_DECL(state); \
- GLOBAL_ATOM_DECL(status); \
- GLOBAL_ATOM_DECL(stream); \
- GLOBAL_ATOM_DECL(syncnt); \
- GLOBAL_ATOM_DECL(tclass); \
- GLOBAL_ATOM_DECL(tcp); \
- GLOBAL_ATOM_DECL(throughput); \
- GLOBAL_ATOM_DECL(timestamp); \
- GLOBAL_ATOM_DECL(tos); \
- GLOBAL_ATOM_DECL(transparent); \
- GLOBAL_ATOM_DECL(timeout); \
- GLOBAL_ATOM_DECL(true); \
- GLOBAL_ATOM_DECL(trunc); \
- GLOBAL_ATOM_DECL(ttl); \
- GLOBAL_ATOM_DECL(tunnel); \
- GLOBAL_ATOM_DECL(tunnel6); \
- GLOBAL_ATOM_DECL(type); \
- GLOBAL_ATOM_DECL(udp); \
- GLOBAL_ATOM_DECL(unblock_source); \
- GLOBAL_ATOM_DECL(undefined); \
- GLOBAL_ATOM_DECL(unicast_hops); \
- GLOBAL_ATOM_DECL(unspec); \
- GLOBAL_ATOM_DECL(up); \
- GLOBAL_ATOM_DECL(usec); \
- GLOBAL_ATOM_DECL(user); \
- GLOBAL_ATOM_DECL(user_timeout); \
- GLOBAL_ATOM_DECL(use_ext_recvinfo); \
- GLOBAL_ATOM_DECL(use_min_mtu); \
- GLOBAL_ATOM_DECL(use_registry); \
- GLOBAL_ATOM_DECL(value); \
- GLOBAL_ATOM_DECL(void); \
- GLOBAL_ATOM_DECL(v6only); \
- GLOBAL_ATOM_DECL(write_byte); \
- GLOBAL_ATOM_DECL(write_fails); \
- GLOBAL_ATOM_DECL(write_pkg); \
- GLOBAL_ATOM_DECL(write_tries); \
- GLOBAL_ATOM_DECL(write_waits); \
- GLOBAL_ATOM_DECL(zero)
-
-
-
-/* *** Global error reason atoms *** */
-#define GLOBAL_ERROR_REASON_ATOMS \
- GLOBAL_ATOM_DECL(eagain); \
- GLOBAL_ATOM_DECL(einval); \
- GLOBAL_ATOM_DECL(select_read); \
- GLOBAL_ATOM_DECL(select_write)
-
-
-#define GLOBAL_ATOM_DECL(A) ERL_NIF_TERM esock_atom_##A
-GLOBAL_ATOMS;
-GLOBAL_ERROR_REASON_ATOMS;
-#undef GLOBAL_ATOM_DECL
-ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
-
-/* *** Local atoms *** */
-#define LOCAL_ATOMS \
- LOCAL_ATOM_DECL(accepting); \
- LOCAL_ATOM_DECL(adaptation_layer); \
- LOCAL_ATOM_DECL(add); \
- LOCAL_ATOM_DECL(addr_unreach); \
- LOCAL_ATOM_DECL(address); \
- LOCAL_ATOM_DECL(adm_prohibited); \
- LOCAL_ATOM_DECL(association); \
- LOCAL_ATOM_DECL(assoc_id); \
- LOCAL_ATOM_DECL(authentication); \
- LOCAL_ATOM_DECL(base_addr); \
- LOCAL_ATOM_DECL(boolean); \
- LOCAL_ATOM_DECL(bound); \
- LOCAL_ATOM_DECL(bufsz); \
- LOCAL_ATOM_DECL(close); \
- LOCAL_ATOM_DECL(closing); \
- LOCAL_ATOM_DECL(code); \
- LOCAL_ATOM_DECL(cookie_life); \
- LOCAL_ATOM_DECL(counter_wrap); \
- LOCAL_ATOM_DECL(counters); \
- LOCAL_ATOM_DECL(ctype); \
- LOCAL_ATOM_DECL(data_io); \
- LOCAL_ATOM_DECL(debug_filename); \
- LOCAL_ATOM_DECL(del); \
- LOCAL_ATOM_DECL(dest_unreach); \
- LOCAL_ATOM_DECL(dma); \
- LOCAL_ATOM_DECL(do); \
- LOCAL_ATOM_DECL(dont); \
- LOCAL_ATOM_DECL(dtor); \
- LOCAL_ATOM_DECL(exclude); \
- LOCAL_ATOM_DECL(false); \
- LOCAL_ATOM_DECL(frag_needed); \
- LOCAL_ATOM_DECL(gifaddr); \
- LOCAL_ATOM_DECL(gifbrdaddr); \
- LOCAL_ATOM_DECL(gifconf); \
- LOCAL_ATOM_DECL(gifdstaddr); \
- LOCAL_ATOM_DECL(gifflags); \
- LOCAL_ATOM_DECL(gifhwaddr); \
- LOCAL_ATOM_DECL(gifindex); \
- LOCAL_ATOM_DECL(gifmap); \
- LOCAL_ATOM_DECL(gifmtu); \
- LOCAL_ATOM_DECL(gifname); \
- LOCAL_ATOM_DECL(gifnetmask); \
- LOCAL_ATOM_DECL(giftxqlen); \
- LOCAL_ATOM_DECL(host_unknown); \
- LOCAL_ATOM_DECL(host_unreach); \
- LOCAL_ATOM_DECL(how); \
- LOCAL_ATOM_DECL(in4_sockaddr); \
- LOCAL_ATOM_DECL(in6_sockaddr); \
- LOCAL_ATOM_DECL(include); \
- LOCAL_ATOM_DECL(initial); \
- LOCAL_ATOM_DECL(interface); \
- LOCAL_ATOM_DECL(integer); \
- LOCAL_ATOM_DECL(ioctl_flags); \
- LOCAL_ATOM_DECL(ioctl_requests); \
- LOCAL_ATOM_DECL(iov_max); \
- LOCAL_ATOM_DECL(iow); \
- LOCAL_ATOM_DECL(io_num_threads); \
- LOCAL_ATOM_DECL(irq); \
- LOCAL_ATOM_DECL(listening); \
- LOCAL_ATOM_DECL(local_rwnd); \
- LOCAL_ATOM_DECL(map); \
- LOCAL_ATOM_DECL(max); \
- LOCAL_ATOM_DECL(max_attempts); \
- LOCAL_ATOM_DECL(max_init_timeo); \
- LOCAL_ATOM_DECL(max_instreams); \
- LOCAL_ATOM_DECL(asocmaxrxt); \
- LOCAL_ATOM_DECL(mem_end); \
- LOCAL_ATOM_DECL(mem_start); \
- LOCAL_ATOM_DECL(min); \
- LOCAL_ATOM_DECL(missing); \
- LOCAL_ATOM_DECL(mode); \
- LOCAL_ATOM_DECL(msg); \
- LOCAL_ATOM_DECL(msg_flags); \
- LOCAL_ATOM_DECL(mtu); \
- LOCAL_ATOM_DECL(multiaddr); \
- LOCAL_ATOM_DECL(net_unknown); \
- LOCAL_ATOM_DECL(net_unreach); \
- LOCAL_ATOM_DECL(nogroup); \
- LOCAL_ATOM_DECL(none); \
- LOCAL_ATOM_DECL(noroute); \
- LOCAL_ATOM_DECL(not_neighbour); \
- LOCAL_ATOM_DECL(null); \
- LOCAL_ATOM_DECL(num_acceptors); \
- LOCAL_ATOM_DECL(num_cnt_bits); \
- LOCAL_ATOM_DECL(num_dinet); \
- LOCAL_ATOM_DECL(num_dinet6); \
- LOCAL_ATOM_DECL(num_dlocal); \
- LOCAL_ATOM_DECL(num_outstreams); \
- LOCAL_ATOM_DECL(number_peer_destinations); \
- LOCAL_ATOM_DECL(num_pip); \
- LOCAL_ATOM_DECL(num_psctp); \
- LOCAL_ATOM_DECL(num_ptcp); \
- LOCAL_ATOM_DECL(num_pudp); \
- LOCAL_ATOM_DECL(num_readers); \
- LOCAL_ATOM_DECL(num_sockets); \
- LOCAL_ATOM_DECL(num_tdgrams); \
- LOCAL_ATOM_DECL(num_tseqpkgs); \
- LOCAL_ATOM_DECL(num_tstreams); \
- LOCAL_ATOM_DECL(num_writers); \
- LOCAL_ATOM_DECL(offender); \
- LOCAL_ATOM_DECL(onoff); \
- LOCAL_ATOM_DECL(options); \
- LOCAL_ATOM_DECL(origin); \
- LOCAL_ATOM_DECL(otp); \
- LOCAL_ATOM_DECL(otp_socket_option);\
- LOCAL_ATOM_DECL(owner); \
- LOCAL_ATOM_DECL(partial_delivery); \
- LOCAL_ATOM_DECL(peer_error); \
- LOCAL_ATOM_DECL(peer_rwnd); \
- LOCAL_ATOM_DECL(pkt_toobig); \
- LOCAL_ATOM_DECL(policy_fail); \
- LOCAL_ATOM_DECL(port); \
- LOCAL_ATOM_DECL(port_unreach); \
- LOCAL_ATOM_DECL(probe); \
- LOCAL_ATOM_DECL(protocols); \
- LOCAL_ATOM_DECL(rcvctrlbuf); \
- LOCAL_ATOM_DECL(read); \
- LOCAL_ATOM_DECL(read_pkg_max); \
- LOCAL_ATOM_DECL(read_waits); \
- LOCAL_ATOM_DECL(read_write); \
- LOCAL_ATOM_DECL(registry); \
- LOCAL_ATOM_DECL(reject_route); \
- LOCAL_ATOM_DECL(remote); \
- LOCAL_ATOM_DECL(rstates); \
- LOCAL_ATOM_DECL(selected); \
- LOCAL_ATOM_DECL(sender_dry); \
- LOCAL_ATOM_DECL(send_failure); \
- LOCAL_ATOM_DECL(shutdown); \
- LOCAL_ATOM_DECL(sifaddr); \
- LOCAL_ATOM_DECL(sifbrdaddr); \
- LOCAL_ATOM_DECL(sifdstaddr); \
- LOCAL_ATOM_DECL(sifflags); \
- LOCAL_ATOM_DECL(sifmtu); \
- LOCAL_ATOM_DECL(sifnetmask); \
- LOCAL_ATOM_DECL(siftxqlen); \
- LOCAL_ATOM_DECL(slist); \
- LOCAL_ATOM_DECL(sndctrlbuf); \
- LOCAL_ATOM_DECL(sockaddr); \
- LOCAL_ATOM_DECL(socket_debug); \
- LOCAL_ATOM_DECL(socket_level); \
- LOCAL_ATOM_DECL(socket_option); \
- LOCAL_ATOM_DECL(sourceaddr); \
- LOCAL_ATOM_DECL(time_exceeded); \
- LOCAL_ATOM_DECL(true); \
- LOCAL_ATOM_DECL(txqlen); \
- LOCAL_ATOM_DECL(txstatus); \
- LOCAL_ATOM_DECL(txtime); \
- LOCAL_ATOM_DECL(want); \
- LOCAL_ATOM_DECL(write); \
- LOCAL_ATOM_DECL(write_pkg_max); \
- LOCAL_ATOM_DECL(wstates); \
- LOCAL_ATOM_DECL(zerocopy)
-
-/* Local error reason atoms
- * Keep this (commented) for future use...
+/* These three (inline) functions are primarily intended for debugging,
+ * that is, to make it easy to add debug printouts.
*/
-/*
-#define LOCAL_ERROR_REASON_ATOMS \
- LOCAL_ATOM_DECL(select_read); \
- LOCAL_ATOM_DECL(select_write)
-*/
-#define LOCAL_ATOM_DECL(LA) static ERL_NIF_TERM atom_##LA
-LOCAL_ATOMS;
-// LOCAL_ERROR_REASON_ATOMS;
-#undef LOCAL_ATOM_DECL
-
-/* *** Sockets *** */
-static ErlNifResourceType* esocks;
-static ErlNifResourceTypeInit esockInit = {
- esock_dtor,
- esock_stop,
- (ErlNifResourceDown*) esock_down
-};
-
-// Initiated when the nif is loaded
-static ESockData data;
-
-/* Jump table for the I/O backend (async or sync) */
-static ESockIoBackend io_backend = {0};
+// static ESOCK_INLINE void esock_clear_env(const char* slogan, ErlNifEnv* env)
+extern void esock_clear_env(const char* slogan, ErlNifEnv* env)
+{
+ // ESOCK_DBG_PRINTF( TRUE, ("SOCKET", "env clear - %s: 0x%lX\r\n", slogan, env) );
-/* This, the test for NULL), is temporary until we have a win stub */
-#define ESOCK_IO_INIT(NUMT) \
- ((io_backend.init != NULL) ? \
- io_backend.init((NUMT)) : ESOCK_IO_ERR_UNSUPPORTED)
-#define ESOCK_IO_FIN() \
- ((io_backend.finish != NULL) ? \
- io_backend.finish() : ESOCK_IO_ERR_UNSUPPORTED)
-
-#define ESOCK_IO_OPEN_WITH_FD(ENV, FD, EOPTS) \
- ((io_backend.open_with_fd != NULL) ? \
- io_backend.open_with_fd((ENV), (FD), (EOPTS), &data) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_OPEN_PLAIN(ENV, D, T, P, EOPTS) \
- ((io_backend.open_plain != NULL) ? \
- io_backend.open_plain((ENV), (D), \
- (T), (P), (EOPTS), &data) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_BIND(ENV, D, SAP, AL) \
- ((io_backend.bind != NULL) ? \
- io_backend.bind((ENV), (D), (SAP), (AL)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_CONNECT(ENV, D, SR, CR, AP, AL) \
- ((io_backend.connect != NULL) ? \
- io_backend.connect((ENV), (D), \
- (SR), (CR), (AP), (AL)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_LISTEN(ENV, D, BL) \
- ((io_backend.listen != NULL) ? \
- io_backend.listen((ENV), (D), (BL)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_ACCEPT(ENV, D, SR, AR) \
- ((io_backend.accept != NULL) ? \
- io_backend.accept((ENV), (D), (SR), (AR)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_SEND(ENV, D, SR, RF, L, F) \
- ((io_backend.send != NULL) ? \
- io_backend.send((ENV), (D), \
- (SR), (RF), (L), (F)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_SENDTO(ENV, D, \
- SOCKR, SENDR, \
- DP, F, TAP, TAL) \
- ((io_backend.sendto != NULL) ? \
- io_backend.sendto((ENV), (D), \
- (SOCKR), (SENDR), \
- (DP), (F), (TAP), (TAL)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_SENDMSG(ENV, D, \
- SOCKR, SENDR, EM, F, EIOV) \
- ((io_backend.sendmsg != NULL) ? \
- io_backend.sendmsg((ENV), (D), \
- (SOCKR), (SENDR), \
- (EM), (F), (EIOV), &data) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_SENDFILE_START(ENV, D, \
- SOR, SNR, \
- O, CN, FR) \
- ((io_backend.sendfile_start != NULL) ? \
- io_backend.sendfile_start((ENV), (D), \
- (SOR), (SNR), \
- (O), (CN), (FR)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_SENDFILE_CONT(ENV, D, \
- SOR, SNR, \
- O, CP) \
- ((io_backend.sendfile_cont != NULL) ? \
- io_backend.sendfile_cont((ENV), (D), \
- (SOR), (SNR), \
- (O), (CP)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_SENDFILE_DC(ENV, D) \
- ((io_backend.sendfile_dc != NULL) ? \
- io_backend.sendfile_dc((ENV), (D)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_RECV(ENV, D, \
- SR, RR, L, F) \
- ((io_backend.recv != NULL) ? \
- io_backend.recv((ENV), (D), \
- (SR), (RR), (L), (F)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_RECVFROM(ENV, D, \
- SR, RR, L, F) \
- ((io_backend.recvfrom != NULL) ? \
- io_backend.recvfrom((ENV), (D), \
- (SR), (RR), (L), (F)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_RECVMSG(ENV, D, \
- SR, RR, BL, CL, F) \
- ((io_backend.recvmsg != NULL) ? \
- io_backend.recvmsg((ENV), (D), \
- (SR), (RR), \
- (BL), (CL), (F)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_CLOSE(ENV, D) \
- ((io_backend.close != NULL) ? \
- io_backend.close((ENV), (D)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_FIN_CLOSE(ENV, D) \
- ((io_backend.fin_close != NULL) ? \
- io_backend.fin_close((ENV), (D)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_SHUTDOWN(ENV, D, H) \
- ((io_backend.shutdown != NULL) ? \
- io_backend.shutdown((ENV), (D), (H)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_SOCKNAME(ENV, D) \
- ((io_backend.sockname != NULL) ? \
- io_backend.sockname((ENV), (D)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
-#define ESOCK_IO_PEERNAME(ENV, D) \
- ((io_backend.peername != NULL) ? \
- io_backend.peername((ENV), (D)) : \
- enif_raise_exception(env, MKA(env, "notsup")))
+ SGDBG( ("SOCKET", "env clear - %s: 0x%lX\r\n", slogan, env) );
+ if (env != NULL) enif_clear_env(env);
+}
-/* These two (inline) functions are primarily intended for debugging,
- * that is, to make it easy to add debug printouts.
- */
// static ESOCK_INLINE void esock_free_env(const char* slogan, ErlNifEnv* env)
extern void esock_free_env(const char* slogan, ErlNifEnv* env)
{
+ // ESOCK_DBG_PRINTF( TRUE, ("SOCKET", "env free - %s: 0x%lX\r\n", slogan, env) );
+
SGDBG( ("SOCKET", "env free - %s: 0x%lX\r\n", slogan, env) );
- // esock_dbg_printf("SOCK ENV", "free - %s: 0x%lX\r\n", slogan, env);
if (env != NULL) enif_free_env(env);
}
@@ -3869,12 +3726,13 @@ extern void esock_free_env(const char* slogan, ErlNifEnv* env)
// static ESOCK_INLINE ErlNifEnv* esock_alloc_env(const char* slogan)
extern ErlNifEnv* esock_alloc_env(const char* slogan)
{
- ErlNifEnv* env;
+ ErlNifEnv* env = enif_alloc_env();
- ESOCK_ASSERT( (env = enif_alloc_env()) != NULL);
+ // ESOCK_DBG_PRINTF( TRUE, ("SOCKET", "env alloc - %s: 0x%lX\r\n", slogan, env) );
SGDBG( ("SOCKET", "env alloc - %s: 0x%lX\r\n", slogan, env) );
- // esock_dbg_printf("SOCK ENV", "alloc - %s: 0x%lX\r\n", slogan, env);
+
+ ESOCK_ASSERT( env != NULL );
return env;
}
@@ -3938,9 +3796,6 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ERL_NIF_TERM info;
ESockDescriptor* descP;
@@ -3972,7 +3827,6 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
return info;
-#endif
}
@@ -3983,7 +3837,6 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env,
* actually a counter, the num_cnt_bits. This is the "size" of each counter,
* in number of bits: 16 | 24 | 32 | 48 | 64.
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_global_info(ErlNifEnv* env)
{
@@ -4040,14 +3893,29 @@ ERL_NIF_TERM esock_global_info(ErlNifEnv* env)
atom_socket_debug,
esock_atom_use_registry,
atom_iow,
- atom_counters,
- atom_iov_max},
+ esock_atom_counters,
+ atom_iov_max,
+ atom_io_backend},
vals[] = {dbg,
sockDbg,
useReg,
iow,
gcnt,
- iovMax},
+ iovMax,
+ ESOCK_IO_INFO(env)
+ /* This mess is just a temporary hack
+ * and shall be replaced by a callback
+ * function (eventually).
+ * That function should return a 'term' (a map).
+ */
+ /*
+#ifdef __WIN32__
+ MKA(env, "win_esaio")
+#else
+ MKA(env, "unix_essio")
+#endif
+ */
+ },
info;
unsigned int
numKeys = NUM(keys),
@@ -4060,7 +3928,6 @@ ERL_NIF_TERM esock_global_info(ErlNifEnv* env)
}
}
}
-#endif // #ifndef __WIN32__
/*
@@ -4076,7 +3943,6 @@ ERL_NIF_TERM esock_global_info(ErlNifEnv* env)
* writers: The number of current and waiting writers
* acceptors: The number of current and waiting acceptors
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -4102,7 +3968,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
atom_rstates,
atom_wstates,
atom_ctype,
- atom_counters,
+ esock_atom_counters,
atom_num_readers,
atom_num_writers,
atom_num_acceptors};
@@ -4133,13 +3999,11 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
return info;
}
}
-#endif // #ifndef __WIN32__
/*
* Encode the socket domain
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_socket_info_domain(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -4150,13 +4014,11 @@ ERL_NIF_TERM esock_socket_info_domain(ErlNifEnv* env,
esock_encode_domain(env, domain, &edomain);
return edomain;
}
-#endif // #ifndef __WIN32__
/*
* Encode the socket type
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_socket_info_type(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -4168,16 +4030,14 @@ ERL_NIF_TERM esock_socket_info_type(ErlNifEnv* env,
return etype;
}
-#endif // #ifndef __WIN32__
/*
* Encode the socket "create type"
- * That is "show" how this socket was created:
+ * That is; "show" how this socket was created:
*
* normal | fromfd | {fromfd, integer()}
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -4190,7 +4050,7 @@ ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env,
"\r\n", descP->sock,
descP->origFD, B2S(descP->closeOnClose)) );
- if (descP->origFD > 0) {
+ if (descP->origFD != INVALID_SOCKET) {
/* Created from other FD */
if (descP->closeOnClose) {
/* We *have* dup'ed: {fromfd, integer()} */
@@ -4210,16 +4070,12 @@ ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env,
return ctype;
}
-#endif // #ifndef __WIN32__
/*
* Encode the socket "state"
- * That is "show" how this socket was created:
- *
- * normal | fromfd | {fromfd, integer()}
+ * This is a list of atoms, one for each valid 'state' value.
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_socket_info_state(ErlNifEnv* env,
unsigned int state)
@@ -4294,7 +4150,7 @@ ERL_NIF_TERM esock_socket_info_state(ErlNifEnv* env,
if ((state & ESOCK_STATE_DTOR) != 0) {
/*
- SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> dror"
+ SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> dtor"
"\r\n", descP->sock) );
*/
TARRAY_ADD(estate, atom_dtor);
@@ -4304,13 +4160,11 @@ ERL_NIF_TERM esock_socket_info_state(ErlNifEnv* env,
return estateList;
}
-#endif // #ifndef __WIN32__
/*
* Collect all counters for a socket.
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -4399,7 +4253,6 @@ ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env,
return cnts;
}
-#endif // #ifndef __WIN32__
@@ -4427,9 +4280,6 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ERL_NIF_TERM command, cdata, result;
ESOCK_ASSERT( argc == 1 );
@@ -4449,10 +4299,10 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env,
SGDBG( ("SOCKET", "nif_command -> "
"\r\n command: %T"
- "\r\n cdata: %T"
+ "\r\n cdata: %T"
"\r\n", command, cdata) );
- result = esock_command(env, command, cdata);
+ result = ESOCK_IO_CMD(env, command, cdata);
SGDBG( ("SOCKET", "nif_command -> done with result: "
"\r\n %T"
@@ -4460,14 +4310,11 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env,
return result;
-#endif // #ifdef __WIN32__ #else
}
-#ifndef __WIN32__
static
-ERL_NIF_TERM
-esock_command(ErlNifEnv* env, ERL_NIF_TERM command, ERL_NIF_TERM cdata)
+ERL_NIF_TERM esock_command(ErlNifEnv* env, ERL_NIF_TERM command, ERL_NIF_TERM cdata)
{
int cmp;
@@ -4489,10 +4336,8 @@ esock_command(ErlNifEnv* env, ERL_NIF_TERM command, ERL_NIF_TERM cdata)
return esock_raise_invalid(env, MKT2(env, esock_atom_command, command));
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_command_debug(ErlNifEnv* env, ERL_NIF_TERM cdata)
{
@@ -4502,15 +4347,12 @@ ERL_NIF_TERM esock_command_debug(ErlNifEnv* env, ERL_NIF_TERM cdata)
if (esock_decode_bool(cdata, &data.dbg))
result = esock_atom_ok;
else
- result =
- esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata));
+ result = esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata));
return result;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env, ERL_NIF_TERM cdata)
{
@@ -4518,8 +4360,7 @@ ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env, ERL_NIF_TERM cdata)
/* The data *should* be a boolean() */
if (! esock_decode_bool(cdata, &dbg))
- return
- esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata));
+ return esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata));
MLOCK(data.cntMtx);
data.sockDbg = dbg;
@@ -4527,10 +4368,8 @@ ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env, ERL_NIF_TERM cdata)
return esock_atom_ok;;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env,
ERL_NIF_TERM cdata)
@@ -4539,17 +4378,14 @@ ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env,
/* The data *should* be a boolean() */
if (! esock_decode_bool(cdata, &useReg))
- return
- esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata));
+ return esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata));
MLOCK(data.cntMtx);
data.useReg = useReg;
MUNLOCK(data.cntMtx);
- return esock_atom_ok;;
+ return esock_atom_ok;
}
-#endif
-
@@ -4559,14 +4395,14 @@ ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env,
*
* Calculate how many readers | writers | acceptors we have for this socket.
* Current requestor + any waiting requestors (of the type).
- *
+ * Note that "Current requestor" is *not* used on Windows.
*/
#ifndef __WIN32__
#define ESOCK_INFO_REQ_FUNCS \
ESOCK_INFO_REQ_FUNC_DECL(readers, currentReaderP, readersQ) \
- ESOCK_INFO_REQ_FUNC_DECL(writers, currentWriterP, writersQ) \
+ ESOCK_INFO_REQ_FUNC_DECL(writers, currentWriterP, writersQ) \
ESOCK_INFO_REQ_FUNC_DECL(acceptors, currentAcceptorP, acceptorsQ)
#define ESOCK_INFO_REQ_FUNC_DECL(F, CRP, Q) \
@@ -4576,32 +4412,49 @@ ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env,
{ \
return socket_info_reqs(env, descP, descP->CRP, &descP->Q); \
}
+#else
+
+#define ESOCK_INFO_REQ_FUNCS \
+ ESOCK_INFO_REQ_FUNC_DECL(readers, readersQ) \
+ ESOCK_INFO_REQ_FUNC_DECL(writers, writersQ) \
+ ESOCK_INFO_REQ_FUNC_DECL(acceptors, acceptorsQ)
+
+#define ESOCK_INFO_REQ_FUNC_DECL(F, Q) \
+ static \
+ ERL_NIF_TERM esock_socket_info_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP) \
+ { \
+ return socket_info_reqs(env, descP, &descP->Q); \
+ }
+
+#endif
+
ESOCK_INFO_REQ_FUNCS
#undef ESOCK_INFO_REQ_FUNC_DECL
static
ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env,
ESockDescriptor* descP,
+#ifndef __WIN32__
ESockRequestor* currentRequestorP,
+#endif
ESockRequestQueue* q)
{
- ESockRequestQueueElement* tmp;
- ERL_NIF_TERM info;
- unsigned int cnt = 0;
+ ERL_NIF_TERM info;
+ unsigned int cnt;
+#ifdef __WIN32__
+ cnt = 0;
+#else
if (currentRequestorP != NULL) {
// We have an active requestor!
- cnt++;
-
- // And add all the waiting requestors
- tmp = q->first;
- while (tmp != NULL) {
- cnt++;
- tmp = tmp->nextP;
- }
+ cnt = 1;
+ } else {
+ cnt = 0;
}
+#endif
- info = MKUI(env, cnt);
+ info = MKUI(env, cnt + qlength(q));
SSDBG( descP, ("SOCKET", "socket_info_reqs -> done with"
"\r\n info: %T"
@@ -4610,8 +4463,6 @@ ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env,
return info;
}
-#endif // #ifndef __WIN32__
-
/* ----------------------------------------------------------------------
* nif_supports
@@ -4641,29 +4492,28 @@ ERL_NIF_TERM nif_supports(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
SGDBG( ("SOCKET", "nif_supports -> entry with %d args\r\n", argc) );
/* Extract arguments and perform preliminary validation */
if (argc == 0)
- return esock_supports_0(env);
+ return ESOCK_IO_SUPPORTS_0(env);
- ESOCK_ASSERT( argc == 1 );
+ if (argc == 1)
+ return ESOCK_IO_SUPPORTS_1(env, argv[0]);
+
+ return esock_make_error(env, esock_atom_einval);
- return esock_supports_1(env, argv[0]);
-#endif
}
+
/* esock_supports - what features do we support?
*
* This gives information about what features actually
* work on the current platform.
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_supports_0(ErlNifEnv* env)
{
@@ -4711,9 +4561,9 @@ ERL_NIF_TERM esock_supports_0(ErlNifEnv* env)
TARRAY_TOLIST(opts, env, &opts_list);
return opts_list;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
+
+
static
ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key)
{
@@ -4739,37 +4589,52 @@ ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key)
return result;
}
-#endif // #ifndef __WIN32__
-
-#ifndef __WIN32__
static ERL_NIF_TERM esock_supports_msg_flags(ErlNifEnv* env) {
size_t n;
ERL_NIF_TERM result;
result = MKEL(env);
- for (n = 0; n < NUM(msg_flags); n++) {
+ for (n = 0; n < esock_msg_flags_length; n++) {
result =
MKC(env,
MKT2(env,
- *(msg_flags[n].name),
- MKI(env, msg_flags[n].flag)),
+ *(esock_msg_flags[n].name),
+ MKI(env, esock_msg_flags[n].flag)),
result);
}
return result;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env)
{
ERL_NIF_TERM protocols;
+#ifndef __WIN32__
+#if defined(SOL_IP)
+ int protoIP = SOL_IP;
+#else
+ int protoIP = IPPROTO_IP;
+#endif
+#else
+ int protoIP = IPPROTO_IP;
+#endif
+#if defined(HAVE_IPV6)
+#ifndef __WIN32__
+#if defined(SOL_IPV6)
+ int protoIPV6 = SOL_IPV6;
+#else
+ int protoIPV6 = IPPROTO_IPV6;
+#endif
+#else
+ int protoIPV6 = IPPROTO_IPV6;
+#endif
+#endif
protocols = MKEL(env);
@@ -4809,29 +4674,13 @@ ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env)
protocols =
MKC(env,
- MKT2(env,
- MKL1(env, esock_atom_ip),
- MKI(env,
-#ifdef SOL_IP
- SOL_IP
-#else
- IPPROTO_IP
-#endif
- )),
+ MKT2(env, MKL1(env, esock_atom_ip), MKI(env, protoIP)),
protocols);
#ifdef HAVE_IPV6
protocols =
MKC(env,
- MKT2(env,
- MKL1(env, esock_atom_ipv6),
- MKI(env,
-#ifdef SOL_IPV6
- SOL_IPV6
-#else
- IPPROTO_IPV6
-#endif
- )),
+ MKT2(env, MKL1(env, esock_atom_ipv6), MKI(env, protoIPV6)),
protocols);
#endif
@@ -4854,11 +4703,9 @@ ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env)
return protocols;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_supports_ioctl_requests(ErlNifEnv* env)
{
@@ -4942,36 +4789,31 @@ ERL_NIF_TERM esock_supports_ioctl_requests(ErlNifEnv* env)
return requests;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
-static ERL_NIF_TERM esock_supports_ioctl_flags(ErlNifEnv* env)
+static
+ERL_NIF_TERM esock_supports_ioctl_flags(ErlNifEnv* env)
{
size_t n;
ERL_NIF_TERM result;
result = MKEL(env);
- for (n = 0; n < NUM(ioctl_flags); n++) {
+ for (n = 0; n < esock_ioctl_flags_length; n++) {
result =
MKC(env,
MKT2(env,
- *(ioctl_flags[n].name),
- MKI(env, ioctl_flags[n].flag)),
+ *(esock_ioctl_flags[n].name),
+ MKI(env, esock_ioctl_flags[n].flag)),
result);
}
return result;
}
-#endif // #ifndef __WIN32__
-
-#ifndef __WIN32__
-
static
ERL_NIF_TERM esock_supports_options(ErlNifEnv* env)
{
@@ -5010,7 +4852,6 @@ ERL_NIF_TERM esock_supports_options(ErlNifEnv* env)
return levels;
}
-#endif // #ifndef __WIN32__
@@ -5059,9 +4900,6 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ERL_NIF_TERM result;
SGDBG( ("SOCKET", "nif_open -> "
@@ -5145,10 +4983,38 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
return result;
-#endif // #ifdef __WIN32__ #else
}
+extern
+BOOLEAN_T esock_open_is_debug(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ BOOLEAN_T def)
+{
+ return esock_get_bool_from_map(env, eopts, esock_atom_debug, def);
+}
+
+extern
+BOOLEAN_T esock_open_use_registry(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ BOOLEAN_T def)
+{
+ return esock_get_bool_from_map(env, eopts, esock_atom_use_registry, def);
+}
+
+extern
+BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto)
+{
+#if defined(SO_PROTOCOL)
+ if (esock_getopt_int(sock, SOL_SOCKET, SO_PROTOCOL, proto))
+ return TRUE;
+#endif
+ return FALSE;
+}
+
+
+
+
/* ----------------------------------------------------------------------
* nif_bind
*
@@ -5164,9 +5030,6 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM eSockAddr, ret;
ESockAddress sockAddr;
@@ -5184,7 +5047,7 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env,
eSockAddr = argv[1];
if (! esock_decode_sockaddr(env, eSockAddr, &sockAddr, &addrLen))
- return esock_make_invalid(env, atom_sockaddr);
+ return esock_make_invalid(env, esock_atom_sockaddr);
MLOCK(descP->readMtx);
@@ -5204,7 +5067,6 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
return ret;
-#endif // #ifdef __WIN32__ #else
}
@@ -5227,9 +5089,6 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM res, sockRef, connRef;
ESockAddress addr, *addrP;
@@ -5253,14 +5112,14 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env,
return enif_make_badarg(env);
if (! esock_decode_sockaddr(env, eSockAddr, &addr, &addrLen))
- return esock_make_invalid(env, atom_sockaddr);
+ return esock_make_invalid(env, esock_atom_sockaddr);
addrP = &addr;
MLOCK(descP->writeMtx);
SSDBG( descP,
("SOCKET", "nif_connect(%T), {%d0x%X} ->"
- "\r\n ConnRef: %T"
+ "\r\n ConnRef: %T"
"\r\n SockAddr: %T"
"\r\n",
sockRef, descP->sock, descP->writeState,
@@ -5270,7 +5129,7 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env,
ESOCK_ASSERT( argc == 1 );
connRef = esock_atom_undefined;
- addrP = NULL;
+ addrP = NULL;
addrLen = 0;
MLOCK(descP->writeMtx);
@@ -5292,7 +5151,6 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env,
return res;
-#endif // #ifdef __WIN32__ #else
}
@@ -5313,9 +5171,6 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
int backlog;
ERL_NIF_TERM ret;
@@ -5354,11 +5209,52 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
return ret;
-#endif // #ifdef __WIN32__ #else
}
+/* ========================================================================
+ */
+static
+ERL_NIF_TERM esock_listen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int backlog)
+{
+
+ /*
+ * Verify that we are in the proper state
+ */
+
+ SSDBG( descP,
+ ("SOCKET", "esock_listen(%d) -> verify open\r\n", descP->sock) );
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+#if defined(__WIN32__)
+ SSDBG( descP,
+ ("SOCKET", "esock_listen(%d) -> verify bound\r\n", descP->sock) );
+ if (! IS_BOUND(descP->writeState))
+ return esock_make_error(env, esock_atom_not_bound);
+#endif
+
+ /*
+ * And attempt to make socket listening
+ */
+
+ SSDBG( descP, ("SOCKET", "esock_listen(%d) -> try listen with"
+ "\r\n backlog: %d"
+ "\r\n", descP->sock, backlog) );
+
+ if ((sock_listen(descP->sock, backlog)) < 0)
+ return esock_make_error_errno(env, sock_errno());
+
+ descP->readState |= ESOCK_STATE_LISTENING;
+
+ return esock_atom_ok;
+
+}
+
+
/* ----------------------------------------------------------------------
* nif_accept
@@ -5376,10 +5272,6 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
-
ESockDescriptor* descP;
ERL_NIF_TERM sockRef, ref, res;
@@ -5397,6 +5289,7 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env,
MLOCK(descP->readMtx);
+#ifndef __WIN32__
SSDBG( descP,
("SOCKET", "nif_accept%T), {%d,0x%X} ->"
"\r\n ReqRef: %T"
@@ -5413,6 +5306,16 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env,
ESOCK_MON2TERM(env, &descP->currentAcceptor.mon),
descP->currentAcceptor.env,
descP->currentAcceptor.ref) );
+#else
+ SSDBG( descP,
+ ("SOCKET", "nif_accept%T), {%d,0x%X} ->"
+ "\r\n ReqRef: %T"
+ "\r\n First Acceptor addr: %p"
+ "\r\n",
+ sockRef, descP->sock, descP->readState,
+ ref,
+ descP->acceptorsQ.first) );
+#endif
res = ESOCK_IO_ACCEPT(env, descP, sockRef, ref);
@@ -5424,10 +5327,10 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env,
return res;
-#endif // #ifdef __WIN32__ #else
}
+
/* ----------------------------------------------------------------------
* nif_send
*
@@ -5446,9 +5349,6 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM sockRef, sendRef;
ErlNifBinary sndData;
@@ -5511,9 +5411,9 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env,
SGDBG( ("SOCKET", "nif_send -> done with result: "
"\r\n %T"
"\r\n", res) );
+
return res;
-#endif // #ifdef __WIN32__ #else
}
@@ -5536,9 +5436,6 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM sockRef, sendRef;
ErlNifBinary sndData;
@@ -5583,7 +5480,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
"nif_sendto(%T), {%d} -> sockaddr decode failed \r\n",
sockRef, descP->sock) );
- return esock_make_invalid(env, atom_sockaddr);
+ return esock_make_invalid(env, esock_atom_sockaddr);
}
MLOCK(descP->writeMtx);
@@ -5609,7 +5506,6 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
return res;
-#endif // if defined(__WIN32__)
}
@@ -5624,6 +5520,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
* Msg - Message - map() with data and (maybe) control and dest
* Flags - Send flags as an integer().
* SendRef - A unique id reference() for this (send) request.
+ * IOV - List of binaries
*/
static
@@ -5631,9 +5528,6 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ERL_NIF_TERM res, sockRef, sendRef, eMsg, eIOV;
ESockDescriptor* descP;
int flags;
@@ -5686,7 +5580,6 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env,
return res;
-#endif // #ifdef __WIN32__ #else
}
@@ -5783,7 +5676,7 @@ nif_sendfile(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#if defined(__WIN32__) || !defined(HAVE_SENDFILE)
+#if !defined(HAVE_SENDFILE)
return enif_raise_exception(env, MKA(env, "notsup"));
#else
ESockDescriptor *descP;
@@ -5898,7 +5791,7 @@ nif_sendfile(ErlNifEnv* env,
return res;
-#endif // #if defined(__WIN32__) || !defined(HAVE_SENDFILE)
+#endif // !defined(HAVE_SENDFILE)
}
@@ -5925,9 +5818,6 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM sockRef, recvRef;
ErlNifUInt64 elen;
@@ -5992,7 +5882,6 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env,
return res;
-#endif // #ifdef __WIN32__ #else
}
@@ -6026,9 +5915,6 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM sockRef, recvRef;
ErlNifUInt64 elen;
@@ -6063,8 +5949,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env,
if (! a1ok)
return esock_make_error_integer_range(env, argv[1]);
- return
- esock_make_error_integer_range(env, argv[2]);
+ return esock_make_error_integer_range(env, argv[2]);
}
len = (ssize_t) elen;
if (elen != (ErlNifUInt64) len)
@@ -6104,7 +5989,6 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
return res;
-#endif // #ifdef __WIN32__ #else
}
@@ -6143,9 +6027,6 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM sockRef, recvRef;
ErlNifUInt64 eBufSz, eCtrlSz;
@@ -6233,7 +6114,6 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
return res;
-#endif // #ifdef __WIN32__ #else
}
@@ -6252,9 +6132,6 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM res;
@@ -6291,178 +6168,9 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env,
"\r\n", argv[0], res) );
return res;
-#endif // #ifdef __WIN32__ #else
}
-/* Prepare for close - return whether stop is scheduled
- */
-#ifndef __WIN32__ // TEMPORARY - check the prim_socket_int.h file!
-extern
-BOOLEAN_T esock_do_stop(ErlNifEnv* env,
- ESockDescriptor* descP)
-{
- BOOLEAN_T ret;
- int sres;
- ERL_NIF_TERM sockRef;
-
- sockRef = enif_make_resource(env, descP);
-
- if (IS_SELECTED(descP)) {
- ESOCK_ASSERT( (sres = esock_select_stop(env,
- (ErlNifEvent) descP->sock,
- descP))
- >= 0 );
- if ((sres & ERL_NIF_SELECT_STOP_CALLED) != 0) {
- /* The socket is no longer known by the select machinery
- * - it may be closed
- */
- ret = FALSE;
- } else {
- ESOCK_ASSERT( (sres & ERL_NIF_SELECT_STOP_SCHEDULED) != 0 );
- /* esock_stop() is scheduled
- * - socket may be removed by esock_stop() or later
- */
- ret = TRUE;
- }
- } else {
- sres = 0;
- /* The socket has never been used in the select machinery
- * - it may be closed
- */
- ret = FALSE;
- }
-
- /* +++++++ Current and waiting Writers +++++++ */
-
- if (descP->currentWriterP != NULL) {
-
- /* We have a current Writer; was it deselected?
- */
-
- if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) {
-
- /* The current Writer will not get a select message
- * - send it an abort message
- */
-
- esock_stop_handle_current(env,
- "writer",
- descP, sockRef, &descP->currentWriter);
- }
-
- /* Inform the waiting Writers (in the same way) */
-
- SSDBG( descP,
- ("SOCKET",
- "esock_do_stop {%d} -> handle waiting writer(s)\r\n",
- descP->sock) );
-
- inform_waiting_procs(env, "writer",
- descP, sockRef, &descP->writersQ, esock_atom_closed);
-
- descP->currentWriterP = NULL;
- }
-
- /* +++++++ Connector +++++++
- * Note that there should not be Writers and a Connector
- * at the same time so the check for if the
- * current Writer/Connecter was deselected is only correct
- * under that assumption
- */
-
- if (descP->connectorP != NULL) {
-
- /* We have a Connector; was it deselected?
- */
-
- if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) {
-
- /* The Connector will not get a select message
- * - send it an abort message
- */
-
- esock_stop_handle_current(env,
- "connector",
- descP, sockRef, &descP->connector);
- }
-
- descP->connectorP = NULL;
- }
-
- /* +++++++ Current and waiting Readers +++++++ */
-
- if (descP->currentReaderP != NULL) {
-
- /* We have a current Reader; was it deselected?
- */
-
- if (sres & ERL_NIF_SELECT_READ_CANCELLED) {
-
- /* The current Reader will not get a select message
- * - send it an abort message
- */
-
- esock_stop_handle_current(env,
- "reader",
- descP, sockRef, &descP->currentReader);
- }
-
- /* Inform the Readers (in the same way) */
-
- SSDBG( descP,
- ("SOCKET",
- "esock_do_stop {%d} -> handle waiting reader(s)\r\n",
- descP->sock) );
-
- inform_waiting_procs(env, "writer",
- descP, sockRef, &descP->readersQ, esock_atom_closed);
-
- descP->currentReaderP = NULL;
- }
-
- /* +++++++ Current and waiting Acceptors +++++++
- *
- * Note that there should not be Readers and Acceptors
- * at the same time so the check for if the
- * current Reader/Acceptor was deselected is only correct
- * under that assumption
- */
-
- if (descP->currentAcceptorP != NULL) {
-
- /* We have a current Acceptor; was it deselected?
- */
-
- if (sres & ERL_NIF_SELECT_READ_CANCELLED) {
-
- /* The current Acceptor will not get a select message
- * - send it an abort message
- */
-
- esock_stop_handle_current(env,
- "acceptor",
- descP, sockRef, &descP->currentAcceptor);
- }
-
- /* Inform the waiting Acceptor (in the same way) */
-
- SSDBG( descP,
- ("SOCKET",
- "esock_do_stop {%d} -> handle waiting acceptors(s)\r\n",
- descP->sock) );
-
- inform_waiting_procs(env, "acceptor",
- descP, sockRef, &descP->acceptorsQ, esock_atom_closed);
-
- descP->currentAcceptorP = NULL;
- }
-
- return ret;
-}
-#endif // #ifndef __WIN32__
-
-
/* ----------------------------------------------------------------------
* nif_finalize_close
*
@@ -6480,9 +6188,6 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env,
{
ESockDescriptor* descP;
ERL_NIF_TERM result;
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
/* Extract arguments and perform preliminary validation */
@@ -6496,8 +6201,11 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env,
MLOCK(descP->writeMtx);
SSDBG( descP,
- ("SOCKET", "nif_finalize_close(%T), {%d,0x%X}\r\n",
- argv[0], descP->sock, descP->readState) );
+ ("SOCKET", "nif_finalize_close(%T, %d) -> "
+ "\r\n ReadState: 0x%X"
+ "\r\n WriteState: 0x%X"
+ "\r\n",
+ argv[0], descP->sock, descP->readState, descP->writeState) );
result = ESOCK_IO_FIN_CLOSE(env, descP);
@@ -6509,11 +6217,9 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
return result;
-#endif // #ifdef __WIN32__ #else
}
-#ifndef __WIN32__ // TEMPORARY - check the prim_socket_int.h file!
extern
int esock_close_socket(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -6536,7 +6242,6 @@ int esock_close_socket(ErlNifEnv* env,
* finalize_close
*/
descP->sock = INVALID_SOCKET;
- descP->event = INVALID_EVENT;
descP->readState |= ESOCK_STATE_CLOSED;
descP->writeState |= ESOCK_STATE_CLOSED;
esock_dec_socket(descP->domain, descP->type, descP->protocol);
@@ -6545,12 +6250,14 @@ int esock_close_socket(ErlNifEnv* env,
enif_clear_env(descP->meta.env);
descP->meta.ref = esock_atom_undefined;
- sock_close_event(descP->event);
if (descP->closeOnClose) {
if (unlock) {
MUNLOCK(descP->writeMtx);
MUNLOCK(descP->readMtx);
}
+ SSDBG( descP,
+ ("SOCKET", "esock_close_socket(%d) -> "
+ "try socket close\r\n", sock) );
if (sock_close(sock) != 0)
err = sock_errno();
if (unlock) {
@@ -6561,8 +6268,8 @@ int esock_close_socket(ErlNifEnv* env,
if (err != 0) {
SSDBG( descP,
- ("SOCKET", "esock_close_socket {%d} -> %d\r\n",
- sock, err) );
+ ("SOCKET", "esock_close_socket(%d) -> %s (%d)\r\n",
+ sock, erl_errno_id(err), err) );
}
/* (maybe) Update the registry */
@@ -6573,8 +6280,6 @@ int esock_close_socket(ErlNifEnv* env,
return err;
}
-#endif // #ifndef __WIN32__
-
@@ -6594,9 +6299,6 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM ehow, res;
int how;
@@ -6632,7 +6334,24 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env,
"\r\n", argv[0], res) );
return res;
-#endif // #ifdef __WIN32__ #else
+}
+
+
+
+/* ========================================================================
+ */
+static
+ERL_NIF_TERM esock_shutdown(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int how)
+{
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ if (sock_shutdown(descP->sock, how) == 0)
+ return esock_atom_ok;
+ else
+ return esock_make_error_errno(env, sock_errno());
}
@@ -6660,64 +6379,73 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP = NULL;
+ ERL_NIF_TERM esock, elevel, eopt, eval, enval;
int level, opt, nativeValue;
- ERL_NIF_TERM eVal;
ESOCK_ASSERT( argc == 5 );
- SGDBG( ("SOCKET", "nif_setopt -> entry with argc: %d\r\n", argc) );
+ esock = argv[0];
+ elevel = argv[1];
+ eopt = argv[2];
+ eval = argv[3];
+ enval = argv[4];
+
+ SGDBG( ("SOCKET",
+ "nif_setopt -> entry with argc: %d"
+ "\r\n esock: %T"
+ "\r\n elevel: %T"
+ "\r\n eopt: %T"
+ "\r\n eval: %T"
+ "\r\n enval: %T"
+ "\r\n", argc, esock, elevel, eopt, eval, enval) );
/* Extract arguments and perform preliminary validation */
- if ((! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) ||
- (! GET_INT(env, argv[4], &nativeValue))) {
- //
+ if ((! ESOCK_GET_RESOURCE(env, esock, (void**) &descP)) ||
+ (! GET_INT(env, enval, &nativeValue))) {
SGDBG( ("SOCKET", "nif_setopt -> failed initial arg check\r\n") );
return enif_make_badarg(env);
}
- if (! GET_INT(env, argv[2], &opt)) {
+
+ if (! GET_INT(env, eopt, &opt)) {
SSDBG( descP,
("SOCKET", "nif_setopt -> failed initial arg check\r\n") );
- if (! IS_INTEGER(env, argv[2]))
+ if (! IS_INTEGER(env, eopt))
return enif_make_badarg(env);
else
- return esock_make_error_integer_range(env, argv[2]);
- }
- eVal = argv[3];
-
- if (esock_decode_level(env, argv[1], &level)) {
- if (nativeValue == 0)
- return esock_setopt(env, descP, level, opt, eVal);
- else
- return esock_setopt_native(env, descP, level, opt, eVal);
+ return esock_make_error_integer_range(env, eopt);
}
- if (COMPARE(argv[1], atom_otp) == 0) {
+ if (COMPARE(elevel, atom_otp) == 0) {
if (nativeValue == 0) {
- return esock_setopt_otp(env, descP, opt, eVal);
+ return ESOCK_IO_SETOPT_OTP(env, descP, opt, eval);
} else {
SSDBG( descP, ("SOCKET", "nif_setopt -> failed arg check\r\n") );
return enif_make_badarg(env);
}
}
+ if (esock_decode_level(env, elevel, &level)) {
+ if (nativeValue == 0)
+ return ESOCK_IO_SETOPT(env, descP, level, opt, eval);
+ else
+ return ESOCK_IO_SETOPT_NATIVE(env, descP, level, opt, eval);
+ }
+
SGDBG( ("SOCKET", "nif_setopt -> failed arg check\r\n") );
- if (IS_INTEGER(env, argv[1]))
- return esock_make_error_integer_range(env, argv[1]);
+ if (IS_INTEGER(env, elevel))
+ return esock_make_error_integer_range(env, elevel);
else
return enif_make_badarg(env);
-#endif // #ifdef __WIN32__ #else
+
}
/* esock_setopt_otp - Handle OTP (level) options
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -6801,13 +6529,12 @@ ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
return result;
}
-#endif // #ifndef __WIN32__
/* esock_setopt_otp_debug - Handle the OTP (level) debug options
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_otp_debug(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -6830,12 +6557,12 @@ ERL_NIF_TERM esock_setopt_otp_debug(ErlNifEnv* env,
return esock_atom_ok;
}
-#endif // #ifndef __WIN32__
+
/* esock_setopt_otp_iow - Handle the OTP (level) iow options
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_otp_iow(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -6858,13 +6585,13 @@ ERL_NIF_TERM esock_setopt_otp_iow(ErlNifEnv* env,
return esock_atom_ok;
}
-#endif // #ifndef __WIN32__
+
/* esock_setopt_otp_ctrl_proc - Handle the OTP (level)
* controlling_process options
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_otp_ctrl_proc(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -6933,7 +6660,12 @@ ERL_NIF_TERM esock_setopt_otp_ctrl_proc(ErlNifEnv* env,
enif_set_pid_undefined(&descP->ctrlPid);
- esock_down_ctrl(env, descP, &newCtrlPid);
+ /* Shall we use an function pointer argument instead? */
+#ifndef __WIN32__
+ essio_down_ctrl(env, descP, &newCtrlPid);
+#else
+ esaio_down_ctrl(env, descP, &newCtrlPid);
+#endif
descP->readState |= ESOCK_STATE_CLOSING;
descP->writeState |= ESOCK_STATE_CLOSING;
@@ -6946,7 +6678,7 @@ ERL_NIF_TERM esock_setopt_otp_ctrl_proc(ErlNifEnv* env,
return esock_atom_ok;
}
-#endif // #ifndef __WIN32__
+
/* esock_setopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option
@@ -6956,8 +6688,9 @@ ERL_NIF_TERM esock_setopt_otp_ctrl_proc(ErlNifEnv* env,
* {N :: pos_integer(), Sz :: default | pos_integer()}
*
* Where N is the max number of reads.
+ * Note that on Windows the tuple variant is not allowed!
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -6965,7 +6698,9 @@ ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env,
{
const ERL_NIF_TERM* t; // The array of the elements of the tuple
int tsz; // The size of the tuple - should be 2
+#ifndef __WIN32__
unsigned int n;
+#endif
size_t bufSz;
ssize_t z;
@@ -6981,6 +6716,22 @@ ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env,
return esock_make_error_closed(env);
}
+
+#ifdef __WIN32__
+
+ if (!esock_decode_bufsz(env,
+ eVal,
+ ESOCK_RECV_BUFFER_SIZE_DEFAULT,
+ &bufSz)) {
+ SSDBG( descP,
+ ("SOCKET",
+ "esock_setopt_otp_rcvbuf(%d) -> done invalid\r\n",
+ descP->sock) );
+ return esock_make_invalid(env, esock_atom_value);
+ }
+
+#else
+
if (esock_decode_bufsz(env,
eVal,
ESOCK_RECV_BUFFER_SIZE_DEFAULT,
@@ -6992,21 +6743,25 @@ ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env,
(! GET_UINT(env, t[0], &n)) ||
(n == 0) ||
(! esock_decode_bufsz(env, t[1],
- ESOCK_RECV_BUFFER_SIZE_DEFAULT,
+ ESOCK_RECV_BUFFER_SIZE_DEFAULT,
&bufSz))) {
SSDBG( descP,
("SOCKET",
"esock_setopt_otp_rcvbuf {%d} -> done invalid\r\n",
- descP->sock) );
+ descP->sock) );
return esock_make_invalid(env, esock_atom_value);
}
}
+#endif
+
// We do not want a buffer size that does not fit in ssize_t
z = bufSz;
if (bufSz != (size_t) z)
return esock_make_invalid(env, esock_atom_value);
+#ifndef __WIN32__
descP->rNum = n;
+#endif
descP->rBufSz = bufSz;
SSDBG( descP,
@@ -7015,12 +6770,12 @@ ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env,
return esock_atom_ok;
}
-#endif // #ifndef __WIN32__
+
/* esock_setopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_otp_rcvctrlbuf(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -7059,12 +6814,12 @@ ERL_NIF_TERM esock_setopt_otp_rcvctrlbuf(ErlNifEnv* env,
return esock_atom_ok;
}
-#endif // #ifndef __WIN32__
+
/* esock_setopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_otp_sndctrlbuf(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -7103,12 +6858,12 @@ ERL_NIF_TERM esock_setopt_otp_sndctrlbuf(ErlNifEnv* env,
return esock_atom_ok;
}
-#endif // #ifndef __WIN32__
+
/* esock_setopt_otp_meta - Handle the OTP (level) meta options
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_otp_meta(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -7146,12 +6901,12 @@ ERL_NIF_TERM esock_setopt_otp_meta(ErlNifEnv* env,
return esock_atom_ok;
}
-#endif // #ifndef __WIN32__
+
/* esock_setopt_otp_use_registry - Handle the OTP (level) use_registry option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_otp_use_registry(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -7187,13 +6942,13 @@ ERL_NIF_TERM esock_setopt_otp_use_registry(ErlNifEnv* env,
return esock_atom_ok;
}
-#endif // #ifndef __WIN32__
+
/* The option has *not* been encoded. Instead it has been provided
* in "native mode" (value is a binary, an integer or a boolean).
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -7247,13 +7002,13 @@ ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env,
MUNLOCK(descP->writeMtx);
return result;
}
-#endif // #ifndef __WIN32__
+
/* esock_setopt - A "proper" level (option) has been specified,
* and we have an value of known encoding
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -7315,10 +7070,8 @@ ERL_NIF_TERM esock_setopt(ErlNifEnv* env,
MUNLOCK(descP->writeMtx);
return result;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
#if defined(SO_BINDTODEVICE)
static
ERL_NIF_TERM esock_setopt_so_bindtodevice(ErlNifEnv* env,
@@ -7330,10 +7083,8 @@ ERL_NIF_TERM esock_setopt_so_bindtodevice(ErlNifEnv* env,
return esock_setopt_str_opt(env, descP, level, opt, IFNAMSIZ, eVal);
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
#if defined(SO_LINGER)
static
ERL_NIF_TERM esock_setopt_linger(ErlNifEnv* env,
@@ -7361,7 +7112,7 @@ ERL_NIF_TERM esock_setopt_linger(ErlNifEnv* env,
}
if ((! esock_decode_bool(eOnOff, &onOff)) ||
- (! GET_INT(env, eLinger, &val.l_linger)) ||
+ (! GET_INT(env, eLinger, (int*) &val.l_linger)) ||
(val.l_linger < 0)) {
return esock_make_invalid(env, esock_atom_value);
}
@@ -7371,11 +7122,9 @@ ERL_NIF_TERM esock_setopt_linger(ErlNifEnv* env,
&val, sizeof(val));
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE)
/* esock_setopt_msfilter - Level IP MSFILTER option
@@ -7470,14 +7219,12 @@ BOOLEAN_T decode_msfilter_mode(ErlNifEnv* env,
}
#endif // #if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE)
-#endif // #ifndef __WIN32__
/* esock_setopt_ip_mtu_discover - Level IP MTU_DISCOVER option
*
* The value is an atom of the type ip_pmtudisc().
*/
-#ifndef __WIN32__
#if defined(IP_MTU_DISCOVER)
static
ERL_NIF_TERM esock_setopt_ip_mtu_discover(ErlNifEnv* env,
@@ -7495,7 +7242,6 @@ ERL_NIF_TERM esock_setopt_ip_mtu_discover(ErlNifEnv* env,
&val, sizeof(val));
}
#endif // #if defined(IP_MTU_DISCOVER)
-#endif // #ifndef __WIN32__
@@ -7503,7 +7249,6 @@ ERL_NIF_TERM esock_setopt_ip_mtu_discover(ErlNifEnv* env,
*
* The value is either the atom 'any' or a 4-tuple.
*/
-#ifndef __WIN32__
#if defined(IP_MULTICAST_IF)
static
ERL_NIF_TERM esock_setopt_multicast_if(ErlNifEnv* env,
@@ -7526,11 +7271,11 @@ ERL_NIF_TERM esock_setopt_multicast_if(ErlNifEnv* env,
return result;
}
#endif
-#endif // #ifndef __WIN32__
+
/* esock_setopt_tos - Level IP TOS option
*/
-#ifndef __WIN32__
+
#if defined(IP_TOS)
static
ERL_NIF_TERM esock_setopt_tos(ErlNifEnv* env,
@@ -7553,8 +7298,6 @@ ERL_NIF_TERM esock_setopt_tos(ErlNifEnv* env,
return result;
}
#endif
-#endif // #ifndef __WIN32__
-
@@ -7563,7 +7306,7 @@ ERL_NIF_TERM esock_setopt_tos(ErlNifEnv* env,
* The attribute 'interface' is either the atom 'any' or a 4-tuple
* (IPv4 address).
*/
-#ifndef __WIN32__
+
#if defined(IP_ADD_MEMBERSHIP) || defined(IP_DROP_MEMBERSHIP)
static
ERL_NIF_TERM esock_setopt_in_update_membership(ErlNifEnv* env,
@@ -7614,7 +7357,6 @@ ERL_NIF_TERM esock_setopt_in_update_membership(ErlNifEnv* env,
return esock_make_invalid(env, esock_atom_value);
}
#endif
-#endif // #ifndef __WIN32__
/* The value is a map with three attributes: multiaddr, interface and
@@ -7624,7 +7366,7 @@ ERL_NIF_TERM esock_setopt_in_update_membership(ErlNifEnv* env,
* The attribute 'sourceaddr' is always a 4-tuple (IPv4 address).
* (IPv4 address).
*/
-#ifndef __WIN32__
+
#if defined(IP_ADD_SOURCE_MEMBERSHIP) || \
defined(IP_DROP_SOURCE_MEMBERSHIP) || \
defined(IP_BLOCK_SOURCE) || \
@@ -7659,15 +7401,11 @@ ERL_NIF_TERM esock_setopt_in_update_source(ErlNifEnv* env,
return esock_make_invalid(env, esock_atom_value);
}
#endif
-#endif // #ifndef __WIN32__
#if defined(HAVE_IPV6)
-
-
-#ifndef __WIN32__
#if defined(IPV6_ADDRFORM)
static
ERL_NIF_TERM esock_setopt_addrform(ErlNifEnv* env,
@@ -7694,7 +7432,6 @@ ERL_NIF_TERM esock_setopt_addrform(ErlNifEnv* env,
&domain, sizeof(domain));
}
#endif
-#endif // #ifndef __WIN32__
@@ -7702,7 +7439,7 @@ ERL_NIF_TERM esock_setopt_addrform(ErlNifEnv* env,
*
* The value is an atom of the type ipv6_pmtudisc().
*/
-#ifndef __WIN32__
+
#if defined(IPV6_MTU_DISCOVER)
static
ERL_NIF_TERM esock_setopt_ipv6_mtu_discover(ErlNifEnv* env,
@@ -7720,10 +7457,9 @@ ERL_NIF_TERM esock_setopt_ipv6_mtu_discover(ErlNifEnv* env,
&val, sizeof(val));
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
+
#if defined(IPV6_MULTICAST_HOPS)
static
ERL_NIF_TERM esock_setopt_hops(ErlNifEnv* env,
@@ -7741,10 +7477,9 @@ ERL_NIF_TERM esock_setopt_hops(ErlNifEnv* env,
&hops, sizeof(hops));
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
+
#if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP)
static
ERL_NIF_TERM esock_setopt_in6_update_membership(ErlNifEnv* env,
@@ -7793,7 +7528,6 @@ ERL_NIF_TERM esock_setopt_in6_update_membership(ErlNifEnv* env,
return esock_make_invalid(env, esock_atom_value);
}
#endif
-#endif // #ifndef __WIN32__
#endif // defined(HAVE_IPV6)
@@ -7803,7 +7537,6 @@ ERL_NIF_TERM esock_setopt_in6_update_membership(ErlNifEnv* env,
/* esock_setopt_tcp_congestion - Level TCP CONGESTION option
*/
-#ifndef __WIN32__
#if defined(TCP_CONGESTION)
static
ERL_NIF_TERM esock_setopt_tcp_congestion(ErlNifEnv* env,
@@ -7817,17 +7550,13 @@ ERL_NIF_TERM esock_setopt_tcp_congestion(ErlNifEnv* env,
return esock_setopt_str_opt(env, descP, level, opt, max, eVal);
}
#endif
-#endif // #ifndef __WIN32__
-
#if defined(HAVE_SCTP)
-
-
/* esock_setopt_sctp_associnfo - Level SCTP ASSOCINFO option
*/
-#ifndef __WIN32__
+
#if defined(SCTP_ASSOCINFO)
static
ERL_NIF_TERM esock_setopt_sctp_associnfo(ErlNifEnv* env,
@@ -7908,12 +7637,12 @@ ERL_NIF_TERM esock_setopt_sctp_associnfo(ErlNifEnv* env,
return esock_make_invalid(env, esock_atom_value);
}
#endif
-#endif // #ifndef __WIN32__
+
/* esock_setopt_sctp_events - Level SCTP EVENTS option
*/
-#ifndef __WIN32__
+
#if defined(SCTP_EVENTS)
static
ERL_NIF_TERM esock_setopt_sctp_events(ErlNifEnv* env,
@@ -8011,12 +7740,12 @@ static int esock_setopt_sctp_event(ErlNifEnv *env,
return 0;
}
#endif
-#endif // #ifndef __WIN32__
+
/* esock_setopt_sctp_initmsg - Level SCTP INITMSG option
*/
-#ifndef __WIN32__
+
#if defined(SCTP_INITMSG)
static
ERL_NIF_TERM esock_setopt_sctp_initmsg(ErlNifEnv* env,
@@ -8079,12 +7808,12 @@ ERL_NIF_TERM esock_setopt_sctp_initmsg(ErlNifEnv* env,
return esock_make_invalid(env, esock_atom_value);
}
#endif
-#endif // #ifndef __WIN32__
+
/* esock_setopt_sctp_rtoinfo - Level SCTP RTOINFO option
*/
-#ifndef __WIN32__
+
#if defined(SCTP_RTOINFO)
static
ERL_NIF_TERM esock_setopt_sctp_rtoinfo(ErlNifEnv* env,
@@ -8138,9 +7867,6 @@ ERL_NIF_TERM esock_setopt_sctp_rtoinfo(ErlNifEnv* env,
return esock_make_invalid(env, esock_atom_value);
}
#endif
-#endif // #ifndef __WIN32__
-
-
#endif // defined(HAVE_SCTP)
@@ -8149,7 +7875,7 @@ ERL_NIF_TERM esock_setopt_sctp_rtoinfo(ErlNifEnv* env,
/* esock_setopt_bool_opt - set an option that has an (integer) bool value
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_bool_opt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -8167,12 +7893,12 @@ ERL_NIF_TERM esock_setopt_bool_opt(ErlNifEnv* env,
return esock_setopt_level_opt(env, descP, level, opt,
&ival, sizeof(ival));
}
-#endif // #ifndef __WIN32__
+
/* esock_setopt_int_opt - set an option that has an integer value
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -8192,12 +7918,12 @@ ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv* env,
}
return result;
}
-#endif // #ifndef __WIN32__
+
/* esock_setopt_str_opt - set an option that has an string value
*/
-#ifndef __WIN32__
+
#if defined(USE_SETOPT_STR_OPT)
static
ERL_NIF_TERM esock_setopt_str_opt(ErlNifEnv* env,
@@ -8228,12 +7954,12 @@ ERL_NIF_TERM esock_setopt_str_opt(ErlNifEnv* env,
return result;
}
#endif
-#endif // #ifndef __WIN32__
+
/* esock_setopt_timeval_opt - set an option that has an (timeval) bool value
*/
-#ifndef __WIN32__
+
#if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \
&& defined(ESOCK_USE_RCVSNDTIMEO)
static
@@ -8270,10 +7996,8 @@ ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv* env,
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv* env,
ESockDescriptor* descP,
int level,
@@ -8286,7 +8010,7 @@ static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv* env,
else
return esock_atom_ok;
}
-#endif // #ifndef __WIN32__
+
/* +++ socket_setopt +++
@@ -8312,7 +8036,6 @@ static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv* env,
* user feeling socket options are independent.
* </PaN>
*/
-#ifndef __WIN32__
static
int socket_setopt(int sock, int level, int opt,
const void* optVal, const socklen_t optLen)
@@ -8373,7 +8096,6 @@ int socket_setopt(int sock, int level, int opt,
return res;
}
-#endif // #ifndef __WIN32__
@@ -8397,58 +8119,67 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
+ ERL_NIF_TERM esock, elevel, eopt, evspec;
int level, opt;
ESOCK_ASSERT( (argc == 3) || (argc == 4) );
- SGDBG( ("SOCKET", "nif_getopt -> entry with argc: %d\r\n", argc) );
+ esock = argv[0];
+ elevel = argv[1];
+ eopt = argv[2];
+ evspec = ((argc == 4) ? argv[3] : esock_atom_undefined);
- if (! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
- SGDBG( ("SOCKET", "nif_getopt -> failed initial args check\r\n") );
+ SGDBG( ("SOCKET",
+ "nif_getopt -> entry with argc: %d"
+ "\r\n esock: %T"
+ "\r\n elevel: %T"
+ "\r\n eopt: %T"
+ "\r\n evspec: %T"
+ "\r\n", argc, esock, elevel, eopt, evspec) );
+
+ if (! ESOCK_GET_RESOURCE(env, esock, (void**) &descP)) {
+ SGDBG( ("SOCKET",
+ "nif_getopt -> failed initial args check - sock\r\n") );
return enif_make_badarg(env);
}
- if (! GET_INT(env, argv[2], &opt)) {
+ if (! GET_INT(env, eopt, &opt)) {
SSDBG( descP,
- ("SOCKET", "nif_getopt -> failed initial args check\r\n") );
- if (! IS_INTEGER(env, argv[2]))
+ ("SOCKET",
+ "nif_getopt -> failed initial args check - opt\r\n") );
+ if (! IS_INTEGER(env, eopt))
return enif_make_badarg(env);
else
- return esock_make_error_integer_range(env, argv[2]);
+ return esock_make_error_integer_range(env, eopt);
}
- if (esock_decode_level(env, argv[1], &level)) {
+ if ((COMPARE(elevel, atom_otp) == 0) &&
+ (argc == 3)) {
+ return ESOCK_IO_GETOPT_OTP(env, descP, opt) ;
+ }
+
+ if (esock_decode_level(env, elevel, &level)) {
if (argc == 4) {
- ERL_NIF_TERM valueSpec = argv[3];
- return esock_getopt_native(env, descP, level, opt, valueSpec);
+ return ESOCK_IO_GETOPT_NATIVE(env, descP, level, opt, evspec);
} else {
- return esock_getopt(env, descP, level, opt);
+ return ESOCK_IO_GETOPT(env, descP, level, opt);
}
}
- if ((COMPARE(argv[1], atom_otp) == 0) &&
- (argc == 3)) {
- return esock_getopt_otp(env, descP, opt) ;
- }
-
SGDBG( ("SOCKET", "nif_getopt -> failed args check\r\n") );
- if (IS_INTEGER(env, argv[1]))
- return esock_make_error_integer_range(env, argv[1]);
+ if (IS_INTEGER(env, elevel))
+ return esock_make_error_integer_range(env, elevel);
else
return enif_make_badarg(env);
-#endif // #ifdef __WIN32__ #else
}
/* esock_getopt_otp - Handle OTP (level) options
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -8562,11 +8293,11 @@ ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env,
return result;
}
-#endif // #ifndef __WIN32__
+
/* esock_getopt_otp_debug - Handle the OTP (level) debug option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_debug(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8584,11 +8315,11 @@ ERL_NIF_TERM esock_getopt_otp_debug(ErlNifEnv* env,
return esock_make_ok2(env, eVal);
}
-#endif // #ifndef __WIN32__
+
/* esock_getopt_otp_iow - Handle the OTP (level) iow option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_iow(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8611,12 +8342,12 @@ ERL_NIF_TERM esock_getopt_otp_iow(ErlNifEnv* env,
return esock_make_ok2(env, eVal);
}
-#endif // #ifndef __WIN32__
+
/* esock_getopt_otp_ctrl_proc - Handle the OTP (level) controlling_process option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_ctrl_proc(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8640,12 +8371,11 @@ ERL_NIF_TERM esock_getopt_otp_ctrl_proc(ErlNifEnv* env,
return esock_make_ok2(env, eVal);
}
-#endif // #ifndef __WIN32__
/* esock_getopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_rcvbuf(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8659,6 +8389,9 @@ ERL_NIF_TERM esock_getopt_otp_rcvbuf(ErlNifEnv* env,
return esock_make_error_closed(env);
}
+#ifdef __WIN32__
+ eVal = MKUL(env, (unsigned long) descP->rBufSz);
+#else
if (descP->rNum == 0) {
eVal = MKUL(env, (unsigned long) descP->rBufSz);
} else {
@@ -8666,6 +8399,7 @@ ERL_NIF_TERM esock_getopt_otp_rcvbuf(ErlNifEnv* env,
MKI(env, descP->rNum),
MKUL(env, (unsigned long) descP->rBufSz));
}
+#endif
SSDBG( descP,
("SOCKET", "esock_getopt_otp_rcvbuf {%d} ->"
@@ -8674,12 +8408,12 @@ ERL_NIF_TERM esock_getopt_otp_rcvbuf(ErlNifEnv* env,
return esock_make_ok2(env, eVal);
}
-#endif // #ifndef __WIN32__
+
/* esock_getopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_rcvctrlbuf(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8703,12 +8437,12 @@ ERL_NIF_TERM esock_getopt_otp_rcvctrlbuf(ErlNifEnv* env,
return esock_make_ok2(env, eVal);
}
-#endif // #ifndef __WIN32__
+
/* esock_getopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_sndctrlbuf(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8732,12 +8466,12 @@ ERL_NIF_TERM esock_getopt_otp_sndctrlbuf(ErlNifEnv* env,
return esock_make_ok2(env, eVal);
}
-#endif // #ifndef __WIN32__
+
/* esock_getopt_otp_fd - Handle the OTP (level) fd option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_fd(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8760,12 +8494,12 @@ ERL_NIF_TERM esock_getopt_otp_fd(ErlNifEnv* env,
return esock_make_ok2(env, eVal);
}
-#endif // #ifndef __WIN32__
+
/* esock_getopt_otp_meta - Handle the OTP (level) meta option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_meta(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8788,12 +8522,12 @@ ERL_NIF_TERM esock_getopt_otp_meta(ErlNifEnv* env,
return esock_make_ok2(env, eVal);
}
-#endif // #ifndef __WIN32__
+
/* esock_getopt_otp_use_registry - Handle the OTP (level) use_registry option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_use_registry(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8802,13 +8536,13 @@ ERL_NIF_TERM esock_getopt_otp_use_registry(ErlNifEnv* env,
return esock_make_ok2(env, eVal);
}
-#endif
+
/*
* esock_getopt_otp_domain - Handle the OTP (level) domain option
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_domain(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8832,7 +8566,6 @@ ERL_NIF_TERM esock_getopt_otp_domain(ErlNifEnv* env,
return result;
}
-#endif // #ifndef __WIN32__
@@ -8841,7 +8574,7 @@ ERL_NIF_TERM esock_getopt_otp_domain(ErlNifEnv* env,
/*
* esock_getopt_otp_type - Handle the OTP (level) type options.
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_type(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8865,13 +8598,13 @@ ERL_NIF_TERM esock_getopt_otp_type(ErlNifEnv* env,
return result;
}
-#endif // #ifndef __WIN32__
+
/*
* esock_getopt_otp_protocol - Handle the OTP (level) protocol options.
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_protocol(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8895,13 +8628,13 @@ ERL_NIF_TERM esock_getopt_otp_protocol(ErlNifEnv* env,
return result;
}
-#endif // #ifndef __WIN32__
+
/*
* esock_getopt_otp_dtp - Handle the OTP (level) type options.
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_otp_dtp(ErlNifEnv* env,
ESockDescriptor* descP)
@@ -8928,7 +8661,6 @@ ERL_NIF_TERM esock_getopt_otp_dtp(ErlNifEnv* env,
return result;
}
-#endif // #ifndef __WIN32__
#endif // #if 0
@@ -8936,7 +8668,7 @@ ERL_NIF_TERM esock_getopt_otp_dtp(ErlNifEnv* env,
/* How to decode the value is specified with valueSpec
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -9013,32 +8745,31 @@ ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
return result;
}
-#endif // #ifndef __WIN32__
+
/* esock_getopt - An option that we know how to decode
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_getopt(ErlNifEnv* env,
ESockDescriptor* descP,
int level,
int opt)
{
- ERL_NIF_TERM result;
+ ERL_NIF_TERM result;
const struct ESockOpt *optP;
MLOCK(descP->readMtx);
SSDBG( descP,
("SOCKET", "esock_getopt {%d} -> entry with"
- "\r\n level: %d"
- "\r\n opt: %d"
+ "\r\n level: %d"
+ "\r\n opt: %d"
"\r\n", descP->sock, level, opt) );
if (! IS_OPEN(descP->readState)) {
SSDBG( descP,
- ("SOCKET", "esock_getopt {%d} -> done closed\r\n",
+ ("SOCKET", "esock_getopt {%d} -> done when closed\r\n",
descP->sock) );
MUNLOCK(descP->readMtx);
return esock_make_error_closed(env);
@@ -9075,10 +8806,8 @@ ERL_NIF_TERM esock_getopt(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
return result;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
#if defined(SO_BINDTODEVICE)
static
ERL_NIF_TERM esock_getopt_so_bindtodevice(ErlNifEnv* env,
@@ -9089,10 +8818,8 @@ ERL_NIF_TERM esock_getopt_so_bindtodevice(ErlNifEnv* env,
return esock_getopt_str_opt(env, descP, level, opt, IFNAMSIZ+1, FALSE);
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
#if defined(SO_DOMAIN)
static
ERL_NIF_TERM esock_getopt_sock_domain(ErlNifEnv* env,
@@ -9114,10 +8841,8 @@ ERL_NIF_TERM esock_getopt_sock_domain(ErlNifEnv* env,
return result;
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
#if defined(SO_LINGER)
static
ERL_NIF_TERM esock_getopt_linger(ErlNifEnv* env,
@@ -9132,7 +8857,11 @@ ERL_NIF_TERM esock_getopt_linger(ErlNifEnv* env,
sys_memzero((void *) &val, sizeof(val));
+#ifdef __WIN32__
+ res = sock_getopt(descP->sock, level, opt, (char*) &val, &valSz);
+#else
res = sock_getopt(descP->sock, level, opt, &val, &valSz);
+#endif
if (res != 0) {
result = esock_make_error_errno(env, sock_errno());
@@ -9154,11 +8883,9 @@ ERL_NIF_TERM esock_getopt_linger(ErlNifEnv* env,
return result;
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
#if defined(SO_TYPE)
static
ERL_NIF_TERM esock_getopt_sock_type(ErlNifEnv* env,
@@ -9180,10 +8907,8 @@ ERL_NIF_TERM esock_getopt_sock_type(ErlNifEnv* env,
return result;
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
#if defined(SO_PROTOCOL)
static
ERL_NIF_TERM esock_getopt_sock_protocol(ErlNifEnv* env,
@@ -9216,10 +8941,8 @@ ERL_NIF_TERM esock_getopt_sock_protocol(ErlNifEnv* env,
return result;
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
/* esock_getopt_ip_mtu_discover - Level IP MTU_DISCOVER option
*/
#if defined(IP_MTU_DISCOVER)
@@ -9244,12 +8967,11 @@ ERL_NIF_TERM esock_getopt_ip_mtu_discover(ErlNifEnv* env,
}
#endif
-#endif // #ifndef __WIN32__
/* esock_getopt_multicast_if - Level IP MULTICAST_IF option
*/
-#ifndef __WIN32__
+
#if defined(IP_MULTICAST_IF)
static
ERL_NIF_TERM esock_getopt_multicast_if(ErlNifEnv* env,
@@ -9265,7 +8987,11 @@ ERL_NIF_TERM esock_getopt_multicast_if(ErlNifEnv* env,
sys_memzero((void *) &ifAddr, ifAddrSz);
+#ifdef __WIN32__
+ res = sock_getopt(descP->sock, level, opt, (char*) &ifAddr, &ifAddrSz);
+#else
res = sock_getopt(descP->sock, level, opt, &ifAddr, &ifAddrSz);
+#endif
if (res != 0) {
result = esock_make_error_errno(env, sock_errno());
@@ -9278,12 +9004,12 @@ ERL_NIF_TERM esock_getopt_multicast_if(ErlNifEnv* env,
}
#endif
-#endif // #ifndef __WIN32__
+
/* esock_getopt_tos - Level IP TOS option
*/
-#ifndef __WIN32__
+
#if defined(IP_TOS)
static
ERL_NIF_TERM esock_getopt_tos(ErlNifEnv* env,
@@ -9303,15 +9029,14 @@ ERL_NIF_TERM esock_getopt_tos(ErlNifEnv* env,
return result;
}
#endif
-#endif // #ifndef __WIN32__
-#if defined(HAVE_IPV6)
+#if defined(HAVE_IPV6)
/* esock_getopt_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option
*/
-#ifndef __WIN32__
+
#if defined(IPV6_MTU_DISCOVER)
static
ERL_NIF_TERM esock_getopt_ipv6_mtu_discover(ErlNifEnv* env,
@@ -9334,16 +9059,13 @@ ERL_NIF_TERM esock_getopt_ipv6_mtu_discover(ErlNifEnv* env,
}
#endif
-#endif // #ifndef __WIN32__
-
#endif // defined(HAVE_IPV6)
-
/* esock_getopt_tcp_congestion - Level TCP CONGESTION option
*/
-#ifndef __WIN32__
+
#if defined(TCP_CONGESTION)
static
ERL_NIF_TERM esock_getopt_tcp_congestion(ErlNifEnv* env,
@@ -9356,14 +9078,11 @@ ERL_NIF_TERM esock_getopt_tcp_congestion(ErlNifEnv* env,
return esock_getopt_str_opt(env, descP, level, opt, max, TRUE);
}
#endif
-#endif // #ifndef __WIN32__
#if defined(HAVE_SCTP)
-
-
/* esock_getopt_sctp_associnfo - Level SCTP ASSOCINFO option
*
* <KOLLA>
@@ -9527,7 +9246,6 @@ ERL_NIF_TERM esock_getopt_sctp_rtoinfo(ErlNifEnv* env,
/* esock_getopt_bool_opt - get an (integer) bool option
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -9546,12 +9264,10 @@ ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv* env,
}
return result;
}
-#endif // #ifndef __WIN32__
/* esock_getopt_int_opt - get an integer option
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -9565,13 +9281,11 @@ ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv* env,
return esock_make_ok2(env, MKI(env, val));
}
-#endif // #ifndef __WIN32__
/* esock_getopt_int - get an integer option
*/
-#ifndef __WIN32__
extern
BOOLEAN_T esock_getopt_int(SOCKET sock,
int level,
@@ -9581,17 +9295,19 @@ BOOLEAN_T esock_getopt_int(SOCKET sock,
int val = 0;
SOCKOPTLEN_T valSz = sizeof(val);
+#ifdef __WIN32__
+ if (sock_getopt(sock, level, opt, (char*) &val, &valSz) != 0)
+#else
if (sock_getopt(sock, level, opt, &val, &valSz) != 0)
+#endif
return FALSE;
*valP = val;
return TRUE;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -9638,10 +9354,9 @@ ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv* env,
return result;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
+
static
ERL_NIF_TERM esock_getopt_bin_opt(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -9684,7 +9399,7 @@ ERL_NIF_TERM esock_getopt_bin_opt(ErlNifEnv* env,
return result;
}
-#endif // #ifndef __WIN32__
+
/* esock_getopt_timeval_opt - get an timeval option
@@ -9736,11 +9451,11 @@ ERL_NIF_TERM esock_getopt_timeval_opt(ErlNifEnv* env,
* Some platforms (seen on ppc Linux 2.6.29-3.ydl61.3)
* may return 0 as the cmsg_len if the cmsg is to be ignored.
*/
-#define LEN_CMSG_DATA(__CMSG__) \
- ((__CMSG__)->cmsg_len < sizeof (struct cmsghdr) ? 0 : \
- (__CMSG__)->cmsg_len - ((char*)CMSG_DATA(__CMSG__) - (char*)(__CMSG__)))
-#define NEXT_CMSG_HDR(__CMSG__) \
- ((struct cmsghdr*)(((char*)(__CMSG__)) + CMSG_SPACE(LEN_CMSG_DATA(__CMSG__))))
+#define ESOCK_LEN_CMSG_DATA(__CMSG__) \
+ ((__CMSG__)->cmsg_len < sizeof (struct cmsghdr) ? 0 : \
+ (__CMSG__)->cmsg_len - ((char*)ESOCK_CMSG_DATA(__CMSG__) - (char*)(__CMSG__)))
+#define ESOCK_NEXT_CMSG_HDR(__CMSG__) \
+ ((struct cmsghdr*)(((char*)(__CMSG__)) + ESOCK_CMSG_SPACE(ESOCK_LEN_CMSG_DATA(__CMSG__))))
static
ERL_NIF_TERM esock_getopt_pktoptions(ErlNifEnv* env,
@@ -9772,8 +9487,8 @@ ERL_NIF_TERM esock_getopt_pktoptions(ErlNifEnv* env,
for (endOfBuf = (struct cmsghdr*)(cmsgs.data + cmsgs.size),
currentP = (struct cmsghdr*)(cmsgs.data);
(currentP != NULL) && (currentP < endOfBuf);
- currentP = NEXT_CMSG_HDR(currentP)) {
- unsigned char* dataP = UCHARP(CMSG_DATA(currentP));
+ currentP = ESOCK_NEXT_CMSG_HDR(currentP)) {
+ unsigned char* dataP = UCHARP(ESOCK_CMSG_DATA(currentP));
size_t dataPos = dataP - cmsgs.data;
size_t dataLen = (UCHARP(currentP) + currentP->cmsg_len) - dataP;
@@ -9936,9 +9651,6 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM res;
@@ -9967,7 +9679,52 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
return res;
-#endif // #ifdef __WIN32__ #else
+}
+
+
+
+/* ========================================================================
+ */
+
+static
+ERL_NIF_TERM esock_sockname(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ ESockAddress sa;
+ ESockAddress* saP = &sa;
+#ifdef __WIN32__
+ int sz = sizeof(ESockAddress);
+#else
+ SOCKLEN_T sz = sizeof(ESockAddress);
+#endif
+
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ SSDBG( descP,
+ ("SOCKET", "esock_sockname {%d} -> open - try get sockname\r\n",
+ descP->sock) );
+
+ sys_memzero((char*) saP, sz);
+ if (sock_name(descP->sock, (struct sockaddr*) saP, &sz) < 0) {
+ return esock_make_error_errno(env, sock_errno());
+ } else {
+ ERL_NIF_TERM esa;
+
+ SSDBG( descP,
+ ("SOCKET", "esock_sockname {%d} -> "
+ "got sockname - try decode\r\n",
+ descP->sock) );
+
+ esock_encode_sockaddr(env, saP, (SOCKLEN_T) sz, &esa);
+
+ SSDBG( descP,
+ ("SOCKET", "esock_sockname {%d} -> decoded: "
+ "\r\n %T\r\n",
+ descP->sock, esa) );
+
+ return esock_make_ok2(env, esa);
+ }
}
@@ -9987,9 +9744,6 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM res;
@@ -10018,7 +9772,52 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
return res;
-#endif // #ifdef __WIN32__ #else
+}
+
+
+
+/* ========================================================================
+ */
+
+static
+ERL_NIF_TERM esock_peername(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ ESockAddress sa;
+ ESockAddress* saP = &sa;
+#ifdef __WIN32__
+ int sz = sizeof(ESockAddress);
+#else
+ SOCKLEN_T sz = sizeof(ESockAddress);
+#endif
+
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ SSDBG( descP,
+ ("SOCKET", "esock_peername {%d} -> open - try get peername\r\n",
+ descP->sock) );
+
+ sys_memzero((char*) saP, sz);
+ if (sock_peer(descP->sock, (struct sockaddr*) saP, &sz) < 0) {
+ return esock_make_error_errno(env, sock_errno());
+ } else {
+ ERL_NIF_TERM esa;
+
+ SSDBG( descP,
+ ("SOCKET", "esock_peername {%d} -> "
+ "got peername - try decode\r\n",
+ descP->sock) );
+
+ esock_encode_sockaddr(env, saP, (SOCKLEN_T) sz, &esa);
+
+ SSDBG( descP,
+ ("SOCKET", "esock_peername {%d} -> decoded: "
+ "\r\n %T\r\n",
+ descP->sock, esa) );
+
+ return esock_make_ok2(env, esa);
+ }
}
@@ -10047,9 +9846,6 @@ ERL_NIF_TERM nif_ioctl(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM res;
unsigned long req;
@@ -10072,49 +9868,54 @@ ERL_NIF_TERM nif_ioctl(ErlNifEnv* env,
("SOCKET", "nif_ioctl(%T) {%d} -> ioctl request %d"
"\r\n", argv[0], descP->sock, req) );
+ /* Is this really enough? Why not the write mutex also? */
MLOCK(descP->readMtx);
if (! IS_OPEN(descP->readState)) {
res = esock_make_error_closed(env);
} else {
- if (argc == 2) {
-
- /* Only one request with this number of arguments: gifconf
- * Socket and request (=gifconf)
- */
-
- /* Two arguments: Socket and get request */
- res = esock_ioctl1(env, descP, req);
-
- } else if (argc == 3) {
-
- /* (Currently) All *other* get requests has 3 arguments
- * Socket, request and name/index
- */
-
- ERL_NIF_TERM earg = argv[2];
-
- /* Two arguments: request and arg */
- res = esock_ioctl2(env, descP, req, earg);
-
- } else if (argc == 4) {
-
- /* (Currently) Set requests has 4 arguments
- * Socket, request, name and value
- */
-
- ERL_NIF_TERM earg1 = argv[2]; // (currently) Name
- ERL_NIF_TERM earg2 = argv[3]; // Value
-
- /* Three arguments: request, arg1 (name) and arg2 (value) */
- res = esock_ioctl3(env, descP, req, earg1, earg2);
-
- } else {
-
- res = esock_make_error(env, esock_atom_einval);
-
- }
+ switch (argc) {
+ case 2:
+ /* Only one request with this number of arguments: gifconf
+ * Socket and request (=gifconf)
+ */
+
+ /* Two arguments: socket and request */
+ res = ESOCK_IO_IOCTL_2(env, descP, req);
+ break;
+
+ case 3:
+ /* (Currently) All *other* get requests has 3 arguments
+ * Socket, request and name/index
+ */
+ {
+ ERL_NIF_TERM earg = argv[2];
+
+ /* Three arguments: socket, request and arg */
+ res = ESOCK_IO_IOCTL_3(env, descP, req, earg);
+ }
+ break;
+
+ case 4:
+ /* (Currently) Set requests has 4 arguments
+ * Socket, request, name and value
+ */
+ {
+ ERL_NIF_TERM earg1 = argv[2]; // (currently) Name
+ ERL_NIF_TERM earg2 = argv[3]; // Value
+
+ res = ESOCK_IO_IOCTL_4(env, descP, req, earg1, earg2);
+ }
+ break;
+
+ default:
+ /* This is just to protect against programming errors,
+ * since we have an assert above!
+ */
+ res = esock_make_error(env, esock_atom_einval);
+ break;
+ }
}
MUNLOCK(descP->readMtx);
@@ -10124,1132 +9925,8 @@ ERL_NIF_TERM nif_ioctl(ErlNifEnv* env,
argv[0], descP->sock, res) );
return res;
-#endif // #ifdef __WIN32__ #else
-}
-
-
-
-#ifndef __WIN32__
-
-static
-ERL_NIF_TERM esock_ioctl1(ErlNifEnv* env,
- ESockDescriptor* descP,
- unsigned long req)
-{
- switch (req) {
-
-#if defined(SIOCGIFCONF)
- case SIOCGIFCONF:
- return esock_ioctl_gifconf(env, descP);
- break;
-#endif
-
- default:
- return esock_make_error(env, esock_atom_enotsup);
- break;
- }
-
-}
-
-
-/* The type and value of 'arg' depend on the request,
- * which we have not yet "analyzed".
- *
- * Request arg arg type
- * ------- ------- --------
- * gifname ifindex integer
- * gifindex name string
- * gifflags name string
- * gifaddr name string
- * gifdstaddr name string
- * gifbdraddr name string
- * gifnetmask name string
- * gifmtu name string
- * gifhwaddr name string
- * gifmap name string
- * giftxqlen name string
- */
-static
-ERL_NIF_TERM esock_ioctl2(ErlNifEnv* env,
- ESockDescriptor* descP,
- unsigned long req,
- ERL_NIF_TERM arg)
-{
- /* This for *get* requests */
-
- switch (req) {
-
-#if defined(SIOCGIFNAME)
- case SIOCGIFNAME:
- return esock_ioctl_gifname(env, descP, arg);
- break;
-#endif
-
-#if defined(SIOCGIFINDEX)
- case SIOCGIFINDEX:
- return esock_ioctl_gifindex(env, descP, arg);
- break;
-#endif
-
-#if defined(SIOCGIFFLAGS)
- case SIOCGIFFLAGS:
- return esock_ioctl_gifflags(env, descP, arg);
- break;
-#endif
-
-#if defined(SIOCGIFADDR)
- case SIOCGIFADDR:
- return esock_ioctl_gifaddr(env, descP, arg);
- break;
-#endif
-
-#if defined(SIOCGIFDSTADDR)
- case SIOCGIFDSTADDR:
- return esock_ioctl_gifdstaddr(env, descP, arg);
- break;
-#endif
-
-#if defined(SIOCGIFBRDADDR)
- case SIOCGIFBRDADDR:
- return esock_ioctl_gifbrdaddr(env, descP, arg);
- break;
-#endif
-
-#if defined(SIOCGIFNETMASK)
- case SIOCGIFNETMASK:
- return esock_ioctl_gifnetmask(env, descP, arg);
- break;
-#endif
-
-#if defined(SIOCGIFMTU)
- case SIOCGIFMTU:
- return esock_ioctl_gifmtu(env, descP, arg);
- break;
-#endif
-
-#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
- case SIOCGIFHWADDR:
- return esock_ioctl_gifhwaddr(env, descP, arg);
- break;
-#endif
-
-#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
- case SIOCGIFMAP:
- return esock_ioctl_gifmap(env, descP, arg);
- break;
-#endif
-
-#if defined(SIOCGIFTXQLEN)
- case SIOCGIFTXQLEN:
- return esock_ioctl_giftxqlen(env, descP, arg);
- break;
-#endif
-
- default:
- return esock_make_error(env, esock_atom_enotsup);
- break;
- }
-
-}
-
-
-/* The type and value of arg(s) depend on the request,
- * which we have not yet "analyzed".
- *
- * Request arg1 arg1 type arg2 arg2 type
- * ------- ------- --------- ------ ---------
- * sifflags name string Flags #{IntFlag := boolean()}
- * IntFlag is the native flag
- * sifaddr name string Addr sockaddr()
- * sifdstaddr name string DstAddr sockaddr()
- * sifbrdaddr name string BrdAddr sockaddr()
- * sifnetmask name string NetMask sockaddr()
- * gifmtu name string MTU integer()
- * sifhwaddr name string HwAddr sockaddr()
- * giftxqlen name string Len integer()
- */
-static
-ERL_NIF_TERM esock_ioctl3(ErlNifEnv* env,
- ESockDescriptor* descP,
- unsigned long req,
- ERL_NIF_TERM ename,
- ERL_NIF_TERM eval)
-{
-
- switch (req) {
-
-#if defined(SIOCSIFFLAGS)
- case SIOCSIFFLAGS:
- return esock_ioctl_sifflags(env, descP, ename, eval);
- break;
-#endif
-
-#if defined(SIOCSIFADDR)
- case SIOCSIFADDR:
- return esock_ioctl_sifaddr(env, descP, ename, eval);
- break;
-#endif
-
-#if defined(SIOCSIFDSTADDR)
- case SIOCSIFDSTADDR:
- return esock_ioctl_sifdstaddr(env, descP, ename, eval);
- break;
-#endif
-
-#if defined(SIOCSIFBRDADDR)
- case SIOCSIFBRDADDR:
- return esock_ioctl_sifbrdaddr(env, descP, ename, eval);
- break;
-#endif
-
-#if defined(SIOCSIFNETMASK)
- case SIOCSIFNETMASK:
- return esock_ioctl_sifnetmask(env, descP, ename, eval);
- break;
-#endif
-
-#if defined(SIOCSIFMTU)
- case SIOCSIFMTU:
- return esock_ioctl_sifmtu(env, descP, ename, eval);
- break;
-#endif
-
-#if defined(SIOCSIFTXQLEN)
- case SIOCSIFTXQLEN:
- return esock_ioctl_siftxqlen(env, descP, ename, eval);
- break;
-#endif
-
- default:
- return esock_make_error(env, esock_atom_enotsup);
- break;
- }
-
-}
-
-
-/* ===========================================================================
- * The implemented (ioctl) get requests falls into three grops:
- *
- * 1) gifconf - Takes no argument other then the request
- * 2) gifname - Takes the interface index (integer) as an argument
- * 3) other - All other (get) requests takes the interface name (string)
- * as the argument.
- *
- * The functions defined using the macros below are all in the third (3)
- * group.
- *
- */
-
-/* *** esock_ioctl_gifindex *** */
-#if defined(SIOCGIFINDEX)
-#if defined(ESOCK_USE_IFINDEX)
-#define IOCTL_GIFINDEX_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifindex, SIOCGIFINDEX, ivalue, ifreq.ifr_ifindex)
-#elif defined(ESOCK_USE_INDEX)
-#define IOCTL_GIFINDEX_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifindex, SIOCGIFINDEX, ivalue, ifreq.ifr_index)
-#else
-#define IOCTL_GIFINDEX_FUNC_DECL
-#endif
-#else
-#define IOCTL_GIFINDEX_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_gifflags *** */
-#if defined(SIOCGIFFLAGS)
-#define IOCTL_GIFFLAGS_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifflags, SIOCGIFFLAGS, flags, ifreq.ifr_flags)
-#else
-#define IOCTL_GIFFLAGS_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_gifaddr *** */
-#if defined(SIOCGIFADDR)
-#define IOCTL_GIFADDR_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifaddr, SIOCGIFADDR, ifraddr, &ifreq.ifr_addr)
-#else
-#define IOCTL_GIFADDR_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_gifdstaddr *** */
-#if defined(SIOCGIFDSTADDR)
-#define IOCTL_GIFDSTADDR_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifdstaddr, SIOCGIFDSTADDR, ifraddr, &ifreq.ifr_dstaddr)
-#else
-#define IOCTL_GIFDSTADDR_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_gifbrdaddr *** */
-#if defined(SIOCGIFBRDADDR)
-#define IOCTL_GIFBRDADDR_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifbrdaddr, SIOCGIFBRDADDR, ifraddr, &ifreq.ifr_broadaddr)
-#else
-#define IOCTL_GIFBRDADDR_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_gifnetmask *** */
-#if defined(SIOCGIFNETMASK)
-#ifdef __linux__
-#define IOCTL_GIFNETMASK_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifnetmask, SIOCGIFNETMASK, ifraddr, &ifreq.ifr_netmask)
-#else
-#define IOCTL_GIFNETMASK_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifnetmask, SIOCGIFNETMASK, ifraddr, &ifreq.ifr_addr)
-#endif
-#else
-#define IOCTL_GIFNETMASK_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_gifmtu *** */
-#if defined(SIOCGIFMTU)
-#define IOCTL_GIFMTU_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifmtu, SIOCGIFMTU, ivalue, ifreq.ifr_mtu)
-#else
-#define IOCTL_GIFMTU_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_gifhwaddr *** */
-#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
-#define IOCTL_GIFHWADDR_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifhwaddr, SIOCGIFHWADDR, hwaddr, &ifreq.ifr_hwaddr)
-#else
-#define IOCTL_GIFHWADDR_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_gifmap *** */
-#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
-#define IOCTL_GIFMAP_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(gifmap, SIOCGIFMAP, ifrmap, &ifreq.ifr_map)
-#else
-#define IOCTL_GIFMAP_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_giftxqlen *** */
-#if defined(SIOCGIFTXQLEN)
-#define IOCTL_GIFTXQLEN_FUNC_DECL \
- IOCTL_GET_REQUEST_DECL(giftxqlen, SIOCGIFTXQLEN, ivalue, ifreq.ifr_qlen)
-#else
-#define IOCTL_GIFTXQLEN_FUNC_DECL
-#endif
-
-#define IOCTL_GET_FUNCS \
- IOCTL_GIFINDEX_FUNC_DECL \
- IOCTL_GIFFLAGS_FUNC_DECL \
- IOCTL_GIFADDR_FUNC_DECL \
- IOCTL_GIFDSTADDR_FUNC_DECL \
- IOCTL_GIFBRDADDR_FUNC_DECL \
- IOCTL_GIFNETMASK_FUNC_DECL \
- IOCTL_GIFMTU_FUNC_DECL \
- IOCTL_GIFHWADDR_FUNC_DECL \
- IOCTL_GIFMAP_FUNC_DECL \
- IOCTL_GIFTXQLEN_FUNC_DECL
-
-#define IOCTL_GET_REQUEST_DECL(OR, R, EF, UV) \
- static \
- ERL_NIF_TERM esock_ioctl_##OR(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ERL_NIF_TERM ename) \
- { \
- ERL_NIF_TERM result; \
- struct ifreq ifreq; \
- char* ifn = NULL; \
- int nlen; \
- \
- SSDBG( descP, ("SOCKET", "esock_ioctl_" #OR " {%d} -> entry with" \
- "\r\n (e)Name: %T" \
- "\r\n", descP->sock, ename) ); \
- \
- if (!esock_decode_string(env, ename, &ifn)) \
- return enif_make_badarg(env); \
- \
- nlen = esock_strnlen(ifn, IFNAMSIZ); \
- \
- sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); \
- sys_memcpy(ifreq.ifr_name, ifn, \
- (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); \
- \
- SSDBG( descP, \
- ("SOCKET", \
- "esock_ioctl_" #OR " {%d} -> try ioctl\r\n", \
- descP->sock) ); \
- \
- if (ioctl(descP->sock, R, (char *) &ifreq) < 0) { \
- int saveErrno = sock_errno(); \
- ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); \
- \
- SSDBG( descP, \
- ("SOCKET", "esock_ioctl_" #OR " {%d} -> failure: " \
- "\r\n reason: %T (%d)" \
- "\r\n", descP->sock, reason, saveErrno) ); \
- \
- result = esock_make_error(env, reason); \
- \
- } else { \
- SSDBG( descP, \
- ("SOCKET", "esock_ioctl_" #OR " {%d} -> encode value\r\n", \
- descP->sock) ); \
- result = encode_ioctl_##EF(env, descP, UV); \
- } \
- \
- FREE(ifn); \
- \
- return result; \
- \
- }
-IOCTL_GET_FUNCS
-#undef IOCTL_GET_FUNCS
-
-
-/* ===========================================================================
- * The "rest" of the implemented (ioctl) get requests
- *
- * These (get) requests could not be 'generated' by the macros above.
- */
-
-static
-ERL_NIF_TERM esock_ioctl_gifconf(ErlNifEnv* env,
- ESockDescriptor* descP)
-{
- struct ifconf ifc;
- int ifc_len = 0;
- int buflen = 100 * sizeof(struct ifreq);
- char *buf = MALLOC(buflen);
- ERL_NIF_TERM result;
-
- SSDBG( descP, ("SOCKET", "esock_ioctl_gifconf {%d} -> entry\r\n", descP->sock) );
-
- for (;;) {
- ifc.ifc_len = buflen;
- ifc.ifc_buf = buf;
- if (ioctl(descP->sock, SIOCGIFCONF, (char *) &ifc) < 0) {
- int saveErrno = sock_errno();
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_gifconf {%d} -> failure: "
- "\r\n errno: %d (%s)"
- "\r\n", descP->sock, saveErrno, erl_errno_id(saveErrno)) );
-
- if (saveErrno != EINVAL || ifc_len) {
- ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
- FREE(buf);
- return esock_make_error(env, reason);
- }
- } else {
- if (ifc.ifc_len == ifc_len) break; /* buf large enough */
- ifc_len = ifc.ifc_len;
- }
- buflen += 10 * sizeof(struct ifreq);
- buf = (char *) REALLOC(buf, buflen);
- }
-
- result = encode_ioctl_ifconf(env, descP, &ifc);
-
- FREE(ifc.ifc_buf);
-
- return result;
-}
-
-
-#if defined(SIOCGIFNAME)
-static
-ERL_NIF_TERM esock_ioctl_gifname(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eidx)
-{
- ERL_NIF_TERM result;
- struct ifreq ifreq;
- int index;
-
- SSDBG( descP, ("SOCKET", "esock_ioctl_gifname {%d} -> entry with"
- "\r\n (e)Index: %T"
- "\r\n", descP->sock, eidx) );
-
- if (!GET_INT(env, eidx, &index))
- return enif_make_badarg(env);
-
- ifreq.ifr_ifindex = index;
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_gifname {%d} -> try ioctl\r\n", descP->sock) );
-
- if (ioctl(descP->sock, SIOCGIFNAME, (char *) &ifreq) < 0) {
- int saveErrno = sock_errno();
- ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_gifname {%d} -> failure: "
- "\r\n reason: %T (%d)"
- "\r\n", descP->sock, reason, saveErrno) );
-
- result = esock_make_error(env, reason);
-
- } else {
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_gifname {%d} -> encode name\r\n",
- descP->sock) );
-
- result = esock_make_ok2(env, encode_ioctl_ifreq_name(env, ifreq.ifr_name));
- }
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_gifname {%d} -> done with"
- "\r\n result: %T"
- "\r\n",
- descP->sock, result) );
-
- return result;
-
-}
-#endif
-
-
-
-
-/* ===========================================================================
- * The implemented (ioctl) set requests:
- *
- */
-
-/* *** esock_ioctl_sifaddr *** */
-#if defined(SIOCSIFADDR)
-#define IOCTL_SIFADDR_FUNC_DECL \
- IOCTL_SET_REQUEST_DECL(sifaddr, SIOCSIFADDR, sockaddr, \
- ((ESockAddress*) &ifreq.ifr_addr))
-#else
-#define IOCTL_SIFADDR_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_sifdstaddr *** */
-#if defined(SIOCSIFDSTADDR)
-#define IOCTL_SIFDSTADDR_FUNC_DECL \
- IOCTL_SET_REQUEST_DECL(sifdstaddr, SIOCSIFDSTADDR, sockaddr, \
- ((ESockAddress*) &ifreq.ifr_dstaddr))
-#else
-#define IOCTL_SIFDSTADDR_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_sifbrdaddr *** */
-#if defined(SIOCSIFBRDADDR)
-#define IOCTL_SIFBRDADDR_FUNC_DECL \
- IOCTL_SET_REQUEST_DECL(sifbrdaddr, SIOCSIFBRDADDR, sockaddr, \
- ((ESockAddress*) &ifreq.ifr_broadaddr))
-#else
-#define IOCTL_SIFBRDADDR_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_sifnetmask *** */
-#if defined(SIOCSIFNETMASK)
-#ifdef __linux__
-#define IOCTL_SIFNETMASK_FUNC_DECL \
- IOCTL_SET_REQUEST_DECL(sifnetmask, SIOCSIFNETMASK, sockaddr, \
- ((ESockAddress*) &ifreq.ifr_netmask))
-#else
-#define IOCTL_SIFNETMASK_FUNC_DECL \
- IOCTL_SET_REQUEST_DECL(sifnetmask, SIOCSIFNETMASK, sockaddr, \
- ((ESockAddress*) &ifreq.ifr_addr))
-#endif
-#else
-#define IOCTL_SIFNETMASK_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_sifmtu ***
- * On some platforms, MTU is an unsigned int
- */
-#if defined(SIOCSIFMTU)
-#define IOCTL_SIFMTU_FUNC_DECL \
- IOCTL_SET_REQUEST_DECL(sifmtu, SIOCSIFMTU, mtu, (int*) &ifreq.ifr_mtu)
-#else
-#define IOCTL_SIFMTU_FUNC_DECL
-#endif
-
-/* *** esock_ioctl_siftxqlen *** */
-#if defined(SIOCSIFTXQLEN)
-#define IOCTL_SIFTXQLEN_FUNC_DECL \
- IOCTL_SET_REQUEST_DECL(siftxqlen, SIOCSIFTXQLEN, txqlen, &ifreq.ifr_qlen)
-#else
-#define IOCTL_SIFTXQLEN_FUNC_DECL
-#endif
-
-#define IOCTL_SET_FUNCS \
- IOCTL_SIFADDR_FUNC_DECL \
- IOCTL_SIFDSTADDR_FUNC_DECL \
- IOCTL_SIFBRDADDR_FUNC_DECL \
- IOCTL_SIFNETMASK_FUNC_DECL \
- IOCTL_SIFMTU_FUNC_DECL \
- IOCTL_SIFTXQLEN_FUNC_DECL
-
-#define IOCTL_SET_REQUEST_DECL(OR, R, DF, UVP) \
- static \
- ERL_NIF_TERM esock_ioctl_##OR(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ERL_NIF_TERM ename, \
- ERL_NIF_TERM evalue) \
- { \
- ERL_NIF_TERM result; \
- struct ifreq ifreq; \
- char* ifn = NULL; \
- int nlen; \
- \
- SSDBG( descP, ("SOCKET", "esock_ioctl_" #OR " {%d} -> entry with" \
- "\r\n (e)Name: %T" \
- "\r\n (e)Value: %T" \
- "\r\n", descP->sock, ename, evalue) ); \
- \
- if (!esock_decode_string(env, ename, &ifn)) { \
- \
- SSDBG( descP, \
- ("SOCKET", "esock_ioctl_" #OR " {%d} -> failed decode name" \
- "\r\n", descP->sock) ); \
- \
- return enif_make_badarg(env); \
- } \
- \
- if (! decode_ioctl_##DF(env, descP, evalue, UVP)) { \
- \
- SSDBG( descP, \
- ("SOCKET", "esock_ioctl_" #OR " {%d} -> failed decode addr" \
- "\r\n", descP->sock) ); \
- \
- return esock_make_invalid(env, atom_##DF); \
- } \
- \
- nlen = esock_strnlen(ifn, IFNAMSIZ); \
- \
- sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); \
- sys_memcpy(ifreq.ifr_name, ifn, \
- (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); \
- \
- SSDBG( descP, \
- ("SOCKET", "esock_ioctl_" #OR " {%d} -> try ioctl\r\n", \
- descP->sock) ); \
- \
- if (ioctl(descP->sock, R, (char *) &ifreq) < 0) { \
- int saveErrno = sock_errno(); \
- ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); \
- \
- SSDBG( descP, \
- ("SOCKET", "esock_ioctl_" #OR " {%d} -> failure: " \
- "\r\n reason: %T (%d)" \
- "\r\n", descP->sock, reason, saveErrno) ); \
- \
- result = esock_make_error(env, reason); \
- \
- } else { \
- SSDBG( descP, \
- ("SOCKET", "esock_ioctl_" #OR " {%d} -> " \
- "addr successfully set\r\n", \
- descP->sock) ); \
- result = esock_atom_ok; \
- } \
- \
- FREE(ifn); \
- \
- return result; \
- \
- }
-
-IOCTL_SET_FUNCS
-#undef IOCTL_SET_FUNCS
-
-
-/* ===========================================================================
- * The "rest" of the implemented (ioctl) set requests
- *
- * These (set) requests could not be 'generated' by the macros above.
- */
-
-#if defined(SIOCSIFFLAGS)
-static
-ERL_NIF_TERM esock_ioctl_sifflags(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM ename,
- ERL_NIF_TERM eflags)
-{
- ERL_NIF_TERM result;
- struct ifreq ifreq;
- char* ifn = NULL;
- int nlen;
-
- SSDBG( descP, ("SOCKET", "esock_ioctl_sifflags {%d} -> entry with"
- "\r\n (e)Name: %T"
- "\r\n (e)Flags: %T"
- "\r\n", descP->sock, ename, eflags) );
-
- if (!esock_decode_string(env, ename, &ifn)) {
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_sifflags {%d} -> failed decode name"
- "\r\n", descP->sock) );
-
- return enif_make_badarg(env);
- }
-
- // Make sure the length of the string is valid!
- nlen = esock_strnlen(ifn, IFNAMSIZ);
-
- sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); // Just in case
- sys_memcpy(ifreq.ifr_name, ifn,
- (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen);
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_sifflags {%d} -> try (get) ioctl\r\n",
- descP->sock) );
-
- if (ioctl(descP->sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) {
- int saveErrno = sock_errno();
- ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_sifflags {%d} -> "
- "failure: failed reading *current* flags"
- "\r\n reason: %T (%d)"
- "\r\n", descP->sock, reason, saveErrno) );
-
- result = esock_make_error(env, reason);
-
- } else {
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_sifflags {%d} -> (local) update flags\r\n",
- descP->sock) );
-
- if (decode_ioctl_flags(env, descP, eflags, &ifreq.ifr_flags)) {
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_sifflags {%d} -> try (set) ioctl\r\n",
- descP->sock) );
-
- if (ioctl(descP->sock, SIOCSIFFLAGS, (char *) &ifreq) < 0) {
- int saveErrno = sock_errno();
- ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_sifflags {%d} -> failure: "
- "\r\n reason: %T (%d)"
- "\r\n", descP->sock, reason, saveErrno) );
-
- result = esock_make_error(env, reason);
-
- } else {
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_sifflags {%d} -> "
- "updated flags successfully set\r\n",
- descP->sock) );
- result = esock_atom_ok;
- }
-
- /* We know that if esock_decode_string is successful,
- * we have "some" form of string, and therefor memory
- * has been allocated (and need to be freed)... */
- FREE(ifn);
-
- } else {
- result = enif_make_badarg(env);
- }
- }
-
- SSDBG( descP,
- ("SOCKET", "esock_ioctl_sifflags {%d} -> done with result: "
- "\r\n %T"
- "\r\n",
- descP->sock, result) );
-
- return result;
-
-}
-#endif
-
-
-
-/* ===========================================================================
- * ioctl utility functions
- *
- */
-
-static
-ERL_NIF_TERM encode_ioctl_ifconf(ErlNifEnv* env,
- ESockDescriptor* descP,
- struct ifconf* ifcP)
-{
- ERL_NIF_TERM result;
- unsigned int len = ((ifcP == NULL) ? 0 :
- (ifcP->ifc_len / sizeof(struct ifreq)));
-
- SSDBG( descP,
- ("SOCKET", "encode_ioctl_ifconf -> entry (when len = %d)\r\n", len) );
-
- if (len > 0) {
- ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM));
- unsigned int i = 0;
- struct ifreq* p = ifcP->ifc_req;
-
- for (i = 0 ; i < len ; i++) {
- SSDBG( descP,
- ("SOCKET", "encode_ioctl_ifconf -> encode ifreq entry %d\r\n", i) );
- array[i] = encode_ioctl_ifconf_ifreq(env, descP, &p[i]);
- }
-
- SSDBG( descP,
- ("SOCKET", "encode_ioctl_ifconf -> all entries encoded\r\n", i) );
-
- result = esock_make_ok2(env, MKLA(env, array, len));
- FREE(array);
-
- } else {
-
- result = esock_make_ok2(env, MKEL(env));
-
- }
-
- return result;
-}
-
-
-#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
-static
-ERL_NIF_TERM encode_ioctl_ifrmap(ErlNifEnv* env,
- ESockDescriptor* descP,
- struct ifmap* mapP)
-{
- ERL_NIF_TERM mapKeys[] = {atom_mem_start,
- atom_mem_end,
- atom_base_addr,
- atom_irq,
- atom_dma,
- atom_port};
- ERL_NIF_TERM mapVals[] = {MKUL(env, mapP->mem_start),
- MKUL(env, mapP->mem_end),
- MKUI(env, mapP->base_addr),
- MKUI(env, mapP->irq),
- MKUI(env, mapP->dma),
- MKUI(env, mapP->port)};
- unsigned int numMapKeys = NUM(mapKeys);
- unsigned int numMapVals = NUM(mapVals);
- ERL_NIF_TERM emap;
-
- ESOCK_ASSERT( numMapVals == numMapKeys );
- ESOCK_ASSERT( MKMA(env, mapKeys, mapVals, numMapKeys, &emap) );
-
- SSDBG( descP, ("SOCKET", "encode_ioctl_ifrmap -> done with"
- "\r\n Map: %T"
- "\r\n", emap) );
-
- return esock_make_ok2(env, emap);;
-}
-#endif
-
-
-#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
-static
-ERL_NIF_TERM encode_ioctl_hwaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- struct sockaddr* addrP)
-{
- ERL_NIF_TERM eaddr;
- SOCKLEN_T sz = sizeof(struct sockaddr);
-
- esock_encode_hwsockaddr(env, addrP, sz, &eaddr);
-
- SSDBG( descP, ("SOCKET", "encode_ioctl_ifraddr -> done with"
- "\r\n Sock Addr: %T"
- "\r\n", eaddr) );
-
- return esock_make_ok2(env, eaddr);;
-}
-#endif
-
-
-static
-ERL_NIF_TERM encode_ioctl_ifraddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- struct sockaddr* addrP)
-{
- ERL_NIF_TERM eaddr;
-
- esock_encode_sockaddr(env, (ESockAddress*) addrP, -1, &eaddr);
-
- SSDBG( descP, ("SOCKET", "encode_ioctl_ifraddr -> done with"
- "\r\n Sock Addr: %T"
- "\r\n", eaddr) );
-
- return esock_make_ok2(env, eaddr);;
-}
-
-
-static
-ERL_NIF_TERM encode_ioctl_flags(ErlNifEnv* env,
- ESockDescriptor* descP,
- short flags)
-{
- int i, flag, num = NUM(ioctl_flags);
- ERL_NIF_TERM eflags, eflag;
- SocketTArray ta = TARRAY_CREATE(20); // Just to be on the safe side
-
- if (flags == 0) {
- eflags = MKEL(env);
- } else {
- for (i = 0; (i < num) && (flags != 0); i++) {
- flag = ioctl_flags[i].flag;
- if ((flag != 0) && ((flags & flag) == flag)) {
- eflag = *(ioctl_flags[i].name);
- flags &= ~flag;
-
- SSDBG( descP, ("SOCKET", "encode_ioctl_flags {%d} -> "
- "\r\n i: %d"
- "\r\n found flag: %T (%d)"
- "\r\n remaining flags: %d"
- "\r\n", descP->sock, i, eflag, flag, flags) );
-
- TARRAY_ADD(ta, eflag);
- }
- }
- if (flags != 0) {
-
- SSDBG( descP, ("SOCKET", "encode_ioctl_flags {%d} -> unknown flag(s): %d"
- "\r\n", descP->sock, flags) );
-
- TARRAY_ADD(ta, MKI(env, flags));
- }
-
- TARRAY_TOLIST(ta, env, &eflags);
- }
-
-
- SSDBG( descP, ("SOCKET", "encode_ioctl_flags -> done with"
- "\r\n Flags: %T (%d)"
- "\r\n", eflags, flags) );
-
- return esock_make_ok2(env, eflags);;
-}
-
-
-static
-BOOLEAN_T decode_ioctl_sockaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eaddr,
- ESockAddress* addr)
-{
- SOCKLEN_T addrLen;
- BOOLEAN_T result;
-
- result = esock_decode_sockaddr(env, eaddr, (ESockAddress*) addr, &addrLen);
-
- VOID(addrLen);
-
- SSDBG( descP,
- ("SOCKET", "esock_decode_ioctl_sockaddr {%d} -> decode result: %s"
- "\r\n", descP->sock, B2S(result)) );
-
- return result;
-}
-
-
-static
-BOOLEAN_T decode_ioctl_mtu(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM emtu,
- int* mtu)
-{
- BOOLEAN_T result;
-
- if (! GET_INT(env, emtu, mtu)) {
- result = FALSE;
- } else {
- result = TRUE;
- }
-
- SSDBG( descP,
- ("SOCKET", "esock_decode_ioctl_mtu {%d} -> decode result: %s"
- "\r\n", descP->sock, B2S(result)) );
-
- return result;
-}
-
-
-#if defined(SIOCSIFTXQLEN)
-static
-BOOLEAN_T decode_ioctl_txqlen(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM etxqlen,
- int* txqlen)
-{
- return decode_ioctl_ivalue(env, descP, etxqlen, txqlen);
-}
-#endif
-
-/* All uses of the function should be added. For instance:
- * #if defined(SIOCGIFTXQLEN) || defined(FOOBAR) || defined(YXA)
- */
-#if defined(SIOCGIFTXQLEN)
-static
-BOOLEAN_T decode_ioctl_ivalue(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eivalue,
- int* ivalue)
-{
- BOOLEAN_T result;
-
- if (! GET_INT(env, eivalue, ivalue)) {
- result = FALSE;
- } else {
- result = TRUE;
- }
-
- SSDBG( descP,
- ("SOCKET", "esock_decode_ioctl_ivalue {%d} -> decode result: %s"
- "\r\n", descP->sock, B2S(result)) );
-
- return result;
}
-#endif
-
-
-static
-BOOLEAN_T decode_ioctl_flags(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eflags,
- short* flags)
-{
- ERL_NIF_TERM key, value;
- ErlNifMapIterator iter;
- int tmpFlags = (int) *flags; // Current value
- int flag;
-
- SSDBG( descP,
- ("SOCKET", "decode_ioctl_flags {%d} -> entry with"
- "\r\n flags: %d"
- "\r\n",
- descP->sock, tmpFlags) );
-
- enif_map_iterator_create(env, eflags, &iter, ERL_NIF_MAP_ITERATOR_FIRST);
-
- while (enif_map_iterator_get_pair(env, &iter, &key, &value)) {
-
- /* Convert key (eflag) to int */
- if (! GET_INT(env, key, &flag)) {
- enif_map_iterator_destroy(env, &iter);
- return FALSE;
- }
-
- // Update flag
- if (COMPARE(value, esock_atom_true) == 0) {
- SSDBG( descP,
- ("SOCKET", "decode_ioctl_flags {%d} -> set %d\r\n",
- descP->sock, flag) );
- tmpFlags |= flag;
- } else {
- SSDBG( descP,
- ("SOCKET", "decode_ioctl_flags {%d} -> reset %d\r\n",
- descP->sock, flag) );
- tmpFlags &= ~flag;
- }
-
- enif_map_iterator_next(env, &iter);
- }
-
- enif_map_iterator_destroy(env, &iter);
-
- SSDBG( descP,
- ("SOCKET", "decode_ioctl_flags {%d} -> done with"
- "\r\n (new) flags: %d"
- "\r\n",
- descP->sock, tmpFlags) );
- *flags = (short) tmpFlags;
-
- return TRUE;
-}
-
-
-static
-ERL_NIF_TERM encode_ioctl_ivalue(ErlNifEnv* env,
- ESockDescriptor* descP,
- int ivalue)
-{
- ERL_NIF_TERM eivalue = MKI(env, ivalue);
-
- SSDBG( descP, ("SOCKET", "encode_ioctl_ivalue -> done with"
- "\r\n iValue: %T (%d)"
- "\r\n", eivalue, ivalue) );
-
- return esock_make_ok2(env, eivalue);;
-}
-
-static
-ERL_NIF_TERM encode_ioctl_ifconf_ifreq(ErlNifEnv* env,
- ESockDescriptor* descP,
- struct ifreq* ifrP)
-{
- ERL_NIF_TERM ename, eaddr;
-
- ESOCK_ASSERT( ifrP != NULL );
-
- SSDBG( descP, ("SOCKET", "encode_ioctl_ifconf_ifreq -> encode name\r\n") );
- ename = encode_ioctl_ifreq_name(env, ifrP->ifr_name);
-
- SSDBG( descP, ("SOCKET", "encode_ioctl_ifconf_ifreq -> encode sockaddr\r\n") );
- eaddr = encode_ioctl_ifreq_sockaddr(env, &ifrP->ifr_addr);
-
- SSDBG( descP, ("SOCKET", "encode_ioctl_ifconf_ifreq -> make ifreq map with"
- "\r\n Name: %T"
- "\r\n Sock Addr: %T"
- "\r\n", ename, eaddr) );
- return make_ifreq(env, ename, esock_atom_addr, eaddr);
-}
-
-static
-ERL_NIF_TERM encode_ioctl_ifreq_name(ErlNifEnv* env,
- char* name)
-{
- return ((name == NULL) ? esock_atom_undefined : MKS(env, name));
-}
-
-static
-ERL_NIF_TERM encode_ioctl_ifreq_sockaddr(ErlNifEnv* env, struct sockaddr* sa)
-{
- ERL_NIF_TERM esa;
-
- if (sa != NULL) {
-
- esock_encode_sockaddr(env, (ESockAddress*) sa, -1, &esa);
-
- } else {
-
- esa = esock_atom_undefined;
-
- }
-
- return esa;
-}
-
-
-/* The ifreq structure *always* contain a name
- * and *one* other element. The second element
- * depend on the ioctl request.
- */
-static
-ERL_NIF_TERM make_ifreq(ErlNifEnv* env,
- ERL_NIF_TERM name,
- ERL_NIF_TERM key2,
- ERL_NIF_TERM val2)
-{
- ERL_NIF_TERM keys[2];
- ERL_NIF_TERM vals[2];
- ERL_NIF_TERM res;
-
- keys[0] = esock_atom_name;
- vals[0] = name;
-
- keys[1] = key2;
- vals[1] = val2;
-
- ESOCK_ASSERT( MKMA(env, keys, vals, NUM(keys), &res) );
-
- return res;
-}
-
-#endif // #ifndef __WIN32__
@@ -11261,7 +9938,8 @@ ERL_NIF_TERM make_ifreq(ErlNifEnv* env,
*
* Arguments:
* Socket (ref) - Points to the socket descriptor.
- * Operation (atom) - What kind of operation (accept, send, ...) is to be cancelled
+ * Operation (atom) - What kind of operation (accept, send, ...)
+ * is to be cancelled
* Ref (ref) - Unique id for the operation
*/
static
@@ -11269,9 +9947,6 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
-#ifdef __WIN32__
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
ESockDescriptor* descP;
ERL_NIF_TERM op, sockRef, opRef;
@@ -11294,11 +9969,9 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env,
return esock_cancel(env, descP, op, sockRef, opRef);
-#endif // #ifdef __WIN32__ #else
}
-#ifndef __WIN32__
static
ERL_NIF_TERM esock_cancel(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -11306,7 +9979,8 @@ ERL_NIF_TERM esock_cancel(ErlNifEnv* env,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM opRef)
{
- int cmp;
+ ERL_NIF_TERM result;
+ int cmp;
/* <KOLLA>
*
@@ -11318,36 +9992,71 @@ ERL_NIF_TERM esock_cancel(ErlNifEnv* env,
*/
/* Hand crafted binary search */
- if ((cmp = COMPARE(op, esock_atom_recvmsg)) == 0)
- return esock_cancel_recv(env, descP, sockRef, opRef);
+ if ((cmp = COMPARE(op, esock_atom_recvmsg)) == 0) {
+ MLOCK(descP->readMtx);
+ result = ESOCK_IO_CANCEL_RECV(env, descP, sockRef, opRef);
+ MUNLOCK(descP->readMtx);
+ return result;
+ }
if (cmp < 0) {
- if ((cmp = COMPARE(op, esock_atom_recv)) == 0)
- return esock_cancel_recv(env, descP, sockRef, opRef);
+ if ((cmp = COMPARE(op, esock_atom_recv)) == 0) {
+ MLOCK(descP->readMtx);
+ result = ESOCK_IO_CANCEL_RECV(env, descP, sockRef, opRef);
+ MUNLOCK(descP->readMtx);
+ return result;
+ }
if (cmp < 0) {
- if (COMPARE(op, esock_atom_connect) == 0)
- return esock_cancel_connect(env, descP, opRef);
- if (COMPARE(op, esock_atom_accept) == 0)
- return esock_cancel_accept(env, descP, sockRef, opRef);
+ if (COMPARE(op, esock_atom_connect) == 0) {
+ MLOCK(descP->writeMtx);
+ result = ESOCK_IO_CANCEL_CONNECT(env, descP, opRef);
+ MUNLOCK(descP->writeMtx);
+ return result;
+ }
+ if (COMPARE(op, esock_atom_accept) == 0) {
+ MLOCK(descP->readMtx);
+ result = ESOCK_IO_CANCEL_ACCEPT(env, descP, sockRef, opRef);
+ MUNLOCK(descP->readMtx);
+ return result;
+ }
} else {
- if (COMPARE(op, esock_atom_recvfrom) == 0)
- return esock_cancel_recv(env, descP, sockRef, opRef);
+ if (COMPARE(op, esock_atom_recvfrom) == 0) {
+ MLOCK(descP->readMtx);
+ result = ESOCK_IO_CANCEL_RECV(env, descP, sockRef, opRef);
+ MUNLOCK(descP->readMtx);
+ return result;
+ }
}
} else {
- if ((cmp = COMPARE(op, esock_atom_sendmsg)) == 0)
- return esock_cancel_send(env, descP, sockRef, opRef);
+ if ((cmp = COMPARE(op, esock_atom_sendmsg)) == 0) {
+ MLOCK(descP->writeMtx);
+ result = ESOCK_IO_CANCEL_SEND(env, descP, sockRef, opRef);
+ MUNLOCK(descP->writeMtx);
+ return result;
+ }
if (cmp < 0) {
- if (COMPARE(op, esock_atom_send) == 0)
- return esock_cancel_send(env, descP, sockRef, opRef);
- if (COMPARE(op, esock_atom_sendfile) == 0)
- return esock_cancel_send(env, descP, sockRef, opRef);
+ if (COMPARE(op, esock_atom_send) == 0) {
+ MLOCK(descP->writeMtx);
+ result = ESOCK_IO_CANCEL_SEND(env, descP, sockRef, opRef);
+ MUNLOCK(descP->writeMtx);
+ return result;
+ }
+ if (COMPARE(op, esock_atom_sendfile) == 0) {
+ MLOCK(descP->writeMtx);
+ result = ESOCK_IO_CANCEL_SEND(env, descP, sockRef, opRef);
+ MUNLOCK(descP->writeMtx);
+ return result;
+ }
} else {
- if (COMPARE(op, esock_atom_sendto) == 0)
- return esock_cancel_send(env, descP, sockRef, opRef);
+ if (COMPARE(op, esock_atom_sendto) == 0) {
+ MLOCK(descP->writeMtx);
+ result = ESOCK_IO_CANCEL_SEND(env, descP, sockRef, opRef);
+ MUNLOCK(descP->writeMtx);
+ return result;
+ }
}
}
{
- ERL_NIF_TERM result;
const char *reason;
MLOCK(descP->readMtx);
@@ -11373,430 +10082,10 @@ ERL_NIF_TERM esock_cancel(ErlNifEnv* env,
return result;
}
}
-#endif // #ifndef __WIN32__
-
-
-
-/* *** esock_cancel_connect ***
- *
- *
- */
-#ifndef __WIN32__
-static
-ERL_NIF_TERM esock_cancel_connect(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef)
-{
- ERL_NIF_TERM res;
- ErlNifPid self;
-
- ESOCK_ASSERT( enif_self(env, &self) != NULL );
-
- MLOCK(descP->writeMtx);
-
- if (! IS_OPEN(descP->writeState)) {
-
- res = esock_make_error_closed(env);
-
- } else if ((descP->connectorP == NULL) ||
- (COMPARE_PIDS(&self, &descP->connector.pid) != 0) ||
- (COMPARE(opRef, descP->connector.ref) != 0)) {
-
- res = esock_atom_not_found;
-
- } else {
-
- res = esock_cancel_write_select(env, descP, opRef);
- esock_requestor_release("esock_cancel_connect",
- env, descP, &descP->connector);
- descP->connectorP = NULL;
- descP->writeState &= ~ESOCK_STATE_CONNECTING;
- }
-
- SSDBG( descP,
- ("SOCKET",
- "esock_cancel_connect {%d,0x%X} ->"
- "\r\n opRef: %T"
- "\r\n res: %T"
- "\r\n",
- descP->sock, descP->writeState,
- opRef, res) );
-
- MUNLOCK(descP->writeMtx);
-
- return res;
-}
-#endif // #ifndef __WIN32__
-
-
-/* *** esock_cancel_accept ***
- *
- * We have two different cases:
- * *) Its the current acceptor
- * Cancel the select!
- * We need to activate one of the waiting acceptors.
- * *) Its one of the acceptors ("waiting") in the queue
- * Simply remove the acceptor from the queue.
- *
- */
-#ifndef __WIN32__
-static
-ERL_NIF_TERM esock_cancel_accept(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef)
-{
- ERL_NIF_TERM res;
-
- MLOCK(descP->readMtx);
-
- SSDBG( descP,
- ("SOCKET",
- "esock_cancel_accept(%T), {%d,0x%X} ->"
- "\r\n opRef: %T"
- "\r\n %s"
- "\r\n",
- sockRef, descP->sock, descP->readState,
- opRef,
- ((descP->currentAcceptorP == NULL)
- ? "without acceptor" : "with acceptor")) );
-
- if (! IS_OPEN(descP->readState)) {
-
- res = esock_make_error_closed(env);
-
- } else if (descP->currentAcceptorP == NULL) {
-
- res = esock_atom_not_found;
-
- } else {
- ErlNifPid self;
-
- ESOCK_ASSERT( enif_self(env, &self) != NULL );
-
- if (COMPARE_PIDS(&self, &descP->currentAcceptor.pid) == 0) {
- if (COMPARE(opRef, descP->currentAcceptor.ref) == 0)
- res = esock_cancel_accept_current(env, descP, sockRef);
- else
- res = esock_atom_not_found;
- } else {
- res = esock_cancel_accept_waiting(env, descP, opRef, &self);
- }
- }
-
- SSDBG( descP,
- ("SOCKET", "esock_cancel_accept(%T) -> done with result:"
- "\r\n %T"
- "\r\n", sockRef, res) );
-
- MUNLOCK(descP->readMtx);
-
- return res;
-}
-#endif // #ifndef __WIN32__
-/* The current acceptor process has an ongoing select we first must
- * cancel. Then we must re-activate the "first" (the first
- * in the acceptor queue).
- */
#ifndef __WIN32__
-static
-ERL_NIF_TERM esock_cancel_accept_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef)
-{
- ERL_NIF_TERM res;
-
- ESOCK_ASSERT( DEMONP("esock_cancel_accept_current -> current acceptor",
- env, descP, &descP->currentAcceptor.mon) == 0);
- MON_INIT(&descP->currentAcceptor.mon);
- res = esock_cancel_read_select(env, descP, descP->currentAcceptor.ref);
-
- SSDBG( descP,
- ("SOCKET",
- "esock_cancel_accept_current(%T) {%d} -> cancel res: %T"
- "\r\n", sockRef, descP->sock, res) );
-
- if (!esock_activate_next_acceptor(env, descP, sockRef)) {
-
- SSDBG( descP,
- ("SOCKET",
- "esock_cancel_accept_current(%T) {%d} -> "
- "no more acceptors\r\n",
- sockRef, descP->sock) );
-
- descP->readState &= ~ESOCK_STATE_ACCEPTING;
-
- descP->currentAcceptorP = NULL;
- }
-
- return res;
-}
-#endif // #ifndef __WIN32__
-
-
-/* These processes have not performed a select, so we can simply
- * remove them from the acceptor queue.
- */
-#ifndef __WIN32__
-static
-ERL_NIF_TERM esock_cancel_accept_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef,
- const ErlNifPid* selfP)
-{
- /* unqueue request from (acceptor) queue */
-
- if (esock_acceptor_unqueue(env, descP, &opRef, selfP)) {
- return esock_atom_ok;
- } else {
- return esock_atom_not_found;
- }
-}
-#endif // #ifndef __WIN32__
-
-
-
-/* *** esock_cancel_send ***
- *
- * Cancel a send operation.
- * Its either the current writer or one of the waiting writers.
- */
-#ifndef __WIN32__
-static
-ERL_NIF_TERM esock_cancel_send(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef)
-{
- ERL_NIF_TERM res;
-
- MLOCK(descP->writeMtx);
-
- SSDBG( descP,
- ("SOCKET",
- "esock_cancel_send(%T), {%d,0x%X} -> entry with"
- "\r\n opRef: %T"
- "\r\n %s"
- "\r\n",
- sockRef, descP->sock, descP->writeState,
- opRef,
- ((descP->currentWriterP == NULL)
- ? "without writer" : "with writer")) );
-
- if (! IS_OPEN(descP->writeState)) {
-
- res = esock_make_error_closed(env);
-
- } else if (descP->currentWriterP == NULL) {
-
- res = esock_atom_not_found;
-
- } else {
- ErlNifPid self;
-
- ESOCK_ASSERT( enif_self(env, &self) != NULL );
-
- if (COMPARE_PIDS(&self, &descP->currentWriter.pid) == 0) {
- if (COMPARE(opRef, descP->currentWriter.ref) == 0)
- res = esock_cancel_send_current(env, descP, sockRef);
- else
- res = esock_atom_not_found;
- } else {
- res = esock_cancel_send_waiting(env, descP, opRef, &self);
- }
- }
-
- SSDBG( descP,
- ("SOCKET", "esock_cancel_send(%T) {%d} -> done with result:"
- "\r\n %T"
- "\r\n", sockRef, descP->sock, res) );
-
- MUNLOCK(descP->writeMtx);
-
- return res;
-}
-#endif // #ifndef __WIN32__
-
-
-
-/* The current writer process has an ongoing select we first must
- * cancel. Then we must re-activate the "first" (the first
- * in the writer queue).
- */
-#ifndef __WIN32__
-static
-ERL_NIF_TERM esock_cancel_send_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef)
-{
- ERL_NIF_TERM res;
-
- ESOCK_ASSERT( DEMONP("esock_cancel_send_current -> current writer",
- env, descP, &descP->currentWriter.mon) == 0);
- res = esock_cancel_write_select(env, descP, descP->currentWriter.ref);
-
- SSDBG( descP,
- ("SOCKET", "esock_cancel_send_current(%T) {%d} -> cancel res: %T"
- "\r\n", sockRef, descP->sock, res) );
-
- if (!esock_activate_next_writer(env, descP, sockRef)) {
- SSDBG( descP,
- ("SOCKET",
- "esock_cancel_send_current(%T) {%d} -> no more writers"
- "\r\n", sockRef, descP->sock) );
-
- descP->currentWriterP = NULL;
- }
-
- return res;
-}
-#endif // #ifndef __WIN32__
-
-
-/* These processes have not performed a select, so we can simply
- * remove them from the writer queue.
- */
-#ifndef __WIN32__
-static
-ERL_NIF_TERM esock_cancel_send_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef,
- const ErlNifPid* selfP)
-{
- /* unqueue request from (writer) queue */
-
- if (esock_writer_unqueue(env, descP, &opRef, selfP)) {
- return esock_atom_ok;
- } else {
- return esock_atom_not_found;
- }
-}
-#endif // #ifndef __WIN32__
-
-
-
-/* *** esock_cancel_recv ***
- *
- * Cancel a read operation.
- * Its either the current reader or one of the waiting readers.
- */
-#ifndef __WIN32__
-static
-ERL_NIF_TERM esock_cancel_recv(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef)
-{
- ERL_NIF_TERM res;
-
- MLOCK(descP->readMtx);
-
- SSDBG( descP,
- ("SOCKET",
- "esock_cancel_recv(%T), {%d,0x%X} -> entry with"
- "\r\n opRef: %T"
- "\r\n %s"
- "\r\n",
- sockRef, descP->sock, descP->readState,
- opRef,
- ((descP->currentReaderP == NULL)
- ? "without reader" : "with reader")) );
-
- if (! IS_OPEN(descP->readState)) {
-
- res = esock_make_error_closed(env);
-
- } else if (descP->currentReaderP == NULL) {
-
- res = esock_atom_not_found;
-
- } else {
- ErlNifPid self;
-
- ESOCK_ASSERT( enif_self(env, &self) != NULL );
-
- if (COMPARE_PIDS(&self, &descP->currentReader.pid) == 0) {
- if (COMPARE(opRef, descP->currentReader.ref) == 0)
- res = esock_cancel_recv_current(env, descP, sockRef);
- else
- res = esock_atom_not_found;
- } else {
- res = esock_cancel_recv_waiting(env, descP, opRef, &self);
- }
- }
-
- SSDBG( descP,
- ("SOCKET", "esock_cancel_recv(%T) {%d} -> done with result:"
- "\r\n %T"
- "\r\n", sockRef, descP->sock, res) );
-
- MUNLOCK(descP->readMtx);
-
- return res;
-}
-#endif // #ifndef __WIN32__
-
-
-/* The current reader process has an ongoing select we first must
- * cancel. Then we must re-activate the "first" (the first
- * in the reader queue).
- */
-#ifndef __WIN32__
-static
-ERL_NIF_TERM esock_cancel_recv_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef)
-{
- ERL_NIF_TERM res;
-
- ESOCK_ASSERT( DEMONP("esock_cancel_recv_current -> current reader",
- env, descP, &descP->currentReader.mon) == 0);
- res = esock_cancel_read_select(env, descP, descP->currentReader.ref);
-
- SSDBG( descP,
- ("SOCKET", "esock_cancel_recv_current(%T) {%d} -> cancel res: %T"
- "\r\n", sockRef, descP->sock, res) );
-
- if (!esock_activate_next_reader(env, descP, sockRef)) {
- SSDBG( descP,
- ("SOCKET",
- "esock_cancel_recv_current(%T) {%d} -> no more readers"
- "\r\n", sockRef, descP->sock) );
-
- descP->currentReaderP = NULL;
- }
-
- return res;
-}
-#endif // #ifndef __WIN32__
-
-
-/* These processes have not performed a select, so we can simply
- * remove them from the reader queue.
- */
-#ifndef __WIN32__
-static
-ERL_NIF_TERM esock_cancel_recv_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef,
- const ErlNifPid* selfP)
-{
- /* unqueue request from (reader) queue */
-
- if (esock_reader_unqueue(env, descP, &opRef, selfP)) {
- return esock_atom_ok;
- } else {
- return esock_atom_not_found;
- }
-}
-#endif // #ifndef __WIN32__
-
-
-
-#ifndef __WIN32__
-static
+extern
ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM opRef)
@@ -11809,10 +10098,10 @@ ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv* env,
#ifndef __WIN32__
-static
+extern
ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef)
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef)
{
return esock_cancel_mode_select(env, descP, opRef,
ERL_NIF_SELECT_WRITE,
@@ -11822,7 +10111,7 @@ ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv* env,
#ifndef __WIN32__
-static
+extern
ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM opRef,
@@ -11840,7 +10129,7 @@ ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env,
return esock_atom_ok;
} else {
/* Has already sent the message */
- return esock_atom_select_sent;
+ return esock_make_error(env, esock_atom_select_sent);
}
} else {
/* Stopped? */
@@ -11849,7 +10138,7 @@ ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env,
"esock_cancel_mode_select {%d} -> failed: %d (0x%lX)"
"\r\n", descP->sock, selectRes, selectRes) );
- return esock_atom_not_found;
+ return esock_make_error(env, esock_atom_not_found);
}
}
#endif // #ifndef __WIN32__
@@ -11861,7 +10150,6 @@ ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env,
* ----------------------------------------------------------------------
*/
-#ifndef __WIN32__
extern
BOOLEAN_T esock_encode_cmsg(ErlNifEnv* env,
int level,
@@ -11899,11 +10187,10 @@ BOOLEAN_T esock_encode_cmsg(ErlNifEnv* env,
* just return the type number as an erlang integer
*/
-
*eType = MKI(env, type);
+
return FALSE;
}
-#endif
/* +++ esock_encode_msg_flags +++
@@ -11912,7 +10199,6 @@ BOOLEAN_T esock_encode_cmsg(ErlNifEnv* env,
*
*/
-#ifndef __WIN32__
extern
void esock_encode_msg_flags(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -11930,11 +10216,11 @@ void esock_encode_msg_flags(ErlNifEnv* env,
size_t n;
SocketTArray ta = TARRAY_CREATE(10); // Just to be on the safe side
- for (n = 0; n < NUM(msg_flags); n++) {
- int f = msg_flags[n].flag;
+ for (n = 0; n < esock_msg_flags_length; n++) {
+ int f = esock_msg_flags[n].flag;
if ((f != 0) && ((msgFlags & f) == f)) {
msgFlags &= ~f;
- TARRAY_ADD(ta, *(msg_flags[n].name));
+ TARRAY_ADD(ta, *(esock_msg_flags[n].name));
}
if (msgFlags == 0) goto done;
}
@@ -11951,10 +10237,8 @@ void esock_encode_msg_flags(ErlNifEnv* env,
TARRAY_TOLIST(ta, env, flags);
}
}
-#endif
-#ifndef __WIN32__
#ifdef SCM_TIMESTAMP
static
BOOLEAN_T esock_cmsg_encode_timeval(ErlNifEnv *env,
@@ -11988,10 +10272,8 @@ static BOOLEAN_T esock_cmsg_decode_timeval(ErlNifEnv *env,
return TRUE;
}
#endif
-#endif
-#ifndef __WIN32__
#if defined(IP_TOS) || defined(IP_RECVTOS)
static
BOOLEAN_T esock_cmsg_encode_ip_tos(ErlNifEnv *env,
@@ -12028,9 +10310,8 @@ static BOOLEAN_T esock_cmsg_decode_ip_tos(ErlNifEnv *env,
return TRUE;
}
#endif // #ifdef IP_TOS
-#endif // #ifdef __WIN32__
-#ifndef __WIN32__
+
#if defined(IP_TTL) || \
defined(IPV6_HOPLIMIT) || \
defined(IPV6_TCLASS) || defined(IPV6_RECVTCLASS)
@@ -12061,18 +10342,16 @@ BOOLEAN_T esock_cmsg_decode_int(ErlNifEnv* env,
if (! GET_INT(env, eValue, &value))
return FALSE;
- if ((valueP = esock_init_cmsghdr(cmsgP, rem,
- sizeof(*valueP), usedP)) == NULL)
+ valueP = esock_init_cmsghdr(cmsgP, rem, sizeof(*valueP), usedP);
+ if (valueP == NULL)
return FALSE;
*valueP = value;
return TRUE;
}
#endif
-#endif
-#ifndef __WIN32__
extern
BOOLEAN_T esock_cmsg_decode_bool(ErlNifEnv* env,
ERL_NIF_TERM eValue,
@@ -12093,10 +10372,8 @@ BOOLEAN_T esock_cmsg_decode_bool(ErlNifEnv* env,
*valueP = v? 1 : 0;
return TRUE;
}
-#endif
-#ifndef __WIN32__
#ifdef IP_RECVTTL
static
BOOLEAN_T esock_cmsg_encode_uchar(ErlNifEnv *env,
@@ -12113,15 +10390,15 @@ BOOLEAN_T esock_cmsg_encode_uchar(ErlNifEnv *env,
return TRUE;
}
#endif
-#endif
-#ifndef __WIN32__
+
#ifdef IP_PKTINFO
static
BOOLEAN_T esock_cmsg_encode_in_pktinfo(ErlNifEnv *env,
unsigned char *data,
size_t dataLen,
- ERL_NIF_TERM *eResult) {
+ ERL_NIF_TERM *eResult)
+{
struct in_pktinfo* pktInfoP = (struct in_pktinfo*) data;
ERL_NIF_TERM ifIndex;
ERL_NIF_TERM specDst, addr;
@@ -12130,14 +10407,23 @@ BOOLEAN_T esock_cmsg_encode_in_pktinfo(ErlNifEnv *env,
return FALSE;
ifIndex = MKUI(env, pktInfoP->ipi_ifindex);
+#ifndef __WIN32__
+ /* On Windows, the field ipi_spec_dst does not exist */
esock_encode_in_addr(env, &pktInfoP->ipi_spec_dst, &specDst);
+#endif
esock_encode_in_addr(env, &pktInfoP->ipi_addr, &addr);
{
ERL_NIF_TERM keys[] = {esock_atom_ifindex,
esock_atom_spec_dst,
esock_atom_addr};
- ERL_NIF_TERM vals[] = {ifIndex, specDst, addr};
+ ERL_NIF_TERM vals[] = {ifIndex,
+#ifndef __WIN32__
+ specDst,
+#else
+ esock_atom_undefined,
+#endif
+ addr};
unsigned int numKeys = NUM(keys);
unsigned int numVals = NUM(vals);
@@ -12147,7 +10433,7 @@ BOOLEAN_T esock_cmsg_encode_in_pktinfo(ErlNifEnv *env,
return TRUE;
}
#endif
-#endif
+
#ifndef __WIN32__
#ifdef IP_ORIGDSTADDR
@@ -12170,6 +10456,7 @@ BOOLEAN_T esock_cmsg_encode_sockaddr(ErlNifEnv *env,
#endif
#endif
+
#ifndef __WIN32__
#ifdef HAVE_LINUX_ERRQUEUE_H
#if defined(IP_RECVERR) || defined(IPV6_RECVERR)
@@ -12433,7 +10720,6 @@ BOOLEAN_T esock_cmsg_encode_recverr(ErlNifEnv *env,
#endif // #ifdef HAVE_LINUX_ERRQUEUE_H
#endif // #ifndef __WIN32__
-#ifndef __WIN32__
#ifdef IPV6_PKTINFO
static
BOOLEAN_T esock_cmsg_encode_in6_pktinfo(ErlNifEnv *env,
@@ -12459,12 +10745,9 @@ BOOLEAN_T esock_cmsg_encode_in6_pktinfo(ErlNifEnv *env,
return TRUE;
}
#endif
-#endif
-#ifndef __WIN32__
-
static int cmpESockCmsgSpec(const void *vpa, const void *vpb) {
ESockCmsgSpec *a, *b;
a = (ESockCmsgSpec *) vpa;
@@ -12472,8 +10755,14 @@ static int cmpESockCmsgSpec(const void *vpa, const void *vpb) {
return COMPARE(*(a->nameP), *(b->nameP));
}
-static ESockCmsgSpec
- cmsgLevelSocket[] =
+
+#if defined(SCM_CREDENTIALS) || defined(SCM_RIGHTS) || defined(SCM_TIMESTAMP)
+#define HAVE_ESOCK_CMSG_SOCKET
+#endif
+
+
+#if defined(HAVE_ESOCK_CMSG_SOCKET)
+static ESockCmsgSpec cmsgLevelSocket[] =
{
#if defined(SCM_CREDENTIALS)
{SCM_CREDENTIALS, NULL, NULL,
@@ -12493,9 +10782,10 @@ static ESockCmsgSpec
&esock_cmsg_encode_timeval, esock_cmsg_decode_timeval,
&esock_atom_timestamp},
#endif
- },
+ };
+#endif
- cmsgLevelIP[] =
+static ESockCmsgSpec cmsgLevelIP[] =
{
#if defined(IP_TOS)
{IP_TOS, esock_cmsg_encode_ip_tos, esock_cmsg_decode_ip_tos,
@@ -12575,11 +10865,16 @@ static ESockCmsgSpec cmsgLevelIPv6[] =
};
#endif // #ifdef HAVE_IPV6
-static void initCmsgTables(void) {
+static void initCmsgTables(void)
+{
+#if defined(HAVE_ESOCK_CMSG_SOCKET)
ESOCK_SORT_TABLE(cmsgLevelSocket, cmpESockCmsgSpec);
- ESOCK_SORT_TABLE(cmsgLevelIP, cmpESockCmsgSpec);
+#endif
+
+ ESOCK_SORT_TABLE(cmsgLevelIP, cmpESockCmsgSpec);
+
#ifdef HAVE_IPV6
- ESOCK_SORT_TABLE(cmsgLevelIPv6, cmpESockCmsgSpec);
+ ESOCK_SORT_TABLE(cmsgLevelIPv6, cmpESockCmsgSpec);
#endif
}
@@ -12588,24 +10883,34 @@ ESockCmsgSpec* esock_lookup_cmsg_table(int level, size_t *num)
{
switch (level) {
+#if defined(HAVE_ESOCK_CMSG_SOCKET)
case SOL_SOCKET:
*num = NUM(cmsgLevelSocket);
return cmsgLevelSocket;
+#endif
+#ifndef __WIN32__
#ifdef SOL_IP
case SOL_IP:
#else
case IPPROTO_IP:
#endif
+#else
+ case IPPROTO_IP:
+#endif
*num = NUM(cmsgLevelIP);
return cmsgLevelIP;
#ifdef HAVE_IPV6
+#ifndef __WIN32__
#ifdef SOL_IPV6
case SOL_IPV6:
#else
case IPPROTO_IPV6:
#endif
+#else
+ case IPPROTO_IPV6:
+#endif
*num = NUM(cmsgLevelIPv6);
return cmsgLevelIPv6;
#endif
@@ -12627,32 +10932,32 @@ ESockCmsgSpec* esock_lookup_cmsg_spec(ESockCmsgSpec* table,
return bsearch(&key, table, num, sizeof(*table), cmpESockCmsgSpec);
}
-#endif // #ifdef __WIN32__
/* Clear the CMSG space and init the ->cmsg_len member,
* return the position for the data, and the total used space
*/
-#ifndef __WIN32__
extern
void* esock_init_cmsghdr(struct cmsghdr* cmsgP,
size_t rem, // Remaining space
size_t size, // Size of data
size_t* usedP)
{
- size_t space = CMSG_SPACE(size);
+ size_t space = ESOCK_CMSG_SPACE(size);
+ void* dataP;
if (rem < space)
return NULL; // Not enough space
sys_memzero(cmsgP, space);
- cmsgP->cmsg_len = CMSG_LEN(size);
+ cmsgP->cmsg_len = ESOCK_CMSG_LEN(size);
*usedP = space;
- return CMSG_DATA(cmsgP);
+ dataP = ESOCK_CMSG_DATA(cmsgP);
+
+ return dataP;
}
-#endif
/* +++ decode the ip socket option TOS +++
@@ -12664,8 +10969,10 @@ void* esock_init_cmsghdr(struct cmsghdr* cmsgP,
*
* lowdelay | throughput | reliability | mincost
*
+ *
+ * For Windows, the Microsoft recommendation is: *Do not use*
*/
-#ifndef __WIN32__
+
#if defined(IP_TOS)
static
BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
@@ -12674,6 +10981,14 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
if (IS_ATOM(env, eVal)) {
+#ifdef __WIN32__
+
+ /* See above */
+ *val = -1;
+ result = FALSE;
+
+#else
+
if (COMPARE(eVal, esock_atom_lowdelay) == 0) {
*val = IPTOS_LOWDELAY;
result = TRUE;
@@ -12688,11 +11003,14 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
*val = IPTOS_MINCOST;
result = TRUE;
#endif
+
} else {
*val = -1;
result = FALSE;
}
+#endif // ifdef __WIN32__
+
} else if (IS_NUM(env, eVal)) {
if (GET_INT(env, eVal, val)) {
@@ -12710,7 +11028,7 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
return result;
}
#endif
-#endif // #ifndef __WIN32__
+
/* +++ decode the ip socket option MTU_DISCOVER +++
@@ -12722,20 +11040,23 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
*
* want | dont | do | probe
*
+ * Note that on Windows, the 'want' value seems to not exist!
*/
-#ifndef __WIN32__
+
#if defined(IP_MTU_DISCOVER)
static
BOOLEAN_T decode_ip_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
{
if (IS_ATOM(env, eVal)) {
- if (COMPARE(eVal, atom_want) == 0) {
- *val = IP_PMTUDISC_WANT;
- } else if (COMPARE(eVal, atom_dont) == 0) {
+ if (COMPARE(eVal, atom_dont) == 0) {
*val = IP_PMTUDISC_DONT;
} else if (COMPARE(eVal, atom_do) == 0) {
*val = IP_PMTUDISC_DO;
+#if defined(IP_PMTUDISC_WANT)
+ } else if (COMPARE(eVal, atom_want) == 0) {
+ *val = IP_PMTUDISC_WANT;
+#endif
#if defined(IP_PMTUDISC_PROBE)
} else if (COMPARE(eVal, atom_probe) == 0) {
*val = IP_PMTUDISC_PROBE;
@@ -12751,10 +11072,8 @@ BOOLEAN_T decode_ip_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
return TRUE;
}
#endif
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
#if defined(IPV6_MULTICAST_HOPS) || defined(IPV6_UNICAST_HOPS)
static
BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val) {
@@ -12774,7 +11093,6 @@ BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val) {
return TRUE;
}
#endif
-#endif // #ifndef __WIN32__
/* +++ decode the ipv6 socket option MTU_DISCOVER +++
@@ -12786,20 +11104,33 @@ BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val) {
*
* want | dont | do | probe
*
+ * Use same as IP on Windows!!
*/
-#ifndef __WIN32__
+
#if defined(IPV6_MTU_DISCOVER)
static
BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
{
if (IS_ATOM(env, eVal)) {
- if (COMPARE(eVal, atom_want) == 0) {
- *val = IPV6_PMTUDISC_WANT;
- } else if (COMPARE(eVal, atom_dont) == 0) {
+#ifdef __WIN32__
+ /* On Windows, the IP-flags are used */
+ if (COMPARE(eVal, atom_dont) == 0) {
+ *val = IP_PMTUDISC_DONT;
+ } else if (COMPARE(eVal, atom_do) == 0) {
+ *val = IP_PMTUDISC_DO;
+ } else if (COMPARE(eVal, atom_probe) == 0) {
+ *val = IP_PMTUDISC_PROBE;
+ } else {
+ return FALSE;
+ }
+#else
+ if (COMPARE(eVal, atom_dont) == 0) {
*val = IPV6_PMTUDISC_DONT;
} else if (COMPARE(eVal, atom_do) == 0) {
*val = IPV6_PMTUDISC_DO;
+ } else if (COMPARE(eVal, atom_want) == 0) {
+ *val = IPV6_PMTUDISC_WANT;
#if defined(IPV6_PMTUDISC_PROBE)
} else if (COMPARE(eVal, atom_probe) == 0) {
*val = IPV6_PMTUDISC_PROBE;
@@ -12807,6 +11138,7 @@ BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
} else {
return FALSE;
}
+#endif
} else if (! GET_INT(env, eVal, val)) {
return FALSE;
@@ -12815,7 +11147,6 @@ BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
return TRUE;
}
#endif
-#endif // #ifndef __WIN32__
/* +++ encode the ip socket option MTU_DISCOVER +++
@@ -12828,15 +11159,17 @@ BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
* want | dont | do | probe
*
*/
-#ifndef __WIN32__
+
#if defined(IP_MTU_DISCOVER)
static
void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal)
{
switch (val) {
+#if defined(IP_PMTUDISC_WANT)
case IP_PMTUDISC_WANT:
*eVal = atom_want;
break;
+#endif
case IP_PMTUDISC_DONT:
*eVal = atom_dont;
@@ -12860,7 +11193,6 @@ void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal)
return;
}
#endif
-#endif // #ifndef __WIN32__
/* +++ encode the ipv6 socket option MTU_DISCOVER +++
@@ -12872,30 +11204,47 @@ void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal)
*
* want | dont | do | probe
*
+ * Windows uses the IP-flags.
*/
-#ifndef __WIN32__
+
#if defined(IPV6_MTU_DISCOVER)
static
void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal)
{
switch (val) {
+#if defined(IPV6_PMTUDISC_WANT)
case IPV6_PMTUDISC_WANT:
*eVal = atom_want;
break;
+#endif
+#if defined(__WIN32__)
+ case IP_PMTUDISC_DONT:
+#else
case IPV6_PMTUDISC_DONT:
+#endif
*eVal = atom_dont;
break;
+#if defined(__WIN32__)
+ case IP_PMTUDISC_DO:
+#else
case IPV6_PMTUDISC_DO:
+#endif
*eVal = atom_do;
break;
+#if defined(__WIN32__)
+ case IP_PMTUDISC_PROBE:
+ *eVal = atom_probe;
+ break;
+#else
#if defined(IPV6_PMTUDISC_PROBE)
case IPV6_PMTUDISC_PROBE:
*eVal = atom_probe;
break;
#endif
+#endif
default:
*eVal = MKI(env, val);
@@ -12905,7 +11254,7 @@ void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal)
return;
}
#endif
-#endif // #ifndef __WIN32__
+
/* +++ encode the ip socket option tos +++
@@ -12914,24 +11263,30 @@ void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal)
* lowdelay | throughput | reliability | mincost | integer()
*
*/
-#ifndef __WIN32__
+
static
ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val)
{
ERL_NIF_TERM result;
switch (IPTOS_TOS(val)) {
+#if defined(IPTOS_LOWDELAY)
case IPTOS_LOWDELAY:
result = esock_atom_lowdelay;
break;
+#endif
+#if defined(IPTOS_THROUGHPUT)
case IPTOS_THROUGHPUT:
result = esock_atom_throughput;
break;
+#endif
+#if defined(IPTOS_RELIABILITY)
case IPTOS_RELIABILITY:
result = esock_atom_reliability;
break;
+#endif
#if defined(IPTOS_MINCOST)
case IPTOS_MINCOST:
@@ -12946,10 +11301,9 @@ ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val)
return result;
}
-#endif // #ifndef __WIN32__
-#ifndef __WIN32__
+
#if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO)
static
@@ -12993,7 +11347,7 @@ ERL_NIF_TERM encode_sctp_assoc_t(ErlNifEnv* env, sctp_assoc_t val)
}
#endif // #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO)
-#endif // #ifdef __WIN32__
+
/* *** esock_alloc_descriptor ***
@@ -13001,12 +11355,11 @@ ERL_NIF_TERM encode_sctp_assoc_t(ErlNifEnv* env, sctp_assoc_t val)
* Allocate and perform basic initialization of a socket descriptor.
*
*/
-#ifndef __WIN32__
extern
-ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event)
+ESockDescriptor* esock_alloc_descriptor(SOCKET sock)
{
ESockDescriptor* descP;
- char buf[64]; /* Buffer used for building the mutex name */
+ char buf[64]; /* Buffer used for building the mutex name(s) */
ESOCK_ASSERT( (descP =
enif_alloc_resource(esocks, sizeof(ESockDescriptor)))
@@ -13017,11 +11370,14 @@ ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event)
esock_requestor_init(&descP->connector);
descP->connectorP = NULL;
- sprintf(buf, "esock.w[%d]", sock);
+ sprintf(buf, "esock.w[" SOCKET_FORMAT_STR "]", sock);
descP->writeMtx = MCREATE(buf);
descP->writeState = 0;
+#ifndef __WIN32__
+ /* Not used on Windows - see header for more info */
esock_requestor_init(&descP->currentWriter);
descP->currentWriterP = NULL; // currentWriter not used
+#endif
descP->writersQ.first = NULL;
descP->writersQ.last = NULL;
@@ -13038,11 +11394,14 @@ ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event)
descP->sendfileCountersP = NULL;
#endif
- sprintf(buf, "esock.r[%d]", sock);
+ sprintf(buf, "esock.r[" SOCKET_FORMAT_STR "]", sock);
descP->readMtx = MCREATE(buf);
descP->readState = 0;
+#ifndef __WIN32__
+ /* Not used on Windows - see header for more info */
esock_requestor_init(&descP->currentReader);
descP->currentReaderP = NULL; // currentReader not used
+#endif
descP->readersQ.first = NULL;
descP->readersQ.last = NULL;
@@ -13054,9 +11413,12 @@ ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event)
descP->readWaits = 0;
descP->readFails = 0;
- sprintf(buf, "esock.acc[%d]", sock);
+ sprintf(buf, "esock.acc[" SOCKET_FORMAT_STR "]", sock);
+#ifndef __WIN32__
+ /* Not used on Windows - see header for more info */
esock_requestor_init(&descP->currentAcceptor);
descP->currentAcceptorP = NULL; // currentAcceptor not used
+#endif
descP->acceptorsQ.first = NULL;
descP->acceptorsQ.last = NULL;
descP->accSuccess = 0;
@@ -13064,36 +11426,72 @@ ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event)
descP->accTries = 0;
descP->accWaits = 0;
- sprintf(buf, "esock.close[%d]", sock);
+ sprintf(buf, "esock.close[" SOCKET_FORMAT_STR "]", sock);
descP->closeEnv = NULL;
descP->closeRef = esock_atom_undefined;
enif_set_pid_undefined(&descP->closerPid);
MON_INIT(&descP->closerMon);
- sprintf(buf, "esock.cfg[%d]", sock);
+ sprintf(buf, "esock.cfg[" SOCKET_FORMAT_STR "]", sock);
descP->rBufSz = ESOCK_RECV_BUFFER_SIZE_DEFAULT;
+#ifndef __WIN32__
descP->rNum = ESOCK_RECV_BUFFER_COUNT_DEFAULT;
descP->rNumCnt = 0;
+#endif
descP->rCtrlSz = ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT;
descP->wCtrlSz = ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT;
descP->iow = FALSE;
- descP->dbg = ESOCK_DEBUG_DEFAULT; // Overwritten by caller
- descP->useReg = ESOCK_USE_SOCKET_REGISTRY; // Overwritten by caller
+ descP->dbg = ESOCK_DEBUG_DEFAULT; // Overwritten by caller
+ descP->useReg = ESOCK_USE_SOCKET_REGISTRY;// Overwritten by caller
descP->meta.env = esock_alloc_env("esock_alloc_descriptor - "
"meta-env");
descP->meta.ref = esock_atom_undefined;
descP->sock = sock;
- descP->event = event;
descP->origFD = INVALID_SOCKET;
descP->closeOnClose = TRUE;
enif_set_pid_undefined(&descP->ctrlPid);
MON_INIT(&descP->ctrlMon);
+#if defined(ESOCK_DESCRIPTOR_FILLER)
+ sys_memzero(descP->filler, sizeof(descP->filler));
+#endif
+
return descP;
}
-#endif // #ifndef __WIN32__
+
+
+/* This function is *only* called during 'open' after an
+ * descriptor has been allocated but before it has been used.
+ */
+extern
+void esock_dealloc_descriptor(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ if (descP->writeMtx != NULL) {
+ MDESTROY(descP->writeMtx);
+ descP->writeMtx = NULL;
+ }
+
+ if (descP->readMtx != NULL) {
+ MDESTROY(descP->readMtx);
+ descP->readMtx = NULL;
+ }
+
+ if (descP->closeEnv != NULL) {
+ esock_free_env("dealloc descriptor", descP->closeEnv);
+ descP->closeEnv = NULL;
+ }
+
+ if (descP->meta.env != NULL) {
+ esock_free_env("dealloc descriptor", descP->meta.env);
+ descP->meta.env = NULL;
+ }
+
+}
+
+
/* Decrement counters for when a socket is closed
@@ -13196,7 +11594,6 @@ void esock_inc_socket(int domain, int type, int protocol)
/* ehow2how - convert internal (erlang) "shutdown how" to
* (proper) "shutdown how"
*/
-#ifndef __WIN32__
static
BOOLEAN_T ehow2how(ERL_NIF_TERM ehow, int* how)
{
@@ -13204,22 +11601,33 @@ BOOLEAN_T ehow2how(ERL_NIF_TERM ehow, int* how)
cmp = COMPARE(ehow, atom_read_write);
if (cmp == 0)
+#ifdef __WIN32__
+ *how = SD_BOTH;
+#else
*how = SHUT_RDWR;
+#endif
else if (cmp < 0) {
if (COMPARE(ehow, atom_read) == 0)
+#ifdef __WIN32__
+ *how = SD_RECEIVE;
+#else
*how = SHUT_RD;
+#endif
else
return FALSE;
} else {
if (COMPARE(ehow, atom_write) == 0)
+#ifdef __WIN32__
+ *how = SD_SEND;
+#else
*how = SHUT_WR;
+#endif
else
return FALSE;
}
return TRUE;
}
-#endif // #ifndef __WIN32__
@@ -13254,7 +11662,6 @@ size_t my_strnlen(const char *s, size_t maxlen)
* terminate otherwise, so there is no need to test if
* the sending fails.
*/
-#ifndef __WIN32__
extern
void esock_send_reg_add_msg(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -13271,7 +11678,6 @@ void esock_send_reg_add_msg(ErlNifEnv* env,
sockRef, descP->sock, MKPID(env, &data.regPid)) );
}
}
-#endif // #ifndef __WIN32__
@@ -13280,7 +11686,6 @@ void esock_send_reg_add_msg(ErlNifEnv* env,
* terminate otherwise, so there is no need to test if
* the sending fails.
*/
-#ifndef __WIN32__
extern
void esock_send_reg_del_msg(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -13297,7 +11702,6 @@ void esock_send_reg_del_msg(ErlNifEnv* env,
sockRef, descP->sock, MKPID(env, &data.regPid)) );
}
}
-#endif // #ifndef __WIN32__
/* ===========================================================================
@@ -13314,7 +11718,6 @@ void esock_send_reg_del_msg(ErlNifEnv* env,
*
* This message will only be sent if the iow (Inform On Wrap) is TRUE.
*/
-#ifndef __WIN32__
extern
void esock_send_wrap_msg(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -13333,7 +11736,6 @@ void esock_send_wrap_msg(ErlNifEnv* env,
sockRef, descP->sock, MKPID(env, &descP->ctrlPid), cnt) );
}
}
-#endif // #ifndef __WIN32__
/* Send an close message to the specified process:
@@ -13345,7 +11747,6 @@ void esock_send_wrap_msg(ErlNifEnv* env,
* erlang API (close-) function for the socket to be "closed"
* (actually that the 'stop' callback function has been called).
*/
-#ifndef __WIN32__
extern
void esock_send_close_msg(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -13367,10 +11768,12 @@ void esock_send_close_msg(ErlNifEnv* env,
}
}
+
+
#ifdef HAVE_SENDFILE
-static void
-esock_send_sendfile_deferred_close_msg(ErlNifEnv* env,
- ESockDescriptor* descP)
+extern
+void esock_send_sendfile_deferred_close_msg(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM sockRef, msg;
ErlNifPid *pid;
@@ -13388,7 +11791,6 @@ esock_send_sendfile_deferred_close_msg(ErlNifEnv* env,
ESOCK_ASSERT( esock_send_msg(env, pid, msg, NULL) );
}
#endif // #ifdef HAVE_SENDFILE
-#endif // #ifndef __WIN32__
/* Send an abort message to the specified process:
@@ -13399,7 +11801,6 @@ esock_send_sendfile_deferred_close_msg(ErlNifEnv* env,
* This message is for processes that is waiting in the
* erlang API functions for a select message.
*/
-#ifndef __WIN32__
extern
void esock_send_abort_msg(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -13413,10 +11814,11 @@ void esock_send_abort_msg(ErlNifEnv* env,
{
ERL_NIF_TERM msg;
- msg =
- mk_abort_msg(reqP->env,
- /* sockRef not in env so copy */
- CP_TERM(reqP->env, sockRef), reqP->ref, reason);
+ msg = mk_abort_msg(reqP->env,
+ /* sockRef not in env so copy */
+ CP_TERM(reqP->env, sockRef),
+ reqP->ref,
+ CP_TERM(reqP->env, reason));
if (! esock_send_msg(env, &reqP->pid, msg, reqP->env)) {
SSDBG( descP,
@@ -13428,13 +11830,38 @@ void esock_send_abort_msg(ErlNifEnv* env,
}
reqP->env = NULL;
}
-#endif // #ifndef __WIN32__
+
+
+/* Send an *simple* abort message to the specified process:
+ * A message in the form:
+ *
+ * {'$socket', Socket, abort, Info}
+ *
+ */
+extern
+void esock_send_simple_abort_msg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifPid* pid,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM reason)
+{
+ ERL_NIF_TERM msg = mk_simple_abort_msg(env, sockRef, reason);
+
+ if (! esock_send_msg(env, pid, msg, NULL)) {
+
+ SSDBG( descP,
+ ("SOCKET",
+ "esock_send_simple_abort_msg(%T) {%d} failed ->"
+ "\r\n pid: %T"
+ "\r\n",
+ sockRef, descP->sock, MKPID(env, pid)) );
+ }
+}
/* Send a message to the specified process.
*/
-#ifndef __WIN32__
-static
+extern
BOOLEAN_T esock_send_msg(ErlNifEnv* env,
ErlNifPid* pid,
ERL_NIF_TERM msg,
@@ -13445,7 +11872,6 @@ BOOLEAN_T esock_send_msg(ErlNifEnv* env,
return !!res;
}
-#endif // #ifndef __WIN32__
@@ -13456,14 +11882,12 @@ BOOLEAN_T esock_send_msg(ErlNifEnv* env,
* {'$socket', add, Socket}
*
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env,
ERL_NIF_TERM sockRef)
{
return mk_reg_msg(env, atom_add, sockRef);
}
-#endif // #ifndef __WIN32__
/* *** mk_reg_del_msg ***
@@ -13473,14 +11897,12 @@ ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env,
* {'$socket', del, Socket}
*
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env,
ERL_NIF_TERM sockRef)
{
return mk_reg_msg(env, atom_del, sockRef);
}
-#endif // #ifndef __WIN32__
/* *** mk_reg_msg ***
@@ -13491,17 +11913,31 @@ ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env,
* {'$socket', Tag, Socket}
*
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env,
ERL_NIF_TERM tag,
ERL_NIF_TERM sockRef)
{
- ERL_NIF_TERM socket = mk_socket(env, sockRef);
+ ERL_NIF_TERM socket = esock_mk_socket(env, sockRef);
return MKT3(env, esock_atom_socket_tag, tag, socket);
}
-#endif // #ifndef __WIN32__
+
+
+/* *** mk_simple_abort_msg ***
+ *
+ * Create the simple abort message, which has the following form:
+ *
+ * {'$socket', Socket, abort, Info}
+ *
+ */
+static
+ERL_NIF_TERM mk_simple_abort_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM reason)
+{
+ return esock_mk_socket_msg(env, sockRef, esock_atom_abort, reason);
+}
/* *** mk_abort_msg ***
@@ -13513,7 +11949,6 @@ ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env,
* This message is for processes that are waiting in the
* erlang API functions for a select (or this) message.
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env,
ERL_NIF_TERM sockRef,
@@ -13522,9 +11957,8 @@ ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env,
{
ERL_NIF_TERM info = MKT2(env, opRef, reason);
- return mk_socket_msg(env, sockRef, esock_atom_abort, info);
+ return esock_mk_socket_msg(env, sockRef, esock_atom_abort, info);
}
-#endif // #ifndef __WIN32__
/* *** mk_wrap_msg ***
@@ -13534,15 +11968,13 @@ ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env,
* {'$socket', Socket, counter_wrap, Counter}
*
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM mk_wrap_msg(ErlNifEnv* env,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM cnt)
{
- return mk_socket_msg(env, sockRef, atom_counter_wrap, cnt);
+ return esock_mk_socket_msg(env, sockRef, atom_counter_wrap, cnt);
}
-#endif // #ifndef __WIN32__
/* *** mk_close_msg ***
@@ -13552,15 +11984,14 @@ ERL_NIF_TERM mk_wrap_msg(ErlNifEnv* env,
* {'$socket', Socket, close, closeRef}
*
*/
-#ifndef __WIN32__
static
ERL_NIF_TERM mk_close_msg(ErlNifEnv* env,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM closeRef)
{
- return mk_socket_msg(env, sockRef, esock_atom_close, closeRef);
+ return esock_mk_socket_msg(env, sockRef, esock_atom_close, closeRef);
}
-#endif // #ifndef __WIN32__
+
/* *** mk_select_msg ***
@@ -13576,12 +12007,12 @@ ERL_NIF_TERM mk_select_msg(ErlNifEnv* env,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM selectRef)
{
- return mk_socket_msg(env, sockRef, esock_atom_select, selectRef);
+ return esock_mk_socket_msg(env, sockRef, esock_atom_select, selectRef);
}
#endif // #ifndef __WIN32__
-/* *** mk_socket_msg ***
+/* *** esock_mk_socket_msg ***
*
* Construct the socket message:
*
@@ -13592,18 +12023,16 @@ ERL_NIF_TERM mk_select_msg(ErlNifEnv* env,
* Info :: term()
*
*/
-#ifndef __WIN32__
-static
-ERL_NIF_TERM mk_socket_msg(ErlNifEnv* env,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM tag,
- ERL_NIF_TERM info)
+extern
+ERL_NIF_TERM esock_mk_socket_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM tag,
+ ERL_NIF_TERM info)
{
- ERL_NIF_TERM socket = mk_socket(env, sockRef);
+ ERL_NIF_TERM socket = esock_mk_socket(env, sockRef);
return MKT4(env, esock_atom_socket_tag, socket, tag, info);
}
-#endif // #ifndef __WIN32__
/* *** mk_socket ***
@@ -13612,14 +12041,12 @@ ERL_NIF_TERM mk_socket_msg(ErlNifEnv* env,
*
* socket:socket() :: {'$socket', SockRef :: reference()}
*/
-#ifndef __WIN32__
-static
-ERL_NIF_TERM mk_socket(ErlNifEnv* env,
- ERL_NIF_TERM sockRef)
+extern
+ERL_NIF_TERM esock_mk_socket(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef)
{
return MKT2(env, esock_atom_socket_tag, sockRef);
}
-#endif // #ifndef __WIN32__
/* ----------------------------------------------------------------------
@@ -13823,7 +12250,18 @@ ACTIVATE_NEXT_FUNCS
* we make use of set of declaration macros.
*/
-#ifndef __WIN32__
+
+extern
+void esock_free_request_queue(ESockRequestQueue* q)
+{
+ while (q->first) {
+ ESockRequestQueueElement* free_me = q->first;
+ q->first = free_me->nextP;
+ esock_free_env("dtor", free_me->data.env);
+ FREE(free_me);
+ }
+}
+
/* *** esock_acceptor_search4pid ***
* *** esock_writer_search4pid ***
@@ -13849,7 +12287,6 @@ ACTIVATE_NEXT_FUNCS
REQ_SEARCH4PID_FUNCS
#undef REQ_SEARCH4PID_FUNC_DECL
-#endif // #ifndef __WIN32__
@@ -13862,8 +12299,6 @@ REQ_SEARCH4PID_FUNCS
*
*/
-#ifndef __WIN32__
-
#define REQ_PUSH_FUNCS \
REQ_PUSH_FUNC_DECL(acceptor, acceptorsQ) \
REQ_PUSH_FUNC_DECL(writer, writersQ) \
@@ -13872,9 +12307,10 @@ REQ_SEARCH4PID_FUNCS
#define REQ_PUSH_FUNC_DECL(F, Q) \
extern \
void esock_##F##_push(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ErlNifPid pid, /* self() */ \
- ERL_NIF_TERM ref) \
+ ESockDescriptor* descP, \
+ ErlNifPid pid, /* self() */ \
+ ERL_NIF_TERM ref, \
+ void* dataP) \
{ \
ESockRequestQueueElement *e; \
ESockRequestor *reqP; \
@@ -13882,20 +12318,18 @@ REQ_SEARCH4PID_FUNCS
ESOCK_ASSERT( (e = MALLOC(sizeof(ESockRequestQueueElement))) \
!= NULL ); \
reqP = &e->data; \
- reqP->pid = pid; \
+ reqP->dataP = dataP; \
+ reqP->pid = pid; \
ESOCK_ASSERT( MONP("esock_" #F "_push -> " #F " request", \
env, descP, &pid, &reqP->mon) == 0 ); \
- reqP->env = esock_alloc_env("esock_" #F "_push"); \
- reqP->ref = CP_TERM(reqP->env, ref); \
+ reqP->env = esock_alloc_env("esock_" #F "_push"); \
+ reqP->ref = CP_TERM(reqP->env, ref); \
\
qpush(&descP->Q, e); \
}
REQ_PUSH_FUNCS
#undef REQ_PUSH_FUNC_DECL
-#endif // #ifndef __WIN32__
-
-
/* *** esock_acceptor_pop ***
* *** esock_writer_pop ***
@@ -13927,6 +12361,50 @@ REQ_POP_FUNCS
+/* *** esock_acceptor_get ***
+ * *** esock_writer_get ***
+ * *** esock_reader_get ***
+ *
+ * Remove a requestor (acceptor, writer, or reader) from its queue.
+ *
+ */
+
+#ifdef __WIN32__
+
+#define REQ_GET_FUNCS \
+ REQ_GET_FUNC_DECL(acceptor, acceptorsQ) \
+ REQ_GET_FUNC_DECL(writer, writersQ) \
+ REQ_GET_FUNC_DECL(reader, readersQ)
+
+#define REQ_GET_FUNC_DECL(F, Q) \
+ extern \
+ BOOLEAN_T esock_##F##_get(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM* refP, \
+ const ErlNifPid* pidP, \
+ ESockRequestor* reqP) \
+ { \
+ ESockRequestQueueElement* elemP; \
+ \
+ elemP = qget(env, descP, "esock_" #F "_get ", \
+ &descP->Q, refP, pidP); \
+ if (elemP != NULL) { \
+ reqP->pid = elemP->data.pid; \
+ reqP->mon = elemP->data.mon; \
+ reqP->env = elemP->data.env; \
+ reqP->ref = elemP->data.ref; \
+ reqP->dataP = elemP->data.dataP; \
+ return TRUE; \
+ } \
+ return FALSE; \
+ }
+REQ_GET_FUNCS
+#undef REQ_GET_FUNC_DECL
+
+#endif // #ifndef __WIN32__
+
+
+
/* *** esock_acceptor_unqueue ***
* *** esock_writer_unqueue ***
* *** esock_reader_unqueue ***
@@ -13935,8 +12413,6 @@ REQ_POP_FUNCS
*
*/
-#ifndef __WIN32__
-
#define REQ_UNQUEUE_FUNCS \
REQ_UNQUEUE_FUNC_DECL(acceptor, acceptorsQ) \
REQ_UNQUEUE_FUNC_DECL(writer, writersQ) \
@@ -13955,17 +12431,12 @@ REQ_POP_FUNCS
REQ_UNQUEUE_FUNCS
#undef REQ_UNQUEUE_FUNC_DECL
-#endif // #ifndef __WIN32__
-
-
/* *** requestor pop ***
*
* Pop an requestor from its queue.
*/
-#ifndef __WIN32__
-
extern
BOOLEAN_T esock_requestor_pop(ESockRequestQueue* q,
ESockRequestor* reqP)
@@ -13975,10 +12446,11 @@ BOOLEAN_T esock_requestor_pop(ESockRequestQueue* q,
esock_free_env("requestor_pop", reqP->env);
if (e != NULL) {
- reqP->pid = e->data.pid;
- reqP->mon = e->data.mon;
- reqP->env = e->data.env;
- reqP->ref = e->data.ref;
+ reqP->pid = e->data.pid;
+ reqP->mon = e->data.mon;
+ reqP->env = e->data.env;
+ reqP->ref = e->data.ref;
+ reqP->dataP = e->data.dataP;
FREE(e);
return TRUE;
} else {
@@ -13994,8 +12466,9 @@ void esock_requestor_init(ESockRequestor* reqP)
{
enif_set_pid_undefined(&reqP->pid);
MON_INIT(&reqP->mon);
- reqP->env = NULL;
- reqP->ref = esock_atom_undefined;
+ reqP->env = NULL;
+ reqP->ref = esock_atom_undefined;
+ reqP->dataP = NULL;
}
extern
@@ -14004,17 +12477,16 @@ void esock_requestor_release(const char* slogan,
ESockDescriptor* descP,
ESockRequestor* reqP)
{
+ reqP->dataP = NULL;
enif_set_pid_undefined(&reqP->pid);
(void) DEMONP(slogan, env, descP, &reqP->mon);
+ esock_clear_env(slogan, reqP->env);
esock_free_env(slogan, reqP->env);
reqP->env = NULL;
reqP->ref = esock_atom_undefined;
}
-#endif // #ifndef __WIN32__
-
-#ifndef __WIN32__
static
BOOLEAN_T qsearch4pid(ErlNifEnv* env,
@@ -14034,6 +12506,22 @@ BOOLEAN_T qsearch4pid(ErlNifEnv* env,
}
static
+unsigned int qlength(ESockRequestQueue* q)
+{
+ ESockRequestQueueElement* tmp;
+ unsigned int cnt = 0;
+
+ tmp = q->first;
+ while (tmp != NULL) {
+ cnt++;
+ tmp = tmp->nextP;
+ }
+
+ return cnt;
+}
+
+
+static
void qpush(ESockRequestQueue* q,
ESockRequestQueueElement* e)
{
@@ -14048,6 +12536,7 @@ void qpush(ESockRequestQueue* q,
}
}
+
static
ESockRequestQueueElement* qpop(ESockRequestQueue* q)
{
@@ -14066,11 +12555,8 @@ ESockRequestQueueElement* qpop(ESockRequestQueue* q)
return e;
}
-#endif // #ifndef __WIN32__
-
-#ifndef __WIN32__
static
BOOLEAN_T qunqueue(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -14079,19 +12565,40 @@ BOOLEAN_T qunqueue(ErlNifEnv* env,
ERL_NIF_TERM* refP,
const ErlNifPid* pidP)
{
+ ESockRequestQueueElement* e = qget(env, descP, slogan, q, refP, pidP);
+
+ if (e != NULL) {
+ (void) DEMONP(slogan, env, descP, &e->data.mon);
+ esock_clear_env(slogan, e->data.env);
+ esock_free_env(slogan, e->data.env);
+ FREE(e);
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+static
+ESockRequestQueueElement* qget(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const char* slogan,
+ ESockRequestQueue* q,
+ ERL_NIF_TERM* refP,
+ const ErlNifPid* pidP)
+{
ESockRequestQueueElement* e = q->first;
ESockRequestQueueElement* p = NULL;
- /* Check if it was one of the waiting acceptor processes */
+ /* Check if it was one of the waiting requestor processes */
while (e != NULL) {
if (COMPARE_PIDS(&e->data.pid, pidP) == 0) {
if ((refP != NULL) && (COMPARE(e->data.ref, *refP) != 0))
- return FALSE;
+ return NULL;
/* We have a match */
- (void) DEMONP(slogan, env, descP, &e->data.mon);
-
if (p != NULL) {
/* Not the first, but could be the last */
if (q->last == e) {
@@ -14111,10 +12618,7 @@ BOOLEAN_T qunqueue(ErlNifEnv* env,
}
}
- esock_free_env("qunqueue", e->data.env);
- FREE(e);
-
- return TRUE;
+ return e;
}
/* Try next */
@@ -14122,9 +12626,8 @@ BOOLEAN_T qunqueue(ErlNifEnv* env,
e = e->nextP;
}
- return FALSE;
+ return NULL;
}
-#endif // #ifndef __WIN32__
@@ -14171,8 +12674,6 @@ void esock_cnt_dec(ESockCounter* cnt, ESockCounter dec)
* ----------------------------------------------------------------------
*/
-#ifndef __WIN32__
-
extern
int esock_monitor(const char* slogan,
ErlNifEnv* env,
@@ -14261,8 +12762,6 @@ BOOLEAN_T esock_monitor_eq(const ESockMonitor* monP,
return FALSE;
}
-#endif // #ifndef __WIN32__
-
/* ----------------------------------------------------------------------
@@ -14271,18 +12770,6 @@ BOOLEAN_T esock_monitor_eq(const ESockMonitor* monP,
*/
-#ifndef __WIN32__
-static void free_request_queue(ESockRequestQueue* q)
-{
- while (q->first) {
- ESockRequestQueueElement* free_me = q->first;
- q->first = free_me->nextP;
- esock_free_env("dtor", free_me->data.env);
- FREE(free_me);
- }
-}
-#endif // #ifndef __WIN32__
-
/* =========================================================================
* esock_dtor - Callback function for resource destructor
*
@@ -14290,79 +12777,30 @@ static void free_request_queue(ESockRequestQueue* q)
static
void esock_dtor(ErlNifEnv* env, void* obj)
{
-#ifndef __WIN32__
ESockDescriptor* descP = (ESockDescriptor*) obj;
MLOCK(descP->readMtx);
MLOCK(descP->writeMtx);
- SGDBG( ("SOCKET", "dtor {%d,0x%X}\r\n",
+ SGDBG( ("SOCKET", "esock_dtor {%d,0x%X}\r\n",
descP->sock, descP->readState | descP->writeState) );
- if (IS_SELECTED(descP)) {
- /* We have used the socket in the select machinery,
- * so we must have closed it properly to get here
- */
- ESOCK_ASSERT( IS_CLOSED(descP->readState) );
- ESOCK_ASSERT( IS_CLOSED(descP->writeState) );
- ESOCK_ASSERT( descP->sock == INVALID_SOCKET );
- } else {
- /* The socket is only opened, should be safe to close nonblocking */
- (void) sock_close(descP->sock);
- descP->sock = INVALID_SOCKET;
- }
-
- SGDBG( ("SOCKET", "dtor -> set state and pattern\r\n") );
- descP->readState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED);
- descP->writeState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED);
- descP->pattern = (ESOCK_DESC_PATTERN_DTOR | ESOCK_STATE_CLOSED);
-
- esock_free_env("dtor reader", descP->currentReader.env);
- descP->currentReader.env = NULL;
-
- esock_free_env("dtor writer", descP->currentWriter.env);
- descP->currentWriter.env = NULL;
-
- esock_free_env("dtor acceptor", descP->currentAcceptor.env);
- descP->currentAcceptor.env = NULL;
-
- SGDBG( ("SOCKET", "dtor -> try free readers request queue\r\n") );
- free_request_queue(&descP->readersQ);
-
- SGDBG( ("SOCKET", "dtor -> try free writers request queue\r\n") );
- free_request_queue(&descP->writersQ);
-
- SGDBG( ("SOCKET", "dtor -> try free acceptors request queue\r\n") );
- free_request_queue(&descP->acceptorsQ);
-
-#ifdef HAVE_SENDFILE
- ESOCK_ASSERT( descP->sendfileHandle == INVALID_HANDLE );
- if (descP->sendfileCountersP != NULL) {
- FREE(descP->sendfileCountersP);
- descP->sendfileCountersP = NULL;
- }
-#endif
-
- esock_free_env("dtor close env", descP->closeEnv);
- descP->closeEnv = NULL;
-
- esock_free_env("dtor meta env", descP->meta.env);
- descP->meta.env = NULL;
+ ESOCK_IO_DTOR(env, descP);
MUNLOCK(descP->writeMtx);
MUNLOCK(descP->readMtx);
- SGDBG( ("SOCKET", "dtor -> try destroy read mutex\r\n") );
+ SGDBG( ("SOCKET", "esock_dtor -> try destroy read mutex\r\n") );
MDESTROY(descP->readMtx); descP->readMtx = NULL;
- SGDBG( ("SOCKET", "dtor -> try destroy write mutex\r\n") );
+ SGDBG( ("SOCKET", "esock_dtor -> try destroy write mutex\r\n") );
MDESTROY(descP->writeMtx); descP->writeMtx = NULL;
- SGDBG( ("SOCKET", "dtor -> done\r\n") );
-#endif // #ifndef __WIN32__
+ SGDBG( ("SOCKET", "esock_dtor -> done\r\n") );
}
+
/* =========================================================================
* esock_stop - Callback function for resource stop
*
@@ -14381,7 +12819,6 @@ void esock_dtor(ErlNifEnv* env, void* obj)
static
void esock_stop(ErlNifEnv* env, void* obj, ErlNifEvent fd, int is_direct_call)
{
-#ifndef __WIN32__
ESockDescriptor* descP = (ESockDescriptor*) obj;
if (is_direct_call) {
@@ -14434,96 +12871,26 @@ void esock_stop(ErlNifEnv* env, void* obj, ErlNifEvent fd, int is_direct_call)
(unsigned long) descP->accWaits,
(unsigned long) descP->accFails) );
-#ifdef HAVE_SENDFILE
- if (descP->sendfileCountersP != NULL) {
- ESockSendfileCounters *cP = descP->sendfileCountersP;
-
- SSDBG( descP, ("SOCKET", "esock_stop {%d/%d} ->"
- "\r\nsendfileCounters:"
- "\r\n cnt: %lu"
- "\r\n byteCnt: %lu"
- "\r\n fails: %lu"
- "\r\n max: %lu"
- "\r\n pkg: %lu"
- "\r\n pkgMax %lu"
- "\r\n tries: %lu"
- "\r\n waits: %lu"
- "\r\n",
- descP->sock, fd,
- (unsigned long) cP->cnt,
- (unsigned long) cP->byteCnt,
- (unsigned long) cP->fails,
- (unsigned long) cP->max,
- (unsigned long) cP->pkg,
- (unsigned long) cP->pkgMax,
- (unsigned long) cP->tries,
- (unsigned long) cP->waits) );
- }
-#endif
-
- /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- *
- * Inform waiting Closer, or close socket
- *
- * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- */
-
- if (! enif_is_pid_undefined(&descP->closerPid)) {
- /* We have a waiting closer process after nif_close()
- * - send message to trigger nif_finalize_close()
- */
-
- SSDBG( descP,
- ("SOCKET",
- "esock_stop {%d/%d} -> send close msg to %T\r\n",
- descP->sock, fd, MKPID(env, &descP->closerPid)) );
-
- esock_send_close_msg(env, descP, &descP->closerPid);
- /* Message send frees closeEnv */
- descP->closeEnv = NULL;
- descP->closeRef = esock_atom_undefined;
- } else {
- int err;
-
- /* We do not have a closer process
- * - have to do an unclean (non blocking) close */
-
-#ifdef HAVE_SENDFILE
- if (descP->sendfileHandle != INVALID_HANDLE)
- esock_send_sendfile_deferred_close_msg(env, descP);
-#endif
-
- err = esock_close_socket(env, descP, FALSE);
+ ESOCK_IO_STOP(env, descP);
- if (err != 0)
- esock_warning_msg("Failed closing socket without "
- "closer process: "
- "\r\n Controlling Process: %T"
- "\r\n Descriptor: %d"
- "\r\n Errno: %d (%T)"
- "\r\n",
- descP->ctrlPid, descP->sock,
- err, MKA(env, erl_errno_id(err)));
- }
+ MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
SSDBG( descP,
("SOCKET",
"esock_stop {%d/%d} -> done\r\n",
descP->sock, fd) );
- MUNLOCK(descP->writeMtx);
- MUNLOCK(descP->readMtx);
-#endif // #ifndef __WIN32__
}
-/* *** esock_stop_handle_current ***
+/* *** esock_stop_handle_currentalloc_desc ***
*
* Handle current requestor (reader, writer or acceptor) during
* socket stop.
*/
-static
+extern
void esock_stop_handle_current(ErlNifEnv* env,
const char* role,
ESockDescriptor* descP,
@@ -14549,14 +12916,13 @@ void esock_stop_handle_current(ErlNifEnv* env,
* nif_abort message with the specified reason to each member,
* and empty the queue.
*/
-#ifndef __WIN32__
-static
-void inform_waiting_procs(ErlNifEnv* env,
- const char* role,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ESockRequestQueue* q,
- ERL_NIF_TERM reason)
+extern
+void esock_inform_waiting_procs(ErlNifEnv* env,
+ const char* role,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ESockRequestQueue* q,
+ ERL_NIF_TERM reason)
{
ESockRequestQueueElement* currentP = q->first;
ESockRequestQueueElement* nextP;
@@ -14596,7 +12962,6 @@ void inform_waiting_procs(ErlNifEnv* env,
q->first = NULL;
q->last = NULL;
}
-#endif // #ifndef __WIN32__
/* =========================================================================
@@ -14609,7 +12974,6 @@ void esock_down(ErlNifEnv* env,
const ErlNifPid* pidP,
const ErlNifMonitor* monP)
{
-#ifndef __WIN32__
ESockDescriptor* descP = (ESockDescriptor*) obj;
MLOCK(descP->readMtx);
@@ -14623,362 +12987,17 @@ void esock_down(ErlNifEnv* env,
B2S(IS_CLOSED(descP->readState)),
B2S(IS_CLOSING(descP->readState))) );
- if (COMPARE_PIDS(&descP->closerPid, pidP) == 0) {
-
- /* The closer process went down
- * - it will not call nif_finalize_close
- */
-
- enif_set_pid_undefined(&descP->closerPid);
-
- if (MON_EQ(&descP->closerMon, monP)) {
- MON_INIT(&descP->closerMon);
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down {%d} -> closer process exit\r\n",
- descP->sock) );
-
- } else {
- // The owner is the closer so we used its monitor
-
- ESOCK_ASSERT( MON_EQ(&descP->ctrlMon, monP) );
- MON_INIT(&descP->ctrlMon);
- enif_set_pid_undefined(&descP->ctrlPid);
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down {%d} -> closer controlling process exit\r\n",
- descP->sock) );
- }
-
- /* Since the closer went down there was one,
- * hence esock_close() must have run or scheduled esock_stop(),
- * or the socket has never been selected upon
- */
-
- if (descP->closeEnv == NULL) {
- int err;
-
- /* Since there is no closeEnv,
- * esock_close() did not schedule esock_stop()
- * and is about to call esock_finalize_close() but died,
- * or esock_stop() has run, sent close_msg to the closer
- * and cleared ->closeEnv but the closer died
- * - we have to do an unclean (non blocking) socket close here
- */
-
-#ifdef HAVE_SENDFILE
- if (descP->sendfileHandle != INVALID_HANDLE)
- esock_send_sendfile_deferred_close_msg(env, descP);
-#endif
-
- err = esock_close_socket(env, descP, FALSE);
- if (err != 0)
- esock_warning_msg("Failed closing socket for terminating "
- "closer process: "
- "\r\n Closer Process: %T"
- "\r\n Descriptor: %d"
- "\r\n Errno: %d (%T)"
- "\r\n",
- MKPID(env, pidP), descP->sock,
- err, MKA(env, erl_errno_id(err)));
- } else {
- /* Since there is a closeEnv esock_stop() has not run yet
- * - when it finds that there is no closer process
- * it will close the socket and ignore the close_msg
- */
- esock_free_env("esock_down - close-env", descP->closeEnv);
- descP->closeEnv = NULL;
- descP->closeRef = esock_atom_undefined;
- }
-
- } else if (MON_EQ(&descP->ctrlMon, monP)) {
- MON_INIT(&descP->ctrlMon);
- /* The owner went down */
- enif_set_pid_undefined(&descP->ctrlPid);
-
- if (IS_OPEN(descP->readState)) {
- SSDBG( descP,
- ("SOCKET",
- "esock_down {%d} -> controller process exit"
- "\r\n initiate close\r\n",
- descP->sock) );
-
- esock_down_ctrl(env, descP, pidP);
-
- descP->readState |= ESOCK_STATE_CLOSING;
- descP->writeState |= ESOCK_STATE_CLOSING;
- } else {
- SSDBG( descP,
- ("SOCKET",
- "esock_down {%d} -> controller process exit"
- "\r\n already closed or closing\r\n",
- descP->sock) );
- }
-
- } else if (descP->connectorP != NULL &&
- MON_EQ(&descP->connector.mon, monP)) {
- MON_INIT(&descP->connector.mon);
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down {%d} -> connector process exit\r\n",
- descP->sock) );
-
- /* connectorP is only set during connection.
- * Forget all about the ongoing connection.
- * We might end up connected, but the process that initiated
- * the connection has died and will never know
- */
-
- esock_requestor_release("esock_down->connector",
- env, descP, &descP->connector);
- descP->connectorP = NULL;
- descP->writeState &= ~ESOCK_STATE_CONNECTING;
-
- } else {
- ERL_NIF_TERM sockRef;
-
- /* check all operation queue(s): acceptor, writer and reader.
- *
- * Is it really any point in doing this if the socket is closed?
- *
- */
-
- sockRef = enif_make_resource(env, descP);
-
- if (IS_CLOSED(descP->readState)) {
- SSDBG( descP,
- ("SOCKET",
- "esock_down(%T) {%d} -> stray down: %T\r\n",
- sockRef, descP->sock, pidP) );
- } else {
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down(%T) {%d} -> other process term\r\n",
- sockRef, descP->sock) );
-
- if (descP->currentReaderP != NULL)
- esock_down_reader(env, descP, sockRef, pidP, monP);
- if (descP->currentAcceptorP != NULL)
- esock_down_acceptor(env, descP, sockRef, pidP, monP);
- if (descP->currentWriterP != NULL)
- esock_down_writer(env, descP, sockRef, pidP, monP);
- }
- }
+ ESOCK_IO_DOWN(env, descP, pidP, monP);
MUNLOCK(descP->writeMtx);
MUNLOCK(descP->readMtx);
SSDBG( descP, ("SOCKET", "esock_down -> done\r\n") );
-#endif // #ifndef __WIN32__
-}
-
-
-
-/* *** esock_down_ctrl ***
- *
- * Stop after a downed controller
- *
- */
-#ifndef __WIN32__
-static
-void esock_down_ctrl(ErlNifEnv* env,
- ESockDescriptor* descP,
- const ErlNifPid* pidP)
-{
- SSDBG( descP,
- ("SOCKET", "esock_down_ctrl {%d} ->"
- "\r\n Pid: %T"
- "\r\n", descP->sock, MKPID(env, pidP)) );
-
- if (esock_do_stop(env, descP)) {
- /* esock_stop() is scheduled
- * - it has to close the socket
- */
- SSDBG( descP,
- ("SOCKET", "esock_down_ctrl {%d} -> stop was scheduled\r\n",
- descP->sock) );
- } else {
- int err;
-
- /* Socket is not in the select machinery
- * so esock_stop() will not be called
- * - we have to do an unclean (non blocking) socket close here
- */
-
-#ifdef HAVE_SENDFILE
- if (descP->sendfileHandle != INVALID_HANDLE)
- esock_send_sendfile_deferred_close_msg(env, descP);
-#endif
-
- err = esock_close_socket(env, descP, FALSE);
- if (err != 0)
- esock_warning_msg("Failed closing socket for terminating "
- "owner process: "
- "\r\n Owner Process: %T"
- "\r\n Descriptor: %d"
- "\r\n Errno: %d (%T)"
- "\r\n",
- MKPID(env, pidP), descP->sock,
- err, MKA(env, erl_errno_id(err)));
- }
}
-#endif // #ifndef __WIN32__
-/* *** esock_down_acceptor ***
- *
- * Check and then handle a downed acceptor process.
- *
- */
-#ifndef __WIN32__
-static
-void esock_down_acceptor(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- const ErlNifPid* pidP,
- const ErlNifMonitor* monP)
-{
- if (MON_EQ(&descP->currentAcceptor.mon, monP)) {
- MON_INIT(&descP->currentAcceptor.mon);
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down_acceptor(%T) {%d} -> "
- "current acceptor - try activate next\r\n",
- sockRef, descP->sock) );
-
- if (!esock_activate_next_acceptor(env, descP, sockRef)) {
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down_acceptor(%T) {%d} -> no more writers\r\n",
- sockRef, descP->sock) );
-
- descP->readState &= ~ESOCK_STATE_ACCEPTING;
-
- descP->currentAcceptorP = NULL;
- }
-
- } else {
-
- /* Maybe unqueue one of the waiting acceptors */
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down_acceptor(%T) {%d} -> "
- "not current acceptor - maybe a waiting acceptor\r\n",
- sockRef, descP->sock) );
-
- esock_acceptor_unqueue(env, descP, NULL, pidP);
- }
-}
-#endif // #ifndef __WIN32__
-
-
-/* *** esock_down_writer ***
- *
- * Check and then handle a downed writer process.
- *
- */
-#ifndef __WIN32__
-static
-void esock_down_writer(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- const ErlNifPid* pidP,
- const ErlNifMonitor* monP)
-{
- if (MON_EQ(&descP->currentWriter.mon, monP)) {
- MON_INIT(&descP->currentWriter.mon);
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down_writer(%T) {%d} -> "
- "current writer - try activate next\r\n",
- sockRef, descP->sock) );
-
- if (!esock_activate_next_writer(env, descP, sockRef)) {
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down_writer(%T) {%d} -> no active writer\r\n",
- sockRef, descP->sock) );
-
- descP->currentWriterP = NULL;
- }
-
- } else {
-
- /* Maybe unqueue one of the waiting writer(s) */
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down_writer(%T) {%d} -> "
- "not current writer - maybe a waiting writer\r\n",
- sockRef, descP->sock) );
-
- esock_writer_unqueue(env, descP, NULL, pidP);
- }
-}
-#endif // #ifndef __WIN32__
-
-
-
-
-/* *** esock_down_reader ***
- *
- * Check and then handle a downed reader process.
- *
- */
-#ifndef __WIN32__
-static
-void esock_down_reader(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- const ErlNifPid* pidP,
- const ErlNifMonitor* monP)
-{
- if (MON_EQ(&descP->currentReader.mon, monP)) {
- MON_INIT(&descP->currentReader.mon);
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down_reader(%T) {%d} -> "
- "current reader - try activate next\r\n",
- sockRef, descP->sock) );
-
- if (! esock_activate_next_reader(env, descP, sockRef)) {
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down_reader(%T) {%d} -> no more readers\r\n",
- sockRef, descP->sock) );
-
- descP->currentReaderP = NULL;
- }
-
- } else {
-
- /* Maybe unqueue one of the waiting reader(s) */
-
- SSDBG( descP,
- ("SOCKET",
- "esock_down_reader(%T) {%d} -> "
- "not current reader - maybe a waiting reader\r\n",
- sockRef, descP->sock) );
-
- esock_reader_unqueue(env, descP, NULL, pidP);
- }
-}
-#endif // #ifndef __WIN32__
-
-
/*
* The idea with this function is that it should call esock_io_finish
* and release anything allocated by the I/O backend (just to be a
@@ -14990,11 +13009,14 @@ void esock_down_reader(ErlNifEnv* env,
static
void esock_on_halt(void* priv_data)
{
+ // We do not *currently* use this (priv_data), so ignore
#ifndef __WIN32__
VOID(priv_data);
#else
VOIDP(priv_data);
#endif
+
+ ESOCK_IO_FIN();
}
@@ -15052,7 +13074,6 @@ ErlNifFunc esock_funcs[] =
};
-#ifndef __WIN32__
static
char* extract_debug_filename(ErlNifEnv* env,
ERL_NIF_TERM map)
@@ -15074,7 +13095,6 @@ char* extract_debug_filename(ErlNifEnv* env,
filename[bin.size] = '\0';
return filename;
}
-#endif // #ifndef __WIN32__
@@ -15085,6 +13105,9 @@ char* extract_debug_filename(ErlNifEnv* env,
static
int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
+ ErlNifSysInfo sysInfo;
+ unsigned int ioNumThreads, ioNumThreadsDef;
+
/* +++ Local atoms and error reason atoms +++ */
#define LOCAL_ATOM_DECL(A) atom_##A = MKA(env, #A)
LOCAL_ATOMS;
@@ -15099,86 +13122,6 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
esock_atom_socket_tag = MKA(env, "$socket");
- /* This is (currently) intended for Windows use */
- {
- ErlNifSysInfo sysInfo;
- unsigned int ioNumThreadsDef; // Used as "default" value
-
- enif_system_info(&sysInfo, sizeof(ErlNifSysInfo));
-
- ioNumThreadsDef =
- (unsigned int) (sysInfo.scheduler_threads > 0) ?
- 2*sysInfo.scheduler_threads : 1; // ESOCK_IO_NUM_THREADS);
-
- data.ioNumThreads =
- esock_get_uint_from_map(env, load_info,
- atom_io_num_threads,
- ioNumThreadsDef);
- }
-
-
-#ifdef __WIN32__
-
- io_backend.init = NULL;
- io_backend.finish = NULL;
- io_backend.open_with_fd = NULL;
- io_backend.open_plain = NULL;
- io_backend.bind = NULL;
- io_backend.connect = NULL;
- io_backend.listen = NULL;
- io_backend.accept = NULL;
- io_backend.send = NULL;
- io_backend.sendto = NULL;
- io_backend.sendmsg = NULL;
- io_backend.sendfile_start = NULL;
- io_backend.sendfile_cont = NULL;
- io_backend.sendfile_dc = NULL;
- io_backend.recv = NULL;
- io_backend.recvfrom = NULL;
- io_backend.recvmsg = NULL;
- io_backend.close = NULL;
- io_backend.fin_close = NULL;
- io_backend.shutdown = NULL;
- io_backend.sockname = NULL;
- io_backend.peername = NULL;
-
-#else
-
- io_backend.init = essio_init;
- io_backend.finish = essio_finish;
- io_backend.open_with_fd = essio_open_with_fd;
- io_backend.open_plain = essio_open_plain;
- io_backend.bind = essio_bind;
- io_backend.connect = essio_connect;
- io_backend.listen = essio_listen;
- io_backend.accept = essio_accept;
- io_backend.send = essio_send;
- io_backend.sendto = essio_sendto;
- io_backend.sendmsg = essio_sendmsg;
- io_backend.sendfile_start = essio_sendfile_start;
- io_backend.sendfile_cont = essio_sendfile_cont;
- io_backend.sendfile_dc = essio_sendfile_deferred_close;
- io_backend.recv = essio_recv;
- io_backend.recvfrom = essio_recvfrom;
- io_backend.recvmsg = essio_recvmsg;
- io_backend.close = essio_close;
- io_backend.fin_close = essio_fin_close;
- io_backend.shutdown = essio_shutdown;
- io_backend.sockname = essio_sockname;
- io_backend.peername = essio_peername;
-
-#endif
-
-#ifndef __WIN32__
- // This ifndef is only temporary!!
- if (ESOCK_IO_INIT(data.ioNumThreads) != ESOCK_IO_OK) {
- esock_error_msg("Failed initiating I/O backend");
- return 1; // Failure
- }
-#endif
-
-#ifndef __WIN32__
-
if (! esock_extract_pid_from_map(env, load_info,
atom_registry,
&data.regPid)) {
@@ -15186,16 +13129,19 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
return 1; // Failure - no registry pid
}
+ /* --esock-disable-registry */
data.useReg =
esock_get_bool_from_map(env, load_info,
esock_atom_use_registry,
ESOCK_USE_SOCKET_REGISTRY);
+ /* --esock-enable-iow */
data.iow =
esock_get_bool_from_map(env, load_info,
atom_iow,
ESOCK_NIF_IOW_DEFAULT);
+ /* --esock-debug-file=<filename> */
{
char *debug_filename;
@@ -15234,9 +13180,37 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
data.numProtoUDP = 0;
data.numProtoSCTP = 0;
+
initOpts();
initCmsgTables();
+
+ // #define ESOCK_DISPLAY_OPTION_TABLES 1
+#if defined(ESOCK_DISPLAY_OPTION_TABLES)
+ {
+ /* Display option table(s) after init */
+ ESOCK_EPRINTF("\r\n[ESOCK] Option tables after init:\r\n");
+
+ for (int levelIdx = 0; levelIdx < NUM(optLevels); levelIdx++) {
+ int numOpts = optLevels[levelIdx].num;
+ int level = optLevels[levelIdx].level;
+ ERL_NIF_TERM lname = *optLevels[levelIdx].nameP;
+ struct ESockOpt* opts = optLevels[levelIdx].opts;
+
+ ESOCK_EPRINTF("[ESOCK] [%d] Option table for level %T (%d) (%d options):\r\n",
+ levelIdx, lname, level, numOpts);
+
+ for (int optIdx = 0; optIdx < numOpts; optIdx++) {
+ ESOCK_EPRINTF("[ESOCK] %T[%d]: %T -> %d\r\n",
+ lname, optIdx, lname, opts[optIdx].opt);
+
+ }
+ ESOCK_EPRINTF("\r\n");
+ }
+ }
+#endif
+
+
data.iov_max =
#if defined(NO_SYSCONF) || (! defined(_SC_IOV_MAX))
# ifdef IOV_MAX
@@ -15250,7 +13224,129 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
;
ESOCK_ASSERT( data.iov_max > 0 );
-#endif // #ifndef __WIN32__
+
+ /* This is (currently) intended for Windows use */
+ enif_system_info(&sysInfo, sizeof(ErlNifSysInfo));
+
+ /* We should have a config options for this:
+ * --esock-num-io-threads=16
+ *
+ * ESOCK_IO_NUM_THREADS
+ */
+ ioNumThreadsDef =
+ (unsigned int) (sysInfo.scheduler_threads > 0) ?
+ 2*sysInfo.scheduler_threads : 2;
+
+ ioNumThreads = esock_get_uint_from_map(env, load_info,
+ atom_io_num_threads,
+ ioNumThreadsDef);
+
+#ifdef __WIN32__
+
+ io_backend.init = esaio_init;
+ io_backend.finish = esaio_finish;
+
+ io_backend.info = esaio_info;
+ io_backend.cmd = esock_command;
+ io_backend.supports_0 = esock_supports_0;
+ io_backend.supports_1 = esock_supports_1;
+
+ io_backend.open_with_fd = NULL;
+ io_backend.open_plain = esaio_open_plain;
+ io_backend.bind = esaio_bind;
+ io_backend.connect = esaio_connect;
+ io_backend.listen = esock_listen;
+ io_backend.accept = esaio_accept;
+ io_backend.send = esaio_send;
+ io_backend.sendto = esaio_sendto;
+ io_backend.sendmsg = esaio_sendmsg;
+ io_backend.sendfile_start = NULL;
+ io_backend.sendfile_cont = NULL;
+ io_backend.sendfile_dc = NULL;
+ io_backend.recv = esaio_recv;
+ io_backend.recvfrom = esaio_recvfrom;
+ io_backend.recvmsg = esaio_recvmsg;
+ io_backend.close = esaio_close;
+ io_backend.fin_close = esaio_fin_close;
+ io_backend.shutdown = esock_shutdown;
+ io_backend.sockname = esock_sockname;
+ io_backend.peername = esock_peername;
+ io_backend.cancel_connect = esaio_cancel_connect;
+ io_backend.cancel_accept = esaio_cancel_accept;
+ io_backend.cancel_send = esaio_cancel_send;
+ io_backend.cancel_recv = esaio_cancel_recv;
+
+ io_backend.setopt = esock_setopt;
+ io_backend.setopt_native = esock_setopt_native;
+ io_backend.setopt_otp = esock_setopt_otp;
+ io_backend.getopt = esock_getopt;
+ io_backend.getopt_native = esock_getopt_native;
+ io_backend.getopt_otp = esock_getopt_otp;
+
+ io_backend.ioctl_2 = NULL;
+ io_backend.ioctl_3 = NULL;
+ io_backend.ioctl_4 = NULL;
+
+ io_backend.dtor = esaio_dtor;
+ io_backend.stop = NULL; // esaio_stop;
+ io_backend.down = esaio_down;
+
+#else
+
+ io_backend.init = essio_init;
+ io_backend.finish = essio_finish;
+
+ io_backend.info = essio_info;
+ io_backend.cmd = esock_command;
+ io_backend.supports_0 = esock_supports_0;
+ io_backend.supports_1 = esock_supports_1;
+
+ io_backend.open_with_fd = essio_open_with_fd;
+ io_backend.open_plain = essio_open_plain;
+ io_backend.bind = essio_bind;
+ io_backend.connect = essio_connect;
+ io_backend.listen = esock_listen;
+ io_backend.accept = essio_accept;
+ io_backend.send = essio_send;
+ io_backend.sendto = essio_sendto;
+ io_backend.sendmsg = essio_sendmsg;
+ io_backend.sendfile_start = essio_sendfile_start;
+ io_backend.sendfile_cont = essio_sendfile_cont;
+ io_backend.sendfile_dc = essio_sendfile_deferred_close;
+ io_backend.recv = essio_recv;
+ io_backend.recvfrom = essio_recvfrom;
+ io_backend.recvmsg = essio_recvmsg;
+ io_backend.close = essio_close;
+ io_backend.fin_close = essio_fin_close;
+ io_backend.shutdown = esock_shutdown;
+ io_backend.sockname = esock_sockname;
+ io_backend.peername = esock_peername;
+ io_backend.cancel_connect = essio_cancel_connect;
+ io_backend.cancel_accept = essio_cancel_accept;
+ io_backend.cancel_send = essio_cancel_send;
+ io_backend.cancel_recv = essio_cancel_recv;
+
+ io_backend.setopt = esock_setopt;
+ io_backend.setopt_native = esock_setopt_native;
+ io_backend.setopt_otp = esock_setopt_otp;
+ io_backend.getopt = esock_getopt;
+ io_backend.getopt_native = esock_getopt_native;
+ io_backend.getopt_otp = esock_getopt_otp;
+
+ io_backend.ioctl_2 = essio_ioctl2;
+ io_backend.ioctl_3 = essio_ioctl3;
+ io_backend.ioctl_4 = essio_ioctl4;
+
+ io_backend.dtor = essio_dtor;
+ io_backend.stop = essio_stop;
+ io_backend.down = essio_down;
+
+#endif
+
+ if (ESOCK_IO_INIT(ioNumThreads) != ESOCK_IO_OK) {
+ esock_error_msg("Failed initiating I/O backend");
+ return 1; // Failure
+ }
esocks = enif_open_resource_type_x(env,
"sockets",
diff --git a/erts/emulator/nifs/common/socket_asyncio.h b/erts/emulator/nifs/common/socket_asyncio.h
new file mode 100644
index 0000000000..059ee2bf08
--- /dev/null
+++ b/erts/emulator/nifs/common/socket_asyncio.h
@@ -0,0 +1,172 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2023-2023. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ *
+ * ----------------------------------------------------------------------
+ * Purpose : Asynchronous I/O functions.
+ * ----------------------------------------------------------------------
+ *
+ * essio = ESock Asynchronous I/O
+ *
+ */
+
+#ifndef SOCKET_ASYNCIO_H__
+#define SOCKET_ASYNCIO_H__
+
+#include "socket_io.h"
+
+extern int esaio_init(unsigned int numThreads,
+ const ESockData* dataP);
+extern void esaio_finish(void);
+extern ERL_NIF_TERM esaio_info(ErlNifEnv* env);
+
+/*
+extern ERL_NIF_TERM esaio_open_with_fd(ErlNifEnv* env,
+ int fd,
+ ERL_NIF_TERM eopts,
+ const ESockData* dataP);
+ */
+extern ERL_NIF_TERM esaio_open_plain(ErlNifEnv* env,
+ int domain,
+ int type,
+ int protocol,
+ ERL_NIF_TERM eopts,
+ const ESockData* dataP);
+extern ERL_NIF_TERM esaio_bind(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESockAddress* sockAddrP,
+ SOCKLEN_T addrLen);
+extern ERL_NIF_TERM esaio_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM connRef,
+ ESockAddress* addrP,
+ SOCKLEN_T addrLen);
+/*
+extern ERL_NIF_TERM esaio_listen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int backlog);
+*/
+extern ERL_NIF_TERM esaio_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef);
+extern ERL_NIF_TERM esaio_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ErlNifBinary* sndDataP,
+ int flags);
+extern ERL_NIF_TERM esaio_sendto(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ErlNifBinary* dataP,
+ int flags,
+ ESockAddress* toAddrP,
+ SOCKLEN_T toAddrLen);
+extern ERL_NIF_TERM esaio_sendmsg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ERL_NIF_TERM eMsg,
+ int flags,
+ ERL_NIF_TERM eIOV,
+ const ESockData* dataP);
+/*
+extern
+ERL_NIF_TERM esaio_sendfile_start(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ off_t offset,
+ size_t count,
+ ERL_NIF_TERM fRef);
+extern
+ERL_NIF_TERM esaio_sendfile_cont(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ off_t offset,
+ size_t count);
+extern
+ERL_NIF_TERM esaio_sendfile_deferred_close(ErlNifEnv* env,
+ ESockDescriptor* descP);
+*/
+
+extern ERL_NIF_TERM esaio_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef,
+ ssize_t len,
+ int flags);
+extern ERL_NIF_TERM esaio_recvfrom(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef,
+ ssize_t len,
+ int flags);
+extern ERL_NIF_TERM esaio_recvmsg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef,
+ ssize_t bufLen,
+ ssize_t ctrlLen,
+ int flags);
+extern ERL_NIF_TERM esaio_close(ErlNifEnv* env,
+ ESockDescriptor* descP);
+extern ERL_NIF_TERM esaio_fin_close(ErlNifEnv* env,
+ ESockDescriptor* descP);
+extern ERL_NIF_TERM esaio_shutdown(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int how);
+extern ERL_NIF_TERM esaio_sockname(ErlNifEnv* env,
+ ESockDescriptor* descP);
+extern ERL_NIF_TERM esaio_peername(ErlNifEnv* env,
+ ESockDescriptor* descP);
+extern ERL_NIF_TERM esaio_cancel_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef);
+extern ERL_NIF_TERM esaio_cancel_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+extern ERL_NIF_TERM esaio_cancel_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+extern ERL_NIF_TERM esaio_cancel_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+
+extern void esaio_dtor(ErlNifEnv* env,
+ ESockDescriptor* descP);
+extern void esaio_stop(ErlNifEnv* env,
+ ESockDescriptor* descP);
+extern void esaio_down(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+
+/* Temporary (I hope) workaround */
+extern void esaio_down_ctrl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP);
+
+#endif // SOCKET_ASYNCIO_H__
diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h
index ec76f661f4..2613fe5a1e 100644
--- a/erts/emulator/nifs/common/socket_int.h
+++ b/erts/emulator/nifs/common/socket_int.h
@@ -33,7 +33,9 @@
#ifdef __WIN32__
/* All this just to replace sys/socket.h, netinet/in.h and sys/un.h??? */
+#if !defined(INCL_WINSOCK_API_TYPEDEFS)
#define INCL_WINSOCK_API_TYPEDEFS 1
+#endif
#ifndef WINDOWS_H_INCLUDES_WINSOCK2_H
#include <winsock2.h>
#endif
@@ -156,6 +158,13 @@ typedef int BOOLEAN_T;
#define B2S(__B__) ((__B__) ? "true" : "false")
+#define TYPE2STR(T) (((T) == SOCK_STREAM) ? "stream" : \
+ (((T) == SOCK_DGRAM) ? "dgram" : \
+ (((T) == SOCK_RAW) ? "raw" : \
+ (((T) == SOCK_RDM) ? "rdm" : \
+ (((T) == SOCK_SEQPACKET) ? "seqpacket" : \
+ "undefined")))))
+
#define NUM(Array) (sizeof(Array) / sizeof(*(Array)))
@@ -219,10 +228,12 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(autoclose); \
GLOBAL_ATOM_DEF(automedia); \
GLOBAL_ATOM_DEF(bad_data); \
+ GLOBAL_ATOM_DEF(base_addr); \
GLOBAL_ATOM_DEF(bindtodevice); \
GLOBAL_ATOM_DEF(block_source); \
GLOBAL_ATOM_DEF(broadcast); \
GLOBAL_ATOM_DEF(busy_poll); \
+ GLOBAL_ATOM_DEF(cancel); \
GLOBAL_ATOM_DEF(cantconfig); \
GLOBAL_ATOM_DEF(chaos); \
GLOBAL_ATOM_DEF(checksum); \
@@ -230,6 +241,7 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(closed); \
GLOBAL_ATOM_DEF(cmsg_cloexec); \
GLOBAL_ATOM_DEF(command); \
+ GLOBAL_ATOM_DEF(completion); \
GLOBAL_ATOM_DEF(confirm); \
GLOBAL_ATOM_DEF(congestion); \
GLOBAL_ATOM_DEF(connect); \
@@ -237,6 +249,7 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(connecting); \
GLOBAL_ATOM_DEF(context); \
GLOBAL_ATOM_DEF(cork); \
+ GLOBAL_ATOM_DEF(counters); \
GLOBAL_ATOM_DEF(credentials); \
GLOBAL_ATOM_DEF(ctrl); \
GLOBAL_ATOM_DEF(ctrunc); \
@@ -249,6 +262,7 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(dgram); \
GLOBAL_ATOM_DEF(disable_fragments); \
GLOBAL_ATOM_DEF(dlci); \
+ GLOBAL_ATOM_DEF(dma); \
GLOBAL_ATOM_DEF(domain); \
GLOBAL_ATOM_DEF(dontfrag); \
GLOBAL_ATOM_DEF(dontroute); \
@@ -282,6 +296,7 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(fragment_interleave); \
GLOBAL_ATOM_DEF(freebind); \
GLOBAL_ATOM_DEF(frelay); \
+ GLOBAL_ATOM_DEF(get_overlapped_result); \
GLOBAL_ATOM_DEF(get_peer_addr_info); \
GLOBAL_ATOM_DEF(hatype); \
GLOBAL_ATOM_DEF(hdrincl); \
@@ -309,6 +324,7 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(ipcomp_level); \
GLOBAL_ATOM_DEF(ipip); \
GLOBAL_ATOM_DEF(ipv6); \
+ GLOBAL_ATOM_DEF(irq); \
GLOBAL_ATOM_DEF(i_want_mapped_v4_addr); \
GLOBAL_ATOM_DEF(join_group); \
GLOBAL_ATOM_DEF(keepalive); \
@@ -321,9 +337,9 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(level); \
GLOBAL_ATOM_DEF(linger); \
GLOBAL_ATOM_DEF(link); \
- GLOBAL_ATOM_DEF(link0); \
- GLOBAL_ATOM_DEF(link1); \
- GLOBAL_ATOM_DEF(link2); \
+ GLOBAL_ATOM_DEF(link0); \
+ GLOBAL_ATOM_DEF(link1); \
+ GLOBAL_ATOM_DEF(link2); \
GLOBAL_ATOM_DEF(local); \
GLOBAL_ATOM_DEF(localtlk); \
GLOBAL_ATOM_DEF(local_auth_chunks); \
@@ -335,6 +351,8 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(maxburst); \
GLOBAL_ATOM_DEF(maxseg); \
GLOBAL_ATOM_DEF(md5sig); \
+ GLOBAL_ATOM_DEF(mem_end); \
+ GLOBAL_ATOM_DEF(mem_start); \
GLOBAL_ATOM_DEF(metricom); \
GLOBAL_ATOM_DEF(mincost); \
GLOBAL_ATOM_DEF(minttl); \
@@ -362,8 +380,16 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(nopush); \
GLOBAL_ATOM_DEF(nosignal); \
GLOBAL_ATOM_DEF(notrailers); \
+ GLOBAL_ATOM_DEF(not_bound); \
GLOBAL_ATOM_DEF(not_found); \
+ GLOBAL_ATOM_DEF(num_general_errors); \
GLOBAL_ATOM_DEF(not_owner); \
+ GLOBAL_ATOM_DEF(num_threads); \
+ GLOBAL_ATOM_DEF(num_unexpected_accepts); \
+ GLOBAL_ATOM_DEF(num_unexpected_connects); \
+ GLOBAL_ATOM_DEF(num_unexpected_reads); \
+ GLOBAL_ATOM_DEF(num_unexpected_writes); \
+ GLOBAL_ATOM_DEF(num_unknown_cmds); \
GLOBAL_ATOM_DEF(oactive); \
GLOBAL_ATOM_DEF(ok); \
GLOBAL_ATOM_DEF(oob); \
@@ -406,6 +432,7 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(read_fails); \
GLOBAL_ATOM_DEF(read_pkg); \
GLOBAL_ATOM_DEF(read_tries); \
+ GLOBAL_ATOM_DEF(read_waits); \
GLOBAL_ATOM_DEF(recv); \
GLOBAL_ATOM_DEF(recvdstaddr); \
GLOBAL_ATOM_DEF(recverr); \
@@ -460,6 +487,7 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(sndbufforce); \
GLOBAL_ATOM_DEF(sndlowat); \
GLOBAL_ATOM_DEF(sndtimeo); \
+ GLOBAL_ATOM_DEF(sockaddr); \
GLOBAL_ATOM_DEF(socket); \
GLOBAL_ATOM_DEF(socket_tag); \
GLOBAL_ATOM_DEF(spec_dst); \
@@ -480,11 +508,13 @@ typedef long ssize_t;
GLOBAL_ATOM_DEF(ttl); \
GLOBAL_ATOM_DEF(tunnel); \
GLOBAL_ATOM_DEF(tunnel6); \
+ GLOBAL_ATOM_DEF(txqlen); \
GLOBAL_ATOM_DEF(type); \
GLOBAL_ATOM_DEF(udp); \
GLOBAL_ATOM_DEF(unblock_source); \
GLOBAL_ATOM_DEF(undefined); \
GLOBAL_ATOM_DEF(unicast_hops); \
+ GLOBAL_ATOM_DEF(unknown); \
GLOBAL_ATOM_DEF(unspec); \
GLOBAL_ATOM_DEF(up); \
GLOBAL_ATOM_DEF(usec); \
@@ -508,11 +538,15 @@ typedef long ssize_t;
* Error reason atoms
*/
-#define GLOBAL_ERROR_REASON_ATOM_DEFS \
- GLOBAL_ATOM_DEF(eagain); \
- GLOBAL_ATOM_DEF(einval); \
- GLOBAL_ATOM_DEF(select_read); \
- GLOBAL_ATOM_DEF(select_write)
+#define GLOBAL_ERROR_REASON_ATOM_DEFS \
+ GLOBAL_ATOM_DEF(add_socket); \
+ GLOBAL_ATOM_DEF(create_accept_socket); \
+ GLOBAL_ATOM_DEF(eagain); \
+ GLOBAL_ATOM_DEF(einval); \
+ GLOBAL_ATOM_DEF(select_read); \
+ GLOBAL_ATOM_DEF(select_write); \
+ GLOBAL_ATOM_DEF(update_accept_context); \
+ GLOBAL_ATOM_DEF(update_connect_context)
#define GLOBAL_ATOM_DEF(A) extern ERL_NIF_TERM esock_atom_##A
@@ -537,6 +571,7 @@ GLOBAL_ERROR_REASON_ATOM_DEFS;
#define MKL1(E,T) enif_make_list1((E), (T))
#define MKEL(E) enif_make_list((E), 0)
#define MKC(E,H,T) enif_make_list_cell((E), (H), (T))
+#define MKEMA(E) enif_make_new_map((E))
#define MKMA(E,KA,VA,L,M) enif_make_map_from_arrays((E), (KA), (VA), (L), (M))
#define MKPID(E, P) enif_make_pid((E), (P))
#define MKREF(E) enif_make_ref((E))
@@ -568,6 +603,7 @@ GLOBAL_ERROR_REASON_ATOM_DEFS;
#define COMPARE(A, B) enif_compare((A), (B))
#define COMPARE_PIDS(P1, P2) enif_compare_pids((P1), (P2))
+#define IS_ZERO(R) (COMPARE((R), esock_atom_zero) == 0)
#define IS_ATOM(E, TE) enif_is_atom((E), (TE))
#define IS_BIN(E, TE) enif_is_binary((E), (TE))
@@ -577,6 +613,9 @@ GLOBAL_ERROR_REASON_ATOM_DEFS;
#define IS_TUPLE(E, TE) enif_is_tuple((E), (TE))
#define IS_INTEGER(E, TE) esock_is_integer((E), (TE))
+#define IS_PID_UNDEF(P) enif_is_pid_undefined((P))
+#define SET_PID_UNDEF(P) enif_set_pid_undefined((P))
+
#define GET_ATOM_LEN(E, TE, LP) \
enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1)
#define GET_ATOM(E, TE, BP, MAX) \
@@ -600,7 +639,22 @@ GLOBAL_ERROR_REASON_ATOM_DEFS;
#define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP))
#define FREE_BIN(BP) enif_release_binary((BP))
+#define FREE_IOVEC(IV) enif_free_iovec((IV))
+
/* Copy term T into environment E */
-#define CP_TERM(E, T) enif_make_copy((E), (T))
+#define CP_TERM(E, T) enif_make_copy((E), (T))
+
+#define CLEAR_ENV(E) esock_clear_env(__FUNCTION__, (E))
+#define FREE_ENV(E) esock_free_env(__FUNCTION__, (E))
+
+#define TCREATE(NAME, TID, FUNC, ARGS, OPTS) \
+ enif_thread_create((NAME), (TID), (FUNC), (ARGS), (OPTS))
+#define TEXIT(EVAL) enif_thread_exit((EVAL))
+#define TJOIN(TID, EV) enif_thread_join((TID), (EV))
+#define TOCREATE(NAME) enif_thread_opts_create((NAME))
+#define TODESTROY(OPTS) enif_thread_opts_destroy((OPTS))
+
+#define ESOCK_PRINTF(...) enif_fprintf(stdout, __VA_ARGS__)
+#define ESOCK_EPRINTF(...) enif_fprintf(stderr, __VA_ARGS__)
#endif // SOCKET_INT_H__
diff --git a/erts/emulator/nifs/common/socket_io.h b/erts/emulator/nifs/common/socket_io.h
index f5b2b2a875..100ca372be 100644
--- a/erts/emulator/nifs/common/socket_io.h
+++ b/erts/emulator/nifs/common/socket_io.h
@@ -33,11 +33,21 @@
#define ESOCK_IO_OK 0
#define ESOCK_IO_ERR_UNSUPPORTED -1
-typedef int (*ESockIOInit)(unsigned int numThreads);
+typedef int (*ESockIOInit)(unsigned int numThreads,
+ const ESockData* dataP);
typedef void (*ESockIOFinish)(void);
+typedef ERL_NIF_TERM (*ESockIOInfo)(ErlNifEnv* env);
+typedef ERL_NIF_TERM (*ESockIOCommand)(ErlNifEnv* env,
+ ERL_NIF_TERM command,
+ ERL_NIF_TERM cdata);
+typedef ERL_NIF_TERM (*ESockIOSupports0)(ErlNifEnv* env);
+typedef ERL_NIF_TERM (*ESockIOSupports1)(ErlNifEnv* env,
+ ERL_NIF_TERM key);
+
+
typedef ERL_NIF_TERM (*ESockIOOpenWithFd)(ErlNifEnv* env,
int fd,
ERL_NIF_TERM eopts,
@@ -150,5 +160,73 @@ typedef ERL_NIF_TERM (*ESockIOSockName)(ErlNifEnv* env,
typedef ERL_NIF_TERM (*ESockIOPeerName)(ErlNifEnv* env,
ESockDescriptor* descP);
+typedef ERL_NIF_TERM (*ESockIOCancelConnect)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef);
+
+typedef ERL_NIF_TERM (*ESockIOCancelAccept)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+
+typedef ERL_NIF_TERM (*ESockIOCancelSend)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+
+typedef ERL_NIF_TERM (*ESockIOCancelRecv)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+
+typedef ERL_NIF_TERM (*ESockIOSetopt)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal);
+typedef ERL_NIF_TERM (*ESockIOSetoptNative)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal);
+typedef ERL_NIF_TERM (*ESockIOSetoptOtp)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal);
+
+typedef ERL_NIF_TERM (*ESockIOGetopt)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt);
+typedef ERL_NIF_TERM (*ESockIOGetoptNative)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM valueSpec);
+typedef ERL_NIF_TERM (*ESockIOGetoptOtp)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt);
+
+typedef ERL_NIF_TERM (*ESockIOIoctl_2)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req);
+typedef ERL_NIF_TERM (*ESockIOIoctl_3)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req,
+ ERL_NIF_TERM arg);
+typedef ERL_NIF_TERM (*ESockIOIoctl_4)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req,
+ ERL_NIF_TERM arg1,
+ ERL_NIF_TERM arg2);
+
+typedef void (*ESockIODTor)(ErlNifEnv* env,
+ ESockDescriptor* descP);
+typedef void (*ESockIOStop)(ErlNifEnv* env,
+ ESockDescriptor* descP);
+typedef void (*ESockIODown)(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
#endif // SOCKET_IO_H__
diff --git a/erts/emulator/nifs/common/socket_syncio.h b/erts/emulator/nifs/common/socket_syncio.h
index 15dac1af3e..2d548b8645 100644
--- a/erts/emulator/nifs/common/socket_syncio.h
+++ b/erts/emulator/nifs/common/socket_syncio.h
@@ -30,8 +30,10 @@
#include "socket_io.h"
-extern int essio_init(unsigned int numThreads);
+extern int essio_init(unsigned int numThreads,
+ const ESockData* dataP);
extern void essio_finish(void);
+extern ERL_NIF_TERM essio_info(ErlNifEnv* env);
extern ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env,
int fd,
@@ -53,9 +55,11 @@ extern ERL_NIF_TERM essio_connect(ErlNifEnv* env,
ERL_NIF_TERM connRef,
ESockAddress* addrP,
SOCKLEN_T addrLen);
+/*
extern ERL_NIF_TERM essio_listen(ErlNifEnv* env,
ESockDescriptor* descP,
int backlog);
+*/
extern ERL_NIF_TERM essio_accept(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
@@ -124,12 +128,47 @@ extern ERL_NIF_TERM essio_close(ErlNifEnv* env,
ESockDescriptor* descP);
extern ERL_NIF_TERM essio_fin_close(ErlNifEnv* env,
ESockDescriptor* descP);
-extern ERL_NIF_TERM essio_shutdown(ErlNifEnv* env,
- ESockDescriptor* descP,
- int how);
-extern ERL_NIF_TERM essio_sockname(ErlNifEnv* env,
- ESockDescriptor* descP);
-extern ERL_NIF_TERM essio_peername(ErlNifEnv* env,
- ESockDescriptor* descP);
+extern ERL_NIF_TERM essio_cancel_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef);
+extern ERL_NIF_TERM essio_cancel_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+extern ERL_NIF_TERM essio_cancel_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+extern ERL_NIF_TERM essio_cancel_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+
+extern ERL_NIF_TERM essio_ioctl2(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req);
+extern ERL_NIF_TERM essio_ioctl3(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req,
+ ERL_NIF_TERM arg);
+extern ERL_NIF_TERM essio_ioctl4(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req,
+ ERL_NIF_TERM ename,
+ ERL_NIF_TERM eval);
+
+extern void essio_dtor(ErlNifEnv* env,
+ ESockDescriptor* descP);
+extern void essio_stop(ErlNifEnv* env,
+ ESockDescriptor* descP);
+extern void essio_down(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+
+/* Temporary (I hope) workaround */
+extern void essio_down_ctrl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP);
#endif // SOCKET_SYNCIO_H__
diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c
index efeb055240..8eeb98dba7 100644
--- a/erts/emulator/nifs/common/socket_util.c
+++ b/erts/emulator/nifs/common/socket_util.c
@@ -2286,15 +2286,15 @@ ERL_NIF_TERM esock_encode_bool(BOOLEAN_T val)
/* *** esock_decode_level ***
*
- * Decode option or cmsg level - 'socket' or protocol number.
+ * Decode option or cmsg level - 'socket' or level number.
*
*/
extern
-BOOLEAN_T esock_decode_level(ErlNifEnv* env, ERL_NIF_TERM eVal, int *val)
+BOOLEAN_T esock_decode_level(ErlNifEnv* env, ERL_NIF_TERM elevel, int *level)
{
- if (COMPARE(esock_atom_socket, eVal) == 0)
- *val = SOL_SOCKET;
- else if (! GET_INT(env, eVal, val))
+ if (COMPARE(esock_atom_socket, elevel) == 0)
+ *level = SOL_SOCKET;
+ else if (! GET_INT(env, elevel, level))
return FALSE;
return TRUE;
@@ -2329,6 +2329,101 @@ ERL_NIF_TERM esock_make_ok2(ErlNifEnv* env, ERL_NIF_TERM any)
}
+/* Takes an 'errno' value and converts it to a term.
+ *
+ * If the errno can be translated using erl_errno_id,
+ * then we use that value otherwise we use the errno
+ * integer value converted to a term.
+ * Unless there is a specific error code that can be
+ * handled specially.
+ */
+extern
+ERL_NIF_TERM esock_errno_to_term(ErlNifEnv* env, int err)
+{
+ switch (err) {
+#if defined(NO_ERROR)
+ case NO_ERROR:
+ return MKA(env, "no_error");
+ break;
+#endif
+
+#if defined(WSA_IO_PENDING)
+ case WSA_IO_PENDING:
+ return MKA(env, "io_pending");
+ break;
+#endif
+
+#if defined(WSA_IO_INCOMPLETE)
+ case WSA_IO_INCOMPLETE:
+ return MKA(env, "io_incomplete");
+ break;
+#endif
+
+#if defined(WSA_OPERATION_ABORTED)
+ case WSA_OPERATION_ABORTED:
+ return MKA(env, "operation_aborted");
+ break;
+#endif
+
+#if defined(WSA_INVALID_PARAMETER)
+ case WSA_INVALID_PARAMETER:
+ return MKA(env, "invalid_parameter");
+ break;
+#endif
+
+ default:
+ {
+ char* str = erl_errno_id(err);
+ if ( strcmp(str, "unknown") == 0 )
+ return MKI(env, err);
+ else
+ return MKA(env, str);
+ }
+ break;
+ }
+
+ /* This is just in case of programming error.
+ * We should not get this far!
+ */
+ return MKI(env, err);
+}
+
+
+
+/* *** esock_make_extra_error_info_term ***
+ * This is used primarily for debugging.
+ * Is supposed to be called via the 'MKEEI' macro.
+ */
+extern
+ERL_NIF_TERM esock_make_extra_error_info_term(ErlNifEnv* env,
+ const char* file,
+ const char* function,
+ const int line,
+ ERL_NIF_TERM rawinfo,
+ ERL_NIF_TERM info)
+{
+ ERL_NIF_TERM keys[] = {MKA(env, "file"),
+ MKA(env, "function"),
+ MKA(env, "line"),
+ MKA(env, "raw_info"),
+ MKA(env, "info")};
+ ERL_NIF_TERM vals[] = {MKS(env, file),
+ MKS(env, function),
+ MKI(env, line),
+ rawinfo,
+ info};
+ unsigned int numKeys = NUM(keys);
+ unsigned int numVals = NUM(vals);
+ ERL_NIF_TERM map;
+
+ ESOCK_ASSERT( numKeys == numVals );
+ ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &map) );
+
+ return map;
+}
+
+
+
/* Create an error two (2) tuple in the form:
*
* {error, Reason}
@@ -2387,6 +2482,23 @@ ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err)
/* Create an error two (2) tuple in the form:
*
+ * {error, {Tag, Reason}}
+ *
+ * Both 'Tag' and 'Reason' are already in the form of an
+ * ERL_NIF_TERM so all we have to do is create "the" tuple.
+ */
+extern
+ERL_NIF_TERM esock_make_error_t2r(ErlNifEnv* env,
+ ERL_NIF_TERM tag,
+ ERL_NIF_TERM reason)
+{
+ return MKT2(env, esock_atom_error, MKT2(env, tag, reason));
+}
+
+
+
+/* Create an error two (2) tuple in the form:
+ *
* {error, {invalid, What}}}
*/
extern
diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h
index 7f77630845..04f0cc3d94 100644
--- a/erts/emulator/nifs/common/socket_util.h
+++ b/erts/emulator/nifs/common/socket_util.h
@@ -46,6 +46,31 @@
#define ESOCK_ABORT(E) esock_abort(E, __func__, __FILE__, __LINE__)
#define ESOCK_ASSERT(e) ((void) ((e) ? 1 : (ESOCK_ABORT(#e), 0)))
+#define MKEEI(E, RI, I) \
+ esock_make_extra_error_info_term((E), \
+ __FILE__, \
+ __FUNCTION__, \
+ __LINE__, \
+ (RI), (I))
+
+#define ESOCK_VERBOSE_ERRNO 1
+#if defined(ESOCK_VERBOSE_ERRNO)
+#define ENO2T(E, ENO) MKEEI((E), \
+ MKI((E), (ENO)), \
+ esock_errno_to_term((E), (ENO)))
+#else
+#define ENO2T(E, ENO) esock_errno_to_term((E), (ENO))
+#endif
+
+
+extern
+ERL_NIF_TERM esock_make_extra_error_info_term(ErlNifEnv* env,
+ const char* file,
+ const char* function,
+ const int line,
+ ERL_NIF_TERM rawinfo,
+ ERL_NIF_TERM info);
+
extern
unsigned int esock_get_uint_from_map(ErlNifEnv* env,
ERL_NIF_TERM map,
@@ -247,6 +272,8 @@ ERL_NIF_TERM esock_self(ErlNifEnv* env);
extern
ERL_NIF_TERM esock_make_ok2(ErlNifEnv* env, ERL_NIF_TERM any);
extern
+ERL_NIF_TERM esock_errno_to_term(ErlNifEnv* env, int err);
+extern
ERL_NIF_TERM esock_make_error(ErlNifEnv* env, ERL_NIF_TERM reason);
extern
ERL_NIF_TERM esock_make_error_closed(ErlNifEnv* env);
@@ -255,6 +282,9 @@ ERL_NIF_TERM esock_make_error_str(ErlNifEnv* env, char* reason);
extern
ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err);
extern
+ERL_NIF_TERM esock_make_error_t2r(ErlNifEnv* env,
+ ERL_NIF_TERM tag, ERL_NIF_TERM reason);
+extern
ERL_NIF_TERM esock_make_error_invalid(ErlNifEnv* env, ERL_NIF_TERM what);
extern
ERL_NIF_TERM esock_make_error_integer_range(ErlNifEnv* env, ERL_NIF_TERM i);
diff --git a/erts/emulator/nifs/unix/unix_socket_syncio.c b/erts/emulator/nifs/unix/unix_socket_syncio.c
index 88f45a7b51..3e3cfcaeef 100644
--- a/erts/emulator/nifs/unix/unix_socket_syncio.c
+++ b/erts/emulator/nifs/unix/unix_socket_syncio.c
@@ -45,6 +45,16 @@
#endif
#include "sys.h"
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include <net/if.h>
+
#include "prim_socket_int.h"
#include "socket_util.h"
#include "socket_io.h"
@@ -66,12 +76,12 @@
#define sock_accept(s, addr, len) accept((s), (addr), (len))
#endif
#define sock_bind(s, addr, len) bind((s), (addr), (len))
-// #define sock_close(s) close((s))
+#define sock_close(s) close((s))
// #define sock_close_event(e) /* do nothing */
#define sock_connect(s, addr, len) connect((s), (addr), (len))
#define sock_errno() errno
-#define sock_listen(s, b) listen((s), (b))
-#define sock_name(s, addr, len) getsockname((s), (addr), (len))
+// #define sock_listen(s, b) listen((s), (b))
+// #define sock_name(s, addr, len) getsockname((s), (addr), (len))
#define sock_open(domain, type, proto) socket((domain), (type), (proto))
#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
@@ -85,21 +95,38 @@
#define sock_shutdown(s, how) shutdown((s), (how))
+/* =================================================================== *
+ * *
+ * Various esaio macros *
+ * *
+ * =================================================================== */
+
+/* Global socket debug */
+#define SGDBG( proto ) ESOCK_DBG_PRINTF( ctrl.dbg , proto )
+
+
+/* =================================================================== *
+ * *
+ * Local types *
+ * *
+ * =================================================================== */
+
+typedef struct {
+ /* Misc stuff */
+ BOOLEAN_T dbg;
+ BOOLEAN_T sockDbg;
+} ESSIOControl;
+
+
+
/* ======================================================================== *
* Function Forwards *
* ======================================================================== *
*/
-static BOOLEAN_T open_is_debug(ErlNifEnv* env,
- ERL_NIF_TERM eopts,
- BOOLEAN_T def);
-static BOOLEAN_T open_use_registry(ErlNifEnv* env,
- ERL_NIF_TERM eopts,
- BOOLEAN_T def);
static BOOLEAN_T open_todup(ErlNifEnv* env,
ERL_NIF_TERM eopts);
static BOOLEAN_T open_which_domain(SOCKET sock, int* domain);
static BOOLEAN_T open_which_type(SOCKET sock, int* type);
-static BOOLEAN_T open_which_protocol(SOCKET sock, int* proto);
static BOOLEAN_T open_get_domain(ErlNifEnv* env,
ERL_NIF_TERM eopts,
int* domain);
@@ -122,6 +149,28 @@ static BOOLEAN_T restore_network_namespace(BOOLEAN_T dbg,
static BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err);
+static ERL_NIF_TERM essio_cancel_accept_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM essio_cancel_accept_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP);
+static ERL_NIF_TERM essio_cancel_send_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM essio_cancel_send_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP);
+static ERL_NIF_TERM essio_cancel_recv_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM essio_cancel_recv_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP);
+
static ERL_NIF_TERM essio_accept_listening_error(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
@@ -228,7 +277,7 @@ static void encode_msg(ErlNifEnv* env,
struct msghdr* msgHdrP,
ErlNifBinary* dataBufP,
ErlNifBinary* ctrlBufP,
- ERL_NIF_TERM* eSockAddr);
+ ERL_NIF_TERM* eMsg);
static void encode_cmsgs(ErlNifEnv* env,
ESockDescriptor* descP,
ErlNifBinary* cmsgBinP,
@@ -366,6 +415,266 @@ static ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env,
ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM essio_ioctl_gifconf(ErlNifEnv* env,
+ ESockDescriptor* descP);
+#if defined(SIOCGIFNAME)
+static ERL_NIF_TERM essio_ioctl_gifname(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eidx);
+#endif
+
+/* esock_ioctl_gifindex */
+#if defined(SIOCGIFINDEX)
+#define IOCTL_GIFINDEX_FUNC_DEF IOCTL_GET_FUNC_DEF(gifindex)
+#else
+#define IOCTL_GIFINDEX_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifflags */
+#if defined(SIOCGIFFLAGS)
+#define IOCTL_GIFFLAGS_FUNC_DEF IOCTL_GET_FUNC_DEF(gifflags)
+#else
+#define IOCTL_GIFFLAGS_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifaddr */
+#if defined(SIOCGIFADDR)
+#define IOCTL_GIFADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifaddr)
+#else
+#define IOCTL_GIFADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifdstaddr */
+#if defined(SIOCGIFDSTADDR)
+#define IOCTL_GIFDSTADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifdstaddr)
+#else
+#define IOCTL_GIFDSTADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifbrdaddr */
+#if defined(SIOCGIFBRDADDR)
+#define IOCTL_GIFBRDADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifbrdaddr)
+#else
+#define IOCTL_GIFBRDADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifnetmask */
+#if defined(SIOCGIFNETMASK)
+#define IOCTL_GIFNETMASK_FUNC_DEF IOCTL_GET_FUNC_DEF(gifnetmask)
+#else
+#define IOCTL_GIFNETMASK_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifmtu */
+#if defined(SIOCGIFMTU)
+#define IOCTL_GIFMTU_FUNC_DEF IOCTL_GET_FUNC_DEF(gifmtu)
+#else
+#define IOCTL_GIFMTU_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifhwaddr */
+#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
+#define IOCTL_GIFHWADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifhwaddr)
+#else
+#define IOCTL_GIFHWADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifmap */
+#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
+#define IOCTL_GIFMAP_FUNC_DEF IOCTL_GET_FUNC_DEF(gifmap)
+#else
+#define IOCTL_GIFMAP_FUNC_DEF
+#endif
+
+/* esock_ioctl_giftxqlen */
+#if defined(SIOCGIFTXQLEN)
+#define IOCTL_GIFTXQLEN_FUNC_DEF IOCTL_GET_FUNC_DEF(giftxqlen)
+#else
+#define IOCTL_GIFTXQLEN_FUNC_DEF
+#endif
+
+#define IOCTL_GET_FUNCS_DEF \
+ IOCTL_GIFINDEX_FUNC_DEF; \
+ IOCTL_GIFFLAGS_FUNC_DEF; \
+ IOCTL_GIFADDR_FUNC_DEF; \
+ IOCTL_GIFDSTADDR_FUNC_DEF; \
+ IOCTL_GIFBRDADDR_FUNC_DEF; \
+ IOCTL_GIFNETMASK_FUNC_DEF; \
+ IOCTL_GIFMTU_FUNC_DEF; \
+ IOCTL_GIFHWADDR_FUNC_DEF; \
+ IOCTL_GIFMAP_FUNC_DEF; \
+ IOCTL_GIFTXQLEN_FUNC_DEF;
+#define IOCTL_GET_FUNC_DEF(F) \
+ static ERL_NIF_TERM essio_ioctl_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM ename)
+IOCTL_GET_FUNCS_DEF
+#undef IOCTL_GET_FUNC_DEF
+
+/* esock_ioctl_sifflags */
+#if defined(SIOCSIFFLAGS)
+#define IOCTL_SIFFLAGS_FUNC_DEF IOCTL_SET_FUNC_DEF(sifflags)
+#else
+#define IOCTL_SIFFLAGS_FUNC_DEF
+#endif
+
+/* esock_ioctl_sifaddr */
+#if defined(SIOCSIFADDR)
+#define IOCTL_SIFADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifaddr)
+#else
+#define IOCTL_SIFADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_sifdstaddr */
+#if defined(SIOCSIFDSTADDR)
+#define IOCTL_SIFDSTADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifdstaddr)
+#else
+#define IOCTL_SIFDSTADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_sifbrdaddr */
+#if defined(SIOCSIFBRDADDR)
+#define IOCTL_SIFBRDADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifbrdaddr)
+#else
+#define IOCTL_SIFBRDADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_sifnetmask */
+#if defined(SIOCSIFNETMASK)
+#define IOCTL_SIFNETMASK_FUNC_DEF IOCTL_SET_FUNC_DEF(sifnetmask)
+#else
+#define IOCTL_SIFNETMASK_FUNC_DEF
+#endif
+
+/* esock_ioctl_sifmtu */
+#if defined(SIOCSIFMTU)
+#define IOCTL_SIFMTU_FUNC_DEF IOCTL_SET_FUNC_DEF(sifmtu)
+#else
+#define IOCTL_SIFMTU_FUNC_DEF
+#endif
+
+/* esock_ioctl_siftxqlen */
+#if defined(SIOCSIFTXQLEN)
+#define IOCTL_SIFTXQLEN_FUNC_DEF IOCTL_SET_FUNC_DEF(siftxqlen)
+#else
+#define IOCTL_SIFTXQLEN_FUNC_DEF
+#endif
+
+#define IOCTL_SET_FUNCS_DEF \
+ IOCTL_SIFFLAGS_FUNC_DEF; \
+ IOCTL_SIFADDR_FUNC_DEF; \
+ IOCTL_SIFDSTADDR_FUNC_DEF; \
+ IOCTL_SIFBRDADDR_FUNC_DEF; \
+ IOCTL_SIFNETMASK_FUNC_DEF; \
+ IOCTL_SIFMTU_FUNC_DEF; \
+ IOCTL_SIFTXQLEN_FUNC_DEF;
+#define IOCTL_SET_FUNC_DEF(F) \
+ static ERL_NIF_TERM essio_ioctl_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM ename, \
+ ERL_NIF_TERM evalue)
+IOCTL_SET_FUNCS_DEF
+#undef IOCTL_SET_FUNC_DEF
+
+
+static ERL_NIF_TERM encode_ioctl_ifconf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifconf* ifcP);
+static ERL_NIF_TERM encode_ioctl_ifconf_ifreq(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifreq* ifrP);
+static ERL_NIF_TERM encode_ioctl_ifreq_name(ErlNifEnv* env,
+ char* name);
+static ERL_NIF_TERM encode_ioctl_ifreq_sockaddr(ErlNifEnv* env,
+ struct sockaddr* sa);
+static ERL_NIF_TERM make_ifreq(ErlNifEnv* env,
+ ERL_NIF_TERM name,
+ ERL_NIF_TERM key2,
+ ERL_NIF_TERM val2);
+#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
+static ERL_NIF_TERM encode_ioctl_ifrmap(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifmap* mapP);
+#endif
+#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
+static ERL_NIF_TERM encode_ioctl_hwaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct sockaddr* addrP);
+#endif
+static ERL_NIF_TERM encode_ioctl_ifraddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct sockaddr* addrP);
+static ERL_NIF_TERM encode_ioctl_flags(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ short flags);
+#if defined(SIOCSIFFLAGS)
+static BOOLEAN_T decode_ioctl_flags(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eflags,
+ short* flags);
+#endif
+static BOOLEAN_T decode_ioctl_sockaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eaddr,
+ ESockAddress* addr);
+#if defined(SIOCSIFMTU)
+static BOOLEAN_T decode_ioctl_mtu(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM emtu,
+ int* mtu);
+#endif
+#if defined(SIOCSIFTXQLEN)
+static BOOLEAN_T decode_ioctl_txqlen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM etxqlen,
+ int* txqlen);
+#endif
+#if defined(SIOCSIFTXQLEN)
+static BOOLEAN_T decode_ioctl_ivalue(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eivalue,
+ int* ivalue);
+#endif
+static ERL_NIF_TERM encode_ioctl_ivalue(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int ivalue);
+
+
+/*
+static void essio_down_ctrl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP);
+*/
+static void essio_down_acceptor(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+static void essio_down_writer(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+static void essio_down_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+
+static BOOLEAN_T do_stop(ErlNifEnv* env,
+ ESockDescriptor* descP);
+
+
+/* =================================================================== *
+ * *
+ * Local (global) variables *
+ * *
+ * =================================================================== */
+
+static ESSIOControl ctrl = {0};
+
+
+
/* ======================================================================== *
* ESSIO Functions *
* ======================================================================== *
@@ -376,10 +685,14 @@ static ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env,
* this is just a dummy function.
*/
extern
-int essio_init(unsigned int numThreads)
+int essio_init(unsigned int numThreads,
+ const ESockData* dataP)
{
VOID(numThreads);
+ ctrl.dbg = dataP->dbg;
+ ctrl.sockDbg = dataP->sockDbg;
+
return ESOCK_IO_OK;
}
@@ -395,6 +708,28 @@ void essio_finish(void)
}
+
+/* *******************************************************************
+ * essio_info - Return info "about" this I/O backend.
+ */
+
+extern
+ERL_NIF_TERM essio_info(ErlNifEnv* env)
+{
+ ERL_NIF_TERM info;
+ ERL_NIF_TERM keys[] = {esock_atom_name};
+ ERL_NIF_TERM vals[] = {MKA(env, "unix_essio")};
+ unsigned int numKeys = NUM(keys);
+ unsigned int numVals = NUM(vals);
+
+ ESOCK_ASSERT( numKeys == numVals );
+ ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &info) );
+
+ return info;
+}
+
+
+
/* ========================================================================
* essio_open - create an endpoint (from an existing fd) for communication
*
@@ -410,15 +745,14 @@ ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env,
ERL_NIF_TERM eopts,
const ESockData* dataP)
{
- BOOLEAN_T dbg = open_is_debug(env, eopts, dataP->sockDbg);
- BOOLEAN_T useReg = open_use_registry(env, eopts, dataP->useReg);
+ BOOLEAN_T dbg = esock_open_is_debug(env, eopts, dataP->sockDbg);
+ BOOLEAN_T useReg = esock_open_use_registry(env, eopts, dataP->useReg);
ESockDescriptor* descP;
ERL_NIF_TERM sockRef;
int domain, type, protocol;
int save_errno = 0;
BOOLEAN_T closeOnClose;
SOCKET sock;
- ErlNifEvent event;
ErlNifPid self;
/* Keep track of the creator
@@ -464,7 +798,7 @@ ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env,
return esock_make_invalid(env, esock_atom_type);
}
- if (! open_which_protocol(fd, &protocol)) {
+ if (! esock_open_which_protocol(fd, &protocol)) {
SSDBG2( dbg,
("UNIX-ESSIO",
"essio_open2 -> failed get protocol from system\r\n") );
@@ -506,12 +840,11 @@ ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env,
closeOnClose = FALSE;
}
- event = sock;
SET_NONBLOCKING(sock);
/* Create and initiate the socket "descriptor" */
- descP = esock_alloc_descriptor(sock, event);
+ descP = esock_alloc_descriptor(sock);
descP->ctrlPid = self;
descP->domain = domain;
descP->type = type;
@@ -560,23 +893,6 @@ ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env,
static
-BOOLEAN_T open_is_debug(ErlNifEnv* env,
- ERL_NIF_TERM eopts,
- BOOLEAN_T def)
-{
- return esock_get_bool_from_map(env, eopts, esock_atom_debug, def);
-}
-
-static
-BOOLEAN_T open_use_registry(ErlNifEnv* env,
- ERL_NIF_TERM eopts,
- BOOLEAN_T def)
-{
- return esock_get_bool_from_map(env, eopts, esock_atom_use_registry, def);
-}
-
-
-static
BOOLEAN_T open_which_domain(SOCKET sock, int* domain)
{
#if defined(SO_DOMAIN)
@@ -633,16 +949,6 @@ BOOLEAN_T open_get_type(ErlNifEnv* env,
return TRUE;
}
-static
-BOOLEAN_T open_which_protocol(SOCKET sock, int* proto)
-{
-#if defined(SO_PROTOCOL)
- if (esock_getopt_int(sock, SOL_SOCKET, SO_PROTOCOL, proto))
- return TRUE;
-#endif
- return FALSE;
-}
-
/* The eopts contains an integer 'type' key.
*/
static
@@ -674,8 +980,8 @@ ERL_NIF_TERM essio_open_plain(ErlNifEnv* env,
ERL_NIF_TERM eopts,
const ESockData* dataP)
{
- BOOLEAN_T dbg = open_is_debug(env, eopts, dataP->sockDbg);
- BOOLEAN_T useReg = open_use_registry(env, eopts, dataP->useReg);
+ BOOLEAN_T dbg = esock_open_is_debug(env, eopts, dataP->sockDbg);
+ BOOLEAN_T useReg = esock_open_use_registry(env, eopts, dataP->useReg);
ESockDescriptor* descP;
ERL_NIF_TERM sockRef;
int proto = protocol;
@@ -735,7 +1041,7 @@ ERL_NIF_TERM essio_open_plain(ErlNifEnv* env,
*/
if (proto == 0)
- (void) open_which_protocol(sock, &proto);
+ (void) esock_open_which_protocol(sock, &proto);
#ifdef HAVE_SETNS
if (netns != NULL) {
@@ -750,7 +1056,7 @@ ERL_NIF_TERM essio_open_plain(ErlNifEnv* env,
/* Create and initiate the socket "descriptor" */
- descP = esock_alloc_descriptor(sock, sock);
+ descP = esock_alloc_descriptor(sock);
descP->ctrlPid = self;
descP->domain = domain;
descP->type = type;
@@ -766,7 +1072,7 @@ ERL_NIF_TERM essio_open_plain(ErlNifEnv* env,
descP->dbg = dbg;
descP->useReg = useReg;
- esock_inc_socket(domain, type, protocol);
+ esock_inc_socket(domain, type, proto);
/* And finally (maybe) update the registry */
if (descP->useReg) esock_send_reg_add_msg(env, descP, sockRef);
@@ -1008,7 +1314,7 @@ ERL_NIF_TERM essio_connect(ErlNifEnv* env,
MKI(env, sres)));
/* Initiate connector */
descP->connector.pid = self;
- ESOCK_ASSERT( MONP("esock_connect -> conn",
+ ESOCK_ASSERT( MONP("essio_connect -> conn",
env, descP,
&self, &descP->connector.mon) == 0 );
descP->connector.env = esock_alloc_env("connector");
@@ -1084,33 +1390,7 @@ BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err)
-/* ========================================================================
- */
-extern
-ERL_NIF_TERM essio_listen(ErlNifEnv* env,
- ESockDescriptor* descP,
- int backlog)
-{
-
- /*
- * Verify that we are in the proper state
- */
-
- if (! IS_OPEN(descP->readState))
- return esock_make_error_closed(env);
-
- /*
- * And attempt to make socket listening
- */
-
- if ((sock_listen(descP->sock, backlog)) < 0)
- return esock_make_error_errno(env, sock_errno());
-
- descP->readState |= ESOCK_STATE_LISTENING;
-
- return esock_atom_ok;
-
-}
+/* *** essio_listen *** */
/* ========================================================================
@@ -1121,7 +1401,7 @@ ERL_NIF_TERM essio_accept(ErlNifEnv* env,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM accRef)
{
- ErlNifPid caller;
+ ErlNifPid caller;
ESOCK_ASSERT( enif_self(env, &caller) != NULL );
@@ -1135,7 +1415,7 @@ ERL_NIF_TERM essio_accept(ErlNifEnv* env,
return esock_make_error_invalid(env, esock_atom_state);
if (descP->currentAcceptorP == NULL) {
- SOCKET accSock;
+ SOCKET accSock;
/* We have no active acceptor (and therefore no acceptors in queue)
*/
@@ -1454,7 +1734,7 @@ ERL_NIF_TERM essio_accept_accepting_other(ErlNifEnv* env,
ErlNifPid caller)
{
if (! esock_acceptor_search4pid(env, descP, &caller)) {
- esock_acceptor_push(env, descP, caller, ref);
+ esock_acceptor_push(env, descP, caller, ref, NULL);
return esock_atom_select;
} else {
/* Acceptor already in queue */
@@ -1536,7 +1816,7 @@ BOOLEAN_T essio_accept_accepted(ErlNifEnv* env,
ESOCK_CNT_INC(env, descP, sockRef,
esock_atom_acc_success, &descP->accSuccess, 1);
- accDescP = esock_alloc_descriptor(accSock, accSock);
+ accDescP = esock_alloc_descriptor(accSock);
accDescP->domain = descP->domain;
accDescP->type = descP->type;
accDescP->protocol = descP->protocol;
@@ -1567,7 +1847,7 @@ BOOLEAN_T essio_accept_accepted(ErlNifEnv* env,
SET_NONBLOCKING(accDescP->sock);
- descP->writeState |= ESOCK_STATE_CONNECTED;
+ accDescP->writeState |= ESOCK_STATE_CONNECTED;
MUNLOCK(descP->writeMtx);
@@ -1843,7 +2123,7 @@ ERL_NIF_TERM essio_sendmsg(ErlNifEnv* env,
}
SSDBG( descP,
("UNIX-ESSIO",
- "essio_sendmsg {%d} ->"
+ "essio_sendmsg {%d} -> iovec size verified"
"\r\n iov length: %lu"
"\r\n data size: %u"
"\r\n",
@@ -1899,13 +2179,14 @@ ERL_NIF_TERM essio_sendmsg(ErlNifEnv* env,
sockRef, sendRef);
done_free_iovec:
- enif_free_iovec(iovec);
+ FREE_IOVEC( iovec );
if (ctrlBuf != NULL) FREE(ctrlBuf);
SSDBG( descP,
- ("UNIX-ESSIO", "essio_sendmsg {%d} ->"
+ ("UNIX-ESSIO", "essio_sendmsg {%d} -> done"
"\r\n %T"
"\r\n", descP->sock, res) );
+
return res;
}
@@ -2764,7 +3045,7 @@ ERL_NIF_TERM essio_close(ErlNifEnv* env,
/* Prepare for closing the socket */
descP->readState |= ESOCK_STATE_CLOSING;
descP->writeState |= ESOCK_STATE_CLOSING;
- if (esock_do_stop(env, descP)) {
+ if (do_stop(env, descP)) {
// stop() has been scheduled - wait for it
SSDBG( descP,
("UNIX-ESSIO", "essio_close {%d} -> stop was scheduled\r\n",
@@ -2787,6 +3068,177 @@ ERL_NIF_TERM essio_close(ErlNifEnv* env,
}
+
+/* Prepare for close - return whether stop is scheduled or not
+ */
+static
+BOOLEAN_T do_stop(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ BOOLEAN_T ret;
+ int sres;
+ ERL_NIF_TERM sockRef;
+
+ sockRef = enif_make_resource(env, descP);
+
+ if (IS_SELECTED(descP)) {
+ ESOCK_ASSERT( (sres = esock_select_stop(env,
+ (ErlNifEvent) descP->sock,
+ descP))
+ >= 0 );
+ if ((sres & ERL_NIF_SELECT_STOP_CALLED) != 0) {
+ /* The socket is no longer known by the select machinery
+ * - it may be closed
+ */
+ ret = FALSE;
+ } else {
+ ESOCK_ASSERT( (sres & ERL_NIF_SELECT_STOP_SCHEDULED) != 0 );
+ /* esock_stop() is scheduled
+ * - socket may be removed by esock_stop() or later
+ */
+ ret = TRUE;
+ }
+ } else {
+ sres = 0;
+ /* The socket has never been used in the select machinery
+ * - it may be closed
+ */
+ ret = FALSE;
+ }
+
+ /* +++++++ Current and waiting Writers +++++++ */
+
+ if (descP->currentWriterP != NULL) {
+
+ /* We have a current Writer; was it deselected?
+ */
+
+ if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) {
+
+ /* The current Writer will not get a select message
+ * - send it an abort message
+ */
+
+ esock_stop_handle_current(env,
+ "writer",
+ descP, sockRef, &descP->currentWriter);
+ }
+
+ /* Inform the waiting Writers (in the same way) */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "do_stop {%d} -> handle waiting writer(s)\r\n",
+ descP->sock) );
+
+ esock_inform_waiting_procs(env, "writer",
+ descP, sockRef, &descP->writersQ,
+ esock_atom_closed);
+
+ descP->currentWriterP = NULL;
+ }
+
+ /* +++++++ Connector +++++++
+ * Note that there should not be Writers and a Connector
+ * at the same time so the check for if the
+ * current Writer/Connecter was deselected is only correct
+ * under that assumption
+ */
+
+ if (descP->connectorP != NULL) {
+
+ /* We have a Connector; was it deselected?
+ */
+
+ if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) {
+
+ /* The Connector will not get a select message
+ * - send it an abort message
+ */
+
+ esock_stop_handle_current(env,
+ "connector",
+ descP, sockRef, &descP->connector);
+ }
+
+ descP->connectorP = NULL;
+ }
+
+ /* +++++++ Current and waiting Readers +++++++ */
+
+ if (descP->currentReaderP != NULL) {
+
+ /* We have a current Reader; was it deselected?
+ */
+
+ if (sres & ERL_NIF_SELECT_READ_CANCELLED) {
+
+ /* The current Reader will not get a select message
+ * - send it an abort message
+ */
+
+ esock_stop_handle_current(env,
+ "reader",
+ descP, sockRef, &descP->currentReader);
+ }
+
+ /* Inform the Readers (in the same way) */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "do_stop {%d} -> handle waiting reader(s)\r\n",
+ descP->sock) );
+
+ esock_inform_waiting_procs(env, "writer",
+ descP, sockRef, &descP->readersQ,
+ esock_atom_closed);
+
+ descP->currentReaderP = NULL;
+ }
+
+ /* +++++++ Current and waiting Acceptors +++++++
+ *
+ * Note that there should not be Readers and Acceptors
+ * at the same time so the check for if the
+ * current Reader/Acceptor was deselected is only correct
+ * under that assumption
+ */
+
+ if (descP->currentAcceptorP != NULL) {
+
+ /* We have a current Acceptor; was it deselected?
+ */
+
+ if (sres & ERL_NIF_SELECT_READ_CANCELLED) {
+
+ /* The current Acceptor will not get a select message
+ * - send it an abort message
+ */
+
+ esock_stop_handle_current(env,
+ "acceptor",
+ descP, sockRef, &descP->currentAcceptor);
+ }
+
+ /* Inform the waiting Acceptor (in the same way) */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "do_stop {%d} -> handle waiting acceptors(s)\r\n",
+ descP->sock) );
+
+ esock_inform_waiting_procs(env, "acceptor",
+ descP, sockRef, &descP->acceptorsQ,
+ esock_atom_closed);
+
+ descP->currentAcceptorP = NULL;
+ }
+
+ return ret;
+}
+
+
+
/* ========================================================================
* Perform the final step in the socket close.
*/
@@ -2876,102 +3328,1560 @@ ERL_NIF_TERM essio_fin_close(ErlNifEnv* env,
/* ========================================================================
+ * *** essio_shutdown should go here - if we need one ***
+ */
+
+
+/* ========================================================================
+ * *** essio_sockname should go here - if we need one ***
*/
+
+
+/* ========================================================================
+ * *** essio_peername should go here - if we need one ***
+ */
+
+
+/* ========================================================================
+ * Cancel a connect request.
+ */
+
extern
-ERL_NIF_TERM essio_shutdown(ErlNifEnv* env,
- ESockDescriptor* descP,
- int how)
+ERL_NIF_TERM essio_cancel_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef)
{
- if (! IS_OPEN(descP->readState))
- return esock_make_error_closed(env);
+ ERL_NIF_TERM res;
+ ErlNifPid self;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_connect {%d} -> entry with"
+ "\r\n writeState: 0x%X"
+ "\r\n opRef: %T"
+ "\r\n",
+ descP->sock, descP->writeState, opRef) );
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ if (! IS_OPEN(descP->writeState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if ((descP->connectorP == NULL) ||
+ (COMPARE_PIDS(&self, &descP->connector.pid) != 0) ||
+ (COMPARE(opRef, descP->connector.ref) != 0)) {
+
+ res = esock_make_error(env, esock_atom_not_found);
+
+ } else {
+
+ res = esock_cancel_write_select(env, descP, opRef);
+ esock_requestor_release("esock_cancel_connect",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+ descP->writeState &= ~ESOCK_STATE_CONNECTING;
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_connect {%d} -> done when"
+ "\r\n res: %T"
+ "\r\n",
+ descP->sock, descP->writeState,
+ opRef, res) );
+
+ return res;
+}
+
+
+
+/* ========================================================================
+ * Cancel accept request
+ *
+ * We have two different cases:
+ * *) Its the current acceptor
+ * Cancel the select!
+ * We need to activate one of the waiting acceptors.
+ * *) Its one of the acceptors ("waiting") in the queue
+ * Simply remove the acceptor from the queue.
+ *
+ */
+extern
+ERL_NIF_TERM essio_cancel_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
+{
+ ERL_NIF_TERM res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_accept(%T), {%d,0x%X} ->"
+ "\r\n opRef: %T"
+ "\r\n %s"
+ "\r\n",
+ sockRef, descP->sock, descP->readState,
+ opRef,
+ ((descP->currentAcceptorP == NULL)
+ ? "without acceptor" : "with acceptor")) );
+
+ if (! IS_OPEN(descP->readState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if (descP->currentAcceptorP == NULL) {
+
+ res = esock_atom_not_found;
+
+ } else {
+ ErlNifPid self;
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ if (COMPARE_PIDS(&self, &descP->currentAcceptor.pid) == 0) {
+ if (COMPARE(opRef, descP->currentAcceptor.ref) == 0)
+ res = essio_cancel_accept_current(env, descP, sockRef);
+ else
+ res = esock_atom_not_found;
+ } else {
+ res = essio_cancel_accept_waiting(env, descP, opRef, &self);
+ }
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_cancel_accept(%T) -> done with result:"
+ "\r\n %T"
+ "\r\n", sockRef, res) );
+
+ return res;
+}
+
+
+/* The current acceptor process has an ongoing select we first must
+ * cancel. Then we must re-activate the "first" (the first
+ * in the acceptor queue).
+ */
+static
+ERL_NIF_TERM essio_cancel_accept_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM res;
+
+ ESOCK_ASSERT( DEMONP("essio_cancel_accept_current -> current acceptor",
+ env, descP, &descP->currentAcceptor.mon) == 0);
+ MON_INIT(&descP->currentAcceptor.mon);
+ res = esock_cancel_read_select(env, descP, descP->currentAcceptor.ref);
- if (sock_shutdown(descP->sock, how) == 0)
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_accept_current(%T) {%d} -> cancel res: %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+ if (!esock_activate_next_acceptor(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_accept_current(%T) {%d} -> "
+ "no more acceptors\r\n",
+ sockRef, descP->sock) );
+
+ descP->readState &= ~ESOCK_STATE_ACCEPTING;
+
+ descP->currentAcceptorP = NULL;
+ }
+
+ return res;
+}
+
+
+/* These processes have not performed a select, so we can simply
+ * remove them from the acceptor queue.
+ */
+static
+ERL_NIF_TERM essio_cancel_accept_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP)
+{
+ /* unqueue request from (acceptor) queue */
+
+ if (esock_acceptor_unqueue(env, descP, &opRef, selfP)) {
return esock_atom_ok;
- else
- return esock_make_error_errno(env, sock_errno());
+ } else {
+ return esock_atom_not_found;
+ }
}
+
/* ========================================================================
+ * Cancel send request
+ *
+ * Cancel a send operation.
+ * Its either the current writer or one of the waiting writers.
*/
+
extern
-ERL_NIF_TERM essio_sockname(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM essio_cancel_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
{
- ESockAddress sa;
- ESockAddress* saP = &sa;
- SOCKLEN_T sz = sizeof(ESockAddress);
+ ERL_NIF_TERM res;
- if (! IS_OPEN(descP->readState))
- return esock_make_error_closed(env);
-
SSDBG( descP,
- ("UNIX-ESSIO", "essio_sockname {%d} -> open - try get sockname\r\n",
- descP->sock) );
+ ("UNIX-ESSIO",
+ "essio_cancel_send(%T), {%d,0x%X} -> entry with"
+ "\r\n opRef: %T"
+ "\r\n %s"
+ "\r\n",
+ sockRef, descP->sock, descP->writeState,
+ opRef,
+ ((descP->currentWriterP == NULL)
+ ? "without writer" : "with writer")) );
+
+ if (! IS_OPEN(descP->writeState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if (descP->currentWriterP == NULL) {
+
+ res = esock_atom_not_found;
- sys_memzero((char*) saP, sz);
- if (sock_name(descP->sock, (struct sockaddr*) saP, &sz) < 0) {
- return esock_make_error_errno(env, sock_errno());
} else {
- ERL_NIF_TERM esa;
+ ErlNifPid self;
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+ if (COMPARE_PIDS(&self, &descP->currentWriter.pid) == 0) {
+ if (COMPARE(opRef, descP->currentWriter.ref) == 0)
+ res = essio_cancel_send_current(env, descP, sockRef);
+ else
+ res = esock_atom_not_found;
+ } else {
+ res = essio_cancel_send_waiting(env, descP, opRef, &self);
+ }
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_cancel_send(%T) {%d} -> done with result:"
+ "\r\n %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+ return res;
+}
+
+
+
+/* The current writer process has an ongoing select we first must
+ * cancel. Then we must re-activate the "first" (the first
+ * in the writer queue).
+ */
+static
+ERL_NIF_TERM essio_cancel_send_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM res;
+
+ ESOCK_ASSERT( DEMONP("essio_cancel_send_current -> current writer",
+ env, descP, &descP->currentWriter.mon) == 0);
+ res = esock_cancel_write_select(env, descP, descP->currentWriter.ref);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_cancel_send_current(%T) {%d} -> cancel res: %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+ if (!esock_activate_next_writer(env, descP, sockRef)) {
SSDBG( descP,
- ("UNIX-ESSIO", "essio_sockname {%d} -> "
- "got sockname - try decode\r\n",
- descP->sock) );
+ ("UNIX-ESSIO",
+ "essio_cancel_send_current(%T) {%d} -> no more writers"
+ "\r\n", sockRef, descP->sock) );
+
+ descP->currentWriterP = NULL;
+ }
+
+ return res;
+}
+
- esock_encode_sockaddr(env, saP, sz, &esa);
+/* These processes have not performed a select, so we can simply
+ * remove them from the writer queue.
+ */
+static
+ERL_NIF_TERM essio_cancel_send_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP)
+{
+ /* unqueue request from (writer) queue */
+
+ if (esock_writer_unqueue(env, descP, &opRef, selfP)) {
+ return esock_atom_ok;
+ } else {
+ return esock_atom_not_found;
+ }
+}
+
+
+
+/* ========================================================================
+ * Cancel receive request
+ *
+ * Cancel a read operation.
+ * Its either the current reader or one of the waiting readers.
+ */
+extern
+ERL_NIF_TERM essio_cancel_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
+{
+ ERL_NIF_TERM res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_recv(%T), {%d,0x%X} -> entry with"
+ "\r\n opRef: %T"
+ "\r\n %s"
+ "\r\n",
+ sockRef, descP->sock, descP->readState,
+ opRef,
+ ((descP->currentReaderP == NULL)
+ ? "without reader" : "with reader")) );
+
+ if (! IS_OPEN(descP->readState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if (descP->currentReaderP == NULL) {
+
+ res = esock_atom_not_found;
+
+ } else {
+ ErlNifPid self;
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ if (COMPARE_PIDS(&self, &descP->currentReader.pid) == 0) {
+ if (COMPARE(opRef, descP->currentReader.ref) == 0)
+ res = essio_cancel_recv_current(env, descP, sockRef);
+ else
+ res = esock_atom_not_found;
+ } else {
+ res = essio_cancel_recv_waiting(env, descP, opRef, &self);
+ }
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_cancel_recv(%T) {%d} -> done with result:"
+ "\r\n %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+
+ return res;
+
+}
+
+
+/* The current reader process has an ongoing select we first must
+ * cancel. Then we must re-activate the "first" (the first
+ * in the reader queue).
+ */
+static
+ERL_NIF_TERM essio_cancel_recv_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM res;
+
+ ESOCK_ASSERT( DEMONP("essio_cancel_recv_current -> current reader",
+ env, descP, &descP->currentReader.mon) == 0);
+ res = esock_cancel_read_select(env, descP, descP->currentReader.ref);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_cancel_recv_current(%T) {%d} -> cancel res: %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+ if (!esock_activate_next_reader(env, descP, sockRef)) {
SSDBG( descP,
- ("UNIX-ESSIO", "essio_sockname {%d} -> decoded: "
- "\r\n %T\r\n",
- descP->sock, esa) );
+ ("UNIX-ESSIO",
+ "essio_cancel_recv_current(%T) {%d} -> no more readers"
+ "\r\n", sockRef, descP->sock) );
- return esock_make_ok2(env, esa);
+ descP->currentReaderP = NULL;
+ }
+
+ return res;
+}
+
+
+/* These processes have not performed a select, so we can simply
+ * remove them from the reader queue.
+ */
+static
+ERL_NIF_TERM essio_cancel_recv_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP)
+{
+ /* unqueue request from (reader) queue */
+
+ if (esock_reader_unqueue(env, descP, &opRef, selfP)) {
+ return esock_atom_ok;
+ } else {
+ return esock_atom_not_found;
}
}
+
+/* ========================================================================
+ * IOCTL with two args (socket and request "key")
+ *
+ */
+extern
+ERL_NIF_TERM essio_ioctl2(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req)
+{
+ switch (req) {
+
+#if defined(SIOCGIFCONF)
+ case SIOCGIFCONF:
+ return essio_ioctl_gifconf(env, descP);
+ break;
+#endif
+
+ default:
+ return esock_make_error(env, esock_atom_enotsup);
+ break;
+ }
+
+}
+
+
+
+/* ========================================================================
+ * IOCTL with three args (socket, request "key" and one argument)
+ *
+ * The type and value of 'arg' depend on the request,
+ * which we have not yet "analyzed".
+ *
+ * Request arg arg type
+ * ------- ------- --------
+ * gifname ifindex integer
+ * gifindex name string
+ * gifflags name string
+ * gifaddr name string
+ * gifdstaddr name string
+ * gifbdraddr name string
+ * gifnetmask name string
+ * gifmtu name string
+ * gifhwaddr name string
+ * gifmap name string
+ * giftxqlen name string
+ */
+extern
+ERL_NIF_TERM essio_ioctl3(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req,
+ ERL_NIF_TERM arg)
+{
+ /* This for *get* requests */
+
+ switch (req) {
+
+#if defined(SIOCGIFNAME)
+ case SIOCGIFNAME:
+ return essio_ioctl_gifname(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFINDEX)
+ case SIOCGIFINDEX:
+ return essio_ioctl_gifindex(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFFLAGS)
+ case SIOCGIFFLAGS:
+ return essio_ioctl_gifflags(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFADDR)
+ case SIOCGIFADDR:
+ return essio_ioctl_gifaddr(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFDSTADDR)
+ case SIOCGIFDSTADDR:
+ return essio_ioctl_gifdstaddr(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFBRDADDR)
+ case SIOCGIFBRDADDR:
+ return essio_ioctl_gifbrdaddr(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFNETMASK)
+ case SIOCGIFNETMASK:
+ return essio_ioctl_gifnetmask(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFMTU)
+ case SIOCGIFMTU:
+ return essio_ioctl_gifmtu(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
+ case SIOCGIFHWADDR:
+ return essio_ioctl_gifhwaddr(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
+ case SIOCGIFMAP:
+ return essio_ioctl_gifmap(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFTXQLEN)
+ case SIOCGIFTXQLEN:
+ return essio_ioctl_giftxqlen(env, descP, arg);
+ break;
+#endif
+
+ default:
+ return esock_make_error(env, esock_atom_enotsup);
+ break;
+ }
+
+}
+
+
+
/* ========================================================================
+ * IOCTL with four args (socket, request "key" and two arguments)
+ *
+ * The type and value of arg(s) depend on the request,
+ * which we have not yet "analyzed".
+ *
+ * Request arg1 arg1 type arg2 arg2 type
+ * ------- ------- --------- ------ ---------
+ * sifflags name string Flags #{IntFlag := boolean()}
+ * IntFlag is the native flag
+ * sifaddr name string Addr sockaddr()
+ * sifdstaddr name string DstAddr sockaddr()
+ * sifbrdaddr name string BrdAddr sockaddr()
+ * sifnetmask name string NetMask sockaddr()
+ * gifmtu name string MTU integer()
+ * sifhwaddr name string HwAddr sockaddr()
+ * giftxqlen name string Len integer()
*/
extern
-ERL_NIF_TERM essio_peername(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM essio_ioctl4(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req,
+ ERL_NIF_TERM ename,
+ ERL_NIF_TERM eval)
+{
+
+ switch (req) {
+
+#if defined(SIOCSIFFLAGS)
+ case SIOCSIFFLAGS:
+ return essio_ioctl_sifflags(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFADDR)
+ case SIOCSIFADDR:
+ return essio_ioctl_sifaddr(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFDSTADDR)
+ case SIOCSIFDSTADDR:
+ return essio_ioctl_sifdstaddr(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFBRDADDR)
+ case SIOCSIFBRDADDR:
+ return essio_ioctl_sifbrdaddr(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFNETMASK)
+ case SIOCSIFNETMASK:
+ return essio_ioctl_sifnetmask(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFMTU)
+ case SIOCSIFMTU:
+ return essio_ioctl_sifmtu(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFTXQLEN)
+ case SIOCSIFTXQLEN:
+ return essio_ioctl_siftxqlen(env, descP, ename, eval);
+ break;
+#endif
+
+ default:
+ return esock_make_error(env, esock_atom_enotsup);
+ break;
+ }
+
+}
+
+
+
+/* ===========================================================================
+ * The implemented (ioctl) get requests falls into three grops:
+ *
+ * 1) gifconf - Takes no argument other then the request
+ * 2) gifname - Takes the interface index (integer) as an argument
+ * 3) other - All other (get) requests takes the interface name (string)
+ * as the argument.
+ *
+ * The functions defined using the macros below are all in the third (3)
+ * group.
+ *
+ */
+
+/* *** essio_ioctl_gifindex *** */
+#if defined(SIOCGIFINDEX)
+#if defined(ESOCK_USE_IFINDEX)
+#define IOCTL_GIFINDEX_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifindex, SIOCGIFINDEX, ivalue, ifreq.ifr_ifindex)
+#elif defined(ESOCK_USE_INDEX)
+#define IOCTL_GIFINDEX_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifindex, SIOCGIFINDEX, ivalue, ifreq.ifr_index)
+#else
+#define IOCTL_GIFINDEX_FUNC_DECL
+#endif
+#else
+#define IOCTL_GIFINDEX_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifflags *** */
+#if defined(SIOCGIFFLAGS)
+#define IOCTL_GIFFLAGS_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifflags, SIOCGIFFLAGS, flags, ifreq.ifr_flags)
+#else
+#define IOCTL_GIFFLAGS_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifaddr *** */
+#if defined(SIOCGIFADDR)
+#define IOCTL_GIFADDR_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifaddr, SIOCGIFADDR, ifraddr, &ifreq.ifr_addr)
+#else
+#define IOCTL_GIFADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifdstaddr *** */
+#if defined(SIOCGIFDSTADDR)
+#define IOCTL_GIFDSTADDR_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifdstaddr, SIOCGIFDSTADDR, ifraddr, &ifreq.ifr_dstaddr)
+#else
+#define IOCTL_GIFDSTADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifbrdaddr *** */
+#if defined(SIOCGIFBRDADDR)
+#define IOCTL_GIFBRDADDR_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifbrdaddr, SIOCGIFBRDADDR, ifraddr, &ifreq.ifr_broadaddr)
+#else
+#define IOCTL_GIFBRDADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifnetmask *** */
+#if defined(SIOCGIFNETMASK)
+#ifdef __linux__
+#define IOCTL_GIFNETMASK_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifnetmask, SIOCGIFNETMASK, ifraddr, &ifreq.ifr_netmask)
+#else
+#define IOCTL_GIFNETMASK_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifnetmask, SIOCGIFNETMASK, ifraddr, &ifreq.ifr_addr)
+#endif
+#else
+#define IOCTL_GIFNETMASK_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifmtu *** */
+#if defined(SIOCGIFMTU)
+#define IOCTL_GIFMTU_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifmtu, SIOCGIFMTU, ivalue, ifreq.ifr_mtu)
+#else
+#define IOCTL_GIFMTU_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifhwaddr *** */
+#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
+#define IOCTL_GIFHWADDR_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifhwaddr, SIOCGIFHWADDR, hwaddr, &ifreq.ifr_hwaddr)
+#else
+#define IOCTL_GIFHWADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifmap *** */
+#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
+#define IOCTL_GIFMAP_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifmap, SIOCGIFMAP, ifrmap, &ifreq.ifr_map)
+#else
+#define IOCTL_GIFMAP_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_giftxqlen *** */
+#if defined(SIOCGIFTXQLEN)
+#define IOCTL_GIFTXQLEN_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(giftxqlen, SIOCGIFTXQLEN, ivalue, ifreq.ifr_qlen)
+#else
+#define IOCTL_GIFTXQLEN_FUNC_DECL
+#endif
+
+#define IOCTL_GET_FUNCS \
+ IOCTL_GIFINDEX_FUNC_DECL \
+ IOCTL_GIFFLAGS_FUNC_DECL \
+ IOCTL_GIFADDR_FUNC_DECL \
+ IOCTL_GIFDSTADDR_FUNC_DECL \
+ IOCTL_GIFBRDADDR_FUNC_DECL \
+ IOCTL_GIFNETMASK_FUNC_DECL \
+ IOCTL_GIFMTU_FUNC_DECL \
+ IOCTL_GIFHWADDR_FUNC_DECL \
+ IOCTL_GIFMAP_FUNC_DECL \
+ IOCTL_GIFTXQLEN_FUNC_DECL
+
+#define IOCTL_GET_REQUEST_DECL(OR, R, EF, UV) \
+ static \
+ ERL_NIF_TERM essio_ioctl_##OR(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM ename) \
+ { \
+ ERL_NIF_TERM result; \
+ struct ifreq ifreq; \
+ char* ifn = NULL; \
+ int nlen; \
+ \
+ SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> entry with" \
+ "\r\n (e)Name: %T" \
+ "\r\n", descP->sock, ename) ); \
+ \
+ if (!esock_decode_string(env, ename, &ifn)) \
+ return enif_make_badarg(env); \
+ \
+ nlen = esock_strnlen(ifn, IFNAMSIZ); \
+ \
+ sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); \
+ sys_memcpy(ifreq.ifr_name, ifn, \
+ (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", \
+ "essio_ioctl_" #OR " {%d} -> try ioctl\r\n", \
+ descP->sock) ); \
+ \
+ if (ioctl(descP->sock, R, (char *) &ifreq) < 0) { \
+ int saveErrno = sock_errno(); \
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failure: " \
+ "\r\n reason: %T (%d)" \
+ "\r\n", descP->sock, reason, saveErrno) ); \
+ \
+ result = esock_make_error(env, reason); \
+ \
+ } else { \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> encode value\r\n", \
+ descP->sock) ); \
+ result = encode_ioctl_##EF(env, descP, UV); \
+ } \
+ \
+ FREE(ifn); \
+ \
+ return result; \
+ \
+ }
+IOCTL_GET_FUNCS
+#undef IOCTL_GET_FUNCS
+
+
+/* ===========================================================================
+ * The "rest" of the implemented (ioctl) get requests
+ *
+ * These (get) requests could not be 'generated' by the macros above.
+ */
+
+static
+ERL_NIF_TERM essio_ioctl_gifconf(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ struct ifconf ifc;
+ int ifc_len = 0;
+ int buflen = 100 * sizeof(struct ifreq);
+ char *buf = MALLOC(buflen);
+ ERL_NIF_TERM result;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_gifconf {%d} -> entry\r\n", descP->sock) );
+
+ for (;;) {
+ ifc.ifc_len = buflen;
+ ifc.ifc_buf = buf;
+ if (ioctl(descP->sock, SIOCGIFCONF, (char *) &ifc) < 0) {
+ int saveErrno = sock_errno();
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_gifconf {%d} -> failure: "
+ "\r\n errno: %d (%s)"
+ "\r\n", descP->sock, saveErrno, erl_errno_id(saveErrno)) );
+
+ if (saveErrno != EINVAL || ifc_len) {
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
+ FREE(buf);
+ return esock_make_error(env, reason);
+ }
+ } else {
+ if (ifc.ifc_len == ifc_len) break; /* buf large enough */
+ ifc_len = ifc.ifc_len;
+ }
+ buflen += 10 * sizeof(struct ifreq);
+ buf = (char *) REALLOC(buf, buflen);
+ }
+
+ result = encode_ioctl_ifconf(env, descP, &ifc);
+
+ FREE(ifc.ifc_buf);
+
+ return result;
+}
+
+
+#if defined(SIOCGIFNAME)
+static
+ERL_NIF_TERM essio_ioctl_gifname(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eidx)
{
- ESockAddress sa;
- ESockAddress* saP = &sa;
- SOCKLEN_T sz = sizeof(ESockAddress);
+ ERL_NIF_TERM result;
+ struct ifreq ifreq;
+ int index;
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> entry with"
+ "\r\n (e)Index: %T"
+ "\r\n", descP->sock, eidx) );
- if (! IS_OPEN(descP->readState))
- return esock_make_error_closed(env);
+ if (!GET_INT(env, eidx, &index))
+ return enif_make_badarg(env);
+
+ ifreq.ifr_ifindex = index;
SSDBG( descP,
- ("UNIX-ESSIO", "essio_peername {%d} -> open - try get peername\r\n",
- descP->sock) );
+ ("UNIX-ESSIO",
+ "essio_ioctl_gifname {%d} -> try ioctl\r\n", descP->sock) );
+
+ if (ioctl(descP->sock, SIOCGIFNAME, (char *) &ifreq) < 0) {
+ int saveErrno = sock_errno();
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> failure: "
+ "\r\n reason: %T (%d)"
+ "\r\n", descP->sock, reason, saveErrno) );
+
+ result = esock_make_error(env, reason);
- sys_memzero((char*) saP, sz);
- if (sock_peer(descP->sock, (struct sockaddr*) saP, &sz) < 0) {
- return esock_make_error_errno(env, sock_errno());
} else {
- ERL_NIF_TERM esa;
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> encode name\r\n",
+ descP->sock) );
+
+ result = esock_make_ok2(env, encode_ioctl_ifreq_name(env, ifreq.ifr_name));
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> done with"
+ "\r\n result: %T"
+ "\r\n",
+ descP->sock, result) );
+
+ return result;
+
+}
+#endif
+
+
+
+
+/* ===========================================================================
+ * The implemented (ioctl) set requests:
+ *
+ */
+
+/* *** essio_ioctl_sifaddr *** */
+#if defined(SIOCSIFADDR)
+#define IOCTL_SIFADDR_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifaddr, SIOCSIFADDR, sockaddr, \
+ ((ESockAddress*) &ifreq.ifr_addr))
+#else
+#define IOCTL_SIFADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_sifdstaddr *** */
+#if defined(SIOCSIFDSTADDR)
+#define IOCTL_SIFDSTADDR_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifdstaddr, SIOCSIFDSTADDR, sockaddr, \
+ ((ESockAddress*) &ifreq.ifr_dstaddr))
+#else
+#define IOCTL_SIFDSTADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_sifbrdaddr *** */
+#if defined(SIOCSIFBRDADDR)
+#define IOCTL_SIFBRDADDR_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifbrdaddr, SIOCSIFBRDADDR, sockaddr, \
+ ((ESockAddress*) &ifreq.ifr_broadaddr))
+#else
+#define IOCTL_SIFBRDADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_sifnetmask *** */
+#if defined(SIOCSIFNETMASK)
+#ifdef __linux__
+#define IOCTL_SIFNETMASK_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifnetmask, SIOCSIFNETMASK, sockaddr, \
+ ((ESockAddress*) &ifreq.ifr_netmask))
+#else
+#define IOCTL_SIFNETMASK_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifnetmask, SIOCSIFNETMASK, sockaddr, \
+ ((ESockAddress*) &ifreq.ifr_addr))
+#endif
+#else
+#define IOCTL_SIFNETMASK_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_sifmtu ***
+ * On some platforms, MTU is an unsigned int
+ */
+#if defined(SIOCSIFMTU)
+#define IOCTL_SIFMTU_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifmtu, SIOCSIFMTU, mtu, (int*) &ifreq.ifr_mtu)
+#else
+#define IOCTL_SIFMTU_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_siftxqlen *** */
+#if defined(SIOCSIFTXQLEN)
+#define IOCTL_SIFTXQLEN_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(siftxqlen, SIOCSIFTXQLEN, txqlen, &ifreq.ifr_qlen)
+#else
+#define IOCTL_SIFTXQLEN_FUNC_DECL
+#endif
+
+#define IOCTL_SET_FUNCS \
+ IOCTL_SIFADDR_FUNC_DECL \
+ IOCTL_SIFDSTADDR_FUNC_DECL \
+ IOCTL_SIFBRDADDR_FUNC_DECL \
+ IOCTL_SIFNETMASK_FUNC_DECL \
+ IOCTL_SIFMTU_FUNC_DECL \
+ IOCTL_SIFTXQLEN_FUNC_DECL
+
+#define IOCTL_SET_REQUEST_DECL(OR, R, DF, UVP) \
+ static \
+ ERL_NIF_TERM essio_ioctl_##OR(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM ename, \
+ ERL_NIF_TERM evalue) \
+ { \
+ ERL_NIF_TERM result; \
+ struct ifreq ifreq; \
+ char* ifn = NULL; \
+ int nlen; \
+ \
+ SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> entry with" \
+ "\r\n (e)Name: %T" \
+ "\r\n (e)Value: %T" \
+ "\r\n", descP->sock, ename, evalue) ); \
+ \
+ if (!esock_decode_string(env, ename, &ifn)) { \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failed decode name" \
+ "\r\n", descP->sock) ); \
+ \
+ return enif_make_badarg(env); \
+ } \
+ \
+ if (! decode_ioctl_##DF(env, descP, evalue, UVP)) { \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failed decode addr" \
+ "\r\n", descP->sock) ); \
+ \
+ return esock_make_invalid(env, esock_atom_##DF); \
+ } \
+ \
+ nlen = esock_strnlen(ifn, IFNAMSIZ); \
+ \
+ sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); \
+ sys_memcpy(ifreq.ifr_name, ifn, \
+ (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> try ioctl\r\n", \
+ descP->sock) ); \
+ \
+ if (ioctl(descP->sock, R, (char *) &ifreq) < 0) { \
+ int saveErrno = sock_errno(); \
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failure: " \
+ "\r\n reason: %T (%d)" \
+ "\r\n", descP->sock, reason, saveErrno) ); \
+ \
+ result = esock_make_error(env, reason); \
+ \
+ } else { \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> " \
+ "addr successfully set\r\n", \
+ descP->sock) ); \
+ result = esock_atom_ok; \
+ } \
+ \
+ FREE(ifn); \
+ \
+ return result; \
+ \
+ }
+
+IOCTL_SET_FUNCS
+#undef IOCTL_SET_FUNCS
+
+
+/* ===========================================================================
+ * The "rest" of the implemented (ioctl) set requests
+ *
+ * These (set) requests could not be 'generated' by the macros above.
+ */
+
+#if defined(SIOCSIFFLAGS)
+static
+ERL_NIF_TERM essio_ioctl_sifflags(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM ename,
+ ERL_NIF_TERM eflags)
+{
+ ERL_NIF_TERM result;
+ struct ifreq ifreq;
+ char* ifn = NULL;
+ int nlen;
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> entry with"
+ "\r\n (e)Name: %T"
+ "\r\n (e)Flags: %T"
+ "\r\n", descP->sock, ename, eflags) );
+
+ if (!esock_decode_string(env, ename, &ifn)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> failed decode name"
+ "\r\n", descP->sock) );
+
+ return enif_make_badarg(env);
+ }
+
+ // Make sure the length of the string is valid!
+ nlen = esock_strnlen(ifn, IFNAMSIZ);
+
+ sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); // Just in case
+ sys_memcpy(ifreq.ifr_name, ifn,
+ (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> try (get) ioctl\r\n",
+ descP->sock) );
+
+ if (ioctl(descP->sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) {
+ int saveErrno = sock_errno();
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> "
+ "failure: failed reading *current* flags"
+ "\r\n reason: %T (%d)"
+ "\r\n", descP->sock, reason, saveErrno) );
+
+ result = esock_make_error(env, reason);
+
+ } else {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_ioctl_sifflags {%d} -> (local) update flags\r\n",
+ descP->sock) );
+
+ if (decode_ioctl_flags(env, descP, eflags, &ifreq.ifr_flags)) {
SSDBG( descP,
- ("UNIX-ESSIO", "essio_peername {%d} -> "
- "got peername - try decode\r\n",
- descP->sock) );
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> try (set) ioctl\r\n",
+ descP->sock) );
+
+ if (ioctl(descP->sock, SIOCSIFFLAGS, (char *) &ifreq) < 0) {
+ int saveErrno = sock_errno();
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
- esock_encode_sockaddr(env, saP, sz, &esa);
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> failure: "
+ "\r\n reason: %T (%d)"
+ "\r\n", descP->sock, reason, saveErrno) );
+ result = esock_make_error(env, reason);
+
+ } else {
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> "
+ "updated flags successfully set\r\n",
+ descP->sock) );
+ result = esock_atom_ok;
+ }
+
+ /* We know that if esock_decode_string is successful,
+ * we have "some" form of string, and therefor memory
+ * has been allocated (and need to be freed)... */
+ FREE(ifn);
+
+ } else {
+ result = enif_make_badarg(env);
+ }
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> done with result: "
+ "\r\n %T"
+ "\r\n",
+ descP->sock, result) );
+
+ return result;
+
+}
+#endif
+
+
+
+/* ===========================================================================
+ * ioctl utility functions
+ *
+ */
+
+static
+ERL_NIF_TERM encode_ioctl_ifconf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifconf* ifcP)
+{
+ ERL_NIF_TERM result;
+ unsigned int len = ((ifcP == NULL) ? 0 :
+ (ifcP->ifc_len / sizeof(struct ifreq)));
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "encode_ioctl_ifconf -> entry (when len = %d)\r\n", len) );
+
+ if (len > 0) {
+ ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM));
+ unsigned int i = 0;
+ struct ifreq* p = ifcP->ifc_req;
+
+ for (i = 0 ; i < len ; i++) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "encode_ioctl_ifconf -> encode ifreq entry %d\r\n", i) );
+ array[i] = encode_ioctl_ifconf_ifreq(env, descP, &p[i]);
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_ioctl_ifconf -> all entries encoded\r\n", i) );
+
+ result = esock_make_ok2(env, MKLA(env, array, len));
+ FREE(array);
+
+ } else {
+
+ result = esock_make_ok2(env, MKEL(env));
+
+ }
+
+ return result;
+}
+
+
+#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
+static
+ERL_NIF_TERM encode_ioctl_ifrmap(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifmap* mapP)
+{
+ ERL_NIF_TERM mapKeys[] = {esock_atom_mem_start,
+ esock_atom_mem_end,
+ esock_atom_base_addr,
+ esock_atom_irq,
+ esock_atom_dma,
+ esock_atom_port};
+ ERL_NIF_TERM mapVals[] = {MKUL(env, mapP->mem_start),
+ MKUL(env, mapP->mem_end),
+ MKUI(env, mapP->base_addr),
+ MKUI(env, mapP->irq),
+ MKUI(env, mapP->dma),
+ MKUI(env, mapP->port)};
+ unsigned int numMapKeys = NUM(mapKeys);
+ unsigned int numMapVals = NUM(mapVals);
+ ERL_NIF_TERM emap;
+
+ ESOCK_ASSERT( numMapVals == numMapKeys );
+ ESOCK_ASSERT( MKMA(env, mapKeys, mapVals, numMapKeys, &emap) );
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ifrmap -> done with"
+ "\r\n Map: %T"
+ "\r\n", emap) );
+
+ return esock_make_ok2(env, emap);;
+}
+#endif
+
+
+#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
+static
+ERL_NIF_TERM encode_ioctl_hwaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct sockaddr* addrP)
+{
+ ERL_NIF_TERM eaddr;
+ SOCKLEN_T sz = sizeof(struct sockaddr);
+
+ esock_encode_hwsockaddr(env, addrP, sz, &eaddr);
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ifraddr -> done with"
+ "\r\n Sock Addr: %T"
+ "\r\n", eaddr) );
+
+ return esock_make_ok2(env, eaddr);;
+}
+#endif
+
+
+static
+ERL_NIF_TERM encode_ioctl_ifraddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct sockaddr* addrP)
+{
+ ERL_NIF_TERM eaddr;
+
+ esock_encode_sockaddr(env, (ESockAddress*) addrP, -1, &eaddr);
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ifraddr -> done with"
+ "\r\n Sock Addr: %T"
+ "\r\n", eaddr) );
+
+ return esock_make_ok2(env, eaddr);;
+}
+
+
+static
+ERL_NIF_TERM encode_ioctl_flags(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ short flags)
+{
+ int i, flag, num = esock_ioctl_flags_length; // NUM(ioctl_flags);
+ ERL_NIF_TERM eflags, eflag;
+ SocketTArray ta = TARRAY_CREATE(20); // Just to be on the safe side
+
+ if (flags == 0) {
+ eflags = MKEL(env);
+ } else {
+ for (i = 0; (i < num) && (flags != 0); i++) {
+ flag = esock_ioctl_flags[i].flag;
+ if ((flag != 0) && ((flags & flag) == flag)) {
+ eflag = *(esock_ioctl_flags[i].name);
+ flags &= ~flag;
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_flags {%d} -> "
+ "\r\n i: %d"
+ "\r\n found flag: %T (%d)"
+ "\r\n remaining flags: %d"
+ "\r\n", descP->sock, i, eflag, flag, flags) );
+
+ TARRAY_ADD(ta, eflag);
+ }
+ }
+ if (flags != 0) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_ioctl_flags {%d} -> unknown flag(s): %d"
+ "\r\n", descP->sock, flags) );
+
+ TARRAY_ADD(ta, MKI(env, flags));
+ }
+
+ TARRAY_TOLIST(ta, env, &eflags);
+ }
+
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_flags -> done with"
+ "\r\n Flags: %T (%d)"
+ "\r\n", eflags, flags) );
+
+ return esock_make_ok2(env, eflags);
+}
+
+
+static
+BOOLEAN_T decode_ioctl_sockaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eaddr,
+ ESockAddress* addr)
+{
+ SOCKLEN_T addrLen;
+ BOOLEAN_T result;
+
+ result = esock_decode_sockaddr(env, eaddr, (ESockAddress*) addr, &addrLen);
+
+ VOID(addrLen);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_sockaddr {%d} -> decode result: %s"
+ "\r\n", descP->sock, B2S(result)) );
+
+ return result;
+}
+
+
+static
+BOOLEAN_T decode_ioctl_mtu(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM emtu,
+ int* mtu)
+{
+ BOOLEAN_T result;
+
+ if (! GET_INT(env, emtu, mtu)) {
+ result = FALSE;
+ } else {
+ result = TRUE;
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_mtu {%d} -> decode result: %s"
+ "\r\n", descP->sock, B2S(result)) );
+
+ return result;
+}
+
+
+#if defined(SIOCSIFTXQLEN)
+static
+BOOLEAN_T decode_ioctl_txqlen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM etxqlen,
+ int* txqlen)
+{
+ return decode_ioctl_ivalue(env, descP, etxqlen, txqlen);
+}
+#endif
+
+/* All uses of the function should be added. For instance:
+ * #if defined(SIOCGIFTXQLEN) || defined(FOOBAR) || defined(YXA)
+ */
+#if defined(SIOCGIFTXQLEN)
+static
+BOOLEAN_T decode_ioctl_ivalue(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eivalue,
+ int* ivalue)
+{
+ BOOLEAN_T result;
+
+ if (! GET_INT(env, eivalue, ivalue)) {
+ result = FALSE;
+ } else {
+ result = TRUE;
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_ivalue {%d} -> decode result: %s"
+ "\r\n", descP->sock, B2S(result)) );
+
+ return result;
+}
+#endif
+
+
+static
+BOOLEAN_T decode_ioctl_flags(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eflags,
+ short* flags)
+{
+ ERL_NIF_TERM key, value;
+ ErlNifMapIterator iter;
+ int tmpFlags = (int) *flags; // Current value
+ int flag;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> entry with"
+ "\r\n flags: %d"
+ "\r\n",
+ descP->sock, tmpFlags) );
+
+ enif_map_iterator_create(env, eflags, &iter, ERL_NIF_MAP_ITERATOR_FIRST);
+
+ while (enif_map_iterator_get_pair(env, &iter, &key, &value)) {
+
+ /* Convert key (eflag) to int */
+ if (! GET_INT(env, key, &flag)) {
+ enif_map_iterator_destroy(env, &iter);
+ return FALSE;
+ }
+
+ // Update flag
+ if (COMPARE(value, esock_atom_true) == 0) {
SSDBG( descP,
- ("UNIX-ESSIO", "esock_peername {%d} -> decoded: "
- "\r\n %T\r\n",
- descP->sock, esa) );
+ ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> set %d\r\n",
+ descP->sock, flag) );
+ tmpFlags |= flag;
+ } else {
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> reset %d\r\n",
+ descP->sock, flag) );
+ tmpFlags &= ~flag;
+ }
+
+ enif_map_iterator_next(env, &iter);
+ }
+
+ enif_map_iterator_destroy(env, &iter);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> done with"
+ "\r\n (new) flags: %d"
+ "\r\n",
+ descP->sock, tmpFlags) );
+
+ *flags = (short) tmpFlags;
+
+ return TRUE;
+}
+
+
+static
+ERL_NIF_TERM encode_ioctl_ivalue(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int ivalue)
+{
+ ERL_NIF_TERM eivalue = MKI(env, ivalue);
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ivalue -> done with"
+ "\r\n iValue: %T (%d)"
+ "\r\n", eivalue, ivalue) );
+
+ return esock_make_ok2(env, eivalue);;
+}
+
+static
+ERL_NIF_TERM encode_ioctl_ifconf_ifreq(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifreq* ifrP)
+{
+ ERL_NIF_TERM ename, eaddr;
+
+ ESOCK_ASSERT( ifrP != NULL );
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_ioctl_ifconf_ifreq -> encode name\r\n") );
+ ename = encode_ioctl_ifreq_name(env, ifrP->ifr_name);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_ioctl_ifconf_ifreq -> encode sockaddr\r\n") );
+ eaddr = encode_ioctl_ifreq_sockaddr(env, &ifrP->ifr_addr);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_ioctl_ifconf_ifreq -> make ifreq map with"
+ "\r\n Name: %T"
+ "\r\n Sock Addr: %T"
+ "\r\n", ename, eaddr) );
+ return make_ifreq(env, ename, esock_atom_addr, eaddr);
+}
+
+static
+ERL_NIF_TERM encode_ioctl_ifreq_name(ErlNifEnv* env,
+ char* name)
+{
+ return ((name == NULL) ? esock_atom_undefined : MKS(env, name));
+}
+
+static
+ERL_NIF_TERM encode_ioctl_ifreq_sockaddr(ErlNifEnv* env, struct sockaddr* sa)
+{
+ ERL_NIF_TERM esa;
+
+ if (sa != NULL) {
+
+ esock_encode_sockaddr(env, (ESockAddress*) sa, -1, &esa);
+
+ } else {
+
+ esa = esock_atom_undefined;
- return esock_make_ok2(env, esa);
}
+
+ return esa;
}
+/* The ifreq structure *always* contain a name
+ * and *one* other element. The second element
+ * depend on the ioctl request.
+ */
+static
+ERL_NIF_TERM make_ifreq(ErlNifEnv* env,
+ ERL_NIF_TERM name,
+ ERL_NIF_TERM key2,
+ ERL_NIF_TERM val2)
+{
+ ERL_NIF_TERM keys[2];
+ ERL_NIF_TERM vals[2];
+ ERL_NIF_TERM res;
+
+ keys[0] = esock_atom_name;
+ vals[0] = name;
+
+ keys[1] = key2;
+ vals[1] = val2;
+
+ ESOCK_ASSERT( MKMA(env, keys, vals, NUM(keys), &res) );
+
+ return res;
+}
+
+
+
+
/* ----------------------------------------------------------------------
* U t i l i t y F u n c t i o n s
* ----------------------------------------------------------------------
@@ -3004,7 +4914,7 @@ BOOLEAN_T send_check_writer(ErlNifEnv* env,
"\r\n", descP->sock, ref) );
if (! esock_writer_search4pid(env, descP, &caller)) {
- esock_writer_push(env, descP, caller, ref);
+ esock_writer_push(env, descP, caller, ref, NULL);
*checkResult = esock_atom_select;
} else {
/* Writer already in queue */
@@ -3096,7 +5006,7 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env,
res = send_check_retry(env, descP, written, sockRef, sendRef);
} else if (dataInTail) {
- /* Not the entire package */
+ /* We sent all we could, but not everything (data in tail) */
SSDBG( descP,
("UNIX-ESSIO",
"send_check_result(%T) {%d} -> "
@@ -3178,16 +5088,17 @@ ERL_NIF_TERM send_check_fail(ErlNifEnv* env,
int saveErrno,
ERL_NIF_TERM sockRef)
{
- ERL_NIF_TERM reason;
+ ERL_NIF_TERM reason;
ESOCK_CNT_INC(env, descP, sockRef,
esock_atom_write_fails, &descP->writeFails, 1);
- SSDBG( descP, ("UNIX-ESSIO", "send_check_fail(%T) {%d} -> error: %d\r\n",
- sockRef, descP->sock, saveErrno) );
-
reason = MKA(env, erl_errno_id(saveErrno));
+ SSDBG( descP,
+ ("UNIX-ESSIO", "send_check_fail(%T) {%d} -> error: %d (%T)\r\n",
+ sockRef, descP->sock, saveErrno, reason) );
+
if (saveErrno != EINVAL) {
/*
@@ -3205,6 +5116,7 @@ ERL_NIF_TERM send_check_fail(ErlNifEnv* env,
descP->currentWriterP = NULL;
}
}
+
return esock_make_error(env, reason);
}
@@ -3382,7 +5294,7 @@ ERL_NIF_TERM send_check_retry(ErlNifEnv* env,
* or a (erlang) binary that just needs to be appended to the control
* buffer.
*
- * Our "problem" is that we have no idea much memory we actually need.
+ * Our "problem" is that we have no idea how much memory we actually need.
*
*/
@@ -3732,7 +5644,7 @@ void encode_msg(ErlNifEnv* env,
struct msghdr* msgHdrP,
ErlNifBinary* dataBufP,
ErlNifBinary* ctrlBufP,
- ERL_NIF_TERM* eSockAddr)
+ ERL_NIF_TERM* eMsg)
{
ERL_NIF_TERM addr, iov, ctrl, flags;
@@ -3802,7 +5714,7 @@ void encode_msg(ErlNifEnv* env,
if (msgHdrP->msg_namelen == 0)
numKeys--; // No addr
- ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eSockAddr) );
+ ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eMsg) );
SSDBG( descP,
("UNIX-ESSIO",
@@ -4318,6 +6230,317 @@ ERL_NIF_TERM essio_sendfile_ok(ErlNifEnv* env,
#endif // #ifdef HAVE_SENDFILE
+/* ====================================================================
+ *
+ * NIF (I/O backend) Resource callback functions: dtor, stop and down
+ *
+ * ====================================================================
+ */
+
+extern
+void essio_dtor(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ SGDBG( ("UNIX-ESSIO", "dtor -> entry\r\n") );
+
+ if (IS_SELECTED(descP)) {
+ /* We have used the socket in the select machinery,
+ * so we must have closed it properly to get here
+ */
+ ESOCK_ASSERT( IS_CLOSED(descP->readState) );
+ ESOCK_ASSERT( IS_CLOSED(descP->writeState) );
+ ESOCK_ASSERT( descP->sock == INVALID_SOCKET );
+ } else {
+ /* The socket is only opened, should be safe to close nonblocking */
+ (void) sock_close(descP->sock);
+ descP->sock = INVALID_SOCKET;
+ }
+
+ SGDBG( ("UNIX-ESSIO", "dtor -> set state and pattern\r\n") );
+ descP->readState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED);
+ descP->writeState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED);
+ descP->pattern = (ESOCK_DESC_PATTERN_DTOR | ESOCK_STATE_CLOSED);
+
+ esock_free_env("dtor reader", descP->currentReader.env);
+ descP->currentReader.env = NULL;
+
+ esock_free_env("dtor writer", descP->currentWriter.env);
+ descP->currentWriter.env = NULL;
+
+ esock_free_env("dtor acceptor", descP->currentAcceptor.env);
+ descP->currentAcceptor.env = NULL;
+
+ SGDBG( ("UNIX-ESSIO", "dtor -> try free readers request queue\r\n") );
+ esock_free_request_queue(&descP->readersQ);
+
+ SGDBG( ("UNIX-ESSIO", "dtor -> try free writers request queue\r\n") );
+ esock_free_request_queue(&descP->writersQ);
+
+ SGDBG( ("UNIX-ESSIO", "dtor -> try free acceptors request queue\r\n") );
+ esock_free_request_queue(&descP->acceptorsQ);
+
+#ifdef HAVE_SENDFILE
+ ESOCK_ASSERT( descP->sendfileHandle == INVALID_HANDLE );
+ if (descP->sendfileCountersP != NULL) {
+ FREE(descP->sendfileCountersP);
+ descP->sendfileCountersP = NULL;
+ }
+#endif
+
+ esock_free_env("dtor close env", descP->closeEnv);
+ descP->closeEnv = NULL;
+
+ esock_free_env("dtor meta env", descP->meta.env);
+ descP->meta.env = NULL;
+
+ SGDBG( ("UNIX-ESSIO", "dtor -> done\r\n") );
+}
+
+
+extern
+void essio_stop(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+#ifdef HAVE_SENDFILE
+ if (descP->sendfileCountersP != NULL) {
+ ESockSendfileCounters* cntP = descP->sendfileCountersP;
+
+ SSDBG( descP, ("UNIX-ESSIO", "esock_stop(%d) -> sendfileCounters:"
+ "\r\n cnt: %lu"
+ "\r\n byteCnt: %lu"
+ "\r\n fails: %lu"
+ "\r\n max: %lu"
+ "\r\n pkg: %lu"
+ "\r\n pkgMax %lu"
+ "\r\n tries: %lu"
+ "\r\n waits: %lu"
+ "\r\n",
+ descP->sock,
+ (unsigned long) cntP->cnt,
+ (unsigned long) cntP->byteCnt,
+ (unsigned long) cntP->fails,
+ (unsigned long) cntP->max,
+ (unsigned long) cntP->pkg,
+ (unsigned long) cntP->pkgMax,
+ (unsigned long) cntP->tries,
+ (unsigned long) cntP->waits) );
+ }
+#endif
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Inform waiting Closer, or close socket
+ *
+ * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ */
+
+ if (! enif_is_pid_undefined(&descP->closerPid)) {
+ /* We have a waiting closer process after nif_close()
+ * - send message to trigger nif_finalize_close()
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "esock_stop(%d) -> send close msg to %T\r\n",
+ descP->sock, MKPID(env, &descP->closerPid)) );
+
+ esock_send_close_msg(env, descP, &descP->closerPid);
+ /* Message send frees closeEnv */
+ descP->closeEnv = NULL;
+ descP->closeRef = esock_atom_undefined;
+
+ } else {
+ int err;
+
+ /* We do not have a closer process
+ * - have to do an unclean (non blocking) close */
+
+#ifdef HAVE_SENDFILE
+ if (descP->sendfileHandle != INVALID_HANDLE)
+ esock_send_sendfile_deferred_close_msg(env, descP);
+#endif
+
+ err = esock_close_socket(env, descP, FALSE);
+
+ if (err != 0)
+ esock_warning_msg("[UNIX-ESSIO] Failed closing socket without "
+ "closer process: "
+ "\r\n Controlling Process: %T"
+ "\r\n Descriptor: %d"
+ "\r\n Errno: %d (%T)"
+ "\r\n",
+ descP->ctrlPid, descP->sock,
+ err, MKA(env, erl_errno_id(err)));
+ }
+
+}
+
+
+/* A 'down' has occured.
+ * Check the possible processes we monitor in turn:
+ * closer, controlling process (owner), connector, reader, acceptor and writer.
+ *
+ */
+extern
+void essio_down(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+ if (COMPARE_PIDS(&descP->closerPid, pidP) == 0) {
+
+ /* The closer process went down
+ * - it will not call nif_finalize_close
+ */
+
+ enif_set_pid_undefined(&descP->closerPid);
+
+ if (MON_EQ(&descP->closerMon, monP)) {
+ MON_INIT(&descP->closerMon);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down {%d} -> closer process exit\r\n",
+ descP->sock) );
+
+ } else {
+ // The owner is the closer so we used its monitor
+
+ ESOCK_ASSERT( MON_EQ(&descP->ctrlMon, monP) );
+ MON_INIT(&descP->ctrlMon);
+ enif_set_pid_undefined(&descP->ctrlPid);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down {%d} -> closer controlling process exit\r\n",
+ descP->sock) );
+ }
+
+ /* Since the closer went down there was one,
+ * hence esock_close() must have run or scheduled esock_stop(),
+ * or the socket has never been "selected" upon.
+ */
+
+ if (descP->closeEnv == NULL) {
+ int err;
+
+ /* Since there is no closeEnv,
+ * esock_close() did not schedule esock_stop()
+ * and is about to call esock_finalize_close() but died,
+ * or esock_stop() has run, sent close_msg to the closer
+ * and cleared ->closeEnv but the closer died
+ * - we have to do an unclean (non blocking) socket close here
+ */
+
+#ifdef HAVE_SENDFILE
+ if (descP->sendfileHandle != INVALID_HANDLE)
+ esock_send_sendfile_deferred_close_msg(env, descP);
+#endif
+
+ err = esock_close_socket(env, descP, FALSE);
+ if (err != 0)
+ esock_warning_msg("[UNIX-ESSIO] "
+ "Failed closing socket for terminating "
+ "closer process: "
+ "\r\n Closer Process: %T"
+ "\r\n Descriptor: %d"
+ "\r\n Errno: %d (%T)"
+ "\r\n",
+ MKPID(env, pidP), descP->sock,
+ err, MKA(env, erl_errno_id(err)));
+ } else {
+ /* Since there is a closeEnv esock_stop() has not run yet
+ * - when it finds that there is no closer process
+ * it will close the socket and ignore the close_msg
+ */
+ esock_clear_env("essio_down - close-env", descP->closeEnv);
+ esock_free_env("essio_down - close-env", descP->closeEnv);
+ descP->closeEnv = NULL;
+ descP->closeRef = esock_atom_undefined;
+ }
+
+ } else if (MON_EQ(&descP->ctrlMon, monP)) {
+ MON_INIT(&descP->ctrlMon);
+ /* The owner went down */
+ enif_set_pid_undefined(&descP->ctrlPid);
+
+ if (IS_OPEN(descP->readState)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down {%d} -> controller process exit"
+ "\r\n initiate close\r\n",
+ descP->sock) );
+
+ essio_down_ctrl(env, descP, pidP);
+
+ descP->readState |= ESOCK_STATE_CLOSING;
+ descP->writeState |= ESOCK_STATE_CLOSING;
+
+ } else {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down {%d} -> controller process exit"
+ "\r\n already closed or closing\r\n",
+ descP->sock) );
+
+ }
+
+ } else if (descP->connectorP != NULL &&
+ MON_EQ(&descP->connector.mon, monP)) {
+ MON_INIT(&descP->connector.mon);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down {%d} -> connector process exit\r\n",
+ descP->sock) );
+
+ /* connectorP is only set during connection.
+ * Forget all about the ongoing connection.
+ * We might end up connected, but the process that initiated
+ * the connection has died and will never know
+ */
+
+ esock_requestor_release("esock_down->connector",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+ descP->writeState &= ~ESOCK_STATE_CONNECTING;
+
+ } else {
+ ERL_NIF_TERM sockRef = enif_make_resource(env, descP);
+
+ /* check all operation queue(s): acceptor, writer and reader.
+ *
+ * Is it really any point in doing this if the socket is closed?
+ *
+ */
+
+ if (IS_CLOSED(descP->readState)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down(%T) {%d} -> stray down: %T\r\n",
+ sockRef, descP->sock, pidP) );
+ } else {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down(%T) {%d} -> other process term\r\n",
+ sockRef, descP->sock) );
+
+ if (descP->currentReaderP != NULL)
+ essio_down_reader(env, descP, sockRef, pidP, monP);
+ if (descP->currentAcceptorP != NULL)
+ essio_down_acceptor(env, descP, sockRef, pidP, monP);
+ if (descP->currentWriterP != NULL)
+ essio_down_writer(env, descP, sockRef, pidP, monP);
+ }
+ }
+
+}
+
+
+/* ==================================================================== */
+
/* *** Recv/recvfrom/recvmsg utility functions *** */
/* *** recv_check_reader ***
@@ -4353,7 +6576,7 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env,
if (! esock_reader_search4pid(env, descP, &caller)) {
if (COMPARE(ref, esock_atom_zero) == 0)
goto done_ok;
- esock_reader_push(env, descP, caller, ref);
+ esock_reader_push(env, descP, caller, ref, NULL);
*checkResult = esock_atom_select;
} else {
/* Reader already in queue */
@@ -4967,3 +7190,198 @@ void recv_error_current_reader(ErlNifEnv* env,
}
+/* *** essio_down_ctrl ***
+ *
+ * Stop after a downed controller (controlling process = owner process)
+ *
+ * This is 'extern' because its currently called from prim_socket_nif
+ * (esock_setopt_otp_ctrl_proc).
+ */
+extern
+void essio_down_ctrl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP)
+{
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_down_ctrl {%d} ->"
+ "\r\n Pid: %T"
+ "\r\n", descP->sock, MKPID(env, pidP)) );
+
+ if (do_stop(env, descP)) {
+ /* esock_stop() is scheduled
+ * - it has to close the socket
+ */
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_down_ctrl {%d} -> stop was scheduled\r\n",
+ descP->sock) );
+ } else {
+ int err;
+
+ /* Socket is not in the select machinery
+ * so esock_stop() will not be called
+ * - we have to do an unclean (non blocking) socket close here
+ */
+
+#ifdef HAVE_SENDFILE
+ if (descP->sendfileHandle != INVALID_HANDLE)
+ esock_send_sendfile_deferred_close_msg(env, descP);
+#endif
+
+ err = esock_close_socket(env, descP, FALSE);
+ if (err != 0)
+ esock_warning_msg("[UNIX-ESSIO] "
+ "Failed closing socket for terminating "
+ "owner process: "
+ "\r\n Owner Process: %T"
+ "\r\n Descriptor: %d"
+ "\r\n Errno: %d (%T)"
+ "\r\n",
+ MKPID(env, pidP), descP->sock,
+ err, MKA(env, erl_errno_id(err)));
+ }
+}
+
+
+
+/* *** essio_down_acceptor ***
+ *
+ * Check and then handle a downed acceptor process.
+ *
+ */
+static
+void essio_down_acceptor(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+ if (MON_EQ(&descP->currentAcceptor.mon, monP)) {
+ MON_INIT(&descP->currentAcceptor.mon);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_acceptor(%T) {%d} -> "
+ "current acceptor - try activate next\r\n",
+ sockRef, descP->sock) );
+
+ if (!esock_activate_next_acceptor(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_acceptor(%T) {%d} -> no more writers\r\n",
+ sockRef, descP->sock) );
+
+ descP->readState &= ~ESOCK_STATE_ACCEPTING;
+
+ descP->currentAcceptorP = NULL;
+ }
+
+ } else {
+
+ /* Maybe unqueue one of the waiting acceptors */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_acceptor(%T) {%d} -> "
+ "not current acceptor - maybe a waiting acceptor\r\n",
+ sockRef, descP->sock) );
+
+ esock_acceptor_unqueue(env, descP, NULL, pidP);
+ }
+}
+
+
+/* *** essio_down_writer ***
+ *
+ * Check and then handle a downed writer process.
+ *
+ */
+
+static
+void essio_down_writer(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+ if (MON_EQ(&descP->currentWriter.mon, monP)) {
+ MON_INIT(&descP->currentWriter.mon);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_writer(%T) {%d} -> "
+ "current writer - try activate next\r\n",
+ sockRef, descP->sock) );
+
+ if (!esock_activate_next_writer(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_writer(%T) {%d} -> no active writer\r\n",
+ sockRef, descP->sock) );
+
+ descP->currentWriterP = NULL;
+ }
+
+ } else {
+
+ /* Maybe unqueue one of the waiting writer(s) */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_writer(%T) {%d} -> "
+ "not current writer - maybe a waiting writer\r\n",
+ sockRef, descP->sock) );
+
+ esock_writer_unqueue(env, descP, NULL, pidP);
+ }
+}
+
+
+/* *** essio_down_reader ***
+ *
+ * Check and then handle a downed reader process.
+ *
+ */
+
+static
+void essio_down_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+ if (MON_EQ(&descP->currentReader.mon, monP)) {
+ MON_INIT(&descP->currentReader.mon);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_reader(%T) {%d} -> "
+ "current reader - try activate next\r\n",
+ sockRef, descP->sock) );
+
+ if (! esock_activate_next_reader(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_reader(%T) {%d} -> no more readers\r\n",
+ sockRef, descP->sock) );
+
+ descP->currentReaderP = NULL;
+ }
+
+ } else {
+
+ /* Maybe unqueue one of the waiting reader(s) */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_reader(%T) {%d} -> "
+ "not current reader - maybe a waiting reader\r\n",
+ sockRef, descP->sock) );
+
+ esock_reader_unqueue(env, descP, NULL, pidP);
+ }
+}
+
+
diff --git a/erts/emulator/nifs/win32/win_socket_asyncio.c b/erts/emulator/nifs/win32/win_socket_asyncio.c
new file mode 100644
index 0000000000..f91e7edc48
--- /dev/null
+++ b/erts/emulator/nifs/win32/win_socket_asyncio.c
@@ -0,0 +1,9600 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2023-2023. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ *
+ * ----------------------------------------------------------------------
+ * Purpose : Windows version of the asyncronous I/O backend.
+ * ----------------------------------------------------------------------
+ *
+ * Misc:
+ * This is based of the Windows concept: I/O Completion Port.
+ * This feature works on "all" kinds of I/O, even file I/O. But,
+ * our implementation only deals with socket I/O.
+ * The "asynchronous" access functions (that "can" block) that we
+ * use are as following:
+ *
+ * * WSASend, WSASendTo, WSASendMsg, WSARecv, WSARecvFrom, WSARecvMsg
+ * * AccepxEx
+ * (this is actually a function pointer, obtained at run time by
+ * making a call to the WSAIoctl function with the
+ * SIO_GET_EXTENSION_FUNCTION_POINTER opcode specified.
+ * The input buffer passed to the WSAIoctl function must contain
+ * WSAID_ACCEPTEX).
+ * To get the local and remote addresses, the GetAcceptExSockaddrs
+ * must be called. This function is *also* a function pointer
+ * obtained at run time by making a call to the WSAIoctl function.
+ * * ConnectEx:
+ * (this is actually a function pointer, obtained at run time by
+ * The function pointer for the ConnectEx function must be
+ * making a call to the WSAIoctl function with the
+ * SIO_GET_EXTENSION_FUNCTION_POINTER opcode specified.
+ * The input buffer passed to the WSAIoctl function must contain
+ * WSAID_CONNECTEX).
+ * * WSASendMsg & WSARecvMsg are actually *also* function pointers!!
+ *
+ * But since we want them to "behave" the same way, we need to add
+ * some wrapper code to simulate the "completion behaviour".
+ *
+ * These functions (in erlang) should return simething *like* this:
+ *
+ * ok | completion | {error, Reason}
+ *
+ * And if the return value was 'completion', the caller shall expect
+ * the following message, when the "operation" has "completed" (success
+ * or failure):
+ *
+ * {'$socket', Socket, completion, {Ref, CompletionStatus}}
+ *
+ * Where 'Socket' is the socket on which the call was made (for example,
+ * 'socket:send(Socket, ...)), and CompletionStatus is the result of
+ * actual operation: ok | {error, Reason}
+ *
+ * Examples:
+ * * https://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancediomethod5i.html
+ * * https://www.codeproject.com/Articles/13382/A-simple-application-using-I-O-Completion-Ports-an
+ * * https://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedscalableapp6b.html
+ *
+ * More useful links:
+ * * https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex
+ * * https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaioctl
+ * * https://learn.microsoft.com/en-us/windows/win32/winsock/socket-options-and-ioctls-2
+ *
+ * Note:
+ * -
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+// #include <Ws2def.h>
+// #include <winsock2.h>
+// #include <windows.h>
+#include <ws2tcpip.h>
+#include <mswsock.h>
+#include <stdio.h>
+
+#include <sys.h>
+
+#include "socket_int.h"
+#include "socket_io.h"
+#include "socket_asyncio.h"
+#include "socket_util.h"
+#include "socket_tarray.h"
+#include "socket_dbg.h"
+
+
+/* =================================================================== *
+ * *
+ * Local Constants *
+ * *
+ * =================================================================== */
+
+#define ESAIO_OK ESOCK_IO_OK
+#define ESAIO_ERR_WINSOCK_INIT 0x0001
+#define ESAIO_ERR_IOCPORT_CREATE 0x0002
+#define ESAIO_ERR_FSOCK_CREATE 0x0003
+#define ESAIO_ERR_IOCTL_ACCEPT_GET 0x0004
+#define ESAIO_ERR_IOCTL_CONNECT_GET 0x0005
+#define ESAIO_ERR_IOCTL_SENDMSG_GET 0x0006
+#define ESAIO_ERR_IOCTL_RECVMSG_GET 0x0007
+#define ESAIO_ERR_THREAD_OPTS_CREATE 0x0011
+#define ESAIO_ERR_THREAD_CREATE 0x0012
+
+#define ERRNO_BLOCK WSAEWOULDBLOCK
+
+
+/* ======================================================================== *
+ * Socket wrappers *
+ * ======================================================================== *
+ */
+
+#define sock_accept_O(s, as, b, al, rb, o) \
+ ctrl.accept((s), (as), (b), 0, (al), (al), (rb), (o))
+#define sock_bind(s, addr, len) bind((s), (addr), (len))
+#define sock_close(s) closesocket((s))
+#define sock_connect(s, a, al) connect((s), (a), (al))
+#define sock_connect_O(s, a, al, sent, o) \
+ ctrl.connect((s), (struct sockaddr*) (a), (al), NULL, 0, (sent), (o))
+#define sock_errno() WSAGetLastError()
+// #define sock_listen(s, b) listen((s), (b))
+// #define sock_name(s, addr, len) getsockname((s), (addr), (len))
+#define sock_open(domain, type, proto) socket((domain), (type), (proto))
+#define sock_open_O(domain, type, proto) \
+ WSASocket((domain), (type), (proto), NULL, 0, WSA_FLAG_OVERLAPPED)
+#define sock_recv_O(s,buf,flag,ol) \
+ WSARecv((s), (buf), 1, NULL, (flag), (ol), NULL)
+#define sock_recvfrom_O(s,buf,flag,fa,fal,ol) \
+ WSARecvFrom((s), (buf), 1, NULL, (flag), (fa), (fal), (ol), NULL)
+#define sock_recvmsg_O(s,msg,o) \
+ ctrl.recvmsg((s), (msg), NULL, (o), NULL)
+#define sock_send_O(s,buf,flag,o) \
+ WSASend((s), (buf), 1, NULL, (flag), (o), NULL)
+/* #define sock_sendmsg_O(s,buf,flag,ol) \ */
+/* WSASendMsg((s), (buf), (flag), NULL, (ol), NULL) */
+#define sock_sendmsg_O(s,buf,flag,ol) \
+ ctrl.sendmsg((s), (buf), (flag), NULL, (ol), NULL)
+#define sock_sendto_O(s,buf,flag,ta,tal,o) \
+ WSASendTo((s), (buf), 1, NULL, (flag), (ta), (tal), (o), NULL)
+#define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln))
+
+
+#define ESAIO_UPDATE_ACCEPT_CONTEXT(AS, LS) \
+ sock_setopt( (AS), SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, \
+ (char*) &(LS), sizeof( (LS) ))
+#define ESAIO_UPDATE_CONNECT_CONTEXT(S) \
+ sock_setopt((S), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0)
+
+
+#define ESOCK_CMSG_FIRSTHDR(M) WSA_CMSG_FIRSTHDR((M))
+#define ESOCK_CMSG_NXTHDR(M,C) WSA_CMSG_NXTHDR((M), (C))
+#define ESOCK_CMSG_DATA(C) WSA_CMSG_DATA((C))
+
+
+/* =================================================================== *
+ * *
+ * Local types *
+ * *
+ * =================================================================== */
+
+typedef struct {
+ Uint16 id; /* Thread id: mainly used for debugging,
+ * and name creation */
+
+ /* Thread state(s) */
+#define ESAIO_THREAD_STATE_UNDEF 0xFFFF
+#define ESAIO_THREAD_STATE_INITIATING 0x0000
+#define ESAIO_THREAD_STATE_OPERATIONAL 0x0001
+#define ESAIO_THREAD_STATE_TERMINATING 0x0002
+#define ESAIO_THREAD_STATE_TERMINATED 0x0003
+
+ Uint16 state; /* State of the thread:
+ * undefined, initiating, operational, terminating */
+
+ /* Thread error 'state(s)'.
+ * If the thread is "not running", this value tells why.
+ */
+#define ESAIO_THREAD_ERROR_UNDEF 0xFFFF
+#define ESAIO_THREAD_ERROR_OK 0x0000
+#define ESAIO_THREAD_ERROR_TOCREATE 0x0001
+#define ESAIO_THREAD_ERROR_TCREATE 0x0002
+#define ESAIO_THREAD_ERROR_GET 0x0003
+#define ESAIO_THREAD_ERROR_CMD 0x0004
+
+ Uint32 error; /* In case the thread exits,
+ * this is where the (error) reason is stored.
+ * Not sure i f we would ever be able to
+ * read this (if things are bad enough that the
+ * threads terminate)...
+ */
+
+ /* Do we need this?
+ * "The environment of the calling thread (process bound or
+ * callback environment) or NULL if calling from a custom
+ * thread not spawned by ERTS."
+ */
+ ErlNifEnv* env; /* Used when sending messages */
+
+#define ESAIO_THREAD_CNT_MAX 0xFFFFFFFF
+
+ Uint32 cnt; /* Run-counter: mainly used for debugging */
+
+ unsigned int latest; /* Latest request (tag) */
+} ESAIOThreadData;
+
+typedef struct {
+ ErlNifThreadOpts* optsP;
+ ErlNifTid tid;
+ ESAIOThreadData data;
+} ESAIOThread;
+
+typedef struct {
+ WSADATA wsaData;
+ HANDLE cport;
+
+ SOCKET dummy; // Used for extracting AcceptEx and ConnectEx
+
+ LPFN_ACCEPTEX accept;
+ LPFN_CONNECTEX connect;
+ LPFN_WSASENDMSG sendmsg;
+ LPFN_WSARECVMSG recvmsg;
+
+ /* Thread pool stuff.
+ * The size of the pool is configurable. */
+ DWORD numThreads;
+ ESAIOThread* threads;
+
+ /* Misc stuff */
+ BOOLEAN_T dbg;
+ BOOLEAN_T sockDbg;
+
+ /* Counter stuff */
+ ErlNifMutex* cntMtx;
+ ESockCounter unexpectedConnects;
+ ESockCounter unexpectedAccepts;
+ ESockCounter unexpectedWrites;
+ ESockCounter unexpectedReads;
+ ESockCounter genErrs;
+ ESockCounter unknownCmds;
+
+} ESAIOControl;
+
+
+typedef struct __ESAIOOpDataAccept {
+ /* AcceptEx; ; lookup with WSAID_ACCEPTEX */
+
+ /* The socket, sock, is created empty and then provided as an
+ * argumented to AcceptEx (together with the listen socket
+ * and the other arguments).
+ * When AcceptEx has completed successfully, the socket, s, is
+ * usable.
+ * But in order for the functions sockname and peername to work,
+ * the SO_UPDATE_ACCEPT_CONTEXT option must be set on the
+ * accepted socket, sock. */
+ SOCKET lsock; /* The listen socket */
+ SOCKET asock; /* The "accepted" socket.
+ * This is created "in advance"
+ * and then sent to AcceptEx as an argument.
+ */
+ char* buf; /* Size depends on domain.
+ * This is used for 'initial data',
+ * 'local address' and 'remote address'.
+ * We use neither of these, but the
+ * AcceptEx function requires this argument!
+ */
+ ERL_NIF_TERM lSockRef; /* The listen socket */
+ ERL_NIF_TERM accRef; /* The (unique) reference (ID) of the accept */
+} ESAIOOpDataAccept;
+
+typedef struct __ESAIOOpDataConnect {
+ /* ConnectEx; ; lookup with WSAID_CONNECTEX */
+
+ /* When ConnectEx has completed successfully,
+ * the socket is usable.
+ * *But*, in order for the functions sockname and peername to work,
+ * the SO_UPDATE_CONNECT_CONTEXT option must be set on the socket.
+ */
+ ERL_NIF_TERM sockRef; /* The socket */
+ ERL_NIF_TERM connRef; /* The (unique) reference (ID)
+ * of the connect request */
+} ESAIOOpDataConnect;
+
+typedef struct __ESAIOOpDataSend {
+ /* WSASend */
+ WSABUF wbuf; /* During ongoing sending, this buffer cannot
+ * be de-allocated. */
+ ERL_NIF_TERM sockRef; /* The socket */
+ ERL_NIF_TERM sendRef; /* The (unique) reference (ID)
+ * of the send request */
+} ESAIOOpDataSend;
+
+typedef struct __ESAIOOpDataSendTo {
+ /* WSASendTo */
+ WSABUF wbuf; /* During ongoing sending, this buffer cannot
+ * be de-allocated. */
+
+ /* Do we actually need these (remote address)?
+ * Debugging/logging?
+ */
+ ESockAddress remoteAddr;
+ SOCKLEN_T remoteAddrLen;
+
+ ERL_NIF_TERM sockRef; /* The socket */
+ ERL_NIF_TERM sendRef; /* The (unique) reference (ID)
+ * of the send request */
+} ESAIOOpDataSendTo;
+
+typedef struct __ESAIOOpDataSendMsg {
+ /* WSASendMsg; lookup with WSAID_WSASENDMSG */
+ WSAMSG msg;
+ ErlNifIOVec* iovec;
+ char* ctrlBuf;
+ ESockAddress addr;
+ ERL_NIF_TERM sockRef; /* The socket */
+ ERL_NIF_TERM sendRef; /* The (unique) reference (ID)
+ * of the send request */
+} ESAIOOpDataSendMsg;
+
+typedef struct __ESAIOOpDataRecv {
+ /* WSARecv */
+ DWORD toRead; /* Can be 0 (= zero)
+ * "just to indicate: give me what you got"
+ */
+ ErlNifBinary buf;
+ ERL_NIF_TERM sockRef; /* The socket */
+ ERL_NIF_TERM recvRef; /* The (unique) reference (ID)
+ * of the recv request */
+} ESAIOOpDataRecv;
+
+typedef struct __ESAIOOpDataRecvFrom {
+ /* WSARecvFrom */
+ DWORD toRead; /* Can be 0 (= zero)
+ * "just to indicate: give me what you got"
+ */
+ ErlNifBinary buf;
+
+ ESockAddress fromAddr;
+ INT addrLen; // SOCKLEN_T
+
+ ERL_NIF_TERM sockRef; /* The socket */
+ ERL_NIF_TERM recvRef; /* The (unique) reference (ID)
+ * of the recv request */
+} ESAIOOpDataRecvFrom;
+
+typedef struct __ESAIOOpDataRecvMsg {
+ /* WSARecvMsg; lookup with WSAID_WSARECVMSG */
+
+ /* If we used I/O - vectors of different size(s),
+ * we would (maybe) need to malloc them, and simply
+ * have a pointer here.
+ */
+
+ WSAMSG msg;
+ WSABUF wbufs[1];
+ ErlNifBinary data[1];
+ ErlNifBinary ctrl;
+ ESockAddress addr;
+
+ ERL_NIF_TERM sockRef; /* The socket */
+ ERL_NIF_TERM recvRef; /* The (unique) reference (ID)
+ * of the recv request */
+} ESAIOOpDataRecvMsg;
+
+/* An 'operation', recv/recvfrom/recvmsg and send/sendto/sendmsg,
+ * accept or connect, is 'encoded' into this structure, which is
+ * "passed around".
+ */
+typedef struct __ESAIOOperation {
+ /* Has to be first and is *only* used by I/O Completion Port framework */
+ WSAOVERLAPPED ol;
+
+ /* *** Commands (=tags) *** */
+#define ESAIO_OP_NONE 0x0000 // None
+ /* "system" commands */
+#define ESAIO_OP_TERMINATE 0x0001 // Terminate
+#define ESAIO_OP_DEBUG 0x0002 // Change debug level for thread(s)
+ /* Commands for establishing connections; connect and accept */
+#define ESAIO_OP_CONNECT 0x0011 // ConnectEx (function pointer)
+#define ESAIO_OP_ACCEPT 0x0012 // AcceptEx (function pointer)
+ /* Commands for sending */
+#define ESAIO_OP_SEND 0x0021 // WSASend
+#define ESAIO_OP_SENDTO 0x0022 // WSASendTo
+#define ESAIO_OP_SENDMSG 0x0023 // WSASendMsg
+ /* Commands for receiving */
+#define ESAIO_OP_RECV 0x0031 // WSARecv
+#define ESAIO_OP_RECVFROM 0x0032 // WSARecvFrom
+#define ESAIO_OP_RECVMSG 0x0033 // WSARecvMsg
+
+ unsigned int tag; /* The 'tag' of the operation */
+
+ ErlNifPid caller; /* Almost every request (not connect)
+ * operations require a caller */
+ ErlNifEnv* env; /* Almost every request
+ * needs an environment */
+
+ /* Generic "data" field.
+ * This is different for each 'operation'!
+ * Also, not all opererations have this!
+ */
+
+ union {
+ /* +++ accept +++ */
+ ESAIOOpDataAccept accept;
+
+ /* +++ connect +++ */
+ ESAIOOpDataConnect connect;
+
+ /* +++ send +++ */
+ ESAIOOpDataSend send;
+
+ /* +++ sendto +++ */
+ ESAIOOpDataSendTo sendto;
+
+ /* +++ sendmsg +++ */
+ ESAIOOpDataSendMsg sendmsg;
+
+ /* +++ recv +++ */
+ ESAIOOpDataRecv recv;
+
+ /* +++ recvfrom +++ */
+ ESAIOOpDataRecvFrom recvfrom;
+
+ /* +++ recvmsg +++ */
+ ESAIOOpDataRecvMsg recvmsg;
+
+ } data;
+
+} ESAIOOperation;
+
+
+
+/* =================================================================== *
+ * *
+ * Function Forwards *
+ * *
+ * =================================================================== */
+
+static ERL_NIF_TERM esaio_connect_stream(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM connRef,
+ ESockAddress* addrP,
+ SOCKLEN_T addrLen);
+static ERL_NIF_TERM connect_stream_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ BOOL cres);
+static ERL_NIF_TERM esaio_connect_dgram(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM connRef,
+ ESockAddress* addrP,
+ SOCKLEN_T addrLen);
+
+static ERL_NIF_TERM accept_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ BOOL ares,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef,
+ SOCKET accSock,
+ ErlNifPid caller);
+static ERL_NIF_TERM accept_check_pending(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef);
+static ERL_NIF_TERM accept_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ int saveErrno,
+ SOCKET accSock,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM esaio_accept_accepted(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifPid pid,
+ ERL_NIF_TERM sockRef,
+ SOCKET accSock);
+
+static ERL_NIF_TERM send_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ int send_result,
+ ssize_t dataSize,
+ BOOLEAN_T dataInTail,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ BOOLEAN_T* cleanup);
+static ERL_NIF_TERM send_check_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ DWORD written,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM send_check_pending(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef);
+static ERL_NIF_TERM send_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef);
+
+static BOOLEAN_T init_sendmsg_sockaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eMsg,
+ WSAMSG* msgP,
+ ESockAddress* addrP);
+static BOOLEAN_T verify_sendmsg_iovec_size(const ESockData* dataP,
+ ESockDescriptor* descP,
+ ErlNifIOVec* iovec);
+static BOOLEAN_T verify_sendmsg_iovec_tail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM* tail);
+static BOOLEAN_T check_sendmsg_iovec_overflow(ESockDescriptor* descP,
+ ErlNifIOVec* iovec,
+ ssize_t* dataSize);
+static BOOLEAN_T decode_cmsghdrs(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eCMsg,
+ char* cmsgHdrBufP,
+ size_t cmsgHdrBufLen,
+ size_t* cmsgHdrBufUsed);
+static BOOLEAN_T decode_cmsghdr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eCMsg,
+ char* bufP,
+ size_t rem,
+ size_t* used);
+static BOOLEAN_T decode_cmsghdr_value(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ ERL_NIF_TERM eType,
+ ERL_NIF_TERM eValue,
+ char* dataP,
+ size_t dataLen,
+ size_t* dataUsedP);
+static BOOLEAN_T decode_cmsghdr_data(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ ERL_NIF_TERM eType,
+ ERL_NIF_TERM eData,
+ char* dataP,
+ size_t dataLen,
+ size_t* dataUsedP);
+static void encode_msg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ WSAMSG* msgP,
+ ErlNifBinary* dataBufP,
+ ErlNifBinary* ctrlBufP,
+ ERL_NIF_TERM* eMsg);
+static void encode_cmsgs(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifBinary* cmsgBinP,
+ WSAMSG* msgP,
+ ERL_NIF_TERM* eCMsg);
+static ERL_NIF_TERM recv_check_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+
+static ERL_NIF_TERM recv_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ int recv_result,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recv_check_pending(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recv_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM recv_check_failure(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ int recv_result,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recvfrom_check_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recvfrom_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ int recv_result,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recvmsg_check_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recvmsg_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef);
+
+static void* esaio_completion_main(void* threadDataP);
+static BOOLEAN_T esaio_completion_terminate(ESAIOThreadData* dataP,
+ OVERLAPPED* ovl);
+static BOOLEAN_T esaio_completion_unknown(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ DWORD numBytes,
+ int error);
+static void esaio_completion_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const char* opStr,
+ int error,
+ BOOLEAN_T inform);
+
+static BOOLEAN_T esaio_completion_connect(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataConnect* opDataP,
+ int error);
+static void esaio_completion_connect_success(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOpDataConnect* opDataP);
+static void esaio_completion_connect_aborted(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOpDataConnect* opDataP);
+static void esaio_completion_connect_failure(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOpDataConnect* opDataP,
+ int error);
+static void esaio_completion_connect_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOpDataConnect* opDataPP);
+static void esaio_completion_connect_not_active(ESockDescriptor* descP);
+static void esaio_completion_connect_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform);
+
+static BOOLEAN_T esaio_completion_accept(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataAccept* opDataP,
+ int error);
+static void esaio_completion_accept_success(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataAccept* opDataP);
+static void esaio_completion_accept_aborted(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifPid* opCaller,
+ ESAIOOpDataAccept* opDataP);
+static void esaio_completion_accept_failure(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifPid* opCaller,
+ ESAIOOpDataAccept* opDataP,
+ int error);
+static void esaio_completion_accept_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataAccept* opDataP,
+ ESockRequestor* reqP);
+static void esaio_completion_accept_not_active(ESockDescriptor* descP);
+static void esaio_completion_accept_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform);
+
+static BOOLEAN_T esaio_completion_send(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataSend* opDataP,
+ int error);
+static void esaio_completion_send_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* sender,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ DWORD toWrite,
+ ESockRequestor* reqP);
+static ERL_NIF_TERM esaio_completion_send_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ DWORD written);
+static ERL_NIF_TERM esaio_completion_send_partial(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ DWORD written);
+static void esaio_completion_send_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform);
+static void esaio_completion_send_not_active(ESockDescriptor* descP);
+static BOOLEAN_T esaio_completion_sendto(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataSendTo* opDataP,
+ int error);
+static void esaio_completion_sendto_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform);
+static BOOLEAN_T esaio_completion_sendmsg(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataSendMsg* opDataP,
+ int error);
+static void esaio_completion_sendmsg_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform);
+static BOOLEAN_T esaio_completion_recv(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecv* opDataP,
+ int error);
+static void esaio_completion_recv_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecv* opDataP,
+ ESockRequestor* reqP);
+static ERL_NIF_TERM esaio_completion_recv_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecv* opDataP,
+ DWORD flags);
+static ERL_NIF_TERM esaio_completion_recv_partial(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecv* opDataP,
+ ESockRequestor* reqP,
+ DWORD read,
+ DWORD flags);
+static ERL_NIF_TERM esaio_completion_recv_partial_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecv* opDataP,
+ ssize_t read,
+ DWORD flags);
+static ERL_NIF_TERM esaio_completion_recv_partial_part(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecv* opDataP,
+ ssize_t read,
+ DWORD flags);
+static void esaio_completion_recv_not_active(ESockDescriptor* descP);
+static void esaio_completion_recv_closed(ESockDescriptor* descP,
+ int error);
+static void esaio_completion_recv_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform);
+static BOOLEAN_T esaio_completion_recvfrom(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecvFrom* opDataP,
+ int error);
+static void esaio_completion_recvfrom_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecvFrom* opDataP,
+ ESockRequestor* reqP);
+static ERL_NIF_TERM esaio_completion_recvfrom_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecvFrom* opDataP,
+ DWORD flags);
+static ERL_NIF_TERM esaio_completion_recvfrom_partial(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecvFrom* opDataP,
+ ESockRequestor* reqP,
+ DWORD read,
+ DWORD flags);
+static void esaio_completion_recvfrom_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform);
+static BOOLEAN_T esaio_completion_recvmsg(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecvMsg* opDataP,
+ int error);
+static void esaio_completion_recvmsg_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecvMsg* opDataP,
+ ESockRequestor* reqP);
+static ERL_NIF_TERM esaio_completion_recvmsg_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecvMsg* opDataP,
+ DWORD flags);
+static ERL_NIF_TERM esaio_completion_recvmsg_partial(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecvMsg* opDataP,
+ ESockRequestor* reqP,
+ DWORD read,
+ DWORD flags);
+static void esaio_completion_recvmsg_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform);
+
+static ERL_NIF_TERM esaio_completion_get_ovl_result_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error);
+
+static BOOL get_send_ovl_result(SOCKET sock,
+ OVERLAPPED* ovl,
+ DWORD* written);
+static BOOL get_recv_ovl_result(SOCKET sock,
+ OVERLAPPED* ovl,
+ DWORD* read,
+ DWORD* flags);
+static BOOL get_recvmsg_ovl_result(SOCKET sock,
+ OVERLAPPED* ovl,
+ DWORD* read);
+static BOOL get_ovl_result(SOCKET sock,
+ OVERLAPPED* ovl,
+ DWORD* transfer,
+ DWORD* flags);
+
+static void esaio_completion_inc(ESAIOThreadData* dataP);
+
+static int esaio_add_socket(ESockDescriptor* descP);
+
+static void esaio_send_completion_msg(ErlNifEnv* sendEnv,
+ ESockDescriptor* descP,
+ ErlNifPid* pid,
+ ErlNifEnv* msgEnv,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM connRef);
+static ERL_NIF_TERM mk_completion_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM completionRef);
+
+static void esaio_down_acceptor(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+static void esaio_down_writer(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+static void esaio_down_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+
+static BOOLEAN_T do_stop(ErlNifEnv* env,
+ ESockDescriptor* descP);
+
+
+/* =================================================================== *
+ * *
+ * Local (global) variables *
+ * *
+ * =================================================================== */
+
+static ESAIOControl ctrl = {0};
+
+
+
+/* =================================================================== *
+ * *
+ * Various esaio macros *
+ * *
+ * =================================================================== */
+
+/* Global socket debug */
+#define SGDBG( proto ) ESOCK_DBG_PRINTF( ctrl.dbg , proto )
+
+/* These are just wrapper macros for the I/O Completion Port */
+#define ESAIO_IOCP_CREATE(NT) \
+ CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (u_long) 0, (NT))
+#define ESAIO_IOCP_ADD(SOCK, DP) \
+ CreateIoCompletionPort((SOCK), ctrl.cport, (ULONG*) (DP), 0)
+
+/* These are just wrapper macros for the I/O Completion Port queue */
+#define ESAIO_IOCQ_POP(NB, DP, OLP) \
+ GetQueuedCompletionStatus(ctrl.cport, (DP), (DP), (OLP), INFINITE)
+#define ESAIO_IOCQ_PUSH(OP) \
+ PostQueuedCompletionStatus(ctrl.cport, 0, 0, (OVERLAPPED*) (OP))
+#define ESAIO_IOCQ_CANCEL(H, OP) \
+ CancelIoEx((H), (OVERLAPPED*) (OP))
+
+
+/* =================================================================== *
+ * *
+ * I/O Backend exports *
+ * *
+ * =================================================================== */
+
+
+/* *******************************************************************
+ * This function is called during (esock) nif loading
+ * The only argument that we actually *need* is the 'numThreads'.
+ * 'dataP' is just for convenience (dbg and stuff).
+ */
+extern
+int esaio_init(unsigned int numThreads,
+ const ESockData* dataP)
+{
+ int ires, save_errno;
+ unsigned int i;
+ DWORD dummy;
+ GUID guidAcceptEx = WSAID_ACCEPTEX;
+ GUID guidConnectEx = WSAID_CONNECTEX;
+ GUID guidSendMsg = WSAID_WSASENDMSG;
+ GUID guidRecvMsg = WSAID_WSARECVMSG;
+
+ /* Enabling this results in a core dump when calling socket:accept
+ * multiple times (the second call fails in env alloc).!
+ */
+ // ctrl.dbg = TRUE;
+ ctrl.dbg = dataP->dbg;
+ ctrl.sockDbg = dataP->sockDbg;
+
+ SGDBG( ("WIN-ESAIO", "esaio_init -> entry\r\n") );
+
+ ctrl.cntMtx = MCREATE("win-esaio.cnt");
+ ctrl.unexpectedConnects = 0;
+ ctrl.unexpectedAccepts = 0;
+ ctrl.unexpectedWrites = 0;
+ ctrl.unexpectedReads = 0;
+ ctrl.genErrs = 0;
+ ctrl.unknownCmds = 0;
+
+ /* We should actually check the value of 'numThreads'
+ * Since if its zero (the default), we should instead
+ * assign: 2 * 'number of schedulers'
+ * Or shall we trust the 'prim_socket' preloaded to
+ * select the proper value?
+ */
+ ctrl.numThreads = (DWORD) numThreads;
+
+ // Initialize Winsock
+ SGDBG( ("WIN-ESAIO", "esaio_init -> try initialize winsock\r\n") );
+ ires = WSAStartup(MAKEWORD(2, 2), &ctrl.wsaData);
+ if (ires != NO_ERROR) {
+ save_errno = sock_errno();
+
+ esock_error_msg("Failed initialize winsock: %d"
+ "\r\n %s (%d)"
+ "\r\n",
+ ires, erl_errno_id(save_errno), save_errno);
+
+ return ESAIO_ERR_WINSOCK_INIT;
+ }
+
+ // Create a handle for the completion port
+ SGDBG( ("WIN-ESAIO", "esaio_init -> try create I/O completion port\r\n") );
+ ctrl.cport = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
+ NULL, (u_long) 0, ctrl.numThreads);
+ if (ctrl.cport == NULL) {
+ save_errno = sock_errno();
+
+ esock_error_msg("Failed create I/O Completion Port:"
+ "\r\n %s (%d)"
+ "\r\n", erl_errno_id(save_errno), save_errno);
+
+ WSACleanup();
+
+ return ESAIO_ERR_IOCPORT_CREATE;
+ }
+
+
+ /* Create the "dummy" socket and then
+ * extract the AcceptEx and ConnectEx functions.
+ */
+ SGDBG( ("WIN-ESAIO", "esaio_init -> try create 'dummy' socket\r\n") );
+ ctrl.dummy = sock_open(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (ctrl.dummy == INVALID_SOCKET) {
+ save_errno = sock_errno();
+
+ esock_error_msg("Failed create 'dummy' socket: "
+ "\r\n %s (%d)"
+ "\r\n",
+ erl_errno_id(save_errno), save_errno);
+
+ WSACleanup();
+
+ return ESAIO_ERR_FSOCK_CREATE;
+ }
+
+
+ /* Load the AcceptEx function into memory using WSAIoctl.
+ * The WSAIoctl function is an extension of the ioctlsocket()
+ * function that can use overlapped I/O.
+ * The function's 3rd through 6th parameters are input and output
+ * buffers where we pass the pointer to our AcceptEx function.
+ * This is used so that we can call the AcceptEx function directly,
+ * rather than refer to the Mswsock.lib library.
+ */
+ SGDBG( ("WIN-ESAIO", "esaio_init -> try extract 'accept' function\r\n") );
+ ires = WSAIoctl(ctrl.dummy, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &guidAcceptEx, sizeof (guidAcceptEx),
+ &ctrl.accept, sizeof (ctrl.accept),
+ &dummy, NULL, NULL);
+ if (ires == SOCKET_ERROR) {
+ save_errno = sock_errno();
+
+ esock_error_msg("Failed extracting 'accept' function: %d"
+ "\r\n %s (%d)"
+ "\r\n",
+ ires, erl_errno_id(save_errno), save_errno);
+
+ (void) sock_close(ctrl.dummy);
+ ctrl.dummy = INVALID_SOCKET;
+ ctrl.accept = NULL;
+
+ WSACleanup();
+
+ return ESAIO_ERR_IOCTL_ACCEPT_GET;
+ }
+
+
+ /* Basically the same as for AcceptEx above */
+ SGDBG( ("WIN-ESAIO", "esaio_init -> try extract 'connect' function\r\n") );
+ ires = WSAIoctl(ctrl.dummy, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &guidConnectEx, sizeof (guidConnectEx),
+ &ctrl.connect, sizeof (ctrl.connect),
+ &dummy, NULL, NULL);
+ if (ires == SOCKET_ERROR) {
+ save_errno = sock_errno();
+
+ esock_error_msg("Failed extracting 'connect' function: %d"
+ "\r\n %s (%d)"
+ "\r\n",
+ ires, erl_errno_id(save_errno), save_errno);
+
+ (void) sock_close(ctrl.dummy);
+ ctrl.dummy = INVALID_SOCKET;
+ ctrl.accept = NULL;
+
+ WSACleanup();
+ return ESAIO_ERR_IOCTL_CONNECT_GET;
+ }
+
+
+ /* Basically the same as for AcceptEx above */
+ SGDBG( ("WIN-ESAIO", "esaio_init -> try extract 'sendmsg' function\r\n") );
+ ires = WSAIoctl(ctrl.dummy, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &guidSendMsg, sizeof (guidSendMsg),
+ &ctrl.sendmsg, sizeof (ctrl.sendmsg),
+ &dummy, NULL, NULL);
+ if (ires == SOCKET_ERROR) {
+ save_errno = sock_errno();
+
+ esock_error_msg("Failed extracting 'sendmsg' function: %d"
+ "\r\n %s (%d)"
+ "\r\n",
+ ires, erl_errno_id(save_errno), save_errno);
+
+ (void) sock_close(ctrl.dummy);
+ ctrl.dummy = INVALID_SOCKET;
+ ctrl.accept = NULL;
+ ctrl.connect = NULL;
+
+ WSACleanup();
+ return ESAIO_ERR_IOCTL_SENDMSG_GET;
+ }
+
+
+ /* Basically the same as for AcceptEx above */
+ SGDBG( ("WIN-ESAIO", "esaio_init -> try extract 'recvmsg' function\r\n") );
+ ires = WSAIoctl(ctrl.dummy, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &guidRecvMsg, sizeof (guidRecvMsg),
+ &ctrl.recvmsg, sizeof (ctrl.recvmsg),
+ &dummy, NULL, NULL);
+ if (ires == SOCKET_ERROR) {
+ save_errno = sock_errno();
+
+ esock_error_msg("Failed extracting 'recvmsg' function: %d"
+ "\r\n %s (%d)"
+ "\r\n",
+ ires, erl_errno_id(save_errno), save_errno);
+
+ (void) sock_close(ctrl.dummy);
+ ctrl.dummy = INVALID_SOCKET;
+ ctrl.accept = NULL;
+ ctrl.connect = NULL;
+ ctrl.sendmsg = NULL;
+
+ WSACleanup();
+ return ESAIO_ERR_IOCTL_RECVMSG_GET;
+ }
+
+
+ /*
+ * Create the completion port thread pool.
+ */
+ SGDBG( ("WIN-ESAIO", "esaio_init -> try alloc thread pool memory\r\n") );
+ ctrl.threads = MALLOC(numThreads * sizeof(ESAIOThread));
+ ESOCK_ASSERT( ctrl.threads != NULL );
+
+ SGDBG( ("WIN-ESAIO", "esaio_init -> basic init of thread data\r\n") );
+ for (i = 0; i < numThreads; i++) {
+ ctrl.threads[i].data.id = i;
+ ctrl.threads[i].data.state = ESAIO_THREAD_STATE_UNDEF;
+ ctrl.threads[i].data.error = ESAIO_THREAD_ERROR_UNDEF;
+ ctrl.threads[i].data.env = NULL;
+ ctrl.threads[i].data.cnt = 0;
+ }
+
+ SGDBG( ("WIN-ESAIO", "esaio_init -> try create thread(s)\r\n") );
+ for (i = 0; i < numThreads; i++) {
+ char buf[64]; /* Buffer used for building the names */
+ int j;
+
+ /* We set these here to avoid raise with the thread.
+ * *If* we fail in the creation, then we set an error */
+
+ ctrl.threads[i].data.state = ESAIO_THREAD_STATE_INITIATING;
+ ctrl.threads[i].data.error = ESAIO_THREAD_ERROR_OK;
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_init -> try create %d thread opts\r\n", i) );
+ sprintf(buf, "esaio-opts[%d]", i);
+ ctrl.threads[i].optsP = TOCREATE(buf);
+ if (ctrl.threads[i].optsP == NULL) {
+ esock_error_msg("Failed create thread opts %d\r\n");
+
+ ctrl.threads[i].data.error = ESAIO_THREAD_ERROR_TOCREATE;
+
+ for (j = 0; j < i; j++) {
+ SGDBG( ("WIN-ESAIO",
+ "esaio_init -> destroy thread opts %d\r\n", j) );
+ TODESTROY(ctrl.threads[j].optsP);
+ }
+ return ESAIO_ERR_THREAD_OPTS_CREATE;
+ }
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_init -> try create thread %d\r\n", i) );
+ sprintf(buf, "esaio[%d]", i);
+ if (0 != TCREATE(buf,
+ &ctrl.threads[i].tid,
+ esaio_completion_main,
+ (void*) &ctrl.threads[i].data,
+ ctrl.threads[i].optsP)) {
+
+ ctrl.threads[i].data.error = ESAIO_THREAD_ERROR_TCREATE;
+
+ for (j = 0; j <= i; j++) {
+ SGDBG( ("WIN-ESAIO",
+ "esaio_init -> destroy thread opts %d\r\n", j) );
+ TODESTROY(ctrl.threads[j].optsP);
+ }
+ return ESAIO_ERR_THREAD_CREATE;
+ }
+
+ }
+
+
+ SGDBG( ("WIN-ESAIO", "esaio_init -> done\r\n") );
+
+ return ESAIO_OK;
+}
+
+
+
+/* *******************************************************************
+ * Finish, terminate, the ESock Async I/O backend.
+ * This means principally to terminate (threads of) the thread pool.
+ * Issue a "message" via PostQueuedCompletionStatus
+ * instructing all (completion) threads to terminate.
+ */
+extern
+void esaio_finish()
+{
+ int t, lastThread;
+
+ SGDBG( ("WIN-ESAIO", "esaio_finish -> entry\r\n") );
+
+ if (ctrl.dummy != INVALID_SOCKET) {
+ SGDBG( ("WIN-ESAIO", "esaio_finish -> close 'dummy' socket\r\n") );
+ (void) sock_close(ctrl.dummy);
+ ctrl.dummy = INVALID_SOCKET;
+ }
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_finish -> try terminate %d worker threads\r\n",
+ ctrl.numThreads) );
+ for (t = 0, lastThread = -1, lastThread; t < ctrl.numThreads; t++) {
+ ESAIOOperation* opP;
+ BOOL qres;
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_finish -> "
+ "[%d] try allocate (terminate-) operation\r\n", t) );
+
+ /* Where is this FREE'ed??
+ * By the thread after it has been received?
+ */
+
+ opP = MALLOC( sizeof(ESAIOOperation) );
+
+ /* We should actually check that the alloc was successful
+ * and if not ...
+ * Note that this function is only called when we are terminating
+ * the VM. So, is there actuall any pointy in "doing" something?
+ * Or should we solve this another way? Instead of allocating
+ * a memory block; Send in a constant, ESAIO_OP_TERMINATE,
+ * *instead of* the overlapped pointer!
+ */
+
+ /* If this dows not work, there is not much we can do!
+ * And since this is *only* done when we terminate the VM...
+ */
+
+ if (opP != NULL) {
+ sys_memzero((char *) opP, sizeof(ESAIOOperation));
+
+ opP->tag = ESAIO_OP_TERMINATE;
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_finish -> "
+ "try post (terminate-) package %d\r\n", t) );
+
+ qres = PostQueuedCompletionStatus(ctrl.cport,
+ 0, 0, (OVERLAPPED*) opP);
+
+ if (!qres) {
+ int save_errno = sock_errno();
+ esock_error_msg("Failed posting 'terminate' command: "
+ "\r\n %s (%d)"
+ "\r\n", erl_errno_id(save_errno), save_errno);
+ break;
+ } else {
+ lastThread = t;
+ }
+
+ } else {
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_finish -> "
+ "failed allocate (terminate-) operation %d\r\n", t) );
+
+ }
+ }
+
+ if (lastThread >= 0) {
+ SGDBG( ("WIN-ESAIO",
+ "esaio_finish -> await (worker) thread(s) termination\r\n") );
+ for (t = 0; t < (lastThread+1); t++) {
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_finish -> try join with thread %d\r\n", t) );
+
+ (void) TJOIN(ctrl.threads[t].tid, NULL);
+
+ SGDBG( ("WIN-ESAIO", "esaio_finish -> joined with %d\r\n", t) );
+
+ }
+ }
+
+ /* This is overkill,
+ * since this function, esaio_finish, is called when the VM is halt'ing...
+ * ...but just to be a nice citizen...
+ */
+ SGDBG( ("WIN-ESAIO", "esaio_finish -> free the thread pool data\r\n") );
+ FREE( ctrl.threads );
+
+ SGDBG( ("WIN-ESAIO", "esaio_finish -> invalidate functions\r\n") );
+ ctrl.accept = NULL;
+ ctrl.connect = NULL;
+
+ SGDBG( ("WIN-ESAIO", "esaio_finish -> done\r\n") );
+
+ return;
+}
+
+
+
+/* *******************************************************************
+ * esaio_info - Return info "about" this I/O backend.
+ */
+
+extern
+ERL_NIF_TERM esaio_info(ErlNifEnv* env)
+{
+ ERL_NIF_TERM info, numThreads,
+ numUnexpAccs, numUnexpConns, numUnexpWs, numUnexpRs,
+ numGenErrs,
+ numUnknownCmds;
+
+ numThreads = MKUI(env, ctrl.numThreads);
+
+ MLOCK(ctrl.cntMtx);
+
+ numUnexpConns = MKUI(env, ctrl.unexpectedConnects);
+ numUnexpAccs = MKUI(env, ctrl.unexpectedAccepts);
+ numUnexpWs = MKUI(env, ctrl.unexpectedWrites);
+ numUnexpRs = MKUI(env, ctrl.unexpectedReads);
+ numGenErrs = MKUI(env, ctrl.genErrs);
+ numUnknownCmds = MKUI(env, ctrl.unknownCmds);
+
+ MUNLOCK(ctrl.cntMtx);
+
+ {
+ ERL_NIF_TERM cntKeys[] = {esock_atom_num_unexpected_connects,
+ esock_atom_num_unexpected_accepts,
+ esock_atom_num_unexpected_writes,
+ esock_atom_num_unexpected_reads,
+ esock_atom_num_general_errors,
+ esock_atom_num_unknown_cmds};
+ ERL_NIF_TERM cntVals[] = {numUnexpConns, numUnexpAccs,
+ numUnexpWs, numUnexpRs,
+ numGenErrs,
+ numUnknownCmds};
+ unsigned int numCntKeys = NUM(cntKeys);
+ unsigned int numCntVals = NUM(cntVals);
+ ERL_NIF_TERM counters;
+
+ ESOCK_ASSERT( numCntKeys == numCntVals );
+ ESOCK_ASSERT( MKMA(env, cntKeys, cntVals, numCntKeys, &counters) );
+
+ {
+ ERL_NIF_TERM keys[] = {esock_atom_name,
+ esock_atom_num_threads,
+ esock_atom_counters};
+ ERL_NIF_TERM vals[] = {MKA(env, "win_esaio"),
+ numThreads,
+ counters};
+ unsigned int numKeys = NUM(keys);
+ unsigned int numVals = NUM(vals);
+
+ ESOCK_ASSERT( numKeys == numVals );
+ ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &info) );
+ }
+ }
+
+ return info;
+}
+
+
+
+/* *******************************************************************
+ * esaio_open_plain - create an endpoint (from an existing fd) for
+ * communication.
+ *
+ * Create an *overlapped* socket, then add it to the I/O
+ * completion port.
+ */
+
+extern
+ERL_NIF_TERM esaio_open_plain(ErlNifEnv* env,
+ int domain,
+ int type,
+ int protocol,
+ ERL_NIF_TERM eopts,
+ const ESockData* dataP)
+{
+ /* We do not actually need the dataP since we already have the dbg... */
+ BOOLEAN_T dbg = esock_open_is_debug(env, eopts, dataP->sockDbg);
+ BOOLEAN_T useReg = esock_open_use_registry(env, eopts, dataP->useReg);
+ ESockDescriptor* descP;
+ ERL_NIF_TERM sockRef;
+ int proto = protocol;
+ DWORD dwFlags = WSA_FLAG_OVERLAPPED;
+ SOCKET sock = INVALID_SOCKET;
+ ErlNifPid self;
+ int res, save_errno;
+
+ /* Keep track of the creator
+ * This should not be a problem, but just in case
+ * the *open* function is used with the wrong kind
+ * of environment...
+ */
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ SSDBG2( dbg,
+ ("WIN-ESAIO", "esaio_open_plain -> entry with"
+ "\r\n domain: %d"
+ "\r\n type: %d"
+ "\r\n protocol: %d"
+ "\r\n eopts: %T"
+ "\r\n", domain, type, protocol, eopts) );
+
+ sock = sock_open_O(domain, type, proto);
+
+ if (sock == INVALID_SOCKET) {
+ save_errno = sock_errno();
+ return esock_make_error_errno(env, save_errno);
+ }
+
+ SSDBG2( dbg, ("WIN-ESAIO",
+ "esaio_open_plain -> open success: %d\r\n", sock) );
+
+ /* NOTE that if the protocol = 0 (default) and the domain is not
+ * local (AF_LOCAL) we need to explicitly get the protocol here!
+ */
+
+ if (proto == 0)
+ (void) esock_open_which_protocol(sock, &proto);
+
+ /* Create and initiate the socket "descriptor" */
+ descP = esock_alloc_descriptor(sock);
+ descP->ctrlPid = self;
+ descP->domain = domain;
+ descP->type = type;
+ descP->protocol = proto;
+
+ SSDBG2( dbg, ("WIN-ESAIO",
+ "esaio_open_plain -> add to completion port\r\n") );
+
+ if (ESAIO_OK != (save_errno = esaio_add_socket(descP))) {
+ // See esock_dtor for what needs done!
+ ERL_NIF_TERM tag = esock_atom_add_socket;
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(save_errno));
+
+ SSDBG2( dbg, ("WIN-ESAIO",
+ "esaio_open_plain -> "
+ "failed adding socket to completion port: "
+ "%T (%d)\r\n", reason, save_errno) );
+
+ esock_dealloc_descriptor(env, descP);
+ sock_close(sock);
+
+ /* This should really be:
+ * {error, {invalid, {add_to_completion_port, Reason}}}
+ */
+
+ return esock_make_error_t2r(env, tag, reason);
+ }
+
+ SSDBG2( dbg, ("WIN-ESAIO",
+ "esaio_open_plain -> create socket ref\r\n") );
+
+ sockRef = enif_make_resource(env, descP);
+ enif_release_resource(descP);
+
+ SSDBG2( dbg, ("WIN-ESAIO",
+ "esaio_open_plain -> monitor owner %T\r\n", descP->ctrlPid) );
+
+ ESOCK_ASSERT( MONP("esaio_open -> ctrl",
+ env, descP,
+ &descP->ctrlPid,
+ &descP->ctrlMon) == 0 );
+
+ descP->dbg = dbg;
+ descP->useReg = useReg;
+ esock_inc_socket(domain, type, proto);
+
+ SSDBG2( dbg, ("WIN-ESAIO",
+ "esaio_open_plain -> maybe update registry\r\n") );
+
+ /* And finally (maybe) update the registry */
+ if (descP->useReg) esock_send_reg_add_msg(env, descP, sockRef);
+
+ SSDBG2( dbg, ("WIN-ESAIO",
+ "esaio_open_plain -> done\r\n") );
+
+ return esock_make_ok2(env, sockRef);
+}
+
+
+
+/* *******************************************************************
+ * esaio_bind - Bind a name to a socket.
+ *
+ */
+extern
+ERL_NIF_TERM esaio_bind(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESockAddress* sockAddrP,
+ SOCKLEN_T addrLen)
+{
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ if (sock_bind(descP->sock, &sockAddrP->sa, addrLen) < 0) {
+ return esock_make_error_errno(env, sock_errno());
+ }
+
+ descP->writeState |= ESOCK_STATE_BOUND;
+
+ return esock_atom_ok;
+}
+
+
+
+/* *******************************************************************
+ * esaio_connect - Connect the socket to a specified address
+ *
+ * The function we use here, ConnectEx, is intended for STREAM/TCP.
+ * But (traditional) connect can be used with DGRAM/UDP.
+ * So, does ConnectEx work with DGRAM/UDP sockets? I think not.
+ * So we may need to test what kind of socket we have, and for
+ * DGRAM/UDP use the "old" connect:
+ *
+ * if (type == DGRAM) && (protocol == UDP)
+ * connect(...);
+ * else
+ * ConnectEx(...);
+ *
+ * Quote from the Microsoft documentation:
+ * "The ConnectEx function can only be used with connection-oriented sockets.
+ * The socket passed in the s parameter must be created with a socket type
+ * of SOCK_STREAM, SOCK_RDM, or SOCK_SEQPACKET."
+ *
+ * Quote from the Microsoft documentation:
+ * When the ConnectEx function successfully completes, the "connected socket"
+ * can be passed to only the following functions:
+ *
+ * * ReadFile (not provided by our API)
+ * * WriteFile (not provided by our API)
+ * * send or WSASend (send not used by us)
+ * * recv or WSARecv (recv not used by us)
+ * * TransmitFile (not provided by our API)
+ * * closesocket
+ *
+ * Socket used *must* be *bound* before calling ConnectEx!
+ */
+
+extern
+ERL_NIF_TERM esaio_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM connRef,
+ ESockAddress* addrP,
+ SOCKLEN_T addrLen)
+{
+ /*
+ * Verify that we are in the proper state
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_connect(%T, %d) -> verify open\r\n",
+ sockRef, descP->sock) );
+
+ if (! IS_OPEN(descP->writeState))
+ return esock_make_error(env, esock_atom_closed);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_connect(%T, %d) -> verify type: %s\r\n",
+ sockRef, descP->sock, TYPE2STR(descP->type)) );
+
+ switch (descP->type) {
+ case SOCK_STREAM:
+ return esaio_connect_stream(env,
+ descP, sockRef, connRef,
+ addrP, addrLen);
+ break;
+
+ case SOCK_DGRAM:
+ return esaio_connect_dgram(env,
+ descP, sockRef, connRef,
+ addrP, addrLen);
+ break;
+
+ default:
+ return enif_make_badarg(env);
+ }
+}
+
+
+
+/* *******************************************************************
+ * esaio_connect_stream - Connect the (stream) socket
+ */
+static
+ERL_NIF_TERM esaio_connect_stream(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM connRef,
+ ESockAddress* addrP,
+ SOCKLEN_T addrLen)
+{
+ int save_errno;
+ BOOL cres;
+ ESAIOOperation* opP;
+ ERL_NIF_TERM eres;
+ ErlNifPid self;
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+
+ /* ConnectEx *requires* the socket to be bound */
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_connect_stream(%T) -> verify bound\r\n", sockRef) );
+ if (! IS_BOUND(descP->writeState))
+ return esock_make_error(env, esock_atom_not_bound);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_connect_stream(%T) -> check if ongoing\r\n",
+ sockRef) );
+ if (descP->connectorP != NULL) {
+
+ /* Connect already in progress, check if its us */
+
+ if (COMPARE_PIDS(&self, &descP->connector.pid) != 0) {
+ /* *Other* process has connect in progress */
+ if (addrP != NULL) {
+ eres = esock_make_error(env, esock_atom_already);
+ } else {
+ /* This is a bad call sequence
+ * - connect without an address is only allowed
+ * for the connecting process
+ */
+ eres = esock_raise_invalid(env, esock_atom_state);
+ }
+ } else {
+
+ /* TRHIS IS NOT HOW IT WORKS ON WINDOWS!
+ * This function should never be called *again*
+ * The completion message contains the full and final answer.
+ * No need to call again!
+ */
+
+ eres = esock_raise_invalid(env, esock_atom_state);
+ }
+
+ } else if (addrP == NULL) {
+
+ /* This is a bad call sequence
+ * - connect without an address is not valid on Windows.
+ */
+ eres = esock_raise_invalid(env, esock_atom_state);
+
+ } else {
+
+ DWORD sentDummy = 0;
+
+ /* No connect in progress */
+
+ /* Initial connect call, with address */
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_connect_stream(%T) -> allocate (connect) operation\r\n",
+ sockRef) );
+
+ opP = MALLOC( sizeof(ESAIOOperation) );
+ ESOCK_ASSERT( opP != NULL);
+ sys_memzero((char*) opP, sizeof(ESAIOOperation));
+
+ opP->tag = ESAIO_OP_CONNECT;
+
+ /* Its a bit annoying that we have to alloc an env and then
+ * copy the ref *before* we know that we actually need it.
+ * How much does this cost?
+ */
+
+ /* Initiate connector */
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_connect_stream(%T) -> initiate connector\r\n",
+ sockRef) );
+
+ descP->connector.pid = self;
+ ESOCK_ASSERT( MONP("esaio_connect_stream -> conn",
+ env, descP,
+ &self, &descP->connector.mon) == 0 );
+ descP->connector.dataP = (void*) opP;
+ descP->connector.env = esock_alloc_env("connector");
+ descP->connector.ref = CP_TERM(descP->connector.env, connRef);
+ descP->connectorP = &descP->connector;
+ descP->writeState |=
+ (ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED);
+
+ opP->env = esock_alloc_env("esaio-connect-stream");
+ opP->caller = self;
+ opP->data.connect.sockRef = CP_TERM(opP->env, sockRef);
+ opP->data.connect.connRef = CP_TERM(opP->env, connRef);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_connect_stream {%d} -> try connect\r\n",
+ descP->sock) );
+
+ /*
+ * BOOL LpfnConnectex(
+ * [in] SOCKET s,
+ * [in] const sockaddr *name,
+ * [in] int namelen,
+ * [in, optional] PVOID lpSendBuffer,
+ * [in] DWORD dwSendDataLength,
+ * [out] LPDWORD lpdwBytesSent,
+ * [in] LPOVERLAPPED lpOverlapped
+ * )
+ */
+
+ cres = sock_connect_O(descP->sock,
+ addrP, addrLen,
+ &sentDummy, (OVERLAPPED*) opP);
+
+ /*
+ * We need to keep using the requestor "queues"!
+ * That is the "only" way to handle the monitoring of
+ * the requestor.
+ * That is if the request (for instance a connect) is
+ * is scheduled, WSA_IO_PENDING, then we need to store
+ * the info about the requestor somewhere we can access it,
+ * in case the requestor for example dies (and we need to
+ * clean up).
+ */
+
+ eres = connect_stream_check_result(env, descP, opP, cres);
+
+ }
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_connect {%d} -> done with"
+ "\r\n eres: %T"
+ "\r\n",
+ descP->sock, eres) );
+
+ return eres;
+}
+
+
+static
+ERL_NIF_TERM connect_stream_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ BOOL cres)
+{
+ ERL_NIF_TERM eres, tag, reason;
+ int save_errno;
+
+ if (cres) {
+
+ /* Success already! */
+
+ int err;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "connect_stream_check_result(%d) -> connected\r\n",
+ descP->sock) );
+
+ /* Clean up the connector stuff, no need for that anymore */
+ esock_requestor_release("connect_stream_check_result -> success",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+
+ /* We need to make sure peername and sockname works! */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "connect_stream_check_result {%d} -> "
+ "update connect context\r\n",
+ descP->sock) );
+
+ err = ESAIO_UPDATE_CONNECT_CONTEXT( descP->sock );
+
+ if (err == 0) {
+
+ descP->writeState &=
+ ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED);
+ descP->writeState |= ESOCK_STATE_CONNECTED;
+
+ eres = esock_atom_ok;
+
+ } else {
+
+ save_errno = sock_errno();
+ tag = esock_atom_update_connect_context;
+ reason = ENO2T(env, save_errno);
+
+ SSDBG( descP, ("WIN-ESAIO",
+ "connect_stream_check_result(%d) -> "
+ "connect context update failed: %T\r\n",
+ descP->sock, reason) );
+
+ sock_close(descP->sock);
+ descP->writeState = ESOCK_STATE_CLOSED;
+
+ WSACleanup();
+
+ eres = esock_make_error_t2r(env, tag, reason);
+ }
+
+ } else {
+
+ /* Connect returned error, check which */
+
+ save_errno = sock_errno();
+
+ if (save_errno == WSA_IO_PENDING) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "connect_stream_check_result(%d) -> connect scheduled\r\n",
+ descP->sock) );
+
+ eres = esock_atom_completion;
+
+ } else {
+ ERL_NIF_TERM ereason = ENO2T(env, save_errno);
+
+ SSDBG( descP, ("WIN-ESAIO",
+ "connect_stream_check_result(%d) -> "
+ "connect attempt failed: %T\r\n",
+ descP->sock, ereason) );
+
+ /* Clean up the connector stuff, no need for that anymore */
+ esock_requestor_release("connect_stream_check_result -> failure",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+
+ /* Will an event be generetade in this case?
+ * Assume not => We need to clean up here!
+ */
+ esock_clear_env("connect_stream_check_result", opP->env);
+ esock_free_env("connect_stream_check_result", opP->env);
+ FREE( opP );
+
+ sock_close(descP->sock);
+ descP->writeState = ESOCK_STATE_CLOSED;
+ WSACleanup();
+
+ eres = esock_make_error(env, ereason);
+ }
+ }
+
+ return eres;
+}
+
+
+
+/* *** esaio_connect_dgram ***
+ * Handle the "fake" connect of a DGRAM socket ("bind" to
+ * a remote address, so user can use send/recv instead of
+ * sendto/recvfrom). Should use sock_connect(...)
+ * This is corrently just a placeholder!
+ */
+static
+ERL_NIF_TERM esaio_connect_dgram(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM connRef,
+ ESockAddress* addrP,
+ SOCKLEN_T addrLen)
+{
+ return enif_make_badarg(env);
+}
+
+
+
+/* *** esaio_listen *** */
+
+
+/* ========================================================================
+ */
+extern
+ERL_NIF_TERM esaio_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef)
+{
+ ErlNifPid caller;
+ ERL_NIF_TERM eres;
+ SOCKET accSock;
+ ESAIOOperation* opP;
+ BOOLEAN_T ares;
+ unsigned int addrSz, bufSz;
+ DWORD recvBytes;
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ /* Ensure that this caller does not already have a
+ * (accept) request waiting */
+ if (esock_acceptor_search4pid(env, descP, &caller)) {
+ /* Acceptor already in queue */
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ /* Accept and Read uses the same flag so they can not be simultaneous.
+ */
+ SSDBG( descP, ("WIN-ESAIO", "esaio_accept {%d} -> verify not reading\r\n",
+ descP->sock) );
+ if (descP->readersQ.first != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Should we verify domain, type and protocol? */
+
+ /* Allocate 'operation' */
+ SSDBG( descP, ("WIN-ESAIO",
+ "esaio_accept {%d} -> allocate 'operation'\r\n",
+ descP->sock) );
+ opP = MALLOC( sizeof(ESAIOOperation) );
+ ESOCK_ASSERT( opP != NULL);
+ sys_memzero((char*) opP, sizeof(ESAIOOperation));
+
+ opP->tag = ESAIO_OP_ACCEPT;
+
+ /* Its a bit annoying that we have to alloc an env and then
+ * copy the ref *before* we know that we actually need it.
+ * How much does this cost?
+ */
+ SSDBG( descP, ("WIN-ESAIO",
+ "esaio_accept {%d} -> initiate 'operation'\r\n",
+ descP->sock) );
+ opP->env = esock_alloc_env("esaio_accept - operation");
+ opP->data.accept.lSockRef = CP_TERM(opP->env, sockRef);
+ opP->data.accept.accRef = CP_TERM(opP->env, accRef);
+ opP->data.accept.lsock = descP->sock;
+ opP->caller = caller;
+
+ /* Create the accepting socket
+ * domain - should be AF_INET | AF_INET6 (sould we make sure?)
+ * type - should be SOCK_STREAM | SOCK_SEQPACKET (should we make sure?)
+ * protocol - should be IPPROTO_TCP | IPPROTO_SCTP (should we make sure?)
+ * See check above!
+ */
+ SSDBG( descP, ("WIN-ESAIO",
+ "esaio_accept {%d} -> try create 'accepting' socket\r\n",
+ descP->sock) );
+ accSock = sock_open(descP->domain, descP->type, descP->protocol);
+ if (accSock == INVALID_SOCKET) {
+ int save_errno = sock_errno();
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(save_errno));
+ ERL_NIF_TERM tag = esock_atom_create_accept_socket;
+
+ esock_clear_env("esaio_accept - invalid accept socket", opP->env);
+ esock_free_env("esaio_accept - invalid accept socket", opP->env);
+ FREE( opP );
+ WSACleanup();
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_accept {%d} -> failed create 'accepting' socket:"
+ "\r\n %T (%d)"
+ "\r\n",
+ descP->sock, reason, save_errno) );
+
+ return esock_make_error_t2r(env, tag, reason);
+ }
+
+ opP->data.accept.asock = accSock;
+
+ /* According to the (Microsoft) documentation, the buffer size of the
+ * local and remote address must be 16 bytes *more* than the size of
+ * the sockaddr structure for the transport protocol in use.
+ */
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_accept {%d} -> "
+ "try calculate address and address buffer size(s)\r\n",
+ descP->sock) );
+ switch (descP->domain) {
+ case AF_INET:
+ addrSz = sizeof(struct sockaddr_in) + 16;
+ break;
+ case AF_INET6:
+ addrSz = sizeof(struct sockaddr_in6) + 16;
+ break;
+ default:
+ return esock_make_error_invalid(env, esock_atom_domain);
+ break;
+ }
+ bufSz = 2 * addrSz;
+
+ opP->data.accept.buf = MALLOC( bufSz );
+ ESOCK_ASSERT( opP->data.accept.buf != NULL);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_accept(%T, %d) -> try accept\r\n",
+ sockRef, descP->sock) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_tries, &descP->accTries, 1);
+
+ ares = sock_accept_O(descP->sock, accSock,
+ opP->data.accept.buf,
+ addrSz,
+ &recvBytes,
+ (OVERLAPPED*) opP);
+
+ eres = accept_check_result(env, descP, opP, ares,
+ sockRef, accRef, accSock, caller);
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_accept(%T, %d) -> done when"
+ "\r\n eres: %T"
+ "\r\n", sockRef, descP->sock, eres) );
+
+ return eres;
+}
+
+
+
+static
+ERL_NIF_TERM accept_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ BOOL ares,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef,
+ SOCKET accSock,
+ ErlNifPid caller)
+{
+ ERL_NIF_TERM eres;
+
+ if (ares) {
+
+ /* Success already!
+ * So, no need to store the data (in the "queue").
+ * And then allocate and initiate the new descriptor.
+ */
+
+ eres = esaio_accept_accepted(env, descP, caller, sockRef, accSock);
+
+ } else {
+
+ /* Accept returned error, check which */
+
+ int save_errno = sock_errno();
+
+ /* As pointed out above, there are basically two kinds of errors:
+ * 1) Pending:
+ * An overlapped operation was successfully initiated.
+ * Completion will be "indicated" at a later time.
+ * 2) An actual error
+ */
+
+ if (save_errno == WSA_IO_PENDING) {
+
+ /* We need to store the data in the queue! */
+
+ eres = accept_check_pending(env, descP, opP, caller,
+ sockRef, accRef);
+
+ } else {
+
+ eres = accept_check_fail(env, descP, opP, save_errno,
+ accSock, sockRef);
+
+ }
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "accept_check_result(%T, %d) -> done with"
+ "\r\n result: %T"
+ "\r\n", sockRef, descP->sock, eres) );
+
+ return eres;
+}
+
+
+
+static
+ERL_NIF_TERM accept_check_pending(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef)
+{
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "accept_check_pending(%T, %d) -> entry with"
+ "\r\n accRef: %T"
+ "\r\n", sockRef, descP->sock, accRef) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_waits, &descP->accWaits, 1);
+
+ if (descP->acceptorsQ.first == NULL)
+ descP->readState |= ESOCK_STATE_ACCEPTING;
+
+ /* Will be picked up by the (worker) threads when the event comes */
+ esock_acceptor_push(env, descP, caller, accRef, opP);
+
+ return esock_atom_completion;
+
+}
+
+
+static
+ERL_NIF_TERM accept_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ int saveErrno,
+ SOCKET accSock,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM reason;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "accept_check_fail(%T, %d) -> entry with"
+ "\r\n errno: %d"
+ "\r\n (acc) socket: %d"
+ "\r\n", sockRef, descP->sock, saveErrno, accSock) );
+
+ reason = MKA(env, erl_errno_id(saveErrno));
+
+ /* Will an event be generetade in this case?
+ * Assume not => We need to clean up here!
+ */
+ esock_clear_env("esaio_accept", opP->env);
+ esock_free_env("esaio_accept", opP->env);
+ FREE( opP->data.accept.buf );
+ FREE( opP );
+
+ sock_close(accSock);
+ WSACleanup();
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_fails, &descP->accFails, 1);
+
+ return esock_make_error(env, reason);
+}
+
+
+
+/* *** esaio_accept_accepted ***
+ *
+ * Generic function handling a successful accept.
+ */
+static
+ERL_NIF_TERM esaio_accept_accepted(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifPid pid,
+ ERL_NIF_TERM sockRef,
+ SOCKET accSock)
+{
+ ESockDescriptor* accDescP;
+ ERL_NIF_TERM accRef;
+ int save_errno;
+
+ /*
+ * We got one
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_accept_accepted(%T, %d) -> entry with"
+ "\r\n (accept) socket: %T"
+ "\r\n", sockRef, descP->sock, accSock) );
+
+ // Allocate the descriptor
+ accDescP = esock_alloc_descriptor(accSock);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_accept_accepted(%T, %d) -> add to completion port\r\n",
+ sockRef, descP->sock) );
+
+ if (ESAIO_OK != (save_errno = esaio_add_socket(accDescP))) {
+ // See esock_dtor for what needs done!
+ ERL_NIF_TERM tag = esock_atom_add_socket;
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(save_errno));
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_fails, &descP->accFails, 1);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_accept_accepted(%T, %d) -> "
+ "failed adding (accepted) socket to completion port: "
+ "%T (%d)\r\n", sockRef, descP->sock, reason, save_errno) );
+
+ esock_dealloc_descriptor(env, accDescP);
+ sock_close(accSock);
+
+ /* This should really be:
+ * {error, {invalid, {add_to_completion_port, Reason}}}
+ */
+
+ return esock_make_error_t2r(env, tag, reason);
+ }
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_success, &descP->accSuccess, 1);
+
+ accDescP->domain = descP->domain;
+ accDescP->type = descP->type;
+ accDescP->protocol = descP->protocol;
+
+ MLOCK(descP->writeMtx);
+
+ accDescP->rBufSz = descP->rBufSz; // Inherit buffer size
+ accDescP->rCtrlSz = descP->rCtrlSz; // Inherit buffer size
+ accDescP->wCtrlSz = descP->wCtrlSz; // Inherit buffer size
+ accDescP->iow = descP->iow; // Inherit iow
+ accDescP->dbg = descP->dbg; // Inherit debug flag
+ accDescP->useReg = descP->useReg; // Inherit useReg flag
+ esock_inc_socket(accDescP->domain, accDescP->type, accDescP->protocol);
+
+ accRef = enif_make_resource(env, accDescP);
+ enif_release_resource(accDescP);
+
+ accDescP->ctrlPid = pid;
+ /* pid has actually been compared equal to self()
+ * in this code path just a little while ago
+ */
+ ESOCK_ASSERT( MONP("esaio_accept_accepted -> ctrl",
+ env, accDescP,
+ &accDescP->ctrlPid,
+ &accDescP->ctrlMon) == 0 );
+
+ accDescP->writeState |= ESOCK_STATE_CONNECTED;
+
+ MUNLOCK(descP->writeMtx);
+
+ /* And finally (maybe) update the registry */
+ if (descP->useReg) esock_send_reg_add_msg(env, descP, accRef);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_accept_accepted(%T, %d) -> done\r\n",
+ sockRef, descP->sock) );
+
+ return esock_make_ok2(env, accRef);
+
+}
+
+
+
+/* ========================================================================
+ * Do the actual send.
+ * Do some initial writer checks, do the actual send and then
+ * analyze the result.
+ *
+ * The following flags are "valid":
+ *
+ * MSG_DONTROUTE, MSG_PARTIAL, and MSG_OOB
+ *
+ */
+extern
+ERL_NIF_TERM esaio_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ErlNifBinary* sndDataP,
+ int flags)
+{
+ ErlNifPid caller;
+ ERL_NIF_TERM eres;
+ BOOLEAN_T cleanup = FALSE;
+ int wres;
+ DWORD toWrite;
+ char* buf;
+ DWORD f = (DWORD) flags;
+ ESAIOOperation* opP;
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (! IS_OPEN(descP->writeState)) {
+ ESOCK_EPRINTF("esaio_send(%T, %d) -> NOT OPEN\r\n",
+ sockRef, descP->sock);
+ return esock_make_error_closed(env);
+ }
+
+ /* Connect and Write can not be simultaneous? */
+ if (descP->connectorP != NULL) {
+ ESOCK_EPRINTF("esaio_send(%T, %d) -> CONNECTING\r\n",
+ sockRef, descP->sock);
+ return esock_make_error_invalid(env, esock_atom_state);
+ }
+
+ /* Ensure that this caller does not *already* have a
+ * (send) request waiting */
+ if (esock_writer_search4pid(env, descP, &caller)) {
+ /* Sender already in queue */
+ ESOCK_EPRINTF("esaio_send(%T, %d) -> ALREADY SENDING\r\n",
+ sockRef, descP->sock);
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ /* This is a size check,
+ * to ensure we do not try to send something *to* large */
+ toWrite = (DWORD) sndDataP->size;
+ if ((size_t) toWrite != sndDataP->size)
+ return esock_make_error_invalid(env, esock_atom_data_size);
+
+ /* Once the send function has been called, this memory
+ * is "owned" by the system. That is, we cannot free it
+ * (or do anything with it) until the *operation* has completed,
+ * so the free is done by the thread(s).
+ */
+ buf = MALLOC( toWrite );
+ ESOCK_ASSERT( buf != NULL );
+ sys_memcpy(buf, sndDataP->data, toWrite);
+
+ opP = MALLOC( sizeof(ESAIOOperation) );
+ ESOCK_ASSERT( opP != NULL);
+ sys_memzero((char*) opP, sizeof(ESAIOOperation));
+
+ opP->tag = ESAIO_OP_SEND;
+ /* Its a bit annoying that we have to alloc an env and then
+ * copy the ref *before* we know that we actually need it.
+ * But after the call is to late, so...
+ */
+ opP->env = esock_alloc_env("esaio-send - operation");
+ opP->data.send.sendRef = CP_TERM(opP->env, sendRef);
+ opP->data.send.sockRef = CP_TERM(opP->env, sockRef);
+ opP->data.send.wbuf.buf = buf;
+ opP->data.send.wbuf.len = toWrite;
+ opP->caller = caller;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_tries, &descP->writeTries, 1);
+
+ wres = sock_send_O(descP->sock, &opP->data.send.wbuf, f, (OVERLAPPED*) opP);
+
+ eres = send_check_result(env, descP, opP, caller,
+ wres, toWrite, FALSE,
+ sockRef, sendRef, &cleanup);
+
+ if (cleanup) {
+
+ /* "Manually" allocated buffer */
+ FREE( opP->data.send.wbuf.buf );
+
+ esock_clear_env("esaio_send - cleanup", opP->env);
+ esock_free_env("esaio_send - cleanup", opP->env);
+
+ FREE( opP );
+
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_send {%d} -> done (%s)"
+ "\r\n %T"
+ "\r\n", descP->sock, B2S(cleanup), eres) );
+
+ return eres;
+}
+
+
+
+/* ========================================================================
+ * Do the actual send.
+ * Do some initial writer checks, do the actual send and then
+ * analyze the result.
+ *
+ * The following flags are "valid":
+ *
+ * MSG_DONTROUTE, MSG_PARTIAL, and MSG_OOB
+ *
+ * "Explicit binding is discouraged for client applications."
+ */
+
+extern
+ERL_NIF_TERM esaio_sendto(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ErlNifBinary* sndDataP,
+ int flags,
+ ESockAddress* toAddrP,
+ SOCKLEN_T toAddrLen)
+{
+ ErlNifPid caller;
+ ERL_NIF_TERM eres;
+ BOOLEAN_T cleanup = FALSE;
+ int wres;
+ DWORD toWrite;
+ char* buf;
+ DWORD f = (DWORD) flags;
+ ESAIOOperation* opP;
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (! IS_OPEN(descP->writeState))
+ return esock_make_error_closed(env);
+
+ /* Connect and Write can not be simultaneous? */
+ if (descP->connectorP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Empty address to allowed */
+ if (toAddrP == NULL)
+ return esock_make_invalid(env, esock_atom_sockaddr);
+
+ /* Ensure that this caller does not *already* have a
+ * (send) request waiting */
+ if (esock_writer_search4pid(env, descP, &caller)) {
+ /* Sender already in queue */
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ /* This is a size check,
+ * to ensure we do not try to send something *to* large */
+ toWrite = (DWORD) sndDataP->size;
+ if ((size_t) toWrite != sndDataP->size)
+ return esock_make_error_invalid(env, esock_atom_data_size);
+
+ /* Once the send function has been called, this memory
+ * (buf) "belongs" to the "system" (so no need to free it).
+ */
+ buf = MALLOC( toWrite );
+ ESOCK_ASSERT( buf != NULL );
+ sys_memcpy(buf, sndDataP->data, toWrite);
+
+ opP = MALLOC( sizeof(ESAIOOperation) );
+ ESOCK_ASSERT( opP != NULL);
+ sys_memzero((char*) opP, sizeof(ESAIOOperation));
+
+ opP->tag = ESAIO_OP_SENDTO;
+ /* Its a bit annoying that we have to alloc an env and then
+ * copy the ref *before* we know that we actually need it.
+ * But after the call is to late, so...
+ */
+ opP->env = esock_alloc_env("esaio-sendto - operation");
+ opP->data.sendto.sendRef = CP_TERM(opP->env, sendRef);
+ opP->data.sendto.sockRef = CP_TERM(opP->env, sockRef);
+ opP->data.sendto.wbuf.buf = buf;
+ opP->data.sendto.wbuf.len = toWrite;
+ opP->data.sendto.remoteAddr = *toAddrP; // Do we need this?
+ opP->data.sendto.remoteAddrLen = toAddrLen;// Do we need this?
+ opP->caller = caller;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_tries, &descP->writeTries, 1);
+
+ wres = sock_sendto_O(descP->sock, &opP->data.sendto.wbuf, f,
+ (struct sockaddr*) toAddrP, toAddrLen,
+ (OVERLAPPED*) opP);
+
+ eres = send_check_result(env, descP, opP, caller,
+ wres, toWrite, FALSE,
+ sockRef, sendRef, &cleanup);
+
+ if (cleanup) {
+
+ /* "Manually" allocated buffer */
+ FREE( opP->data.sendto.wbuf.buf );
+
+ esock_clear_env("esaio_sendto - cleanup", opP->env);
+ esock_free_env("esaio_sendto - cleanup", opP->env);
+
+ FREE( opP );
+
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_sendto {%d} -> done (%s)"
+ "\r\n %T"
+ "\r\n", descP->sock, B2S(cleanup), eres) );
+
+ return eres;
+}
+
+
+
+/* ========================================================================
+ * Do the actual sendmsg.
+ * Do some initial writer checks, do the actual send and then
+ * analyze the result.
+ *
+ * The following flags are "valid":
+ *
+ * MSG_DONTROUTE, MSG_PARTIAL, and MSG_OOB
+ *
+ * "Explicit binding is discouraged for client applications."
+ *
+ * Also, according to Microsoft documentation:
+ *
+ * "can only be used with datagrams and raw sockets."
+ *
+ * So, should we check, or let the user crash and burn?
+ *
+ * Note that this operation *only* works for socket
+ * of types SOCK_DGRAM and SOCK_RAW! Should we check
+ * and throw 'enotsup' otherwise? Would make testing
+ * easier...
+ */
+
+extern
+ERL_NIF_TERM esaio_sendmsg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ERL_NIF_TERM eMsg,
+ int flags,
+ ERL_NIF_TERM eIOV,
+ const ESockData* dataP)
+{
+ ErlNifPid caller;
+ ERL_NIF_TERM eres;
+ BOOLEAN_T cleanup = FALSE;
+ int wres;
+ ERL_NIF_TERM tail;
+ ERL_NIF_TERM eAddr, eCtrl;
+ ssize_t dataSize;
+ size_t ctrlBufLen, ctrlBufUsed;
+ WSABUF* wbufs = NULL;
+ ESAIOOperation* opP = NULL;
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_sendmsg(%T, %d) -> entry with"
+ "\r\n", sockRef, descP->sock) );
+
+ /* This *only* works on socket type(s) DGRAM or RAW.
+ * Other socket types results in einval, which is not very
+ * helpful. So, in order to, atleast, help with testing,
+ * we do this...
+ */
+ if (! ((descP->type == SOCK_DGRAM) || (descP->type == SOCK_RAW))) {
+ return enif_raise_exception(env, MKA(env, "notsup"));
+ }
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (! IS_OPEN(descP->writeState))
+ return esock_make_error_closed(env);
+
+ /* Connect and Write can not be simultaneous? */
+ if (descP->connectorP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Ensure that this caller does not *already* have a
+ * (send) request waiting */
+ if (esock_writer_search4pid(env, descP, &caller)) {
+ /* Sender already in queue */
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ opP = MALLOC( sizeof(ESAIOOperation) );
+ ESOCK_ASSERT( opP != NULL);
+ sys_memzero((char*) opP, sizeof(ESAIOOperation));
+
+ opP->tag = ESAIO_OP_SENDMSG;
+
+ if (! init_sendmsg_sockaddr(env, descP, eMsg,
+ &opP->data.sendmsg.msg,
+ &opP->data.sendmsg.addr)) {
+
+ FREE( opP );
+
+ return esock_make_invalid(env, esock_atom_addr);
+ }
+
+ /* Its a bit annoying that we have to alloc an env and then
+ * copy the ref *before* we know that we actually need it.
+ * How much does this cost?
+ */
+ opP->env = esock_alloc_env("esaio_sendmsg - operation");
+
+ /* Extract the *mandatory* 'iov', which must be an erlang:iovec(),
+ * from which we take at most IOV_MAX binaries.
+ * The env *cannot* be NULL because we don't actually know if
+ * the send succeeds *now*. It could be sceduled!
+ */
+ if ((! enif_inspect_iovec(opP->env,
+ dataP->iov_max, eIOV, &tail,
+ &opP->data.sendmsg.iovec))) {
+
+ SSDBG( descP, ("WIN-ESAIO",
+ "essaio_sendmsg {%d} -> not an iov\r\n",
+ descP->sock) );
+
+ esock_free_env("esaio-sendmsg - iovec failure", opP->env);
+ FREE( opP );
+
+ return esock_make_error_invalid(env, esock_atom_iov);
+ }
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_sendmsg {%d} ->"
+ "\r\n iovcnt: %lu"
+ "\r\n tail: %s"
+ "\r\n", descP->sock,
+ (unsigned long) opP->data.sendmsg.iovec->iovcnt,
+ B2S(! enif_is_empty_list(opP->env, tail))) );
+
+
+ /* We now have an allocated iovec - verify vector size */
+
+ if (! verify_sendmsg_iovec_size(dataP, descP, opP->data.sendmsg.iovec)) {
+
+ /* We can not send the whole packet in one sendmsg() call */
+ SSDBG( descP, ("WIN-ESAIO",
+ "esaio_sendmsg {%d} -> iovcnt > iov_max\r\n",
+ descP->sock) );
+
+ // No need - belongs to op env: FREE_IOVEC( opP->data.sendmsg.iovec );
+ esock_free_env("esaio-sendmsg - iovec failure", opP->env);
+ FREE( opP );
+
+ return esock_make_error_invalid(env, esock_atom_iov);
+ }
+
+
+ /* Verify that we can send the entire message.
+ * On DGRAM the tail must be "empty" (= everything must fit in one message).
+ */
+ if (! verify_sendmsg_iovec_tail(opP->env, descP, &tail)) {
+
+ // No need - belongs to op env: FREE_IOVEC( opP->data.sendmsg.iovec );
+ esock_free_env("esaio-sendmsg - iovec tail failure", opP->env);
+ FREE( opP );
+
+ return esock_make_error_invalid(env, esock_atom_iov);
+
+ }
+
+ if (! check_sendmsg_iovec_overflow(descP,
+ opP->data.sendmsg.iovec, &dataSize)) {
+
+ // No need - belongs to op env: FREE_IOVEC( opP->data.sendmsg.iovec );
+ esock_free_env("esaio-sendmsg - iovec size failure", opP->env);
+ FREE( opP );
+
+ return esock_make_error_invalid(env, esock_atom_iov);
+
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_sendmsg {%d} -> iovec size verified"
+ "\r\n iov length: %lu"
+ "\r\n data size: %u"
+ "\r\n",
+ descP->sock,
+ (unsigned long) opP->data.sendmsg.iovec->iovcnt,
+ (long) dataSize) );
+
+ wbufs = MALLOC(opP->data.sendmsg.iovec->iovcnt * sizeof(WSABUF));
+ ESOCK_ASSERT( wbufs != NULL );
+ for (int i = 0; i < opP->data.sendmsg.iovec->iovcnt; i++) {
+ wbufs[i].len = opP->data.sendmsg.iovec->iov[i].iov_len;
+ wbufs[i].buf = opP->data.sendmsg.iovec->iov[i].iov_base;
+ }
+
+ opP->data.sendmsg.msg.lpBuffers = wbufs;
+ opP->data.sendmsg.msg.dwBufferCount = opP->data.sendmsg.iovec->iovcnt;
+
+ /* And now for the control headers - some default first */
+ eCtrl = esock_atom_undefined;
+ ctrlBufLen = 0;
+ opP->data.sendmsg.ctrlBuf = NULL;
+
+ /* Extract the *optional* 'ctrl' out of the eMsg map */
+ if (GET_MAP_VAL(env, eMsg, esock_atom_ctrl, &eCtrl)) {
+ ctrlBufLen = descP->wCtrlSz;
+ opP->data.sendmsg.ctrlBuf = (char*) MALLOC(ctrlBufLen);
+ ESOCK_ASSERT( opP->data.sendmsg.ctrlBuf != NULL );
+ }
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_sendmsg {%d} -> optional ctrl: "
+ "\r\n ctrlBuf: %p"
+ "\r\n ctrlBufLen: %lu"
+ "\r\n eCtrl: %T"
+ "\r\n", descP->sock,
+ opP->data.sendmsg.ctrlBuf,
+ (unsigned long) ctrlBufLen, eCtrl) );
+
+ if (opP->data.sendmsg.ctrlBuf != NULL) {
+ if (! decode_cmsghdrs(env, descP,
+ eCtrl,
+ opP->data.sendmsg.ctrlBuf, ctrlBufLen,
+ &ctrlBufUsed)) {
+
+ FREE( opP->data.sendmsg.ctrlBuf );
+ FREE( opP->data.sendmsg.msg.lpBuffers );
+ // No need - belongs to op env: FREE_IOVEC( opP->data.sendmsg.iovec );
+ esock_free_env("esaio-sendmsg - iovec size failure", opP->env);
+ FREE( opP );
+
+ return esock_make_invalid(env, esock_atom_ctrl);
+ }
+ } else {
+ ctrlBufUsed = 0;
+ }
+ opP->data.sendmsg.msg.Control.len = ctrlBufUsed;
+ opP->data.sendmsg.msg.Control.buf = opP->data.sendmsg.ctrlBuf;
+
+ /* We do not yet handle the flags (see function header above),
+ * so zero it just in case. */
+ opP->data.sendmsg.msg.dwFlags = 0;
+
+ opP->tag = ESAIO_OP_SENDMSG;
+ opP->caller = caller;
+ opP->data.sendmsg.sockRef = CP_TERM(opP->env, sockRef);
+ opP->data.sendmsg.sendRef = CP_TERM(opP->env, sendRef);
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_tries, &descP->writeTries, 1);
+
+ wres = sock_sendmsg_O(descP->sock, &opP->data.sendmsg.msg, flags,
+ (OVERLAPPED*) opP);
+
+ eres = send_check_result(env, descP, opP, caller,
+ wres, dataSize,
+ (! enif_is_empty_list(opP->env, tail)),
+ sockRef, sendRef, &cleanup);
+
+ if (cleanup) {
+
+ /* "Manually" allocated buffers */
+ FREE( opP->data.sendmsg.msg.lpBuffers );
+ if (opP->data.sendmsg.ctrlBuf != NULL)
+ FREE( opP->data.sendmsg.ctrlBuf );
+
+ /* The i/o vector belongs to the op env,
+ * so it goes when the env goes.
+ */
+ esock_clear_env("esaio_sendto - cleanup", opP->env);
+ esock_free_env("esaio_sendto - cleanup", opP->env);
+
+ FREE( opP );
+
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_sendmsg {%d} -> done (%s)"
+ "\r\n %T"
+ "\r\n", descP->sock, B2S(cleanup), eres) );
+
+ return eres;
+}
+
+
+static
+BOOLEAN_T init_sendmsg_sockaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eMsg,
+ WSAMSG* msgP,
+ ESockAddress* addrP)
+{
+ ERL_NIF_TERM eAddr;
+
+ if (! GET_MAP_VAL(env, eMsg, esock_atom_addr, &eAddr)) {
+
+ SSDBG( descP, ("WIN-ESAIO",
+ "init_sendmsg_sockaddr {%d} -> no address\r\n",
+ descP->sock) );
+
+ msgP->name = NULL;
+ msgP->namelen = 0;
+
+ } else {
+
+ SSDBG( descP, ("WIN-ESAIO", "init_sendmsg_sockaddr {%d} ->"
+ "\r\n address: %T"
+ "\r\n", descP->sock, eAddr) );
+
+ msgP->name = (void*) addrP;
+ msgP->namelen = sizeof(ESockAddress);
+ sys_memzero((char *) msgP->name, msgP->namelen);
+
+ if (! esock_decode_sockaddr(env, eAddr,
+ (ESockAddress*) msgP->name,
+ (SOCKLEN_T*) &msgP->namelen)) {
+
+ SSDBG( descP, ("WIN-ESAIO",
+ "init_sendmsg_sockaddr {%d} -> invalid address\r\n",
+ descP->sock) );
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+
+}
+
+
+
+static
+BOOLEAN_T verify_sendmsg_iovec_size(const ESockData* dataP,
+ ESockDescriptor* descP,
+ ErlNifIOVec* iovec)
+{
+ if (iovec->iovcnt > dataP->iov_max) {
+ if (descP->type == SOCK_STREAM) {
+ iovec->iovcnt = dataP->iov_max;
+ } else {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+static
+BOOLEAN_T verify_sendmsg_iovec_tail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM* tail)
+{
+ ERL_NIF_TERM h, t, tmp = *tail;
+ ErlNifBinary bin;
+
+ /* Find out if there is remaining data in the tail.
+ * Skip empty binaries otherwise break.
+ * If 'tail' after loop exit is the empty list
+ * there was no more data. Otherwise there is more
+ * data or the 'iov' is invalid.
+ */
+
+ for (;;) {
+ if (enif_get_list_cell(env, tmp, &h, &t) &&
+ enif_inspect_binary(env, h, &bin) &&
+ (bin.size == 0)) {
+ tmp = t;
+ continue;
+ } else
+ break;
+ }
+
+ *tail = tmp;
+
+ if ((! enif_is_empty_list(env, tmp)) &&
+ (descP->type != SOCK_STREAM)) {
+
+ /* We can not send the whole packet in one sendmsg() call */
+ SSDBG( descP, ("WIN-ESAIO",
+ "essio_sendmsg {%d} -> invalid tail\r\n",
+ descP->sock) );
+
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+
+
+static
+BOOLEAN_T check_sendmsg_iovec_overflow(ESockDescriptor* descP,
+ ErlNifIOVec* iovec,
+ ssize_t* dataSize)
+{
+ ssize_t dsz = 0;
+ size_t i;
+
+ for (i = 0; i < iovec->iovcnt; i++) {
+ size_t len = iovec->iov[i].iov_len;
+ dsz += len;
+ if (dsz < len) {
+
+ /* Overflow */
+
+ SSDBG( descP, ("WIN-ESAIO",
+ "verify_sendmsg_iovec_size {%d} -> Overflow"
+ "\r\n i: %lu"
+ "\r\n len: %lu"
+ "\r\n dataSize: %ld"
+ "\r\n", descP->sock, (unsigned long) i,
+ (unsigned long) len, (long) dsz) );
+
+ *dataSize = dsz;
+
+ return FALSE;
+
+ }
+ }
+
+ *dataSize = dsz;
+
+ return TRUE;
+
+}
+
+
+
+/* *** Control message utility functions *** */
+
+/* +++ decode_cmsghdrs +++
+ *
+ * Decode a list of cmsg(). There can be 0 or more "blocks".
+ *
+ * Each element can either be a (erlang) map that needs to be decoded,
+ * or a (erlang) binary that just needs to be appended to the control
+ * buffer.
+ *
+ * Our "problem" is that we have no idea how much memory we actually need.
+ *
+ */
+
+static
+BOOLEAN_T decode_cmsghdrs(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eCMsg,
+ char* cmsgHdrBufP,
+ size_t cmsgHdrBufLen,
+ size_t* cmsgHdrBufUsed)
+{
+ ERL_NIF_TERM elem, tail, list;
+ char* bufP;
+ size_t rem, used, totUsed = 0;
+ unsigned int len;
+ int i;
+
+ SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdrs {%d} -> entry with"
+ "\r\n eCMsg: %T"
+ "\r\n cmsgHdrBufP: 0x%lX"
+ "\r\n cmsgHdrBufLen: %d"
+ "\r\n", descP->sock,
+ eCMsg, cmsgHdrBufP, cmsgHdrBufLen) );
+
+ if (! GET_LIST_LEN(env, eCMsg, &len))
+ return FALSE;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdrs {%d} -> list length: %d\r\n",
+ descP->sock, len) );
+
+ for (i = 0, list = eCMsg, rem = cmsgHdrBufLen, bufP = cmsgHdrBufP;
+ i < len; i++) {
+
+ SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdrs {%d} -> process elem %d:"
+ "\r\n (buffer) rem: %u"
+ "\r\n (buffer) totUsed: %u"
+ "\r\n", descP->sock, i, rem, totUsed) );
+
+ /* Extract the (current) head of the (cmsg hdr) list */
+ if (! GET_LIST_ELEM(env, list, &elem, &tail))
+ return FALSE;
+
+ used = 0; // Just in case...
+ if (! decode_cmsghdr(env, descP, elem, bufP, rem, &used))
+ return FALSE;
+
+#ifdef __WIN32__
+ bufP = CHARP( bufP + used );
+#else
+ bufP = CHARP( ULONG(bufP) + used );
+#endif
+ rem = SZT( rem - used );
+ list = tail;
+ totUsed += used;
+
+ }
+
+ *cmsgHdrBufUsed = totUsed;
+
+ SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdrs {%d} -> done"
+ "\r\n all %u ctrl headers processed"
+ "\r\n totUsed = %lu\r\n",
+ descP->sock, len, (unsigned long) totUsed) );
+
+ return TRUE;
+}
+
+
+
+/* +++ decode_cmsghdr +++
+ *
+ * Decode one cmsg(). Put the "result" into the buffer and advance the
+ * pointer (of the buffer) afterwards. Also update 'rem' accordingly.
+ * But before the actual decode, make sure that there is enough room in
+ * the buffer for the cmsg header (sizeof(*hdr) < rem).
+ *
+ * The eCMsg should be a map with three fields:
+ *
+ * level :: socket | protocol() | integer()
+ * type :: atom() | integer()
+ * What values are valid depend on the level
+ * data :: binary() | integer() | boolean()
+ * The type of the data depends on
+ * or level and type, but can be a binary,
+ * which means that the data is already coded.
+ * value :: term() Which is a term matching the decode function
+ */
+
+static
+BOOLEAN_T decode_cmsghdr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eCMsg,
+ char* bufP,
+ size_t rem,
+ size_t* used)
+{
+ ERL_NIF_TERM eLevel, eType, eData, eValue;
+ int level;
+
+ SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d} -> entry with"
+ "\r\n eCMsg: %T"
+ "\r\n", descP->sock, eCMsg) );
+
+ // Get 'level' field
+ if (! GET_MAP_VAL(env, eCMsg, esock_atom_level, &eLevel))
+ return FALSE;
+ SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d} -> eLevel: %T"
+ "\r\n", descP->sock, eLevel) );
+
+ // Get 'type' field
+ if (! GET_MAP_VAL(env, eCMsg, esock_atom_type, &eType))
+ return FALSE;
+ SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d} -> eType: %T"
+ "\r\n", descP->sock, eType) );
+
+ // Decode Level
+ if (! esock_decode_level(env, eLevel, &level))
+ return FALSE;
+ SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d}-> level: %d\r\n",
+ descP->sock, level) );
+
+ // Get 'data' field
+ if (! GET_MAP_VAL(env, eCMsg, esock_atom_data, &eData)) {
+
+ // Get 'value' field
+ if (! GET_MAP_VAL(env, eCMsg, esock_atom_value, &eValue))
+ return FALSE;
+ SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d} -> eValue: %T"
+ "\r\n", descP->sock, eValue) );
+
+ // Decode Value
+ if (! decode_cmsghdr_value(env, descP, level, eType, eValue,
+ bufP, rem, used))
+ return FALSE;
+
+ } else {
+
+ // Verify no 'value' field
+ if (GET_MAP_VAL(env, eCMsg, esock_atom_value, &eValue))
+ return FALSE;
+
+ SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d} -> eData: %T"
+ "\r\n", descP->sock, eData) );
+
+ // Decode Data
+ if (! decode_cmsghdr_data(env, descP, level, eType, eData,
+ bufP, rem, used))
+ return FALSE;
+ }
+
+ SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d}-> used: %lu\r\n",
+ descP->sock, (unsigned long) *used) );
+
+ return TRUE;
+}
+
+
+static
+BOOLEAN_T decode_cmsghdr_value(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ ERL_NIF_TERM eType,
+ ERL_NIF_TERM eValue,
+ char* bufP,
+ size_t rem,
+ size_t* usedP)
+{
+ int type;
+ struct cmsghdr* cmsgP = (struct cmsghdr *) bufP;
+ ESockCmsgSpec* cmsgTable;
+ ESockCmsgSpec* cmsgSpecP = NULL;
+ size_t num = 0;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdr_value {%d} -> entry \r\n"
+ " eType: %T\r\n"
+ " eValue: %T\r\n",
+ descP->sock, eType, eValue) );
+
+ // We have decode functions only for symbolic (atom) types
+ if (! IS_ATOM(env, eType)) {
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdr_value {%d} -> FALSE:\r\n"
+ " eType not an atom\r\n",
+ descP->sock) );
+ return FALSE;
+ }
+
+ /* Try to look up the symbolic type
+ */
+ if (((cmsgTable = esock_lookup_cmsg_table(level, &num)) == NULL) ||
+ ((cmsgSpecP = esock_lookup_cmsg_spec(cmsgTable, num, eType)) == NULL) ||
+ (cmsgSpecP->decode == NULL)) {
+
+ /* We found no table for this level,
+ * we found no symbolic type in the level table,
+ * or no decode function for this type
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdr_value {%d} -> FALSE:\r\n"
+ " cmsgTable: %p\r\n"
+ " cmsgSpecP: %p\r\n",
+ descP->sock, cmsgTable, cmsgSpecP) );
+
+ return FALSE;
+ }
+
+ if (! cmsgSpecP->decode(env, eValue, cmsgP, rem, usedP)) {
+ // Decode function failed
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdr_value {%d} -> FALSE:\r\n"
+ " decode function failed\r\n",
+ descP->sock) );
+ return FALSE;
+ }
+
+ // Successful decode
+
+ type = cmsgSpecP->type;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdr_value {%d} -> TRUE:\r\n"
+ " level: %d\r\n"
+ " type: %d\r\n",
+ " *usedP: %lu\r\n",
+ descP->sock, level, type, (unsigned long) *usedP) );
+
+ cmsgP->cmsg_level = level;
+ cmsgP->cmsg_type = type;
+ return TRUE;
+}
+
+
+static
+BOOLEAN_T decode_cmsghdr_data(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ ERL_NIF_TERM eType,
+ ERL_NIF_TERM eData,
+ char* bufP,
+ size_t rem,
+ size_t* usedP)
+{
+ int type;
+ ErlNifBinary bin;
+ struct cmsghdr* cmsgP = (struct cmsghdr *) bufP;
+ ESockCmsgSpec* cmsgSpecP = NULL;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdr_data {%d} -> entry \r\n"
+ " eType: %T\r\n"
+ " eData: %T\r\n",
+ descP->sock, eType, eData) );
+
+ // Decode Type
+ if (! GET_INT(env, eType, &type)) {
+ ESockCmsgSpec* cmsgTable = NULL;
+ size_t num = 0;
+
+ /* Try to look up the symbolic (atom) type
+ */
+ if ((! IS_ATOM(env, eType)) ||
+ ((cmsgTable = esock_lookup_cmsg_table(level, &num)) == NULL) ||
+ ((cmsgSpecP = esock_lookup_cmsg_spec(cmsgTable, num, eType)) == NULL)) {
+ /* Type was not an atom,
+ * we found no table for this level,
+ * or we found no symbolic type in the level table
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdr_data {%d} -> FALSE:\r\n"
+ " cmsgTable: %p\r\n"
+ " cmsgSpecP: %p\r\n",
+ descP->sock, cmsgTable, cmsgSpecP) );
+ return FALSE;
+ }
+
+ type = cmsgSpecP->type;
+ }
+
+ // Decode Data
+ if (GET_BIN(env, eData, &bin)) {
+ void *p;
+
+ p = esock_init_cmsghdr(cmsgP, rem, bin.size, usedP);
+ if (p == NULL) {
+ /* No room for the data
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdr_data {%d} -> FALSE:\r\n"
+ " rem: %lu\r\n"
+ " bin.size: %lu\r\n",
+ descP->sock,
+ (unsigned long) rem,
+ (unsigned long) bin.size) );
+ return FALSE;
+ }
+
+ // Copy the binary data
+ sys_memcpy(p, bin.data, bin.size);
+
+ } else if ((! esock_cmsg_decode_int(env, eData, cmsgP, rem, usedP)) &&
+ (! esock_cmsg_decode_bool(env, eData, cmsgP, rem, usedP))) {
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdr_data {%d} -> FALSE\r\n",
+ descP->sock) );
+ return FALSE;
+ }
+
+ // Successful decode
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "decode_cmsghdr_data {%d} -> TRUE:\r\n"
+ " level: %d\r\n"
+ " type: %d\r\n"
+ " *usedP: %lu\r\n",
+ descP->sock, level, type, (unsigned long) *usedP) );
+
+ cmsgP->cmsg_level = level;
+ cmsgP->cmsg_type = type;
+ return TRUE;
+}
+
+
+
+
+/* +++ encode_msg +++
+ *
+ * Encode a msg() (recvmsg). In erlang its represented as
+ * a map, which has a specific set of attributes:
+ *
+ * addr (source address) - sockaddr()
+ * iov - [binary()]
+ * ctrl - [cmsg()]
+ * flags - msg_flags()
+ */
+
+static
+void encode_msg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ WSAMSG* msgP,
+ ErlNifBinary* dataBufP,
+ ErlNifBinary* ctrlBufP,
+ ERL_NIF_TERM* eMsg)
+{
+ ERL_NIF_TERM addr, iov, ctrl, flags;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "encode_msg {%d} -> entry with"
+ "\r\n read: %ld"
+ "\r\n", descP->sock, (long) read) );
+
+ /* The address is not used if we are connected (unless, maybe,
+ * family is 'local'), so check (length = 0) before we try to encodel
+ */
+ if (msgP->namelen != 0) {
+ esock_encode_sockaddr(env,
+ (ESockAddress*) msgP->name,
+ msgP->namelen,
+ &addr);
+ } else {
+ addr = esock_atom_undefined;
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "encode_msg {%d} -> encode iov"
+ "\r\n num vectors: %lu"
+ "\r\n", descP->sock, (unsigned long) msgP->dwBufferCount) );
+
+ esock_encode_iov(env, read,
+ (SysIOVec*) msgP->lpBuffers, msgP->dwBufferCount, dataBufP,
+ &iov);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "encode_msg {%d} -> try encode cmsgs\r\n",
+ descP->sock) );
+
+ encode_cmsgs(env, descP, ctrlBufP, msgP, &ctrl);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "encode_msg {%d} -> try encode flags\r\n",
+ descP->sock) );
+
+ esock_encode_msg_flags(env, descP, msgP->dwFlags, &flags);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "encode_msg {%d} -> components encoded:"
+ "\r\n addr: %T"
+ "\r\n ctrl: %T"
+ "\r\n flags: %T"
+ "\r\n", descP->sock, addr, ctrl, flags) );
+
+ {
+ ERL_NIF_TERM keys[] = {esock_atom_iov,
+ esock_atom_ctrl,
+ esock_atom_flags,
+ esock_atom_addr};
+ ERL_NIF_TERM vals[] = {iov, ctrl, flags, addr};
+ size_t numKeys = NUM(keys);
+
+ ESOCK_ASSERT( numKeys == NUM(vals) );
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "encode_msg {%d} -> create map\r\n",
+ descP->sock) );
+
+ if (msgP->namelen == 0)
+ numKeys--; // No addr
+ ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eMsg) );
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "encode_msg {%d}-> map encoded\r\n",
+ descP->sock) );
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "encode_msg {%d} -> done\r\n", descP->sock) );
+}
+
+
+
+/* +++ encode_cmsgs +++
+ *
+ * Encode a list of cmsg(). There can be 0 or more cmsghdr "blocks".
+ *
+ * Our "problem" is that we have no idea how many control messages
+ * we have.
+ *
+ * The cmsgHdrP arguments points to the start of the control data buffer,
+ * an actual binary. Its the only way to create sub-binaries. So, what we
+ * need to continue processing this is to turn that into an binary erlang
+ * term (which can then in turn be turned into sub-binaries).
+ *
+ * We need the cmsgBufP (even though cmsgHdrP points to it) to be able
+ * to create sub-binaries (one for each cmsg hdr).
+ *
+ * The TArray (term array) is created with the size of 128, which should
+ * be enough. But if its not, then it will be automatically realloc'ed during
+ * add. Once we are done adding hdr's to it, we convert the tarray to a list.
+ */
+
+static
+void encode_cmsgs(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifBinary* cmsgBinP,
+ WSAMSG* msgP,
+ ERL_NIF_TERM* eCMsg)
+{
+ ERL_NIF_TERM ctrlBuf = MKBIN(env, cmsgBinP); // The *entire* binary
+ SocketTArray cmsghdrs = TARRAY_CREATE(128);
+ WSACMSGHDR* firstP = ESOCK_CMSG_FIRSTHDR(msgP);
+ WSACMSGHDR* currentP;
+
+ SSDBG( descP, ("WIN-ESAIO", "encode_cmsgs {%d} -> entry when"
+ "\r\n msg ctrl len: %d"
+ "\r\n (ctrl) firstP: 0x%lX"
+ "\r\n", descP->sock, msgP->Control.len, firstP) );
+
+ for (currentP = firstP;
+ (currentP != NULL);
+ /* nifs\win32\win_socket_asyncio.c(3167):
+ * warning C4116: unnamed type definition in parentheses
+ */
+ currentP = ESOCK_CMSG_NXTHDR(msgP, currentP)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "encode_cmsgs {%d} -> process cmsg header when"
+ "\r\n TArray Size: %d"
+ "\r\n", descP->sock, TARRAY_SZ(cmsghdrs)) );
+
+ /* MUST check this since on Linux the returned "cmsg" may actually
+ * go too far!
+ */
+ if (((CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP)) >
+ msgP->Control.len) {
+
+ /* Ouch, fatal error - give up
+ * We assume we cannot trust any data if this is wrong.
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "encode_cmsgs {%d} -> check failed when: "
+ "\r\n currentP: 0x%lX"
+ "\r\n (current) cmsg_len: %d"
+ "\r\n firstP: 0x%lX"
+ "\r\n => %d"
+ "\r\n msg ctrl len: %d"
+ "\r\n", descP->sock,
+ CHARP(currentP), currentP->cmsg_len, CHARP(firstP),
+ (CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP),
+ msgP->Control.len) );
+
+ TARRAY_ADD(cmsghdrs, esock_atom_bad_data);
+ break;
+
+ } else {
+ unsigned char* dataP = UCHARP(ESOCK_CMSG_DATA(currentP));
+ size_t dataPos = dataP - cmsgBinP->data;
+ size_t dataLen =
+ (UCHARP(currentP) + currentP->cmsg_len) - dataP;
+ ERL_NIF_TERM
+ cmsgHdr,
+ keys[] =
+ {esock_atom_level,
+ esock_atom_type,
+ esock_atom_data,
+ esock_atom_value},
+ vals[NUM(keys)];
+ size_t numKeys = NUM(keys);
+ BOOLEAN_T have_value;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "encode_cmsgs {%d} -> cmsg header data: "
+ "\r\n dataPos: %d"
+ "\r\n dataLen: %d"
+ "\r\n", descP->sock, dataPos, dataLen) );
+
+ vals[0] = esock_encode_level(env, currentP->cmsg_level);
+ vals[2] = MKSBIN(env, ctrlBuf, dataPos, dataLen);
+ have_value = esock_encode_cmsg(env,
+ currentP->cmsg_level,
+ currentP->cmsg_type,
+ dataP, dataLen, &vals[1], &vals[3]);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "encode_cmsgs {%d} -> "
+ "\r\n %T: %T"
+ "\r\n %T: %T"
+ "\r\n %T: %T"
+ "\r\n", descP->sock,
+ keys[0], vals[0], keys[1], vals[1], keys[2], vals[2]) );
+ if (have_value)
+ SSDBG( descP,
+ ("WIN-ESAIO", "encode_cmsgs {%d} -> "
+ "\r\n %T: %T"
+ "\r\n", descP->sock, keys[3], vals[3]) );
+
+ /* Guard against cut-and-paste errors */
+ ESOCK_ASSERT( numKeys == NUM(vals) );
+ ESOCK_ASSERT( MKMA(env, keys, vals,
+ numKeys - (have_value ? 0 : 1), &cmsgHdr) );
+
+ /* And finally add it to the list... */
+ TARRAY_ADD(cmsghdrs, cmsgHdr);
+ }
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "encode_cmsgs {%d} -> cmsg headers processed when"
+ "\r\n TArray Size: %d"
+ "\r\n", descP->sock, TARRAY_SZ(cmsghdrs)) );
+
+ /* The tarray is populated - convert it to a list */
+ TARRAY_TOLIST(cmsghdrs, env, eCMsg);
+}
+
+
+
+/* ====== Receive functions ====== */
+
+
+/* ========================================================================
+ * The (read) buffer handling should be optimized!
+ * But for now we make it easy for ourselves by
+ * allocating a binary (of the specified or default
+ * size) and then throwing it away...
+ *
+ * The following flags are supported (with overlapped):
+ * MSG_OOB - Processes OOB data.
+ * MSG_PARTIAL - *message-oriented* sockets only.
+ * Both intput to *and* output from recv.
+ * MSG_PUSH_IMMEDIATE - *stream-oriented* sockets only.
+ * Hint rather than an actual guarantee.
+ * MSG_WAITALL - *stream-oriented* sockets only.
+ * The request will complete only when one of
+ * the following conditions apply:
+ * - buffer is completely full.
+ * - The connection has been closed.
+ * - The request has been canceled or an error occurred.
+ */
+extern
+ERL_NIF_TERM esaio_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef,
+ ssize_t len,
+ int flags)
+{
+ ErlNifPid caller;
+ ESAIOOperation* opP;
+ int rres;
+ WSABUF wbuf;
+ DWORD f = flags;
+ size_t bufSz = (len != 0 ? len : descP->rBufSz);
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_recv {%d} -> entry with"
+ "\r\n length: %ld"
+ "\r\n (buffer) size: %lu"
+ "\r\n", descP->sock,
+ (long) len, (unsigned long) bufSz) );
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ /* Accept and Read can not be simultaneous? */
+ if (descP->acceptorsQ.first != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Ensure that this caller does not *already* have a
+ * (recv) request waiting */
+ if (esock_reader_search4pid(env, descP, &caller)) {
+ /* Reader already in queue */
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ /* Allocate the operation */
+ opP = MALLOC( sizeof(ESAIOOperation) );
+ ESOCK_ASSERT( opP != NULL);
+ sys_memzero((char*) opP, sizeof(ESAIOOperation));
+
+ opP->tag = ESAIO_OP_RECV;
+ /* Its a bit annoying that we have to alloc an env and then
+ * copy the ref *before* we know that we actually need it.
+ * How much does this cost?
+ */
+ opP->env = esock_alloc_env("esaio-recv - operation");
+ opP->data.recv.recvRef = CP_TERM(opP->env, recvRef);
+ opP->data.recv.sockRef = CP_TERM(opP->env, sockRef);
+ opP->caller = caller;
+
+ /* Allocate a buffer:
+ * Either as much as we want to read or (if zero (0)) use the "default"
+ * size (what has been configured).
+ */
+ ESOCK_ASSERT( ALLOC_BIN(bufSz, &opP->data.recv.buf) );
+
+ opP->data.recv.toRead = len;
+ wbuf.buf = opP->data.recv.buf.data;
+ wbuf.len = opP->data.recv.buf.size;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_tries, &descP->readTries, 1);
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_recv {%d} -> try read (%lu)\r\n",
+ descP->sock, (unsigned long) bufSz) );
+
+ rres = sock_recv_O(descP->sock, &wbuf, &f, (OVERLAPPED*) opP);
+
+ return recv_check_result(env, descP, opP, caller, rres,
+ sockRef, recvRef);
+}
+
+
+/* *** recv_check_result ***
+ *
+ * Analyze the result of a receive attempt.
+ * The receive may have been completed directly or scheduled (overlapped).
+ */
+static
+ERL_NIF_TERM recv_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ int recv_result,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM eres;
+
+ if (recv_result == 0) {
+
+ /* +++ Success +++ */
+
+ eres = recv_check_ok(env, descP, opP, caller, sockRef, recvRef);
+
+ } else {
+ int err;
+
+ /* +++ Failure or pending +++ */
+
+ err = sock_errno();
+
+ /* As pointed out above, there are basically two kinds of errors:
+ * 1) Pending:
+ * An overlapped operation was successfully initiated.
+ * Completion will be indicated at a later time.
+ * 2) An actual error
+ */
+
+ if (err == WSA_IO_PENDING) {
+
+ if (! IS_ZERO(recvRef)) {
+
+ eres = recv_check_pending(env, descP, opP, caller,
+ sockRef, recvRef);
+
+ } else {
+
+ /* We are not allowed to wait! => cancel */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recv_check_result(%T, %d) -> "
+ "pending - but we are not allowed to wait => cancel"
+ "\r\n", sockRef, descP->sock) );
+
+ if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) {
+ int save_errno = sock_errno();
+ ERL_NIF_TERM tag = esock_atom_cancel;
+ ERL_NIF_TERM reason = ENO2T(env, save_errno);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recv_check_result(%T, %d) -> "
+ "failed cancel pending operation"
+ "\r\n %T"
+ "\r\n", sockRef, descP->sock, reason) );
+
+ eres = esock_make_error(env, MKT2(env, tag, reason));
+
+ } else {
+
+ eres = esock_atom_ok;
+
+ }
+
+ }
+
+ } else {
+
+ eres = recv_check_fail(env, descP, opP, err, sockRef);
+
+ }
+
+ }
+
+ return eres;
+}
+
+
+
+/* *** recv_check_ok ***
+ *
+ * A successful recv. We *know* that in this case the buffer is filled!
+ */
+
+static
+ERL_NIF_TERM recv_check_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM data, result;
+ DWORD read = 0, flags = 0;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recv_check_ok -> try get overlapped result\r\n") );
+
+ if (get_recv_ovl_result(descP->sock, (OVERLAPPED*) opP, &read, &flags)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recv_check_ok -> overlapped success result: "
+ "\r\n read: %d"
+ "\r\n flags: 0x%X"
+ "\r\n", read, flags) );
+
+ (void) flags; // We should really do something with this...
+
+ /* <KOLLA>
+ *
+ * We need to handle read = 0 for other type(s) (DGRAM) when
+ * its actually valid to read 0 bytes.
+ *
+ * </KOLLA>
+ */
+
+ if ((read == 0) && (descP->type == SOCK_STREAM)) {
+
+ /*
+ * When a stream socket peer has performed an orderly
+ * shutdown, the return value will be 0 (the traditional
+ * "end-of-file" return).
+ *
+ * *We* do never actually try to read 0 bytes!
+ */
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_fails, &descP->readFails, 1);
+
+ result = esock_make_error(env, esock_atom_closed);
+
+ } else {
+
+ if (read == opP->data.recv.buf.size) {
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ data = MKBIN(env, &opP->data.recv.buf);
+
+ } else {
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ data = MKBIN(env, &opP->data.recv.buf);
+ data = MKSBIN(env, data, 0, read);
+
+ }
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ /* (maybe) Update max */
+ if (read > descP->readPkgMax)
+ descP->readPkgMax = read;
+
+
+ result = esock_make_ok2(env, data);
+
+ }
+
+ } else {
+
+ int save_errno = sock_errno();
+
+ switch (save_errno) {
+ case WSA_IO_INCOMPLETE:
+ /*
+ * WSA_IO_INCOMPLETE
+ *
+ * Even though it (the I/O Completion Port framework) told
+ * us it was done, it was not. So we need to postpone and let
+ * the (worker) threads deal with it anyway...effing framework...
+ */
+
+ if (! IS_ZERO(recvRef)) {
+
+ result = recv_check_pending(env, descP, opP, caller,
+ sockRef, recvRef);
+ } else {
+
+ /* But we are not allowed to wait! => cancel */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recv_check_ok(%T, %d) -> "
+ "incomplete - but we are not allowed to wait => cancel"
+ "\r\n", sockRef, descP->sock) );
+
+ if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) {
+ int save_errno = sock_errno();
+ ERL_NIF_TERM tag = esock_atom_cancel;
+ ERL_NIF_TERM reason = ENO2T(env, save_errno);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recv_check_ok(%T, %d) -> "
+ "failed cancel incomplete operation"
+ "\r\n %T"
+ "\r\n", sockRef, descP->sock, reason) );
+
+ result = esock_make_error(env, MKT2(env, tag, reason));
+
+ } else {
+
+ result = esock_atom_ok; // Will trigger {error, timeout}
+
+ }
+ }
+ break;
+
+ default:
+ {
+ ERL_NIF_TERM eerrno = ENO2T(env, save_errno);
+ ERL_NIF_TERM reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ eerrno);
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_fails, &descP->readFails, 1);
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.genErrs, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+ result = esock_make_error(env, reason);
+ }
+ break;
+ }
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "recv_check_ok(%T) {%d} -> done with"
+ "\r\n result: %T"
+ "\r\n",
+ sockRef, descP->sock, result) );
+
+ return result;
+}
+
+
+
+/* *** recv_check_pending ***
+ *
+ * The recv operation was scheduled, that is, its now in the hands
+ * of the I/O Completion Port framework.
+ */
+static
+ERL_NIF_TERM recv_check_pending(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recv_check_pending(%T, %d) -> entry with"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock, recvRef) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_waits, &descP->readWaits, 1);
+
+ descP->readState |= ESOCK_STATE_SELECTED;
+
+ esock_reader_push(env, descP, caller, recvRef, opP);
+
+ return esock_atom_completion;
+
+}
+
+
+
+/* *** recv_check_fail ***
+ *
+ * Processing done upon failed 'recv'.
+ * An actual failure.
+ */
+static
+ERL_NIF_TERM recv_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef)
+{
+ SSDBG( descP,
+ ("WIN-ESAIO", "recv_check_fail(%T) {%d} -> entry with"
+ "\r\n errno: %d"
+ "\r\n",
+ sockRef, descP->sock, saveErrno) );
+
+ FREE_BIN( &opP->data.recv.buf );
+
+ return recv_check_failure(env, descP, opP, saveErrno, sockRef);
+}
+
+
+
+/* *** recv_check_failure ***
+ *
+ * Processing done upon failed recv.
+ * An actual failure.
+ */
+static
+ERL_NIF_TERM recv_check_failure(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "recv_check_failure(%T) {%d} -> error: %d (%T)\r\n",
+ sockRef, descP->sock, saveErrno, reason) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_fails, &descP->readFails, 1);
+
+ esock_clear_env("recv_check_failure", opP->env);
+ esock_free_env("recv_check_failure", opP->env);
+ FREE( opP );
+
+ return esock_make_error(env, reason);
+}
+
+
+
+/* ========================================================================
+ * esaio_recvfrom - Read a "packet" from a socket
+ *
+ * The (read) buffer handling *must* be optimized!
+ * But for now we make it easy for ourselves by
+ * allocating a binary (of the specified or default
+ * size) and then throw'ing it away...
+ */
+extern
+ERL_NIF_TERM esaio_recvfrom(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef,
+ ssize_t len,
+ int flags)
+{
+ ErlNifPid caller;
+ ESAIOOperation* opP;
+ int rres;
+ WSABUF wbuf;
+ DWORD f = flags;
+ size_t bufSz = (len != 0 ? len : descP->rBufSz);
+
+ SSDBG( descP, ("WIN-ESAIO", "essio_recvfrom {%d} -> entry with"
+ "\r\n bufSz: %d"
+ "\r\n", descP->sock, bufSz) );
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ /* Accept and Read can not be simultaneous? */
+ if (descP->acceptorsQ.first != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Ensure that this caller does not *already* have a
+ * (recv) request waiting */
+ if (esock_reader_search4pid(env, descP, &caller)) {
+ /* Reader already in queue */
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ /* Allocate the operation */
+ opP = MALLOC( sizeof(ESAIOOperation) );
+ ESOCK_ASSERT( opP != NULL);
+ sys_memzero((char*) opP, sizeof(ESAIOOperation));
+
+ opP->tag = ESAIO_OP_RECVFROM;
+ /* Its a bit annoying that we have to alloc an env and then
+ * copy the ref *before* we know that we actually need it.
+ * How much does this cost?
+ */
+ opP->env = esock_alloc_env("esaio-recvfrom - operation");
+ opP->data.recvfrom.recvRef = CP_TERM(opP->env, recvRef);
+ opP->data.recvfrom.sockRef = CP_TERM(opP->env, sockRef);
+ opP->caller = caller;
+
+ /* Allocate a buffer:
+ * Either as much as we want to read or (if zero (0)) use the "default"
+ * size (what has been configured).
+ */
+ ESOCK_ASSERT( ALLOC_BIN(bufSz, &opP->data.recv.buf) );
+
+ opP->data.recvfrom.toRead = len;
+ wbuf.buf = opP->data.recvfrom.buf.data;
+ wbuf.len = opP->data.recvfrom.buf.size;
+
+ opP->data.recvfrom.addrLen = sizeof(ESockAddress);
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_tries, &descP->readTries, 1);
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_recvfrom {%d} -> try read (%lu)\r\n",
+ descP->sock, (unsigned long) bufSz) );
+
+ rres = sock_recvfrom_O(descP->sock, &wbuf, &f,
+ (struct sockaddr*) &opP->data.recvfrom.fromAddr,
+ &opP->data.recvfrom.addrLen, (OVERLAPPED*) opP);
+
+ return recvfrom_check_result(env, descP, opP, caller, rres,
+ sockRef, recvRef);
+}
+
+
+
+/* *** recvfrom_check_result ***
+ *
+ * Analyze the result of a receive attempt.
+ * The receive may have been completed directly or scheduled (overlapped).
+ */
+static
+ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ int recv_result,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM eres;
+
+ if (recv_result == 0) {
+
+ /* +++ Success +++ */
+
+ eres = recvfrom_check_ok(env, descP, opP, caller, sockRef, recvRef);
+
+ } else {
+ int err;
+
+ /* +++ Failure +++ */
+
+ err = sock_errno();
+
+ /* As pointed out above, there are basically two kinds of errors:
+ * 1) Pending:
+ * An overlapped operation was successfully initiated.
+ * Completion will be indicated at a later time.
+ * 2) An actual error
+ */
+
+ if (err == WSA_IO_PENDING) {
+
+ if (! IS_ZERO(recvRef)) {
+
+ eres = recv_check_pending(env, descP, opP, caller,
+ sockRef, recvRef);
+ } else {
+
+ /* We are not allowed to wait! => cancel */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvfrom_check_result(%T, %d) -> "
+ "pending - but we are not allowed to wait => cancel"
+ "\r\n", sockRef, descP->sock) );
+
+ if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) {
+ int save_errno = sock_errno();
+ ERL_NIF_TERM tag = esock_atom_cancel;
+ ERL_NIF_TERM reason = ENO2T(env, save_errno);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvfrom_check_result(%T, %d) -> "
+ "failed cancel pending operation"
+ "\r\n %T"
+ "\r\n", sockRef, descP->sock, reason) );
+
+ eres = esock_make_error(env, MKT2(env, tag, reason));
+
+ } else {
+
+ eres = esock_atom_ok; // Will trigger {error, timeout}
+
+ }
+
+ }
+
+ } else {
+
+ eres = recvfrom_check_fail(env, descP, opP, err, sockRef);
+
+ }
+
+ }
+
+ return eres;
+}
+
+
+
+/* *** recvfrom_check_ok ***
+ *
+ * A successful recvfrom. We *know* that in this case the buffer is filled!
+ */
+
+static
+ERL_NIF_TERM recvfrom_check_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM data, result;
+ DWORD read = 0, flags = 0;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvfrom_check_ok -> try get overlapped result\r\n") );
+
+ if (get_recv_ovl_result(descP->sock, (OVERLAPPED*) opP, &read, &flags)) {
+
+ ERL_NIF_TERM eSockAddr;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvfrom_check_ok -> overlapped result: "
+ "\r\n read: %d"
+ "\r\n flags: 0x%X"
+ "\r\n", read, flags) );
+
+ (void) flags; // We should really do something with this...
+
+ esock_encode_sockaddr(env,
+ &opP->data.recvfrom.fromAddr,
+ opP->data.recvfrom.addrLen,
+ &eSockAddr);
+
+ if (read == opP->data.recvfrom.buf.size) {
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ data = MKBIN(env, &opP->data.recvfrom.buf);
+ } else {
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ data = MKBIN(env, &opP->data.recvfrom.buf);
+ data = MKSBIN(env, data, 0, read);
+ }
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ /* (maybe) Update max */
+ if (read > descP->readPkgMax)
+ descP->readPkgMax = read;
+
+ /*
+ * This is: {ok, {Source, Data}}
+ * But it should really be: {ok, {Source, Flags, Data}}
+ */
+ result = esock_make_ok2(env, MKT2(env, eSockAddr, data));
+
+ } else {
+
+ int save_errno = sock_errno();
+
+ switch (save_errno) {
+ case WSA_IO_INCOMPLETE:
+ /*
+ * WSA_IO_INCOMPLETE
+ *
+ * Even though it (the I/O Completion Port framework) told
+ * us it was done, it was not. So we need to postpone and let
+ * the (worker) threads deal with it anyway...effing framework...
+ */
+
+ if (! IS_ZERO(recvRef)) {
+
+ result = recv_check_pending(env, descP, opP, caller,
+ sockRef, recvRef);
+
+ } else {
+
+ /* But we are not allowed to wait! => cancel */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvfrom_check_ok(%T, %d) -> "
+ "incomplete - but we are not allowed to wait => cancel"
+ "\r\n", sockRef, descP->sock) );
+
+ if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) {
+ int save_errno = sock_errno();
+ ERL_NIF_TERM tag = esock_atom_cancel;
+ ERL_NIF_TERM reason = ENO2T(env, save_errno);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvfrom_check_ok(%T, %d) -> "
+ "failed cancel incomplete operation"
+ "\r\n %T"
+ "\r\n", sockRef, descP->sock, reason) );
+
+ result = esock_make_error(env, MKT2(env, tag, reason));
+
+ } else {
+
+ result = esock_atom_ok; // Will trigger {error, timeout}
+
+ }
+ }
+ break;
+
+ default:
+ {
+ ERL_NIF_TERM eerrno = ENO2T(env, save_errno);
+ ERL_NIF_TERM reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ eerrno);
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_fails, &descP->readFails, 1);
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.genErrs, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+ result = esock_make_error(env, reason);
+ }
+ break;
+ }
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "recvfrom_check_ok(%T) {%d} -> done with"
+ "\r\n result: %T"
+ "\r\n",
+ sockRef, descP->sock, result) );
+
+ return result;
+}
+
+
+
+/* *** recvfrom_check_fail ***
+ *
+ * Processing done upon failed 'recvfrom'.
+ * An actual failure.
+ */
+static
+ERL_NIF_TERM recvfrom_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef)
+{
+ SSDBG( descP,
+ ("WIN-ESAIO", "recfrom_check_fail(%T) {%d} -> entry with"
+ "\r\n errno: %d"
+ "\r\n",
+ sockRef, descP->sock, saveErrno) );
+
+ FREE_BIN( &opP->data.recvfrom.buf );
+
+ return recv_check_failure(env, descP, opP, saveErrno, sockRef);
+}
+
+
+
+
+/* ========================================================================
+ * esaio_recvmsg - Read a "message" from a socket
+ * The (read) buffer handling *must* be optimized!
+ * But for now we make it easy for ourselves by
+ * allocating a binary (of the specified or default
+ * size) and then throwing it away...
+ *
+ * Note that this operation *only* works for socket
+ * of types SOCK_DGRAM and SOCK_RAW! Should we check
+ * and throw 'enotsup' otherwise? Would make testing
+ * easier...
+ */
+extern
+ERL_NIF_TERM esaio_recvmsg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef,
+ ssize_t bufLen,
+ ssize_t ctrlLen,
+ int flags)
+{
+ ErlNifPid caller;
+ ESAIOOperation* opP;
+ SOCKLEN_T addrLen;
+ size_t bufSz = (bufLen != 0 ? bufLen : descP->rBufSz);
+ size_t ctrlSz = (ctrlLen != 0 ? ctrlLen : descP->rCtrlSz);
+ int rres;
+ ERL_NIF_TERM eres;
+
+ (void) flags;
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_recvmsg(%T) {%d} -> entry with"
+ "\r\n bufSz: %lu (%ld)"
+ "\r\n ctrlSz: %ld (%ld)"
+ "\r\n", sockRef, descP->sock,
+ (unsigned long) bufSz, (long) bufLen,
+ (unsigned long) ctrlSz, (long) ctrlLen) );
+
+ /* This *only* works on socket type(s) DGRAM or RAW.
+ * Other socket types results in einval, which is not very
+ * helpful. So, in order to, atleast, help with testing,
+ * we do this...
+ */
+ if (! ((descP->type == SOCK_DGRAM) || (descP->type == SOCK_RAW))) {
+ return enif_raise_exception(env, MKA(env, "notsup"));
+ }
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ /* Accept and Read can not be simultaneous? */
+ if (descP->acceptorsQ.first != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Ensure that this caller does not *already* have a
+ * (recv) request waiting */
+ if (esock_reader_search4pid(env, descP, &caller)) {
+ /* Reader already in queue */
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ /* Allocate the operation */
+ opP = MALLOC( sizeof(ESAIOOperation) );
+ ESOCK_ASSERT( opP != NULL);
+ sys_memzero((char*) opP, sizeof(ESAIOOperation));
+
+ opP->tag = ESAIO_OP_RECVMSG;
+ /* Its a bit annoying that we have to alloc an env and then
+ * copy the ref *before* we know that we actually need it.
+ * How much does this cost?
+ */
+ opP->env = esock_alloc_env("esaio-recvmsg - operation");
+ opP->data.recvmsg.recvRef = CP_TERM(opP->env, recvRef);
+ opP->data.recvmsg.sockRef = CP_TERM(opP->env, sockRef);
+ opP->caller = caller;
+
+ /* Allocate the (msg) data buffer:
+ */
+ ESOCK_ASSERT( ALLOC_BIN(bufSz, &opP->data.recvmsg.data[0]) );
+
+ /* Allocate the ctrl (buffer):
+ */
+ ESOCK_ASSERT( ALLOC_BIN(ctrlSz, &opP->data.recvmsg.ctrl) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_tries, &descP->readTries, 1);
+
+ addrLen = sizeof(opP->data.recvmsg.addr);
+ sys_memzero((char*) &opP->data.recvmsg.addr, addrLen);
+ sys_memzero((char*) &opP->data.recvmsg.msg, sizeof(opP->data.recvmsg.msg));
+
+ opP->data.recvmsg.wbufs[0].buf = opP->data.recvmsg.data[0].data;
+ opP->data.recvmsg.wbufs[0].len = opP->data.recvmsg.data[0].size;
+
+ opP->data.recvmsg.msg.name = (SOCKADDR*) &opP->data.recvmsg.addr;
+ opP->data.recvmsg.msg.namelen = addrLen;
+ opP->data.recvmsg.msg.lpBuffers = opP->data.recvmsg.wbufs;
+ opP->data.recvmsg.msg.dwBufferCount = 1; // Should be calculated...
+ opP->data.recvmsg.msg.Control.buf = opP->data.recvmsg.ctrl.data;
+ opP->data.recvmsg.msg.Control.len = opP->data.recvmsg.ctrl.size;
+ opP->data.recvmsg.msg.dwFlags = 0; // TMP
+
+ rres = sock_recvmsg_O(descP->sock,
+ &opP->data.recvmsg.msg,
+ (OVERLAPPED*) opP);
+
+ eres = recvmsg_check_result(env, descP, opP, caller, rres,
+ sockRef, recvRef);
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_recvmsg(%T) {%d} -> done\r\n",
+ sockRef, descP->sock) );
+
+ return eres;
+}
+
+
+static
+BOOLEAN_T recv_check_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifPid* caller,
+ ERL_NIF_TERM ref,
+ ERL_NIF_TERM* checkResult)
+{
+ BOOLEAN_T result;
+
+ /* Check if already reader */
+ if (! esock_reader_search4pid(env, descP, caller)) {
+ /* No; check if we can wait for a result */
+ if (COMPARE(ref, esock_atom_zero) == 0)
+ return FALSE;
+ } else {
+ *checkResult = esock_raise_invalid(env, esock_atom_state);
+ }
+
+ return TRUE;
+}
+
+
+/* *** recvmsg_check_result ***
+ *
+ * The recvmsg function (maybe) delivers one (1) message. If our buffer
+ * is to small, the message will be truncated. So, regardless of
+ * if we filled the buffer or not, we have got what we are going
+ * to get regarding this message.
+ */
+static
+ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ int recv_result,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM eres;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "recvmsg_check_result(%T) {%d} -> entry with"
+ "\r\n recv_result: %d"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock, recv_result, recvRef) );
+
+ if (recv_result == 0) {
+
+ /* +++ Success +++ */
+
+ eres = recvmsg_check_ok(env, descP, opP, caller, sockRef, recvRef);
+
+ } else {
+ int err;
+
+ /* +++ Failure +++ */
+
+ err = sock_errno();
+
+ /* As pointed out above, there are basically two kinds of errors:
+ * 1) Pending:
+ * An overlapped operation was successfully initiated.
+ * Completion will be indicated at a later time.
+ * 2) An actual error
+ */
+
+ if (err == WSA_IO_PENDING) {
+
+ if (! IS_ZERO(recvRef)) {
+
+ eres = recv_check_pending(env, descP, opP, caller,
+ sockRef, recvRef);
+
+ } else {
+
+ /* We are not allowed to wait! => cancel */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvmsg_check_result(%T, %d) -> "
+ "pending - but we are not allowed to wait => cancel"
+ "\r\n", sockRef, descP->sock) );
+
+ if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) {
+ int save_errno = sock_errno();
+ ERL_NIF_TERM tag = esock_atom_cancel;
+ ERL_NIF_TERM reason = ENO2T(env, save_errno);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvmsg_check_result(%T, %d) -> "
+ "failed cancel pending operation"
+ "\r\n %T"
+ "\r\n", sockRef, descP->sock, reason) );
+
+ eres = esock_make_error(env, MKT2(env, tag, reason));
+
+ } else {
+
+ eres = esock_atom_ok; // Will trigger {error, timeout}
+
+ }
+
+ }
+
+ } else {
+
+ eres = recvmsg_check_fail(env, descP, opP, err, sockRef);
+
+ }
+
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "recvmsg_check_result(%T) {%d} -> done\r\n",
+ sockRef, descP->sock) );
+
+ return eres;
+}
+
+
+/* *** recvmsg_check_ok ***
+ *
+ * A successful recvmsg. We *know* that in this case the buffer is filled!
+ */
+
+static
+ERL_NIF_TERM recvmsg_check_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM eMsg, result;
+ DWORD read = 0, flags = 0;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvmsg_check_ok(%T) {%d} -> try get overlapped result\r\n",
+ sockRef, descP->sock) );
+
+ if (get_recv_ovl_result(descP->sock, (OVERLAPPED*) opP, &read, &flags)) {
+
+ ERL_NIF_TERM eSockAddr;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvmsg_check_ok(%T, %d) -> overlapped success result: "
+ "\r\n read: %d"
+ "\r\n flags: 0x%X"
+ "\r\n", sockRef, descP->sock, read, flags) );
+
+ (void) flags; // We should really do something with this...
+
+ encode_msg(env, descP, read,
+ &opP->data.recvmsg.msg,
+ opP->data.recvmsg.data,
+ &opP->data.recvmsg.ctrl,
+ &eMsg);
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ /* (maybe) Update max */
+ if (read > descP->readPkgMax)
+ descP->readPkgMax = read;
+
+ result = esock_make_ok2(env, eMsg);
+
+ } else {
+
+ int save_errno = sock_errno();
+
+ switch (save_errno) {
+ case WSA_IO_INCOMPLETE:
+ /*
+ * WSA_IO_INCOMPLETE
+ *
+ * Even though it (the I/O Completion Port framework) told
+ * us it was done, it was not. So we need to postpone and let
+ * the (worker) threads deal with it anyway...effing framework...
+ */
+
+ if (! IS_ZERO(recvRef)) {
+
+ result = recv_check_pending(env, descP, opP, caller,
+ sockRef, recvRef);
+
+ } else {
+
+ /* But we are not allowed to wait! => cancel */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvmsg_check_ok(%T, %d) -> "
+ "incomplete - but we are not allowed to wait => cancel"
+ "\r\n", sockRef, descP->sock) );
+
+ if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) {
+ int save_errno = sock_errno();
+ ERL_NIF_TERM tag = esock_atom_cancel;
+ ERL_NIF_TERM reason = ENO2T(env, save_errno);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "recvmsg_check_ok(%T, %d) -> "
+ "failed cancel incomplete operation"
+ "\r\n %T"
+ "\r\n", sockRef, descP->sock, reason) );
+
+ result = esock_make_error(env, MKT2(env, tag, reason));
+
+ } else {
+
+ result = esock_atom_ok; // Will trigger {error, timeout}
+
+ }
+ }
+ break;
+
+ default:
+ {
+ ERL_NIF_TERM eerrno = ENO2T(env, save_errno);
+ ERL_NIF_TERM reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ eerrno);
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_fails, &descP->readFails, 1);
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.genErrs, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+ result = esock_make_error(env, reason);
+ }
+ break;
+ }
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "recvmsg_check_ok(%T) {%d} -> done with"
+ "\r\n result: %T"
+ "\r\n",
+ sockRef, descP->sock, result) );
+
+ return result;
+}
+
+
+
+/* *** recvmsg_check_fail ***
+ *
+ * Processing done upon failed 'recvmsg'.
+ * An actual failure.
+ */
+static
+ERL_NIF_TERM recvmsg_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef)
+{
+ SSDBG( descP,
+ ("WIN-ESAIO", "recvmsg_check_fail(%T) {%d} -> entry with"
+ "\r\n errno: %d"
+ "\r\n",
+ sockRef, descP->sock, saveErrno) );
+
+ FREE_BIN( &opP->data.recvmsg.data[0] );
+ FREE_BIN( &opP->data.recvmsg.ctrl );
+
+ return recv_check_failure(env, descP, opP, saveErrno, sockRef);
+}
+
+
+
+
+
+/* *******************************************************************
+ * esaio_close - Close a socket
+ *
+ * Stage 1 of the socket close
+ */
+
+extern
+ERL_NIF_TERM esaio_close(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ if (! IS_OPEN(descP->readState)) {
+ /* A bit of cheeting; maybe not closed yet - do we need a queue? */
+ return esock_make_error_closed(env);
+ }
+
+ /* Store the PID of the caller,
+ * since we need to inform it when we
+ * (that is, the stop callback function)
+ * completes.
+ */
+ ESOCK_ASSERT( enif_self(env, &descP->closerPid) != NULL );
+
+ /* If the caller is not the owner; monitor the caller,
+ * since we should complete this operation even if the caller dies
+ * (for whatever reason).
+ */
+ if (COMPARE_PIDS(&descP->closerPid, &descP->ctrlPid) != 0) {
+
+ ESOCK_ASSERT( MONP("esaio_close-check -> closer",
+ env, descP,
+ &descP->closerPid,
+ &descP->closerMon) == 0 );
+ }
+
+ /* Prepare for closing the socket */
+ descP->readState |= ESOCK_STATE_CLOSING;
+ descP->writeState |= ESOCK_STATE_CLOSING;
+ if (do_stop(env, descP)) {
+
+ // stop() has been scheduled - wait for it
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_close {%d} -> stop was scheduled\r\n",
+ descP->sock) );
+
+ // Create closeRef for the close msg that esock_stop() will send
+ descP->closeEnv = esock_alloc_env("esock_close_do - close-env");
+ descP->closeRef = MKREF(descP->closeEnv);
+
+ return esock_make_ok2(env, CP_TERM(env, descP->closeRef));
+
+ } else {
+ // The socket may be closed - tell caller to finalize
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_close {%d} -> stop was called\r\n",
+ descP->sock) );
+
+ return esock_atom_ok;
+ }
+}
+
+
+
+static
+BOOLEAN_T do_stop(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ BOOLEAN_T ret;
+ ERL_NIF_TERM sockRef;
+
+ sockRef = enif_make_resource(env, descP);
+
+ if (IS_SELECTED(descP)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "do_stop {%d} -> cancel outstanding I/O operations\r\n",
+ descP->sock) );
+
+ /* Cancel *all* outstanding I/O operations on the socket.
+ * We have to wait for the worker threads to process these ops!
+ * (will result in OPERATION_ABORTED for the threads).
+ */
+ if (! CancelIoEx((HANDLE) descP->sock, NULL) ) {
+ int save_errno = sock_errno();
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "do_stop {%d} -> cancel I/O failed: %s (%d)\r\n",
+ descP->sock, erl_errno_id(save_errno), save_errno) );
+
+ /* Only issue an error message for errors *other* than
+ * 'not found' (since 'not found' means there is no active
+ * requests = already completed => race).
+ */
+
+ if (save_errno != ERROR_NOT_FOUND)
+ esock_error_msg("Failed cancel outstanding I/O operations:"
+ "\r\n Socket: " SOCKET_FORMAT_STR
+ "\r\n Reason: %s (%d)"
+ "\r\n",
+ descP->sock,
+ erl_errno_id(save_errno), save_errno);
+
+ ret = FALSE;
+
+ } else {
+
+ /* Cancel of all active requests (to the I/O completion port
+ * machinery) has been successfully requested.
+ * The requests will be aborted and handled by the worker threads.
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "do_stop {%d} -> successfully canceled\r\n", descP->sock) );
+
+ ret = TRUE;
+ }
+
+ } else {
+
+ /* No active requests in the I/O completion port machinery */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "do_stop {%d} -> no active I/O requests\r\n", descP->sock) );
+
+ ret = FALSE;
+ }
+
+ /* We do nothing here with the requests in the various queues
+ * They are handled by the working threads, when the abort is triggered
+ * (one for each request)!
+ */
+
+ /* +++++++ Connector +++++++
+ * Note that there should not be Writers and a Connector
+ * at the same time so the check for if the
+ * current Writer/Connecter was deselected is only correct
+ * under that assumption
+ */
+
+ if (descP->connectorP != NULL) {
+
+ /* We have a Connector;
+ *
+ * The Connector will not get a select message
+ * - send it an abort message
+ */
+
+ esock_stop_handle_current(env,
+ "connector",
+ descP, sockRef, &descP->connector);
+
+ descP->connectorP = NULL;
+ }
+
+ return ret;
+}
+
+
+
+/* ========================================================================
+ * Perform the final step in the socket close.
+ */
+extern
+ERL_NIF_TERM esaio_fin_close(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ int err;
+ ErlNifPid self;
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ if (IS_CLOSED(descP->readState))
+ return esock_make_error_closed(env);
+
+ if (! IS_CLOSING(descP->readState)) {
+ // esock_close() has not been called
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ if (IS_SELECTED(descP) && (descP->closeEnv != NULL)) {
+ // esock_stop() is scheduled but has not been called
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ if (COMPARE_PIDS(&descP->closerPid, &self) != 0) {
+ // This process is not the closer
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ // Close the socket
+
+ /* Stop monitoring the closer.
+ * Demonitoring may fail since this is a dirty NIF
+ * - the caller may have died already.
+ */
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_fin_close {%d} -> demonitor closer process %T\r\n",
+ descP->sock, descP->closerPid) );
+ enif_set_pid_undefined(&descP->closerPid);
+ if (descP->closerMon.isActive) {
+ (void) DEMONP("esaio_fin_close -> closer",
+ env, descP, &descP->closerMon);
+ }
+
+ /* Stop monitoring the owner */
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_fin_close {%d} -> demonitor owner process %T\r\n",
+ descP->sock, descP->ctrlPid) );
+ enif_set_pid_undefined(&descP->ctrlPid);
+ (void) DEMONP("esaio_fin_close -> ctrl",
+ env, descP, &descP->ctrlMon);
+ /* Not impossible to still get a esock_down() call from a
+ * just triggered owner monitor down
+ */
+
+ /* This nif-function is executed in a dirty scheduler just so
+ * that it can "hang" (with minimum effect on the VM) while the
+ * kernel writes our buffers. IF we have set the linger option
+ * for this ({true, integer() > 0}).
+ */
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_fin_close {%d} -> (try) close the socket\r\n",
+ descP->sock, descP->ctrlPid) );
+ err = esock_close_socket(env, descP, TRUE);
+
+ if (err != 0) {
+ if (err == ERRNO_BLOCK) {
+ /* Not all data in the buffers where sent,
+ * make sure the caller gets this.
+ */
+ return esock_make_error(env, esock_atom_timeout);
+ } else {
+ return esock_make_error_errno(env, err);
+ }
+ }
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_fin_close -> done\r\n") );
+
+ return esock_atom_ok;
+}
+
+
+
+/* ========================================================================
+ * Cancel a connect request.
+ */
+extern
+ERL_NIF_TERM esaio_cancel_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef)
+{
+ ERL_NIF_TERM res;
+ ErlNifPid self;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_cancel_connect {%d} -> entry with"
+ "\r\n writeState: 0x%X"
+ "\r\n opRef: %T"
+ "\r\n",
+ descP->sock, descP->writeState, opRef) );
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ if (! IS_OPEN(descP->writeState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if ((descP->connectorP == NULL) ||
+ (COMPARE_PIDS(&self, &descP->connector.pid) != 0) ||
+ (COMPARE(opRef, descP->connector.ref) != 0)) {
+
+ res = esock_make_error(env, esock_atom_not_found);
+
+ } else {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_cancel_connect {%d} -> "
+ "try cancel connect I/O request\r\n",
+ descP->sock) );
+
+ if (! CancelIoEx((HANDLE) descP->sock,
+ (OVERLAPPED*) descP->connector.dataP)) {
+ /* What does this mean?
+ * One of the possible reasons is that the connect succeeded.
+ * In which case, one of the threads in the thread-pool will
+ * be triggered (eventually).
+ * Then we have to deal with a connected socket that no one wants...
+ */
+ int save_errno = sock_errno();
+ res = esock_make_error_errno(env, save_errno);
+ } else {
+ res = esock_atom_ok;
+ }
+
+ esock_requestor_release("esock_cancel_connect",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+ descP->writeState &= ~ESOCK_STATE_CONNECTING;
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_cancel_connect {%d} -> done when"
+ "\r\n res: %T"
+ "\r\n",
+ descP->sock, descP->writeState,
+ opRef, res) );
+
+ return res;
+}
+
+
+
+/* *** esock_cancel_accept ***
+ *
+ * We have three different cases:
+ * *) Socket is closed:
+ * return error: closed
+ * *) Active accept (found in the request store):
+ * Cancel the completion request!
+ * Success will trigger an event (delivered) to the
+ * (completion) worker threads.
+ * *) Not found (in the request store):
+ * This request has already completed (race):
+ * return: not_found
+ *
+ */
+extern
+ERL_NIF_TERM esaio_cancel_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
+{
+ ERL_NIF_TERM res;
+ ESockRequestor req;
+ ErlNifPid caller;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_cancel_accept(%T), {%d,0x%X} ->"
+ "\r\n opRef: %T"
+ "\r\n", sockRef, descP->sock, descP->readState, opRef) );
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (! IS_OPEN(descP->readState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if (esock_acceptor_get(env, descP, &opRef, &caller, &req)) {
+
+ ESOCK_ASSERT( DEMONP("esaio_cancel_accept -> acceptor",
+ env, descP, &req.mon) == 0);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_cancel_accept {%d} -> try cancel accept I/O request\r\n",
+ descP->sock) );
+
+ if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) req.dataP)) {
+
+ /* What does this mean?
+ * One of the possible reasons is that the accept succeeded.
+ * In which case, one of the threads in the thread-pool will
+ * be triggered (eventually).
+ * Then we have to deal with a connected socket that no one wants...
+ */
+
+ int save_errno = sock_errno();
+ res = esock_make_error_errno(env, save_errno);
+
+ } else {
+
+ res = esock_atom_ok;
+
+ }
+
+ /* Request cleanup (demonitor already done above) */
+ esock_clear_env("esaio_cancel_accept -> req cleanup", req.env);
+ esock_free_env("esaio_cancel_accept -> req cleanup", req.env);
+
+ /* *Maybe* update listen socket (read) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->acceptorsQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_ACCEPTING;
+ }
+
+ } else {
+
+ res = esock_make_error(env, esock_atom_not_found);
+
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_cancel_accept(%T) -> done with result:"
+ "\r\n %T"
+ "\r\n", sockRef, res) );
+
+ return res;
+}
+
+
+
+
+/* *** esock_cancel_send ***
+ *
+ * We have three different cases:
+ * *) Socket is closed:
+ * return error: closed
+ * *) Active send (found in the request store):
+ * Cancel the completion request!
+ * Success will trigger an event (delivered) to the
+ * (completion) worker threads.
+ * *) Not found (in the request store):
+ * This request has already completed (race):
+ * return: not_found
+ *
+ */
+extern
+ERL_NIF_TERM esaio_cancel_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
+{
+ ERL_NIF_TERM res;
+ ESockRequestor req;
+ ErlNifPid caller;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_cancel_send(%T), {%d,0x%X} ->"
+ "\r\n opRef: %T"
+ "\r\n", sockRef, descP->sock, descP->readState, opRef) );
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (! IS_OPEN(descP->writeState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if (esock_writer_get(env, descP, &opRef, &caller, &req)) {
+
+ ESOCK_ASSERT( DEMONP("esaio_cancel_send -> sender",
+ env, descP, &req.mon) == 0);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_cancel_send {%d} -> try cancel send I/O request\r\n",
+ descP->sock) );
+
+ if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) req.dataP)) {
+
+ /* What does this mean?
+ * One of the possible reasons is that the send succeeded.
+ * In which case, one of the threads in the thread-pool will
+ * be triggered (eventually).
+ */
+
+ int save_errno = sock_errno();
+ res = esock_make_error_errno(env, save_errno);
+
+ } else {
+
+ res = esock_atom_ok;
+
+ }
+
+ /* Request cleanup (demonitor already done above) */
+ esock_clear_env("esaio_cancel_send -> req cleanup", req.env);
+ esock_free_env("esaio_cancel_send -> req cleanup", req.env);
+
+ } else {
+
+ res = esock_make_error(env, esock_atom_not_found);
+
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_cancel_send(%T) -> done with result:"
+ "\r\n %T"
+ "\r\n", sockRef, res) );
+
+ return res;
+}
+
+
+
+
+/* *** esock_cancel_recv ***
+ *
+ * We have three different cases:
+ * *) Socket is closed:
+ * return error: closed
+ * *) Active receive (found in the request store):
+ * Cancel the completion request!
+ * Success will trigger an event (delivered) to the
+ * (completion) worker threads.
+ * *) Not found (in the request store):
+ * This request has already completed (race):
+ * return: not_found
+ *
+ */
+extern
+ERL_NIF_TERM esaio_cancel_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
+{
+ ERL_NIF_TERM res;
+ ESockRequestor req;
+ ErlNifPid caller;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_cancel_recv(%T), {%d,0x%X} ->"
+ "\r\n opRef: %T"
+ "\r\n", sockRef, descP->sock, descP->readState, opRef) );
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (! IS_OPEN(descP->readState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if (esock_reader_get(env, descP, &opRef, &caller, &req)) {
+
+ ESOCK_ASSERT( DEMONP("esaio_cancel_recv -> reader",
+ env, descP, &req.mon) == 0);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_cancel_recv {%d} -> try cancel send I/O request\r\n",
+ descP->sock) );
+
+ if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) req.dataP)) {
+
+ /* What does this mean?
+ * One of the possible reasons is that the recv succeeded.
+ * In which case, one of the threads in the thread-pool will
+ * be triggered (eventually).
+ */
+
+ int save_errno = sock_errno();
+ res = esock_make_error_errno(env, save_errno);
+
+ } else {
+
+ res = esock_atom_ok;
+
+ }
+
+ /* Request cleanup (demonitor already done above) */
+ esock_clear_env("esaio_cancel_recv -> req cleanup", req.env);
+ esock_free_env("esaio_cancel_recv -> req cleanup", req.env);
+
+ } else {
+
+ res = esock_make_error(env, esock_atom_not_found);
+
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_cancel_recv(%T) -> done with result:"
+ "\r\n %T"
+ "\r\n", sockRef, res) );
+
+ return res;
+}
+
+
+
+
+/* ====================================================================
+ *
+ * The "worker" thread of the I/O Completion Port thread pool.
+ * Shall each thread have its own environment?
+ *
+ * ====================================================================
+ */
+
+static
+void* esaio_completion_main(void* threadDataP)
+{
+ char envName[64]; /* Used for building the (env-) name */
+ BOOLEAN_T done = FALSE;
+ ESAIOThreadData* dataP = (ESAIOThreadData*) threadDataP;
+ ESockDescriptor* descP = NULL;
+ ESAIOOperation* opP;
+ OVERLAPPED* olP;
+ BOOL res;
+ DWORD numBytes, flags = 0;
+ int save_errno;
+
+ SGDBG( ("WIN-ESAIO", "esaio_completion_main -> entry\r\n") );
+
+ dataP->state = ESAIO_THREAD_STATE_INITIATING;
+
+ sprintf(envName, "esaio-completion-main[%d]", dataP->id);
+ dataP->env = esock_alloc_env(envName);
+
+ dataP->state = ESAIO_THREAD_STATE_OPERATIONAL;
+
+ SGDBG( ("WIN-ESAIO", "esaio_completion_main -> initiated\r\n") );
+
+ while (!done) {
+ /*
+ * If this function *fails*, return value FALSE, the (out-) arguments:
+ * - lpNumberOfBytes (numBytes)
+ * - lpCompletionKey (descP)
+ * - lpOverlapped (olP)
+ * *can* contain particular value combinations as follows:
+ *
+ * * If *lpOverlapped is NULL, the function did not dequeue a
+ * completion packet from the completion port.
+ * In this case, the function does not store information in the
+ * variables pointed to by the lpNumberOfBytes and lpCompletionKey
+ * parameters, and their values are indeterminate.
+ *
+ * * If *lpOverlapped is not NULL and the function dequeues a
+ * completion packet for a failed I/O operation from the
+ * completion port, the function stores information about the
+ * failed operation in the variables pointed to by lpNumberOfBytes,
+ * lpCompletionKey, and lpOverlapped.
+ * To get extended error information, call GetLastError.
+ *
+ */
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> [%d] try dequeue packet\r\n",
+ dataP->cnt) );
+
+ res = GetQueuedCompletionStatus(ctrl.cport,
+ &numBytes,
+ (PULONG_PTR) &descP,
+ &olP,
+ INFINITE);
+ save_errno = NO_ERROR;
+
+ if (!res) {
+
+ save_errno = sock_errno(); // Details
+
+ if (olP == NULL) {
+
+ /* First alt.
+ * What shall we do here? Quit? Try again?
+ */
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> [failure 1]"
+ "\r\n %s (%d)"
+ "\r\n", erl_errno_id(save_errno), save_errno) );
+
+ dataP->state = ESAIO_THREAD_STATE_TERMINATING;
+ dataP->error = ESAIO_THREAD_ERROR_GET;
+ opP = NULL;
+ done = TRUE;
+ break;
+
+ } else {
+
+ /* Second alt.
+ * Dequeued a complete packet for a *failed* I/O operation.
+ */
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> [failure 2] "
+ "\r\n %s (%d)"
+ "\r\n", erl_errno_id(save_errno), save_errno) );
+
+ opP = CONTAINING_RECORD(olP, ESAIOOperation, ol);
+ esaio_completion_inc(dataP);
+
+ }
+ } else {
+ opP = CONTAINING_RECORD(olP, ESAIOOperation, ol);
+ esaio_completion_inc(dataP);
+
+ SGDBG( ("WIN-ESAIO", "esaio_completion_main -> success\r\n") );
+
+ } /* if (!res) */
+
+ dataP->latest = opP->tag;
+
+ switch (opP->tag) {
+ case ESAIO_OP_TERMINATE:
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> received terminate cmd\r\n") );
+ done = esaio_completion_terminate(dataP, (OVERLAPPED*) opP);
+ break;
+
+ case ESAIO_OP_CONNECT:
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> received connect cmd\r\n") );
+ done = esaio_completion_connect(dataP, descP, (OVERLAPPED*) opP,
+ opP->env, &opP->caller,
+ &opP->data.connect,
+ save_errno);
+ break;
+
+ case ESAIO_OP_ACCEPT:
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> received accept cmd\r\n") );
+ done = esaio_completion_accept(dataP, descP, (OVERLAPPED*) opP,
+ opP->env, &opP->caller,
+ &opP->data.accept,
+ save_errno);
+ break;
+
+ case ESAIO_OP_SEND:
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> received send cmd\r\n") );
+ done = esaio_completion_send(dataP, descP, (OVERLAPPED*) opP,
+ opP->env, &opP->caller,
+ &opP->data.send,
+ save_errno);
+ break;
+
+ case ESAIO_OP_SENDTO:
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> received sendto cmd\r\n") );
+ done = esaio_completion_sendto(dataP, descP, (OVERLAPPED*) opP,
+ opP->env, &opP->caller,
+ &opP->data.sendto,
+ save_errno);
+ break;
+
+ case ESAIO_OP_SENDMSG:
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> received sendmsg cmd\r\n") );
+ done = esaio_completion_sendmsg(dataP, descP, (OVERLAPPED*) opP,
+ opP->env, &opP->caller,
+ &opP->data.sendmsg,
+ save_errno);
+ break;
+
+ case ESAIO_OP_RECV:
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> received recv cmd\r\n") );
+ done = esaio_completion_recv(dataP, descP, (OVERLAPPED*) opP,
+ opP->env, &opP->caller,
+ &opP->data.recv,
+ save_errno);
+ break;
+
+ case ESAIO_OP_RECVFROM:
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> received recvfrom cmd\r\n") );
+ done = esaio_completion_recvfrom(dataP, descP, (OVERLAPPED*) opP,
+ opP->env, &opP->caller,
+ &opP->data.recvfrom,
+ save_errno);
+ break;
+
+ case ESAIO_OP_RECVMSG:
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> received recvmsg cmd\r\n") );
+ done = esaio_completion_recvmsg(dataP, descP, (OVERLAPPED*) opP,
+ opP->env, &opP->caller,
+ &opP->data.recvmsg,
+ save_errno);
+ break;
+
+ default:
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_main -> received unknown cmd: "
+ "\r\n %d"
+ "\r\n",
+ opP->tag) );
+ done = esaio_completion_unknown(dataP, descP, (OVERLAPPED*) opP,
+ numBytes, save_errno);
+ break;
+
+ }
+
+ FREE(opP);
+
+ } /* while (!done) */
+
+ SGDBG( ("WIN-ESAIO", "esaio_completion_main -> terminating\r\n") );
+
+ TEXIT(threadDataP);
+
+ SGDBG( ("WIN-ESAIO", "esaio_completion_main -> terminated\r\n") );
+
+ dataP->state = ESAIO_THREAD_STATE_TERMINATED;
+
+ SGDBG( ("WIN-ESAIO", "esaio_completion_main -> done\r\n") );
+
+ return threadDataP;
+}
+
+
+/* *** esaio_completion_terminate ***
+ *
+ * We are done
+ *
+ */
+static
+BOOLEAN_T esaio_completion_terminate(ESAIOThreadData* dataP,
+ OVERLAPPED* ovl)
+{
+ (void) ovl;
+
+ dataP->state = ESAIO_THREAD_STATE_TERMINATING;
+ dataP->error = ESAIO_THREAD_ERROR_CMD;
+
+ return TRUE;
+}
+
+
+/* *** esaio_completion_connect ***
+ *
+ * Handle a completed 'connect' (completion) request.
+ * Send a 'completion' message (to requestor) with the request status.
+ *
+ * Completion message:
+ * {'socket tag', socket(), completion, CompletionInfo}
+ *
+ * CompletionInfo: {CompletionHandle, CompletionStatus}
+ * CompletionHandle: reference()
+ * Result: ok | {error, Reason}
+ *
+ *
+ * There is a possibillity of a race here. That is, if the user
+ * calls socket:connect(Socket, ..., nowait), the connect is
+ * scheduled, and then just as it has completed, but before this
+ * thread has been activated to handle the 'connect completed'
+ * the user calls socket:close(Socket) (or exits).
+ * Then when this function is called, the socket is closed.
+ * What to do?
+ */
+static
+BOOLEAN_T esaio_completion_connect(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataConnect* opDataP,
+ int error)
+{
+ ErlNifEnv* env = dataP->env;
+ ERL_NIF_TERM reason;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_connect(%d) -> entry\r\n",
+ descP->sock, error) );
+
+ (void) opCaller;
+
+ switch (error) {
+ case NO_ERROR:
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_connect(%d) -> success"
+ "\r\n", descP->sock) );
+ MLOCK(descP->writeMtx);
+
+ esaio_completion_connect_success(env, descP, opDataP);
+
+ MUNLOCK(descP->writeMtx);
+ break;
+
+ case WSA_OPERATION_ABORTED:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_connect(%d) -> operation aborted"
+ "\r\n", descP->sock) );
+ /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */
+ MLOCK(descP->readMtx);
+ MLOCK(descP->writeMtx);
+
+ esaio_completion_connect_aborted(env, descP, opDataP);
+
+ MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
+ break;
+
+ default:
+ /* We do not know what this is
+ * but we can "assume" that the request failed so we need to
+ * remove it from the "queue" if its still there...
+ * And cleanup...
+ */
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_connect(%d) -> unknown failure:"
+ "\r\n %T"
+ "\r\n", descP->sock, ENO2T(env, error)) );
+ MLOCK(descP->writeMtx);
+
+ esaio_completion_connect_failure(env, descP, opDataP, error);
+
+ MUNLOCK(descP->writeMtx);
+ break;
+ }
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_connect -> clear and delete op env\r\n") );
+
+ /* No need for this "stuff" anymore */
+ esock_clear_env("esaio_completion_connect", opEnv);
+ esock_free_env("esaio_completion_connect", opEnv);
+
+ SGDBG( ("WIN-ESAIO", "esaio_completion_connect -> done\r\n") );
+
+ return FALSE;
+}
+
+
+
+/* *** esaio_completion_connect_success ***
+ * The 'connect' operation was successful.
+ */
+static
+void esaio_completion_connect_success(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOpDataConnect* opDataP)
+{
+ if (descP->connectorP != NULL) {
+ if (IS_OPEN(descP->writeState)) {
+ esaio_completion_connect_completed(env, descP, opDataP);
+ } else {
+ /* A completed (active) request for a socket that is not open.
+ * Is this even possible?
+ * A race (completed just as the socket was closed).
+ */
+
+ /* Clean up the connector stuff, no need for that anymore */
+ esock_requestor_release("esaio_completion_connect_success -> "
+ "not active",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+
+ descP->writeState &=
+ ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED);
+
+ esaio_completion_connect_not_active(descP);
+ }
+ } else {
+ /* Connect was actually completed directly
+ * (and 'connector' was therefor not initiated)
+ * => Nothing to do here, other than cleanup.
+ */
+ descP->writeState &= ~ESOCK_STATE_SELECTED;
+ }
+}
+
+
+
+/* *** esaio_completion_connect_aborted ***
+ * The 'connect' operation was aborted.
+ * The only thing *we* do that could cause an abort is the
+ * 'CancelIoEx' call, which we do when closing the socket
+ * (or cancel a request).
+ * But if we have done that;
+ * - Socket state will not be 'open' and
+ * - we have also set closer (pid and ref).
+ */
+
+static
+void esaio_completion_connect_aborted(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOpDataConnect* opDataP)
+{
+ if (descP->connectorP != NULL) {
+
+ ERL_NIF_TERM reason = esock_atom_closed;
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ descP->connectorP, reason);
+
+ /* Clean up the connector stuff, no need for that anymore */
+ esock_requestor_release("connect_stream_check_result -> abort",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+ descP->writeState &= ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED);
+
+ /* The socket not being open (assumed closing),
+ * means we are in the closing phase...
+ */
+ if (! IS_OPEN(descP->writeState)) {
+
+ esaio_stop(env, descP);
+
+ }
+ } else {
+ descP->writeState &= ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED);
+ }
+}
+
+
+/* *** esaio_completion_connect_failure *
+ * A "general" failure happened while performing the 'connect' operation.
+ */
+static
+void esaio_completion_connect_failure(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOpDataConnect* opDataP,
+ int error)
+{
+ if (descP->connectorP != NULL) {
+ /* Figure out the reason */
+ ERL_NIF_TERM reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ ENO2T(env, error));
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ descP->connectorP, reason);
+ esaio_completion_connect_fail(env, descP, error, FALSE);
+
+ /* Clean up the connector stuff, no need for that anymore */
+ esock_requestor_release("connect_stream_check_result -> failure",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+ descP->writeState &= ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED);
+
+ } else {
+ esaio_completion_connect_fail(env, descP, error, TRUE);
+ }
+}
+
+
+/* *** esaio_completion_connect_completed ***
+ * The connect request has completed.
+ */
+static
+void esaio_completion_connect_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOpDataConnect* opDataP)
+{
+ ERL_NIF_TERM completionStatus, completionInfo;
+ int ucres;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_connect_completed(%d) -> "
+ "success - try update context\r\n", descP->sock) );
+
+ ucres = ESAIO_UPDATE_CONNECT_CONTEXT( descP->sock );
+
+ if (ucres == 0) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_connect_completed({%d) -> success\r\n",
+ descP->sock) );
+
+ descP->writeState &= ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED);
+ descP->writeState |= ESOCK_STATE_CONNECTED;
+
+ completionStatus = esock_atom_ok;
+
+ } else {
+
+ /* It is actually possible that this is an error "we do not know"
+ * which will result in the atom 'unknown', which is not very useful...
+ * So, we should really test if is 'unknown' and if so use the actual
+ * value (the integer) instead.
+ */
+ int save_errno = sock_errno();
+ ERL_NIF_TERM tag = esock_atom_update_connect_context;
+ ERL_NIF_TERM reason = ENO2T(env, save_errno);
+
+ SSDBG( descP, ("WIN-ESAIO",
+ "esaio_completion_connect_completed(%d) -> "
+ "failed update connect context: %T\r\n",
+ descP->sock, reason) );
+
+ descP->writeState = ESOCK_STATE_CLOSED;
+
+ sock_close(descP->sock);
+
+ WSACleanup();
+
+ completionStatus = esock_make_error_t2r(descP->connector.env,
+ tag, reason);
+
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_connect_completed {%d} -> "
+ "completion status: %T\r\n",
+ descP->sock, completionStatus) );
+
+ completionInfo = MKT2(descP->connector.env,
+ descP->connector.ref,
+ completionStatus);
+
+ /* Send a 'connect' completion message */
+ esaio_send_completion_msg(env,
+ descP,
+ &descP->connector.pid,
+ descP->connector.env,
+ CP_TERM(descP->connector.env, opDataP->sockRef),
+ completionInfo);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_connect_completed {%d} -> cleanup\r\n",
+ descP->sock) );
+
+ /* Clean up the connector stuff, no need for that anymore */
+ esock_requestor_release("esaio_completion_connect_completed",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+
+}
+
+
+
+/* *** esaio_completion_connect_not_active ***
+ * A connect has completed but the operation is no longer valid.
+ */
+static
+void esaio_completion_connect_not_active(ESockDescriptor* descP)
+{
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_connect_not_active -> "
+ "success for cancelled connect\r\n") );
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.unexpectedConnects, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+}
+
+
+
+/* *** esaio_completion_connect_fail ***
+ * Unknown operation failure.
+ */
+static
+void esaio_completion_connect_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform)
+{
+ descP->writeState &= ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED);
+ esaio_completion_fail(env, descP, "connect", error, inform);
+}
+
+
+
+
+/* === accept 'stuff' === */
+
+/* *** esaio_completion_accept ***
+ *
+ * Handle a completed 'accept' (completion) request.
+ * Send a 'completion' message (to requestor) with the request status.
+ *
+ * Completion message:
+ * {'socket tag', socket(), completion, CompletionInfo}
+ *
+ * CompletionInfo: {CompletionHandle, CompletionStatus}
+ * CompletionHandle: reference()
+ * Result: ok | {error, Reason}
+ *
+ *
+ * There is a possibillity of a race here. That is, if the user
+ * calls socket:accept(Socket, ..., nowait), the accept is
+ * scheduled, and then just as it has completed, but before this
+ * thread has been activated to handle the 'accept completed'
+ * the user calls socket:close(Socket) (or exits).
+ * Then when this function is called, the socket is closed.
+ * What to do?
+ */
+static
+BOOLEAN_T esaio_completion_accept(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataAccept* opDataP,
+ int error)
+{
+ ErlNifEnv* env = dataP->env;
+ ESockRequestor req;
+ ERL_NIF_TERM reason;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_accept(%d) -> entry with"
+ "\r\n error: %s (%d)"
+ "\r\n", descP->sock, erl_errno_id(error), error) );
+
+ switch (error) {
+ case NO_ERROR:
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_accept(%d) -> success"
+ "\r\n", descP->sock) );
+ MLOCK(descP->readMtx);
+
+ esaio_completion_accept_success(env, descP, opEnv, opCaller, opDataP);
+
+ MUNLOCK(descP->readMtx);
+ break;
+
+ case WSA_OPERATION_ABORTED:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_accept(%d) -> operation aborted"
+ "\r\n", descP->sock) );
+ /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */
+ MLOCK(descP->readMtx);
+ MLOCK(descP->writeMtx);
+
+ esaio_completion_accept_aborted(env, descP, opCaller, opDataP);
+
+ MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
+ break;
+
+ default:
+ /* We do not know what this is
+ * but we can "assume" that the request failed so we need to
+ * remove it from the "queue" if its still there...
+ * And cleanup...
+ */
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_accept(%d) -> unknown failure"
+ "\r\n", descP->sock) );
+ MLOCK(descP->readMtx);
+
+ esaio_completion_accept_failure(env, descP, opCaller, opDataP, error);
+
+ MUNLOCK(descP->readMtx);
+ break;
+ }
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_completion_accept -> clear and delete op env\r\n") );
+
+ /* "Manually" allocated buffer */
+ FREE( opDataP->buf );
+
+ /* No need for this "stuff" anymore */
+ esock_clear_env("esaio_completion_accept - op cleanup", opEnv);
+ esock_free_env("esaio_completion_accept - op cleanup", opEnv);
+
+ SGDBG( ("WIN-ESAIO", "esaio_completion_accept -> done\r\n") );
+
+ return FALSE;
+
+}
+
+
+/* *** esaio_completion_accept_success ***
+ * The 'accept' operation was successful.
+ */
+static
+void esaio_completion_accept_success(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataAccept* opDataP)
+{
+ ESockRequestor req;
+
+ if (esock_acceptor_get(env, descP,
+ &opDataP->accRef,
+ opCaller,
+ &req)) {
+ if (IS_OPEN(descP->readState)) {
+ esaio_completion_accept_completed(env, descP,
+ opEnv, opCaller, opDataP,
+ &req);
+ } else {
+ /* A completed (active) request for a socket that is not open.
+ * Is this even possible?
+ * A race (completed just as the socket was closed).
+ */
+ esaio_completion_accept_not_active(descP);
+ }
+ } else {
+ /* Request was actually completed directly
+ * (and was therefor not put into the "queue")
+ * => Nothing to do here, other than cleanup (see below).
+ * => But we do not free the "buffer" since it was "used up"
+ * when we (as assumed) got the result (directly)...
+ */
+ }
+
+ /* *Maybe* update socket (read) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->acceptorsQ.first == NULL)
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+}
+
+
+/* *** esaio_completion_accept_aborted ***
+ * The 'accept' operation was aborted.
+ * The only thing *we* do that could cause an abort is the
+ * 'CancelIoEx' call, which we do when closing the socket
+ * (or cancel a request).
+ * But if we have done that;
+ * - Socket state will not be 'open' and
+ * - we have also set closer (pid and ref).
+ */
+
+static
+void esaio_completion_accept_aborted(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifPid* opCaller,
+ ESAIOOpDataAccept* opDataP)
+{
+ ESockRequestor req;
+ ERL_NIF_TERM reason;
+
+ if (esock_acceptor_get(env, descP,
+ &opDataP->accRef,
+ opCaller,
+ &req)) {
+
+ ERL_NIF_TERM reason = esock_atom_closed;
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->lSockRef,
+ &req, reason);
+
+ /* The socket not being open (assumed closing),
+ * means we are in the closing phase...
+ */
+ if (! IS_OPEN(descP->readState)) {
+
+ /* We can only send the 'close' message to the closer
+ * when all requests has been processed!
+ */
+
+ /* Check "our" queue */
+ if (descP->acceptorsQ.first == NULL) {
+
+ /* Check "other" queue(s) and if there is a closer pid */
+ if ((descP->readersQ.first == NULL) &&
+ (descP->writersQ.first == NULL)) {
+
+ esaio_stop(env, descP);
+
+ }
+ }
+ }
+
+ }
+
+ /* *Maybe* update socket (read) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->acceptorsQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+
+}
+
+
+/* *** esaio_completion_accept_failure *
+ * A "general" failure happened while performing the 'accept' operation.
+ */
+static
+void esaio_completion_accept_failure(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifPid* opCaller,
+ ESAIOOpDataAccept* opDataP,
+ int error)
+{
+ ESockRequestor req;
+ ERL_NIF_TERM reason;
+
+ if (esock_acceptor_get(env, descP,
+ &opDataP->accRef,
+ opCaller,
+ &req)) {
+
+ reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ ENO2T(env, error));
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->lSockRef,
+ &req, reason);
+ esaio_completion_accept_fail(env, descP, error, FALSE);
+ } else {
+ esaio_completion_accept_fail(env, descP, error, TRUE);
+ }
+
+ /* *Maybe* update socket (read) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->acceptorsQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+
+}
+
+
+/* *** esaio_completion_accept_completed ***
+ * The accept request has completed.
+ */
+static
+void esaio_completion_accept_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataAccept* opDataP,
+ ESockRequestor* reqP)
+{
+ ERL_NIF_TERM completionStatus, completionInfo;
+ int ucres;
+ ESockDescriptor* accDescP;
+ ERL_NIF_TERM accRef, accSocket;
+
+ ESOCK_ASSERT( DEMONP("esaio_completion_accept_completed - acceptor",
+ env, descP, &reqP->mon) == 0);
+
+ /* We need to make sure peername and sockname works! */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_accept_completed -> "
+ "success - try update context\r\n") );
+
+ ucres = ESAIO_UPDATE_ACCEPT_CONTEXT( opDataP->asock, opDataP->lsock );
+
+ if (ucres == 0) {
+
+ int save_errno;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_accept_completed -> "
+ "create (accepted) descriptor\r\n") );
+
+ accDescP = esock_alloc_descriptor(opDataP->asock);
+
+ if (ESAIO_OK != (save_errno = esaio_add_socket(accDescP))) {
+ // See esock_dtor for what needs done!
+ ERL_NIF_TERM tag = esock_atom_add_socket;
+ ERL_NIF_TERM reason = ENO2T(opEnv, save_errno);
+
+ ESOCK_CNT_INC(env, descP, CP_TERM(env, opDataP->lSockRef),
+ esock_atom_acc_fails, &descP->accFails, 1);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_accept_completed -> "
+ "failed adding (accepted) socket to completion port: "
+ "%T\r\n", reason) );
+
+ esock_dealloc_descriptor(env, accDescP);
+ sock_close(opDataP->asock);
+
+ /* This should really be:
+ * {error, {invalid, {add_to_completion_port, Reason}}}
+ */
+
+ completionStatus = esock_make_error_t2r(opEnv, tag, reason);
+
+ } else {
+
+ ESOCK_CNT_INC(env, descP, CP_TERM(env, opDataP->lSockRef),
+ esock_atom_acc_success, &descP->accSuccess, 1);
+
+ accDescP->domain = descP->domain;
+ accDescP->type = descP->type;
+ accDescP->protocol = descP->protocol;
+
+ MLOCK(descP->writeMtx);
+
+ accDescP->rBufSz = descP->rBufSz; // Inherit buffer size
+ accDescP->rCtrlSz = descP->rCtrlSz; // Inherit buffer size
+ accDescP->wCtrlSz = descP->wCtrlSz; // Inherit buffer size
+ accDescP->iow = descP->iow; // Inherit iow
+ accDescP->dbg = descP->dbg; // Inherit debug flag
+ accDescP->useReg = descP->useReg; // Inherit useReg flag
+ esock_inc_socket(accDescP->domain, accDescP->type,
+ accDescP->protocol);
+
+ accRef = enif_make_resource(env, accDescP);
+ enif_release_resource(accDescP);
+ accSocket = esock_mk_socket(opEnv, CP_TERM(opEnv, accRef));
+
+ accDescP->ctrlPid = *opCaller;
+
+ ESOCK_ASSERT( MONP("esaio_completion_accept_completed -> ctrl",
+ env, accDescP,
+ &accDescP->ctrlPid,
+ &accDescP->ctrlMon) == 0 );
+
+ accDescP->writeState |= ESOCK_STATE_CONNECTED;
+
+ MUNLOCK(descP->writeMtx);
+
+ /* And finally (maybe) update the registry */
+ if (descP->useReg)
+ esock_send_reg_add_msg(env, descP, accRef);
+
+ completionStatus = esock_make_ok2(opEnv, accSocket);
+
+ }
+
+ } else {
+
+ /* It is actually possible that this is an error "we do not know"
+ * which will result in the atom 'unknown', which is not very useful...
+ * So, we should really test if is 'unknown' and if so use the actual
+ * value (the integer) instead.
+ */
+ int save_errno = sock_errno();
+ ERL_NIF_TERM tag = esock_atom_update_accept_context;
+ ERL_NIF_TERM reason = ENO2T(env, save_errno);
+
+ SSDBG( descP, ("WIN-ESAIO",
+ "esaio_completion_accept_completed(%d) -> "
+ "accept context update failed: %T (%d)\r\n",
+ descP->sock, reason, save_errno) );
+
+ sock_close(descP->sock);
+ descP->writeState = ESOCK_STATE_CLOSED;
+
+ WSACleanup();
+
+ completionStatus = esock_make_error_t2r(opEnv, tag, reason);
+
+ }
+
+ completionInfo = MKT2(opEnv, opDataP->accRef, completionStatus);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_accept_completed -> "
+ "send completion message to %T with"
+ "\r\n CompletionInfo: %T"
+ "\r\n", MKPID(env, opCaller), completionInfo) );
+
+ /* Send a 'accept' completion message */
+ esaio_send_completion_msg(env, // Send env
+ descP, // Descriptor
+ opCaller, // Msg destination
+ opEnv, // Msg env
+ opDataP->lSockRef, // Dest socket
+ completionInfo); // Info
+
+ /* *** Finalize *** */
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_accept_completed -> finalize\r\n") );
+
+ /* Request cleanup (demonitor already done above) */
+ esock_clear_env("esaio_completion_accept_completed -> req cleanup",
+ reqP->env);
+ esock_free_env("esaio_completion_accept_completed -> req cleanup",
+ reqP->env);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_accept_completed -> done\r\n") );
+}
+
+
+
+/* *** esaio_completion_accept_not_active ***
+ * A accept request has completed but the request is no longer valid.
+ */
+static
+void esaio_completion_accept_not_active(ESockDescriptor* descP)
+{
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_accept_not_active(%d) -> "
+ "success for not active accept request\r\n", descP->sock) );
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.unexpectedAccepts, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_accept_not_active(%d) -> done\r\n",
+ descP->sock) );
+
+}
+
+
+/* *** esaio_completion_accept_fail ***
+ * Unknown operation failure.
+ */
+static
+void esaio_completion_accept_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform)
+{
+ esaio_completion_fail(env, descP, "accept", error, inform);
+}
+
+
+
+
+/* === send 'stuff' === */
+
+/* *** esaio_completion_send ***
+ *
+ * Handle a completed 'send' (completion) request.
+ * Send a 'completion' message (to requestor) with the request status.
+ *
+ * Completion message:
+ * {'socket tag', socket(), completion, CompletionInfo}
+ *
+ * CompletionInfo: {CompletionHandle, CompletionStatus}
+ * CompletionHandle: reference()
+ * Result: ok | {error, Reason}
+ *
+ *
+ * There is a possibillity of a race here. That is, if the user
+ * calls socket:send(Socket, ..., nowait), the send is scheduled,
+ * and then just as it has completed, but before this
+ * thread has been activated to handle the 'send completed'
+ * the user calls socket:close(Socket) (or exits).
+ * Then when this function is called, the socket is closed.
+ * What to do?
+ *
+ * We need to use 'WSAGetOverlappedResult' to actually figure out the
+ * "transfer result" (how much was sent).
+ */
+static
+BOOLEAN_T esaio_completion_send(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataSend* opDataP,
+ int error)
+{
+ ErlNifEnv* env = dataP->env;
+ ESockRequestor req;
+ ERL_NIF_TERM reason;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_send(%d) -> entry with"
+ "\r\n error: %T"
+ "\r\n", descP->sock, ENO2T(env, error)) );
+
+ switch (error) {
+ case NO_ERROR:
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_send(%d) -> no error"
+ "\r\n", descP->sock) );
+ MLOCK(descP->writeMtx);
+ if (esock_writer_get(env, descP,
+ &opDataP->sendRef,
+ opCaller,
+ &req)) {
+ if (IS_OPEN(descP->writeState)) {
+ esaio_completion_send_completed(env, descP,
+ ovl,
+ opEnv,
+ opCaller,
+ opDataP->sockRef,
+ opDataP->sendRef,
+ opDataP->wbuf.len,
+ &req);
+ } else {
+ /* A completed (active) request for a socket that is not open.
+ * Is this even possible?
+ * A race (completed just as the socket was closed).
+ */
+ esaio_completion_send_not_active(descP);
+ }
+
+ } else {
+ /* Request was actually completed directly
+ * (and was therefor not put into the "queue")
+ * => Nothing to do here, other than cleanup (see below).
+ */
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->writersQ.first == NULL) {
+ descP->writeState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ MUNLOCK(descP->writeMtx);
+ break;
+
+ case WSA_OPERATION_ABORTED:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_send(%d) -> operation aborted"
+ "\r\n", descP->sock) );
+ /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */
+ MLOCK(descP->readMtx);
+ MLOCK(descP->writeMtx);
+ /* The only thing *we* do that could cause an abort is the
+ * 'CancelIoEx' call, which we do when closing the socket
+ * (or cancel a request).
+ * But if we have done that;
+ * - Socket state will not be 'open' and
+ * - we have also set closer (pid and ref).
+ */
+
+ if (esock_writer_get(env, descP,
+ &opDataP->sendRef,
+ opCaller,
+ &req)) {
+
+ reason = esock_atom_closed,
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+
+ /* The socket not being open (assumed closing),
+ * means we are in the closing phase...
+ */
+ if (! IS_OPEN(descP->writeState)) {
+
+ /* We can only send the 'close' message to the closer
+ * when all requests has been processed!
+ */
+
+ /* Check "our" queue */
+ if (descP->writersQ.first == NULL) {
+
+ /* Check "other" queue(s) and if there is a closer pid */
+ if ((descP->readersQ.first == NULL) &&
+ (descP->acceptorsQ.first == NULL)) {
+
+ esaio_stop(env, descP);
+
+ }
+ }
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->writersQ.first == NULL) {
+ descP->writeState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ }
+ MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
+ break;
+
+ default:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_send(%d) -> operation unknown failure"
+ "\r\n", descP->sock) );
+ MLOCK(descP->writeMtx);
+ /* We do not know what this is
+ * but we can "assume" that the request failed so we need to
+ * remove it from the "queue" if its still there...
+ * And cleanup...
+ */
+ if (esock_writer_get(env, descP,
+ &opDataP->sendRef,
+ opCaller,
+ &req)) {
+
+ reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ ENO2T(env, error));
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+ esaio_completion_send_fail(env, descP, error, FALSE);
+
+ } else {
+ esaio_completion_send_fail(env, descP, error, TRUE);
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->writersQ.first == NULL) {
+ descP->writeState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ MUNLOCK(descP->writeMtx);
+ break;
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_send(%d) -> cleanup\r\n", descP->sock) );
+
+ FREE( opDataP->wbuf.buf );
+
+ /* No need for this "stuff" anymore */
+ esock_clear_env("esaio_completion_send - op cleanup", opEnv);
+ esock_free_env("esaio_completion_send - op cleanup", opEnv);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_send(%d) -> done\r\n",
+ descP->sock) );
+
+ return FALSE;
+
+}
+
+
+/* *** esaio_completion_send_completed ***
+ * The send request has completed.
+ */
+static
+void esaio_completion_send_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* sender,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ DWORD toWrite,
+ ESockRequestor* reqP)
+{
+ ERL_NIF_TERM completionStatus, completionInfo;
+ DWORD written;
+
+ ESOCK_ASSERT( DEMONP("esaio_completion_send_completed - sender",
+ env, descP, &reqP->mon) == 0);
+
+ /* Success, but we need to check how much we actually got.
+ * Also the 'flags' (which we currentöy ignore)
+ *
+ * CompletionStatus = ok | {ok, RestData}
+ * CompletionInfo = {ConnRef, CompletionStatus}
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_send_completed ->"
+ "success - try get overlapped result\r\n") );
+
+ if (get_send_ovl_result(descP->sock, ovl, &written)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_send_completed -> overlapped result: "
+ "\r\n written: %d"
+ "\r\n buffer size: %d"
+ "\r\n", written, toWrite) );
+
+ if (written == toWrite) {
+
+ /* Sent it all => done */
+
+ completionStatus = esaio_completion_send_done(env,
+ descP, sockRef,
+ written);
+
+ } else {
+
+ /* Only send part of the data =>
+ * needs splitting and (maybe) retry (its up to the caller)!
+ */
+
+ completionStatus = esaio_completion_send_partial(env,
+ descP,
+ sockRef,
+ written);
+ }
+
+ } else {
+
+ int save_errno = sock_errno();
+
+ /* Now what?
+ * We know we wrote "something" but we cannot figure out
+ * how much...
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_send_completed -> "
+ "overlapped result failure: %d\r\n", save_errno) );
+
+ completionStatus =
+ esaio_completion_get_ovl_result_fail(env, descP, save_errno);
+ }
+
+ completionInfo = MKT2(env, CP_TERM(env, sendRef), completionStatus);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_send_completed -> "
+ "send completion message to %T with"
+ "\r\n CompletionInfo: %T"
+ "\r\n", MKPID(env, sender), completionInfo) );
+
+ /* Send a 'send' completion message */
+ esaio_send_completion_msg(env, // Send env
+ descP, // Descriptor
+ sender, // Msg destination
+ opEnv, // Msg env
+ sockRef, // Dest socket
+ completionInfo); // Info
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_send_completed -> done\r\n") );
+}
+
+
+
+/* *** esaio_completion_send_done ***
+ *
+ * A complete write (the entire buffer was sent).
+ *
+ */
+static
+ERL_NIF_TERM esaio_completion_send_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ DWORD written)
+{
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_pkg, &descP->writePkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_byte, &descP->writeByteCnt, written);
+
+ if (written > descP->writePkgMax)
+ descP->writePkgMax = written;
+
+ return esock_atom_ok;
+}
+
+
+
+/* *** esaio_completion_send_partial ***
+ *
+ * A partial send, that is only part of the buffer was used.
+ *
+ */
+static
+ERL_NIF_TERM esaio_completion_send_partial(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ DWORD written)
+{
+ if (written > 0) {
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_pkg, &descP->writePkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_byte, &descP->writeByteCnt, written);
+
+ if (written > descP->writePkgMax)
+ descP->writePkgMax = written;
+
+ }
+
+ return esock_make_ok2(env, MKI64(env, written));
+
+}
+
+
+
+/* *** esaio_completion_send_not_active ***
+ * A send request has completed but the request is no longer valid.
+ */
+static
+void esaio_completion_send_not_active(ESockDescriptor* descP)
+{
+ /* This send request is *not* "active"!
+ * The send (send, sendto, sendmsg) operation
+ * has been (most likely) cancelled => cleanup.
+ * If the op failed, its safe to assume that the error is
+ * the result of the cancellation, and we do not actually
+ * need to do anything here.
+ * If however, the send succeeded, we need to do "some
+ * cleanup".
+ * But what can we do here?
+ * Send an abort message to the sender or/and owner?
+ * Increment a counter (unexpected acceptssends)?
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_send_not_active(%d) -> "
+ "success for not active send request\r\n",
+ descP->sock) );
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.unexpectedWrites, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_send_not_active(%d) -> done\r\n",
+ descP->sock) );
+
+}
+
+
+
+/* *** esaio_completion_send_fail ***
+ * Unknown operation failure.
+ */
+static
+void esaio_completion_send_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform)
+{
+ esaio_completion_fail(env, descP, "send", error, inform);
+}
+
+
+
+/* *** esaio_completion_sendto ***
+ *
+ * Handle a completed 'sendto' (completion) request.
+ * Send a 'completion' message (to requestor) with the request status.
+ *
+ * Completion message:
+ * {'socket tag', socket(), completion, CompletionInfo}
+ *
+ * CompletionInfo: {CompletionHandle, CompletionStatus}
+ * CompletionHandle: reference()
+ * Result: ok | {error, Reason}
+ *
+ *
+ * There is a possibillity of a race here. That is, if the user
+ * calls socket:sendto(Socket, ..., nowait), the send is scheduled,
+ * and then just as it has completed, but before this
+ * thread has been activated to handle the 'send completed'
+ * the user calls socket:close(Socket) (or exits).
+ * Then when this function is called, the socket is closed.
+ * What to do?
+ *
+ * We need to use 'WSAGetOverlappedResult' to actually figure out the
+ * "transfer result" (how much was sent).
+ */
+static
+BOOLEAN_T esaio_completion_sendto(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataSendTo* opDataP,
+ int error)
+{
+ ErlNifEnv* env = dataP->env;
+ ESockRequestor req;
+ ERL_NIF_TERM reason;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_sendto(%d) -> entry"
+ "\r\n error: %T"
+ "\r\n", descP->sock, ENO2T(env, error)) );
+
+ switch (error) {
+ case NO_ERROR:
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_sendto(%d) -> no error"
+ "\r\n", descP->sock) );
+ MLOCK(descP->writeMtx);
+ if (esock_writer_get(env, descP,
+ &opDataP->sendRef,
+ opCaller,
+ &req)) {
+ if (IS_OPEN(descP->writeState)) {
+ esaio_completion_send_completed(env, descP,
+ ovl,
+ opEnv,
+ opCaller,
+ opDataP->sockRef,
+ opDataP->sendRef,
+ opDataP->wbuf.len,
+ &req);
+ } else {
+ /* A completed (active) request for a socket that is not open.
+ * Is this even possible?
+ * A race (completed just as the socket was closed).
+ */
+ esaio_completion_send_not_active(descP);
+ }
+
+ } else {
+ /* Request was actually completed directly
+ * (and was therefor not put into the "queue")
+ * => Nothing to do here, other than cleanup (see below).
+ */
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->writersQ.first == NULL) {
+ descP->writeState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ MUNLOCK(descP->writeMtx);
+ break;
+
+ case WSA_OPERATION_ABORTED:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_sendto(%d) -> operation aborted"
+ "\r\n", descP->sock) );
+ /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */
+ MLOCK(descP->readMtx);
+ MLOCK(descP->writeMtx);
+ /* The only thing *we* do that could cause an abort is the
+ * 'CancelIoEx' call, which we do when closing the socket
+ * (or cancel a request).
+ * But if we have done that;
+ * - Socket state will not be 'open' and
+ * - we have also set closer (pid and ref).
+ */
+
+ if (esock_writer_get(env, descP,
+ &opDataP->sendRef,
+ opCaller,
+ &req)) {
+
+ reason = esock_atom_closed,
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+
+ /* The socket not being open (assumed closing),
+ * means we are in the closing phase...
+ */
+ if (! IS_OPEN(descP->writeState)) {
+
+ /* We can only send the 'close' message to the closer
+ * when all requests has been processed!
+ */
+
+ /* Check "our" queue */
+ if (descP->writersQ.first == NULL) {
+
+ /* Check "other" queue(s) and if there is a closer pid */
+ if ((descP->readersQ.first == NULL) &&
+ (descP->acceptorsQ.first == NULL)) {
+
+ esaio_stop(env, descP);
+
+ }
+ }
+ }
+
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->writersQ.first == NULL) {
+ descP->writeState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
+ break;
+
+ default:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_sendto(%d) -> operation unknown failure"
+ "\r\n", descP->sock) );
+ MLOCK(descP->writeMtx);
+ /* We do not know what this is
+ * but we can "assume" that the request failed so we need to
+ * remove it from the "queue" if its still there...
+ * And cleanup...
+ */
+ if (esock_writer_get(env, descP,
+ &opDataP->sendRef,
+ opCaller,
+ &req)) {
+
+ reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ ENO2T(env, error));
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+ esaio_completion_sendto_fail(env, descP, error, FALSE);
+
+ } else {
+ esaio_completion_sendto_fail(env, descP, error, TRUE);
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->writersQ.first == NULL) {
+ descP->writeState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ MUNLOCK(descP->writeMtx);
+ break;
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_sendto(%d) -> cleanup\r\n",
+ descP->sock) );
+
+ FREE( opDataP->wbuf.buf );
+
+ /* No need for this "stuff" anymore */
+ esock_clear_env("esaio_completion_sendto - op cleanup", opEnv);
+ esock_free_env("esaio_completion_sendto - op cleanup", opEnv);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_sendto(%d) -> done\r\n",
+ descP->sock) );
+
+ return FALSE;
+}
+
+
+
+/* *** esaio_completion_sendto_fail ***
+ * Unknown operation failure.
+ */
+static
+void esaio_completion_sendto_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform)
+{
+ esaio_completion_fail(env, descP, "sendto", error, inform);
+}
+
+
+
+/* *** esaio_completion_sendmsg ***
+ *
+ * Handle a completed 'sendmsg' (completion) request.
+ * Send a 'completion' message (to requestor) with the request status.
+ *
+ * Completion message:
+ * {'socket tag', socket(), completion, CompletionInfo}
+ *
+ * CompletionInfo: {CompletionHandle, CompletionStatus}
+ * CompletionHandle: reference()
+ * Result: ok | {error, Reason}
+ *
+ *
+ * There is a possibillity of a race here. That is, if the user
+ * calls socket:sendto(Socket, ..., nowait), the send is scheduled,
+ * and then just as it has completed, but before this
+ * thread has been activated to handle the 'send completed'
+ * the user calls socket:close(Socket) (or exits).
+ * Then when this function is called, the socket is closed.
+ * What to do?
+ *
+ * We need to use 'WSAGetOverlappedResult' to actually figure out the
+ * "transfer result" (how much was sent).
+ */
+static
+BOOLEAN_T esaio_completion_sendmsg(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataSendMsg* opDataP,
+ int error)
+{
+ ErlNifEnv* env = dataP->env;
+ ESockRequestor req;
+ ERL_NIF_TERM reason;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_sendmsg(%d) -> entry with"
+ "\r\n error: %T"
+ "\r\n", descP->sock, ENO2T(env, error)) );
+
+ switch (error) {
+ case NO_ERROR:
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_sendmsg(%d) -> no error"
+ "\r\n", descP->sock) );
+ MLOCK(descP->writeMtx);
+ if (esock_writer_get(env, descP,
+ &opDataP->sendRef,
+ opCaller,
+ &req)) {
+ if (IS_OPEN(descP->writeState)) {
+
+ DWORD toWrite = 0;
+
+ /* Calculate how much data *in total*
+ * we was supposed to write */
+ for (int i = 0; i < opDataP->iovec->iovcnt; i++) {
+ toWrite += opDataP->iovec->iov[i].iov_len;
+ }
+
+ esaio_completion_send_completed(env, descP,
+ ovl,
+ opEnv,
+ opCaller,
+ opDataP->sockRef,
+ opDataP->sendRef,
+ toWrite,
+ &req);
+
+ } else {
+ /* A completed (active) request for a socket that is not open.
+ * Is this even possible?
+ * A race (completed just as the socket was closed).
+ */
+ esaio_completion_send_not_active(descP);
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->writersQ.first == NULL) {
+ descP->writeState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ } else {
+ /* Request was actually completed directly
+ * (and was therefor not put into the "queue")
+ * => Nothing to do here, other than cleanup (see below).
+ */
+ }
+ MUNLOCK(descP->writeMtx);
+ break;
+
+ case WSA_OPERATION_ABORTED:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_sendmsg(%d) -> operation aborted"
+ "\r\n", descP->sock) );
+ /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */
+ MLOCK(descP->readMtx);
+ MLOCK(descP->writeMtx);
+ /* The only thing *we* do that could cause an abort is the
+ * 'CancelIoEx' call, which we do when closing the socket
+ * (or cancel a request).
+ * But if we have done that;
+ * - Socket state will not be 'open' and
+ * - we have also set closer (pid and ref).
+ */
+
+ if (esock_writer_get(env, descP,
+ &opDataP->sendRef,
+ opCaller,
+ &req)) {
+
+ reason = esock_atom_closed,
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+
+ /* The socket not being open (assumed closing),
+ * means we are in the closing phase...
+ */
+ if (! IS_OPEN(descP->writeState)) {
+
+ /* We can only send the 'close' message to the closer
+ * when all requests has been processed!
+ */
+
+ /* Check "our" queue */
+ if (descP->writersQ.first == NULL) {
+
+ /* Check "other" queue(s) and if there is a closer pid */
+ if ((descP->readersQ.first == NULL) &&
+ (descP->acceptorsQ.first == NULL)) {
+
+ esaio_stop(env, descP);
+
+ }
+ }
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->writersQ.first == NULL) {
+ descP->writeState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ }
+ MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
+ break;
+
+ default:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_sendmsg(%d) -> operation unknown failure"
+ "\r\n", descP->sock) );
+ MLOCK(descP->writeMtx);
+ /* We do not know what this is
+ * but we can "assume" that the request failed so we need to
+ * remove it from the "queue" if its still there...
+ * And cleanup...
+ */
+ if (esock_writer_get(env, descP,
+ &opDataP->sendRef,
+ opCaller,
+ &req)) {
+
+ reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ ENO2T(env, error));
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+ esaio_completion_sendmsg_fail(env, descP, error, FALSE);
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->writersQ.first == NULL) {
+ descP->writeState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ } else {
+ esaio_completion_sendmsg_fail(env, descP, error, TRUE);
+ }
+ MUNLOCK(descP->writeMtx);
+ break;
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_sendmsg(%d) -> cleanup\r\n", descP->sock) );
+
+ /* "Manually" allocated buffers */
+ FREE( opDataP->msg.lpBuffers );
+ if (opDataP->ctrlBuf != NULL)
+ FREE( opDataP->ctrlBuf );
+
+ /* No need for this "stuff" anymore */
+ esock_clear_env("esaio_completion_sendmsg - op cleanup", opEnv);
+ esock_free_env("esaio_completion_sendmsg - op cleanup", opEnv);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_sendmsg(%d) -> done\r\n",
+ descP->sock) );
+
+ return FALSE;
+
+}
+
+
+
+/* *** esaio_completion_sendmsg_fail ***
+ * Unknown operation failure.
+ */
+static
+void esaio_completion_sendmsg_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform)
+{
+ esaio_completion_fail(env, descP, "sendmsg", error, inform);
+}
+
+
+
+/* === receive 'stuff' === */
+
+/* *** esaio_completion_recv ***
+ *
+ * Handle a completed 'recv' (completion) request.
+ * Send a 'completion' message (to requestor) with the request status.
+ *
+ * Completion message:
+ * {'socket tag', socket(), completion, CompletionInfo}
+ *
+ * CompletionInfo: {CompletionHandle, CompletionStatus}
+ * CompletionHandle: reference()
+ * Result: ok | {error, Reason}
+ *
+ *
+ * There is a possibillity of a race here. That is, if the user
+ * calls socket:recv(Socket, ..., nowait), the recv is scheduled,
+ * and then just as it has completed, but before this
+ * thread has been activated to handle the 'recv completed'
+ * the user calls socket:close(Socket) (or exits).
+ * Then when this function is called, the socket is closed.
+ * What to do?
+ */
+static
+BOOLEAN_T esaio_completion_recv(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecv* opDataP,
+ int error)
+{
+ ErlNifEnv* env = dataP->env;
+ ESockRequestor req;
+ ERL_NIF_TERM reason;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recv(%d) -> entry with"
+ "\r\n error: %s (%d)"
+ "\r\n", descP->sock, erl_errno_id(error), error) );
+
+ switch (error) {
+ case NO_ERROR:
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recv(%d) -> no error"
+ "\r\n", descP->sock) );
+ MLOCK(descP->readMtx);
+ if (esock_reader_get(env, descP,
+ &opDataP->recvRef,
+ opCaller,
+ &req)) {
+ if (IS_OPEN(descP->readState)) {
+ esaio_completion_recv_completed(env, descP,
+ ovl,
+ opEnv, opCaller, opDataP,
+ &req);
+ } else {
+ /* A completed (active) request for a socket that is not open.
+ * Is this even possible?
+ * A race (completed just as the socket was closed).
+ */
+ esaio_completion_recv_not_active(descP);
+ FREE_BIN( &opDataP->buf );
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->readersQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ } else {
+ /* Request was actually completed directly
+ * (and was therefor not put into the "queue")
+ * => Nothing to do here, other than cleanup (see below).
+ * => But we do not free the "buffer" since it was "used up"
+ * when we (as assumed) got the result (directly)...
+ */
+ }
+ MUNLOCK(descP->readMtx);
+ break;
+
+ case WSA_OPERATION_ABORTED:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv(%d) -> operation aborted"
+ "\r\n", descP->sock) );
+ /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */
+ MLOCK(descP->readMtx);
+ MLOCK(descP->writeMtx);
+ /* The only thing *we* do that could cause an abort is the
+ * 'CancelIoEx' call, which we do when closing the socket
+ * (or cancel a request).
+ * But if we have done that;
+ * - Socket state will not be 'open' and
+ * - we have also set closer (pid and ref).
+ */
+
+ if (esock_reader_get(env, descP,
+ &opDataP->recvRef,
+ opCaller,
+ &req)) {
+
+ reason = esock_atom_closed,
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+
+ /* The socket not being open (assumed closing),
+ * means we are in the closing phase...
+ */
+ if (! IS_OPEN(descP->readState)) {
+
+ /* We can only send the 'close' message to the closer
+ * when all requests has been processed!
+ */
+
+ /* Check "our" queue */
+ if (descP->readersQ.first == NULL) {
+
+ /* Check "other" queue(s) and if there is a closer pid */
+ if ((descP->writersQ.first == NULL) &&
+ (descP->acceptorsQ.first == NULL)) {
+
+ esaio_stop(env, descP);
+
+ }
+ }
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->readersQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+ }
+ FREE_BIN( &opDataP->buf );
+ MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
+ break;
+
+ default:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv(%d) -> operation unknown failure"
+ "\r\n", descP->sock) );
+ MLOCK(descP->readMtx);
+ /* We do not know what this is
+ * but we can "assume" that the request failed so we need to
+ * remove it from the "queue" if its still there...
+ * And cleanup...
+ */
+ if (esock_reader_get(env, descP,
+ &opDataP->recvRef,
+ opCaller,
+ &req)) {
+ /* Figure out the reason */
+ reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ ENO2T(env, error));
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+ esaio_completion_recv_fail(env, descP, error, FALSE);
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->readersQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ } else {
+ esaio_completion_recv_fail(env, descP, error, TRUE);
+ }
+ FREE_BIN( &opDataP->buf );
+ MUNLOCK(descP->readMtx);
+ break;
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv {%d} -> clear and delete op env\r\n",
+ descP->sock) );
+
+ /* No need for this "stuff" anymore */
+ esock_clear_env("esaio_completion_recv - op cleanup", opEnv);
+ esock_free_env("esaio_completion_recv - op cleanup", opEnv);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recv {%d} -> done\r\n",
+ descP->sock) );
+
+ return FALSE;
+}
+
+
+
+/* *** esaio_completion_recv_completed ***
+ * The recv request has completed.
+ */
+static
+void esaio_completion_recv_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecv* opDataP,
+ ESockRequestor* reqP)
+{
+ ERL_NIF_TERM completionStatus, completionInfo;
+ DWORD read, flags;
+
+ ESOCK_ASSERT( DEMONP("esaio_completion_recv_completed - sender",
+ env, descP, &reqP->mon) == 0);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_completed ->"
+ "success - try get overlapped result\r\n") );
+
+ if (get_recv_ovl_result(descP->sock, ovl, &read, &flags)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_completed -> overlapped result: "
+ "\r\n read: %d"
+ "\r\n buffer size: %d"
+ "\r\n flags: %d"
+ "\r\n", read, opDataP->buf.size, flags) );
+
+ /* *** Success! ***
+ * CompletionStatus = {ok, {Flags, Bin}} (should be)
+ * CompletionInfo = {ConnRef, CompletionStatus}
+ */
+
+ if ((read == 0) && (descP->type == SOCK_STREAM)) {
+
+ /*
+ * When a stream socket peer has performed an orderly
+ * shutdown, the return value will be 0 (the traditional
+ * "end-of-file" return).
+ *
+ * *We* do never actually try to read 0 bytes!
+ */
+
+ ESOCK_CNT_INC(env, descP, opDataP->sockRef,
+ esock_atom_read_fails, &descP->readFails, 1);
+
+ completionStatus = esock_make_error(opEnv, esock_atom_closed);
+
+ } else {
+
+ if (read == opDataP->buf.size) {
+ /* We filled the buffer => done */
+
+ completionStatus =
+ esaio_completion_recv_done(env, descP,
+ opEnv, opDataP,
+ flags);
+
+ } else {
+
+ /* Only used a part of the buffer =>
+ * needs splitting and (maybe) retry (its up to the caller)!
+ */
+
+ completionStatus =
+ esaio_completion_recv_partial(env, descP,
+ opEnv, opDataP,
+ reqP, read, flags);
+ }
+
+ }
+
+ } else {
+
+ int save_errno = sock_errno();
+
+ /* Now what?
+ * We know we read "something" but we cannot figure out
+ * how much...
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_completed -> "
+ "overlapped result failure: %d\r\n", save_errno) );
+
+ completionStatus =
+ esaio_completion_get_ovl_result_fail(opEnv, descP, save_errno);
+ }
+
+ completionInfo = MKT2(opEnv, opDataP->recvRef, completionStatus);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_completed -> "
+ "send completion message to %T with"
+ "\r\n CompletionInfo: %T"
+ "\r\n", MKPID(env, opCaller), completionInfo) );
+
+ /* Send a 'send' completion message */
+ esaio_send_completion_msg(env, // Send env
+ descP, // Descriptor
+ opCaller, // Msg destination
+ opEnv, // Msg env
+ opDataP->sockRef, // Socket
+ completionInfo); // Info
+
+ /* *** Finalize *** */
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recv_completed -> finalize\r\n") );
+
+ /* Request cleanup (demonitor already done above) */
+ esock_clear_env("esaio_completion_recv_completed -> req cleanup",
+ reqP->env);
+ esock_free_env("esaio_completion_recv_completed -> req cleanup",
+ reqP->env);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recv_completed -> done\r\n") );
+}
+
+
+
+/* *** esaio_completion_recv_done ***
+ *
+ * A complete read (filled the provided buffer).
+ *
+ */
+static
+ERL_NIF_TERM esaio_completion_recv_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecv* opDataP,
+ DWORD flags)
+{
+ ERL_NIF_TERM data;
+ ERL_NIF_TERM sockRef = opDataP->sockRef;
+ ERL_NIF_TERM recvRef = opDataP->recvRef;
+ DWORD read = opDataP->buf.size;
+
+ (void) flags;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_done(%T) {%d} -> entry with"
+ "\r\n recvRef: %T"
+ "\r\n flags: 0x%X"
+ "\r\n", sockRef, descP->sock, recvRef, flags) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ if (read > descP->readPkgMax)
+ descP->readPkgMax = read;
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ data = MKBIN(opEnv, &opDataP->buf);
+
+ /* We ignore the flags *for now*.
+ * Needs to be passed up eventually!
+ *
+ * This should eventually be something like:
+ *
+ * {ok, {Flags, Bin}}
+ *
+ * But for now we skip the 'flags' part:
+ *
+ * {ok, Bin}
+ */
+ return esock_make_ok2(opEnv, data);
+}
+
+
+/* *** esaio_completion_recv_partial ***
+ *
+ * A partial read, that is only part of the buffer was used.
+ *
+ */
+static
+ERL_NIF_TERM esaio_completion_recv_partial(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecv* opDataP,
+ ESockRequestor* reqP,
+ DWORD read,
+ DWORD flags)
+{
+ ERL_NIF_TERM res;
+ ERL_NIF_TERM sockRef = opDataP->sockRef;
+ ERL_NIF_TERM recvRef = opDataP->recvRef;
+ DWORD toRead = opDataP->toRead;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_partial(%T) {%d} -> entry with"
+ "\r\n toRead: %ld"
+ "\r\n recvRef: %T"
+ "\r\n read: %ld"
+ "\r\n flags: 0x%X"
+ "\r\n", sockRef, descP->sock,
+ (long) toRead, recvRef, (long) read, flags) );
+
+ /* We only got part of what we wanted, but since we let the user
+ * (or possibly the read loop in socket) decide what to do,
+ * do we actually need this check? Why not just call a
+ * 'esaio_completion_recv_partial' function (that splits the
+ * binary and deliver what we got) and let the user sort it out?
+ */
+
+ if ((toRead == 0) ||
+ (descP->type != SOCK_STREAM)) {
+
+ /* +++ We only got a partial, ***
+ * *** but we should not wait for more. +++
+ * +++ Must split it into a sub-binary. +++
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_partial(%T) {%d} -> done reading\r\n",
+ sockRef, descP->sock) );
+
+ res = esaio_completion_recv_partial_done(env, descP,
+ opEnv, opDataP,
+ read, flags);
+
+ } else {
+
+ /* A stream socket with specified read size
+ * and not a polling read, we got a partial read
+ * - return a result to initiate a retry
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_partial(%T) {%d} ->"
+ " only part of data - expected more"
+ "\r\n", sockRef, descP->sock) );
+
+ res = esaio_completion_recv_partial_part(env, descP,
+ opEnv, opDataP,
+ read, flags);
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_partial(%T) {%d} -> done\r\n",
+ sockRef, descP->sock) );
+
+ return res;
+}
+
+
+
+/* *** esaio_completion_recv_partial_done ***
+ *
+ * A successful but only partial recv, which fulfilled the required read.
+ */
+
+static
+ERL_NIF_TERM esaio_completion_recv_partial_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecv* opDataP,
+ ssize_t read,
+ DWORD flags)
+{
+ ERL_NIF_TERM sockRef = opDataP->sockRef;
+ ERL_NIF_TERM data;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ if (read > descP->readPkgMax)
+ descP->readPkgMax = read;
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ data = MKBIN(opEnv, &opDataP->buf);
+ data = MKSBIN(opEnv, data, 0, read);
+
+ (void) flags;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_partial_done(%T) {%d} -> done\r\n",
+ sockRef, descP->sock) );
+
+ return esock_make_ok2(opEnv, data);
+}
+
+
+
+/* *** esaio_completion_recv_partial_part ***
+ *
+ * A successful but only partial recv, which only partly fulfilled
+ * the required read.
+ * We do *not* want to risk ending up in a "never ending" read loop
+ * here (by trying to read more data (and yet again getting partial)).
+ * [worst case, we could up with all our worker threads busy trying
+ * to read more data, and no one ready to respond to new requests].
+ * So we simply return what we got to the user and let the user
+ * decide what to do.
+ *
+ * What shall we send? {ok, Bin} | {more, Bin}
+ * Presumably the user knows how much to expect, so is therefor
+ * able to check:
+ *
+ * "Expected > byte_size(Bin)" -> read again
+ * "Expected =:= byte_size(Bin)" -> done
+ */
+
+static
+ERL_NIF_TERM esaio_completion_recv_partial_part(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecv* opDataP,
+ ssize_t read,
+ DWORD flags)
+{
+ /* This is just a "placeholder". Is this really all we need to do? */
+ return esaio_completion_recv_partial_done(env, descP,
+ opEnv, opDataP,
+ read, flags);
+}
+
+
+
+/* *** esaio_completion_recv_not_active ***
+ * A recv request has completed but the request is no longer valid.
+ */
+static
+void esaio_completion_recv_not_active(ESockDescriptor* descP)
+{
+ /* This receive request is *not* "active"!
+ * The receive (recv,recvfrom,recvmsg) operation
+ * has been (most likely) cancelled => cleanup.
+ * If the op failed, its safe to assume that the error is
+ * the result of the cancellation, and we do not actually
+ * need to do anything here.
+ * If however, the recv succeeded, we need to do "some
+ * cleanup".
+ * But what can we do here?
+ * Send an abort message to the reader or/and owner?
+ * Increment a counter (unexpected readsa)?
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_not_active {%d} -> "
+ "success for not active read request\r\n", descP->sock) );
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.unexpectedReads, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_not_active {%d} -> done\r\n",
+ descP->sock) );
+
+}
+
+
+
+/* *** esaio_completion_recv_closed ***
+ * A recv request has completed but the socket is closed.
+ * When the socket is closed, all outstanding requests
+ * are "flushed", so we do not actually need to "do" anything
+ * here (other then maybe count unexpected writes), maybe read bytes?.
+ */
+static
+void esaio_completion_recv_closed(ESockDescriptor* descP,
+ int error)
+{
+ if (error == NO_ERROR) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recv_closed -> "
+ "success for closed socket (%d)\r\n",
+ descP->sock) );
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.unexpectedReads, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+ }
+}
+
+
+
+/* *** esaio_completion_recv_fail ***
+ * Unknown operation failure.
+ */
+static
+void esaio_completion_recv_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform)
+{
+ esaio_completion_fail(env, descP, "recv", error, inform);
+}
+
+
+
+/* *** esaio_completion_recvfrom ***
+ *
+ * Handle a completed 'recvfrom' (completion) request.
+ * Send a 'completion' message (to requestor) with the request status.
+ *
+ * Completion message:
+ * {'socket tag', socket(), completion, CompletionInfo}
+ *
+ * CompletionInfo: {CompletionHandle, CompletionStatus}
+ * CompletionHandle: reference()
+ * Result: ok | {error, Reason}
+ *
+ *
+ * There is a possibillity of a race here. That is, if the user
+ * calls socket:recvfrom(Socket, ..., nowait), the receive is scheduled,
+ * and then just as it has completed, but before this
+ * thread has been activated to handle the 'recv completed'
+ * the user calls socket:close(Socket) (or exits).
+ * Then when this function is called, the socket is closed.
+ * What to do?
+ */
+static
+BOOLEAN_T esaio_completion_recvfrom(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecvFrom* opDataP,
+ int error)
+{
+ ErlNifEnv* env = dataP->env;
+ ESockRequestor req;
+ ERL_NIF_TERM reason;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recvfrom(%d) -> entry with"
+ "\r\n error: %T, %s (%d)"
+ "\r\n",
+ descP->sock, ENO2T(env, error),
+ erl_errno_id(error), error) );
+
+ switch (error) {
+ case NO_ERROR:
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recvfrom(%d) -> no error"
+ "\r\n", descP->sock) );
+ MLOCK(descP->readMtx);
+ if (esock_reader_get(env, descP,
+ &opDataP->recvRef,
+ opCaller,
+ &req)) {
+ if (IS_OPEN(descP->readState)) {
+ esaio_completion_recvfrom_completed(env, descP,
+ ovl, opEnv, opCaller,
+ opDataP, &req);
+ } else {
+ /* A completed (active) request for a socket that is not open.
+ * Is this even possible?
+ * A race (completed just as the socket was closed).
+ */
+ esaio_completion_recv_not_active(descP);
+ FREE_BIN( &opDataP->buf );
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->readersQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ } else {
+ /* Request was actually completed directly
+ * (and was therefor not put into the "queue")
+ * => Nothing to do here, other than cleanup (see below).
+ * => But we do not free the "buffer" since it was "used up"
+ * when we (as assumed) got the result (directly)...
+ */
+ }
+ MUNLOCK(descP->readMtx);
+ break;
+
+ case WSA_OPERATION_ABORTED:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom(%d) -> operation aborted"
+ "\r\n", descP->sock) );
+ /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */
+ MLOCK(descP->readMtx);
+ MLOCK(descP->writeMtx);
+ /* The only thing *we* do that could cause an abort is the
+ * 'CancelIoEx' call, which we do when closing the socket
+ * (or cancel a request).
+ * But if we have done that;
+ * - Socket state will not be 'open' and
+ * - we have also set closer (pid and ref).
+ */
+
+ if (esock_reader_get(env, descP,
+ &opDataP->recvRef,
+ opCaller,
+ &req)) {
+
+ reason = esock_atom_closed,
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+
+ /* The socket not being open (assumed closing),
+ * means we are in the closing phase...
+ */
+ if (! IS_OPEN(descP->readState)) {
+
+ /* We can only send the 'close' message to the closer
+ * when all requests has been processed!
+ */
+
+ /* Check "our" queue */
+ if (descP->readersQ.first == NULL) {
+
+ /* Check "other" queue(s) and if there is a closer pid */
+ if ((descP->writersQ.first == NULL) &&
+ (descP->acceptorsQ.first == NULL)) {
+
+ esaio_stop(env, descP);
+
+ }
+ }
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->readersQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ }
+ FREE_BIN( &opDataP->buf );
+ MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
+ break;
+
+ default:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom(%d) -> operation unknown failure"
+ "\r\n", descP->sock) );
+ MLOCK(descP->readMtx);
+ /* We do not know what this is
+ * but we can "assume" that the request failed so we need to
+ * remove it from the "queue" if its still there...
+ * And cleanup...
+ */
+ if (esock_reader_get(env, descP,
+ &opDataP->recvRef,
+ opCaller,
+ &req)) {
+
+ reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ ENO2T(env, error));
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+ esaio_completion_recvfrom_fail(env, descP, error, FALSE);
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->readersQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ } else {
+ esaio_completion_recvfrom_fail(env, descP, error, TRUE);
+ }
+ FREE_BIN( &opDataP->buf );
+ MUNLOCK(descP->readMtx);
+ break;
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom {%d} -> clear and delete op env\r\n") );
+
+ /* No need for this "stuff" anymore */
+ esock_clear_env("esaio_completion_recvfrom - op cleanup", opEnv);
+ esock_free_env("esaio_completion_recvfrom - op cleanup", opEnv);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recvfrom {%d} -> done\r\n") );
+
+ return FALSE;
+}
+
+
+
+/* *** esaio_completion_recvfrom_completed ***
+ * The recvfrom request has completed.
+ */
+static
+void esaio_completion_recvfrom_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecvFrom* opDataP,
+ ESockRequestor* reqP)
+{
+ ERL_NIF_TERM completionStatus, completionInfo;
+ DWORD read, flags;
+
+ ESOCK_ASSERT( DEMONP("esaio_completion_recvfrom_completed - sender",
+ env, descP, &reqP->mon) == 0);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom_completed ->"
+ "success - try get overlapped result\r\n") );
+
+ if (get_recv_ovl_result(descP->sock, ovl, &read, &flags)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom_completed -> overlapped result: "
+ "\r\n read: %d"
+ "\r\n buffer size: %d"
+ "\r\n flags: %d"
+ "\r\n", read, opDataP->buf.size, flags) );
+
+ /* *** Success! ***
+ * CompletionStatus = {ok, {Flags, Bin}} (should be)
+ * CompletionInfo = {ConnRef, CompletionStatus}
+ */
+
+ if (read == opDataP->buf.size) {
+ /* We filled the buffer => done */
+
+ completionStatus =
+ esaio_completion_recvfrom_done(env, descP,
+ opEnv, opDataP,
+ flags);
+
+ } else {
+
+ /* Only used a part of the buffer =>
+ * needs splitting and (maybe) retry (its up to the caller)!
+ */
+
+ completionStatus =
+ esaio_completion_recvfrom_partial(env, descP,
+ opEnv, opDataP,
+ reqP, read, flags);
+ }
+
+ } else {
+
+ int save_errno = sock_errno();
+
+ /* Now what?
+ * We know we read "something" but we cannot figure out
+ * how much...
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom_completed -> "
+ "overlapped result failure: %d\r\n", save_errno) );
+
+ completionStatus =
+ esaio_completion_get_ovl_result_fail(opEnv, descP, save_errno);
+
+ FREE_BIN( &opDataP->buf );
+ }
+
+ completionInfo = MKT2(opEnv, opDataP->recvRef, completionStatus);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom_completed -> "
+ "send completion message to %T with"
+ "\r\n CompletionInfo: %T"
+ "\r\n", MKPID(env, opCaller), completionInfo) );
+
+ /* Send a 'send' completion message */
+ esaio_send_completion_msg(env, // Send env
+ descP, // Descriptor
+ opCaller, // Msg destination
+ opEnv, // Msg env
+ opDataP->sockRef, // Dest socket
+ completionInfo); // Info
+
+ /* *** Finalize *** */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom_completed -> finalize\r\n") );
+
+ /* Request cleanup (demonitor already done above) */
+ esock_clear_env("esaio_completion_recvfrom_completed -> req cleanup",
+ reqP->env);
+ esock_free_env("esaio_completion_recvfrom_completed -> req cleanup",
+ reqP->env);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recvfrom_completed -> done\r\n") );
+}
+
+
+
+/* *** esaio_completion_recvfrom_done ***
+ *
+ * A complete read (filled the provided buffer).
+ *
+ */
+static
+ERL_NIF_TERM esaio_completion_recvfrom_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecvFrom* opDataP,
+ DWORD flags)
+{
+ ERL_NIF_TERM res, data, eSockAddr;
+ ERL_NIF_TERM sockRef = opDataP->sockRef;
+ ERL_NIF_TERM recvRef = opDataP->recvRef;
+ DWORD read = opDataP->buf.size;
+
+ (void) flags;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom_done(%T) {%d} -> entry with"
+ "\r\n recvRef: %T"
+ "\r\n flags: 0x%X"
+ "\r\n", sockRef, descP->sock, recvRef, flags) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ if (read > descP->readPkgMax)
+ descP->readPkgMax = read;
+
+ esock_encode_sockaddr(opEnv,
+ &opDataP->fromAddr,
+ opDataP->addrLen,
+ &eSockAddr);
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ data = MKBIN(opEnv, &opDataP->buf);
+
+ /* We ignore the flags *for now*.
+ * Needs to be passed up eventually!
+ *
+ * This should eventually be something like:
+ *
+ * {ok, {Source, Flags, Data}}
+ *
+ * But for now we skip the 'flags' part:
+ *
+ * {ok, {Source, Data}}
+ */
+ res = esock_make_ok2(opEnv, MKT2(opEnv, eSockAddr, data));
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom_done(%T) {%d} -> done\r\n",
+ sockRef, descP->sock) );
+
+ return res;
+}
+
+
+
+/* *** esaio_completion_recvfrom_partial ***
+ *
+ * A partial read, that is only part of the buffer was used.
+ *
+ */
+static
+ERL_NIF_TERM esaio_completion_recvfrom_partial(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecvFrom* opDataP,
+ ESockRequestor* reqP,
+ DWORD read,
+ DWORD flags)
+{
+ ERL_NIF_TERM res, data, eSockAddr;
+ ERL_NIF_TERM sockRef = opDataP->sockRef;
+ ERL_NIF_TERM recvRef = opDataP->recvRef;
+
+ (void) flags;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom_partial(%T) {%d} -> entry with"
+ "\r\n recvRef: %T"
+ "\r\n read: %ld"
+ "\r\n flags: 0x%X"
+ "\r\n", sockRef, descP->sock, recvRef, (long) read, flags) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ if (read > descP->readPkgMax)
+ descP->readPkgMax = read;
+
+ esock_encode_sockaddr(opEnv,
+ &opDataP->fromAddr,
+ opDataP->addrLen,
+ &eSockAddr);
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ data = MKBIN(opEnv, &opDataP->buf);
+ data = MKSBIN(opEnv, data, 0, read);
+
+ /* We ignore the flags *for now*.
+ * Needs to be passed up eventually!
+ *
+ * This should eventually be something like:
+ *
+ * {ok, {Source, Flags, Data}}
+ *
+ * But for now we skip the 'flags' part:
+ *
+ * {ok, {Source, Data}}
+ */
+
+ res = esock_make_ok2(opEnv, MKT2(opEnv, eSockAddr, data));
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvfrom_partial(%T) {%d} -> done\r\n",
+ sockRef, descP->sock) );
+
+ return res;
+}
+
+
+
+/* *** esaio_completion_recvfrom_fail ***
+ * Unknown operation failure.
+ */
+static
+void esaio_completion_recvfrom_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform)
+{
+ esaio_completion_fail(env, descP, "recvfrom", error, inform);
+}
+
+
+
+/* *** esaio_completion_recvmsg ***
+ *
+ * Handle a completed 'recvmsg' (completion) request.
+ * Send a 'completion' message (to requestor) with the request status.
+ *
+ * Completion message:
+ * {'socket tag', socket(), completion, CompletionInfo}
+ *
+ * CompletionInfo: {CompletionHandle, CompletionStatus}
+ * CompletionHandle: reference()
+ * Result: ok | {error, Reason}
+ *
+ *
+ * There is a possibillity of a race here. That is, if the user
+ * calls socket:recvmsg(Socket, ..., nowait), the receive is scheduled,
+ * and then just as it has completed, but before this
+ * thread has been activated to handle the 'recv completed'
+ * the user calls socket:close(Socket) (or exits).
+ * Then when this function is called, the socket is closed.
+ * What to do?
+ */
+static
+BOOLEAN_T esaio_completion_recvmsg(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecvMsg* opDataP,
+ int error)
+{
+ ErlNifEnv* env = dataP->env;
+ ESockRequestor req;
+ ERL_NIF_TERM reason;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recvmsg(%d) -> entry with"
+ "\r\n error: %T"
+ "\r\n", descP->sock, ENO2T(env, error)) );
+
+ switch (error) {
+ case NO_ERROR:
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recvmsg(%d) -> no error:"
+ "\r\n try get request %T from %T"
+ "\r\n",
+ descP->sock,
+ opDataP->recvRef, MKPID(env, opCaller)) );
+ MLOCK(descP->readMtx);
+ if (esock_reader_get(env, descP,
+ &opDataP->recvRef,
+ opCaller,
+ &req)) {
+ if (IS_OPEN(descP->readState)) {
+ esaio_completion_recvmsg_completed(env, descP,
+ ovl, opEnv, opCaller,
+ opDataP,
+ &req);
+ } else {
+ /* A completed (active) request for a socket that is not open.
+ * Is this even possible?
+ * A race (completed just as the socket was closed).
+ */
+ esaio_completion_recv_not_active(descP);
+ FREE_BIN( &opDataP->data[0] );
+ FREE_BIN( &opDataP->ctrl );
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->readersQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ } else {
+ /* Request was actually completed directly
+ * (and was therefor not put into the "queue")
+ * => Nothing to do here, other than cleanup (see below).
+ * => But we do not free the "buffer" since it was "used up"
+ * when we (as assumed) got the result (directly)...
+ */
+ }
+ MUNLOCK(descP->readMtx);
+ break;
+
+ case WSA_OPERATION_ABORTED:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg(%d) -> operation aborted"
+ "\r\n", descP->sock) );
+ /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */
+ MLOCK(descP->readMtx);
+ MLOCK(descP->writeMtx);
+ /* The only thing *we* do that could cause an abort is the
+ * 'CancelIoEx' call, which we do when closing the socket
+ * (or cancel a request).
+ * But if we have done that;
+ * - Socket state will not be 'open' and
+ * - we have also set closer (pid and ref).
+ */
+
+ if (esock_reader_get(env, descP,
+ &opDataP->recvRef,
+ opCaller,
+ &req)) {
+
+ reason = esock_atom_closed,
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+
+ /* The socket not being open (assumed closing),
+ * means we are in the closing phase...
+ */
+ if (! IS_OPEN(descP->readState)) {
+
+ /* We can only send the 'close' message to the closer
+ * when all requests has been processed!
+ */
+
+ /* Check "our" queue */
+ if (descP->readersQ.first == NULL) {
+
+ /* Check "other" queue(s) and if there is a closer pid */
+ if ((descP->writersQ.first == NULL) &&
+ (descP->acceptorsQ.first == NULL)) {
+
+ esaio_stop(env, descP);
+
+ }
+ }
+ }
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->readersQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ }
+ FREE_BIN( &opDataP->data[0] );
+ FREE_BIN( &opDataP->ctrl );
+ MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
+ break;
+
+ default:
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg(%d) -> operation unknown failure"
+ "\r\n", descP->sock) );
+ MLOCK(descP->readMtx);
+ /* We do not know what this is
+ * but we can "assume" that the request failed so we need to
+ * remove it from the "queue" if its still there...
+ * And cleanup...
+ */
+ if (esock_reader_get(env, descP,
+ &opDataP->recvRef,
+ opCaller,
+ &req)) {
+
+ reason = MKT2(env,
+ esock_atom_get_overlapped_result,
+ ENO2T(env, error));
+
+ /* Inform the user waiting for a reply */
+ esock_send_abort_msg(env, descP, opDataP->sockRef,
+ &req, reason);
+ esaio_completion_recvmsg_fail(env, descP, error, FALSE);
+
+ /* *Maybe* update socket (write) state
+ * (depends on if the queue is now empty)
+ */
+ if (descP->readersQ.first == NULL) {
+ descP->readState &= ~ESOCK_STATE_SELECTED;
+ }
+
+ } else {
+ esaio_completion_recvmsg_fail(env, descP, error, TRUE);
+ }
+ FREE_BIN( &opDataP->data[0] );
+ FREE_BIN( &opDataP->ctrl );
+ MUNLOCK(descP->readMtx);
+ break;
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg {%d} -> clear and delete op env\r\n",
+ descP->sock) );
+
+ /* No need for this "stuff" anymore */
+ esock_clear_env("esaio_completion_recvmsg - op cleanup", opEnv);
+ esock_free_env("esaio_completion_recvmsg - op cleanup", opEnv);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recvmsg {%d} -> done\r\n",
+ descP->sock) );
+
+ return FALSE;
+}
+
+
+
+/* *** esaio_completion_recvmsg_completed ***
+ * The recvmsg request has completed.
+ */
+static
+void esaio_completion_recvmsg_completed(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ ErlNifEnv* opEnv,
+ ErlNifPid* opCaller,
+ ESAIOOpDataRecvMsg* opDataP,
+ ESockRequestor* reqP)
+{
+ ERL_NIF_TERM completionStatus, completionInfo;
+ DWORD read, flags;
+
+ ESOCK_ASSERT( DEMONP("esaio_completion_recvmsg_completed - sender",
+ env, descP, &reqP->mon) == 0);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg_completed ->"
+ "success - try get overlapped result\r\n") );
+
+ if (get_recv_ovl_result(descP->sock, ovl, &read, &flags)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg_completed -> overlapped result: "
+ "\r\n read: %d"
+ "\r\n buffer size: %d"
+ "\r\n flags: %d"
+ "\r\n", read, opDataP->data[0].size, flags) );
+
+ /* *** Success! ***
+ * CompletionStatus = {ok, Msg} (should be)
+ * CompletionInfo = {ConnRef, CompletionStatus}
+ */
+
+ /* We should have "calculated" the entire size before,
+ * but since we have a vector of size one...
+ */
+ if (read == opDataP->data[0].size) {
+ /* We filled the buffer => done */
+
+ completionStatus =
+ esaio_completion_recvmsg_done(env, descP,
+ opEnv, opDataP,
+ flags);
+
+ } else {
+
+ /* Only used a part of the buffer =>
+ * needs splitting and (maybe) retry (its up to the caller)!
+ */
+
+ completionStatus =
+ esaio_completion_recvmsg_partial(env, descP,
+ opEnv, opDataP,
+ reqP, read, flags);
+ }
+
+ } else {
+
+ int save_errno = sock_errno();
+
+ /* Now what?
+ * We know we read "something" but we cannot figure out
+ * how much...
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg_completed -> "
+ "overlapped result failure: %d\r\n", save_errno) );
+
+ completionStatus =
+ esaio_completion_get_ovl_result_fail(opEnv, descP, save_errno);
+
+ }
+
+ completionInfo = MKT2(opEnv, opDataP->recvRef, completionStatus);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg_completed -> "
+ "send completion message to %T with"
+ "\r\n CompletionInfo: %T"
+ "\r\n", MKPID(env, opCaller), completionInfo) );
+
+ /* Send a 'send' completion message */
+ esaio_send_completion_msg(env, // Send env
+ descP, // Descriptor
+ opCaller, // Msg destination
+ opEnv, // Msg env
+ opDataP->sockRef, // Dest socket
+ completionInfo); // Info
+
+ /* *** Finalize *** */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg_completed -> finalize\r\n") );
+
+ /* Request cleanup (demonitor already done above) */
+ esock_clear_env("esaio_completion_recvmsg_completed -> req cleanup",
+ reqP->env);
+ esock_free_env("esaio_completion_recvmsg_completed -> req cleanup",
+ reqP->env);
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_completion_recvmsg_completed -> done\r\n") );
+}
+
+
+
+/* *** esaio_completion_recvmsg_done ***
+ *
+ * A complete read (filled the provided buffer).
+ *
+ */
+static
+ERL_NIF_TERM esaio_completion_recvmsg_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecvMsg* opDataP,
+ DWORD flags)
+{
+ ERL_NIF_TERM res, eMsg;
+ ERL_NIF_TERM sockRef = opDataP->sockRef;
+ ERL_NIF_TERM recvRef = opDataP->recvRef;
+ DWORD read = opDataP->data[0].size;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg_done(%T) {%d} -> entry with"
+ "\r\n recvRef: %T"
+ "\r\n flags: 0x%X"
+ "\r\n", sockRef, descP->sock, recvRef, flags) );
+
+ (void) flags;
+
+ encode_msg(opEnv, descP, read,
+ &opDataP->msg,
+ opDataP->data,
+ &opDataP->ctrl,
+ &eMsg);
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ if (read > descP->readPkgMax)
+ descP->readPkgMax = read;
+
+ res = esock_make_ok2(opEnv, eMsg);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg_done(%T) {%d} -> done\r\n",
+ sockRef, descP->sock) );
+
+ return res;
+}
+
+
+
+/* *** esaio_completion_recvmsg_partial ***
+ *
+ * A partial read, that is only part of the buffer was used.
+ *
+ */
+static
+ERL_NIF_TERM esaio_completion_recvmsg_partial(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifEnv* opEnv,
+ ESAIOOpDataRecvMsg* opDataP,
+ ESockRequestor* reqP,
+ DWORD read,
+ DWORD flags)
+{
+ ERL_NIF_TERM res, eMsg;
+ ERL_NIF_TERM sockRef = opDataP->sockRef;
+ ERL_NIF_TERM recvRef = opDataP->recvRef;
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg_partial(%T) {%d} -> entry with"
+ "\r\n recvRef: %T"
+ "\r\n read: %ld"
+ "\r\n flags: 0x%X"
+ "\r\n", sockRef, descP->sock, recvRef, (long) read, flags) );
+
+ (void) flags;
+
+ /* This function hansles splitting the binaries */
+ encode_msg(opEnv, descP, read,
+ &opDataP->msg,
+ opDataP->data,
+ &opDataP->ctrl,
+ &eMsg);
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ if (read > descP->readPkgMax)
+ descP->readPkgMax = read;
+
+ res = esock_make_ok2(opEnv, eMsg);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_recvmsg_partial(%T) {%d} -> done\r\n",
+ sockRef, descP->sock) );
+
+ return res;
+}
+
+
+
+/* *** esaio_completion_recvmsg_fail ***
+ * Unknown operation failure.
+ */
+static
+void esaio_completion_recvmsg_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error,
+ BOOLEAN_T inform)
+{
+ esaio_completion_fail(env, descP, "recvmsg", error, inform);
+}
+
+
+
+/* *** esaio_completion_get_ovl_result_fail ***
+ * This function is called when the function 'WSAGetOverlappedResult' fails.
+ * It generates a result (returns) in the form of:
+ *
+ * {error, {get_overlapped_result, atom()}}
+ */
+static
+ERL_NIF_TERM esaio_completion_get_ovl_result_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int error)
+{
+ ERL_NIF_TERM eerrno = ENO2T(env, error);
+ ERL_NIF_TERM reason = MKT2(env, esock_atom_get_overlapped_result, eerrno);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_completion_get_ovl_result_fail{%d} -> entry with"
+ "\r\n Errno: %d (%T)"
+ "\r\n", descP->sock, error, eerrno) );
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.genErrs, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+ return esock_make_error(env, reason);
+}
+
+
+
+
+/* === Unknown command 'stuff' === */
+
+/* *** esaio_completion_unknown ***
+ * What shall we actually do here?
+ * Increment counters (bytes, number of unknown packages)?
+ * Send a messge with this info to "someone"?
+ * Write a (error) message to stdout/stderr?
+ */
+static
+BOOLEAN_T esaio_completion_unknown(ESAIOThreadData* dataP,
+ ESockDescriptor* descP,
+ OVERLAPPED* ovl,
+ DWORD numBytes,
+ int error)
+{
+ (void) dataP;
+ (void) descP;
+ (void) ovl;
+ (void) numBytes;
+ (void) error;
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.unknownCmds, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+ return FALSE;
+}
+
+
+
+/* *** esaio_completion_fail ***
+ * Unknown operation failure (not 'unknown operation' failure,
+ * but an unknown 'operation failure').
+ */
+static
+void esaio_completion_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const char* opStr,
+ int error,
+ BOOLEAN_T inform)
+{
+ if (inform)
+ esock_warning_msg("[WIN-ESAIO] Unknown (%s) operation failure: "
+ "\r\n Descriptor: %d"
+ "\r\n Errno: %T"
+ "\r\n",
+ opStr, descP->sock, ENO2T(env, error));
+
+ MLOCK(ctrl.cntMtx);
+
+ esock_cnt_inc(&ctrl.genErrs, 1);
+
+ MUNLOCK(ctrl.cntMtx);
+
+}
+
+
+
+static
+void esaio_completion_inc(ESAIOThreadData* dataP)
+{
+ if (dataP->cnt == ESAIO_THREAD_CNT_MAX) {
+ dataP->cnt = 0;
+ } else {
+ dataP->cnt++;
+ }
+}
+
+
+
+/* ====================================================================
+ *
+ * NIF (I/O backend) Resource callback functions: dtor, stop and down
+ *
+ * ====================================================================
+ */
+
+extern
+void esaio_dtor(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ ERL_NIF_TERM sockRef;
+
+ SGDBG( ("WIN-ESAIO", "esaio_dtor -> entry\r\n") );
+
+ /*
+ ESOCK_PRINTF("esaio_dtor -> entry when"
+ "\r\n is selected: %s"
+ "\r\n read state: 0x%X"
+ "\r\n is read-closed: %s"
+ "\r\n write state: 0x%X"
+ "\r\n is write-closed: %s"
+ "\r\n sock: %d"
+ "\r\n",
+ B2S(IS_SELECTED(descP)),
+ descP->readState,
+ B2S(IS_CLOSED(descP->readState)),
+ descP->writeState,
+ B2S(IS_CLOSED(descP->writeState)),
+ descP->sock);
+ */
+
+ if (IS_SELECTED(descP)) {
+ /* We have used the socket in the "I/O Completion Port" machinery,
+ * so we must have closed it properly to get here
+ */
+ if (! IS_CLOSED(descP->readState) )
+ esock_warning_msg("Socket Read State not CLOSED at dtor\r\n");
+
+ if (! IS_CLOSED(descP->writeState) )
+ esock_warning_msg("Socket Write State not CLOSED at dtor\r\n");
+
+ if ( descP->sock != INVALID_SOCKET )
+ esock_warning_msg("Socket %d still valid\r\n", descP->sock);
+
+ ESOCK_ASSERT( IS_CLOSED(descP->readState) );
+ ESOCK_ASSERT( IS_CLOSED(descP->writeState) );
+ ESOCK_ASSERT( descP->sock == INVALID_SOCKET );
+
+ } else {
+ /* The socket is only opened, should be safe to close nonblocking */
+ (void) sock_close(descP->sock);
+ descP->sock = INVALID_SOCKET;
+ }
+
+ SGDBG( ("WIN-ESAIO", "esaio_dtor -> set state and pattern\r\n") );
+ descP->readState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED);
+ descP->writeState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED);
+ descP->pattern = (ESOCK_DESC_PATTERN_DTOR | ESOCK_STATE_CLOSED);
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_dtor -> try free readers request queue\r\n") );
+ esock_free_request_queue(&descP->readersQ);
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_dtor -> try free writers request queue\r\n") );
+ esock_free_request_queue(&descP->writersQ);
+
+ SGDBG( ("WIN-ESAIO",
+ "esaio_dtor -> try free acceptors request queue\r\n") );
+ esock_free_request_queue(&descP->acceptorsQ);
+
+ esock_free_env("esaio_dtor close env", descP->closeEnv);
+ descP->closeEnv = NULL;
+
+ esock_free_env("esaio_dtor meta env", descP->meta.env);
+ descP->meta.env = NULL;
+
+ SGDBG( ("WIN-ESAIO", "esaio_dtor -> done\r\n") );
+}
+
+
+
+extern
+void esaio_stop(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_stop(%d) -> entry\r\n", descP->sock) );
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Inform waiting Closer, or close socket
+ *
+ * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ */
+
+ if (! IS_PID_UNDEF(&descP->closerPid)) {
+ /* We have a waiting closer process after nif_close()
+ * - send message to trigger nif_finalize_close()
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_stop(%d) -> send close msg to %T\r\n",
+ descP->sock, MKPID(env, &descP->closerPid)) );
+
+ esock_send_close_msg(env, descP, &descP->closerPid);
+ /* Message send frees closeEnv */
+ descP->closeEnv = NULL;
+ descP->closeRef = esock_atom_undefined;
+
+ } else {
+ int err;
+
+ /* We do not have a closer process
+ * - have to do an unclean (non blocking) close */
+
+ err = esock_close_socket(env, descP, FALSE);
+
+ if (err != 0)
+ esock_warning_msg("[WIN-ESAIO] Failed closing socket without "
+ "closer process: "
+ "\r\n Controlling Process: %T"
+ "\r\n socket fd: %d"
+ "\r\n Errno: %T"
+ "\r\n",
+ descP->ctrlPid, descP->sock, ENO2T(env, err));
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_stop(%d) -> done\r\n", descP->sock) );
+
+}
+
+
+
+
+/* A 'down' has occured.
+ * Check the possible processes we monitor in turn:
+ * closer, controlling process (owner), connector, reader, acceptor and writer.
+ *
+ */
+extern
+void esaio_down(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down {%d} -> entry with:"
+ "\r\n Pid: %T"
+ "\r\n Mon: %T"
+ "\r\n", descP->sock, MKPID(env, pidP), MON2T(env, monP)) );
+
+ if (COMPARE_PIDS(&descP->closerPid, pidP) == 0) {
+
+ /* The closer process went down
+ * - it will not call nif_finalize_close
+ */
+
+ enif_set_pid_undefined(&descP->closerPid);
+
+ if (MON_EQ(&descP->closerMon, monP)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down {%d} -> closer process exit\r\n",
+ descP->sock) );
+
+ MON_INIT(&descP->closerMon);
+
+ } else {
+ // The owner is the closer so we used its monitor
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down {%d} -> closer controlling process exit\r\n",
+ descP->sock) );
+
+ ESOCK_ASSERT( MON_EQ(&descP->ctrlMon, monP) );
+ MON_INIT(&descP->ctrlMon);
+ enif_set_pid_undefined(&descP->ctrlPid);
+
+ }
+
+ /* Since the closer went down there was one,
+ * hence esock_close() must have run or scheduled esock_stop(),
+ * or the socket has never been "selected" upon.
+ */
+
+ if (descP->closeEnv == NULL) {
+ int err;
+
+ /* Since there is no closeEnv,
+ * esock_close() did not schedule esock_stop()
+ * and is about to call esock_finalize_close() but died,
+ * or esock_stop() has run, sent close_msg to the closer
+ * and cleared ->closeEnv but the closer died
+ * - we have to do an unclean (non blocking) socket close here
+ */
+
+ err = esock_close_socket(env, descP, FALSE);
+ if (err != 0)
+ esock_warning_msg("[WIN-ESAIO] "
+ "Failed closing socket for terminating "
+ "closer process: "
+ "\r\n Closer Process: %T"
+ "\r\n Descriptor: %d"
+ "\r\n Errno: %d (%T)"
+ "\r\n",
+ MKPID(env, pidP), descP->sock,
+ err, ENO2T(env, err));
+ } else {
+ /* Since there is a closeEnv esock_stop() has not run yet
+ * - when it finds that there is no closer process
+ * it will close the socket and ignore the close_msg
+ *
+ * The 'stop' callback function will never be triggered on
+ * Windows...It may be explicitly called...
+ */
+ esock_clear_env("esaio_down - close-env", descP->closeEnv);
+ esock_free_env("esaio_down - close-env", descP->closeEnv);
+ descP->closeEnv = NULL;
+ descP->closeRef = esock_atom_undefined;
+ }
+
+ } else if (MON_EQ(&descP->ctrlMon, monP)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down {%d} -> controller process exit\r\n",
+ descP->sock) );
+
+ MON_INIT(&descP->ctrlMon);
+ /* The owner went down */
+ enif_set_pid_undefined(&descP->ctrlPid);
+
+ if (IS_OPEN(descP->readState)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down {%d} -> OPEN => initiate close\r\n",
+ descP->sock) );
+
+ esaio_down_ctrl(env, descP, pidP);
+
+ descP->readState |= ESOCK_STATE_CLOSING;
+ descP->writeState |= ESOCK_STATE_CLOSING;
+
+ } else {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down {%d} -> already closed or closing\r\n",
+ descP->sock) );
+
+ }
+
+ } else if (descP->connectorP != NULL &&
+ MON_EQ(&descP->connector.mon, monP)) {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down {%d} -> connector process exit\r\n",
+ descP->sock) );
+
+ MON_INIT(&descP->connector.mon);
+
+ /* connectorP is only set during connection.
+ * Forget all about the ongoing connection.
+ * We might end up connected, but the process that initiated
+ * the connection has died and will never know
+ */
+
+ esock_requestor_release("esaio_down->connector",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+ descP->writeState &= ~ESOCK_STATE_CONNECTING;
+
+ } else {
+ ERL_NIF_TERM sockRef = enif_make_resource(env, descP);
+
+ /* check all operation queue(s): acceptor, writer and reader.
+ *
+ * Is it really any point in doing this if the socket is closed?
+ *
+ */
+
+ if (IS_CLOSED(descP->readState)) {
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down(%T) {%d} -> stray down: %T\r\n",
+ sockRef, descP->sock, pidP) );
+ } else {
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down(%T) {%d} -> "
+ "other process - check readers, writers and acceptors\r\n",
+ sockRef, descP->sock) );
+
+ if (descP->readersQ.first != NULL)
+ esaio_down_reader(env, descP, sockRef, pidP, monP);
+ if (descP->acceptorsQ.first != NULL)
+ esaio_down_acceptor(env, descP, sockRef, pidP, monP);
+ if (descP->writersQ.first != NULL)
+ esaio_down_writer(env, descP, sockRef, pidP, monP);
+ }
+ }
+
+ SSDBG( descP, ("WIN-ESAIO", "esaio_down {%d} -> done\r\n", descP->sock) );
+
+}
+
+
+
+/* *** esaio_down_ctrl ***
+ *
+ * Stop after a downed controller (controlling process = owner process)
+ *
+ * This is 'extern' because its currently called from prim_socket_nif
+ * (esock_setopt_otp_ctrl_proc).
+ */
+extern
+void esaio_down_ctrl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP)
+{
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_down_ctrl {%d} -> entry with"
+ "\r\n Pid: %T"
+ "\r\n", descP->sock, MKPID(env, pidP)) );
+
+ if (do_stop(env, descP)) {
+ /* esock_stop() is scheduled
+ * - it has to close the socket
+ */
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_down_ctrl {%d} -> stop was scheduled\r\n",
+ descP->sock) );
+ } else {
+ int err;
+
+ /* Socket has no *active* requests in the I/O Completion Ports machinery
+ * so esock_stop() will not be called
+ * - we have to do an unclean (non blocking) socket close here
+ */
+
+ err = esock_close_socket(env, descP, FALSE);
+ if (err != 0)
+ esock_warning_msg("[WIN-ESAIO] "
+ "Failed closing socket for terminating "
+ "owner process: "
+ "\r\n Owner Process: %T"
+ "\r\n Descriptor: %d"
+ "\r\n Errno: %d (%T)"
+ "\r\n",
+ MKPID(env, pidP), descP->sock,
+ err, ENO2T(env, err));
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "esaio_down_ctrl {%d} -> done\r\n", descP->sock) );
+
+}
+
+
+
+/* *** esaio_down_acceptor ***
+ *
+ * Check and then handle a downed acceptor process.
+ *
+ */
+static
+void esaio_down_acceptor(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+
+ /* Maybe unqueue one of the waiting acceptors */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down_acceptor(%T) {%d} -> "
+ "maybe unqueue a waiting acceptor\r\n",
+ sockRef, descP->sock) );
+
+ esock_acceptor_unqueue(env, descP, NULL, pidP);
+
+}
+
+
+/* *** esaio_down_writer ***
+ *
+ * Check and then handle a downed writer process.
+ *
+ */
+
+static
+void esaio_down_writer(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+
+ /* Maybe unqueue one of the waiting writer(s) */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down_writer(%T) {%d} -> maybe unqueue a waiting writer\r\n",
+ sockRef, descP->sock) );
+
+ esock_writer_unqueue(env, descP, NULL, pidP);
+
+}
+
+
+/* *** esaio_down_reader ***
+ *
+ * Check and then handle a downed reader process.
+ *
+ */
+
+static
+void esaio_down_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+ /* Maybe unqueue one of the waiting reader(s) */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_down_reader(%T) {%d} -> maybe unqueue a waiting reader\r\n",
+ sockRef, descP->sock) );
+
+ esock_reader_unqueue(env, descP, NULL, pidP);
+
+}
+
+
+/* ==================================================================== *
+ * *
+ * Send Utility functions *
+ * *
+ * ==================================================================== *
+ */
+
+/* *** send_check_result ***
+ *
+ * Check the result of a socket send (WSASend, WSASendTo and WSASendMsg) call.
+ *
+ */
+static
+ERL_NIF_TERM send_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ int send_result,
+ ssize_t dataSize,
+ BOOLEAN_T dataInTail,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ BOOLEAN_T* cleanup)
+{
+ ERL_NIF_TERM res;
+ BOOLEAN_T send_error;
+ int err;
+
+ if (send_result == 0) {
+
+ /* Send success already!
+ * So, no need to store the data (request, in the "queue").
+ * Note that the completion threads will use the
+ * precense or absence of this request 'record' to inform
+ * its actions.
+ */
+
+ *cleanup = FALSE;
+
+ res = send_check_ok(env, descP, dataSize, sockRef);
+
+ } else {
+
+ /* send returned error, check which */
+
+ int save_errno = sock_errno();
+
+ /* There are basically two kinds of errors:
+ * 1) Pending:
+ * An overlapped operation was successfully initiated.
+ * Completion will be "indicated" at a later time.
+ * 2) An actual error
+ */
+
+ if (save_errno == WSA_IO_PENDING) {
+
+ /* We need to store the data in the queue! */
+
+ *cleanup = FALSE;
+
+ res = send_check_pending(env, descP, opP, caller, sockRef, sendRef);
+
+ } else {
+
+ *cleanup = TRUE;
+
+ res = send_check_fail(env, descP, save_errno, sockRef);
+
+ }
+ }
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "send_check_result(%T) {%d} -> done:"
+ "\r\n res: %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+ return res;
+}
+
+
+
+/* *** send_check_ok ***
+ *
+ * Processing done upon successful send.
+ */
+static
+ERL_NIF_TERM send_check_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ DWORD written,
+ ERL_NIF_TERM sockRef)
+{
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_pkg, &descP->writePkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_byte, &descP->writeByteCnt, written);
+
+ /* We can *never* have a partial successs:
+ * Either the entire buffer is sent, or the op is scheduled or we fail.
+ * But since we have a field (writePkgMaxCnt) in the descriptor
+ * we might as well use it.
+ */
+
+ descP->writePkgMaxCnt = written;
+ if (descP->writePkgMaxCnt > descP->writePkgMax)
+ descP->writePkgMax = descP->writePkgMaxCnt;
+ descP->writePkgMaxCnt = 0;
+
+ SSDBG( descP,
+ ("WIN-ESAIO", "send_check_ok(%T) {%d} -> %ld written - done\r\n",
+ sockRef, descP->sock, written) );
+
+ return esock_atom_ok;
+}
+
+
+/* *** send_check_pending ***
+ *
+ * The send operation was scheduled, that is, its now in the handls
+ * of the I/O Completion Port framework.
+ */
+static
+ERL_NIF_TERM send_check_pending(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESAIOOperation* opP,
+ ErlNifPid caller,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef)
+{
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "send_check_pending(%T, %d) -> entry with"
+ "\r\n sendRef: %T"
+ "\r\n", sockRef, descP->sock, sendRef) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_waits, &descP->writeWaits, 1);
+
+ descP->writeState |= ESOCK_STATE_SELECTED;
+
+ esock_writer_push(env, descP, caller, sendRef, opP);
+
+ return esock_atom_completion;
+
+}
+
+
+
+/* *** send_check_fail ***
+ *
+ * Processing done upon failed send.
+ * An actual failure.
+ */
+static
+ERL_NIF_TERM send_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM reason;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_fails, &descP->writeFails, 1);
+
+ reason = ENO2T(env, saveErrno);
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "send_check_fail(%T, %d) -> error: "
+ "\r\n %d (%T)\r\n",
+ sockRef, descP->sock, saveErrno, reason) );
+
+ return esock_make_error(env, reason);
+}
+
+
+
+/* ==================================================================== *
+ * *
+ * Utility functions *
+ * *
+ * ==================================================================== *
+ */
+
+/* *** get_send_ovl_result ***
+ *
+ * Used for all send 'overlapped' operations; send, sendto and sendmsg.
+ */
+static
+BOOL get_send_ovl_result(SOCKET sock,
+ OVERLAPPED* ovl,
+ DWORD* written)
+{
+ DWORD flags = 0;
+ BOOL result = get_ovl_result(sock, ovl, written, &flags);
+
+ (void) flags;
+
+ return result;
+}
+
+
+/* *** get_recv_ovl_result ***
+ *
+ * Used for recv *and* recvfrom 'overlapped' operations.
+ */
+static
+BOOL get_recv_ovl_result(SOCKET sock,
+ OVERLAPPED* ovl,
+ DWORD* read,
+ DWORD* flags)
+{
+ return get_ovl_result(sock, ovl, read, flags);
+}
+
+
+/* *** get_recvmsg_ovl_result ***
+ *
+ * Used for all recvmsg 'overlapped' operations.
+ */
+static
+BOOL get_recvmsg_ovl_result(SOCKET sock,
+ OVERLAPPED* ovl,
+ DWORD* read)
+{
+ DWORD flags = 0;
+ BOOL result = get_ovl_result(sock, ovl, read, &flags);
+
+ (void) flags;
+
+ return result;
+}
+
+
+/* *** get_ovl_result ***
+ *
+ * Simple wrapper function for WSAGetOverlappedResult.
+ */
+static
+BOOL get_ovl_result(SOCKET sock,
+ OVERLAPPED* ovl,
+ DWORD* transfer,
+ DWORD* flags)
+{
+ return WSAGetOverlappedResult(sock, ovl, transfer, FALSE, flags);
+}
+
+
+
+/* *** esaio_add_socket ***
+ *
+ * Add socket to I/O completion port.
+ */
+static
+int esaio_add_socket(ESockDescriptor* descP)
+{
+ int res;
+ HANDLE tmp = CreateIoCompletionPort((HANDLE) descP->sock, ctrl.cport,
+ (ULONG_PTR) descP, 0);
+
+ if (tmp != NULL) {
+ res = ESAIO_OK;
+ } else {
+ res = sock_errno();
+ }
+
+ return res;
+}
+
+
+static
+void esaio_send_completion_msg(ErlNifEnv* sendEnv,
+ ESockDescriptor* descP,
+ ErlNifPid* pid,
+ ErlNifEnv* msgEnv,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM completionInfo)
+{
+ ERL_NIF_TERM msg = mk_completion_msg(msgEnv, sockRef, completionInfo);
+
+ /* This can only fail if:
+ * - The recipient is dead.
+ * Our monitor should clear this up ... eventually.
+ * Possible race?
+ * - We (the sender) are "dead" (which we are clearly not)
+ */
+ if (! esock_send_msg(sendEnv, pid, msg, NULL)) {
+
+ /*
+ ESOCK_DBG_PRINTF( TRUE, ("IN-ESAIO",
+ "esaio_send_completion_msg(%T) {%d} failed ->"
+ "\r\n pid: %T"
+ "\r\n",
+ sockRef, descP->sock, MKPID(sendEnv, pid)) );
+ */
+
+ SSDBG( descP,
+ ("WIN-ESAIO",
+ "esaio_send_completion_msg(%T) {%d} failed ->"
+ "\r\n pid: %T"
+ "\r\n",
+ sockRef, descP->sock, MKPID(sendEnv, pid)) );
+ }
+}
+
+
+/* *** mk_completion_msg ***
+ *
+ * Construct a completion (socket) message. It has the form:
+ *
+ * {'$socket', Socket, completion, CompletionInfo}
+ *
+ */
+
+static
+ERL_NIF_TERM mk_completion_msg(ErlNifEnv* env,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM info)
+{
+ return esock_mk_socket_msg(env, sockRef,
+ esock_atom_completion, info);
+}
diff --git a/erts/preloaded/ebin/prim_socket.beam b/erts/preloaded/ebin/prim_socket.beam
index b58b2f4a22..ba50f42a01 100644
--- a/erts/preloaded/ebin/prim_socket.beam
+++ b/erts/preloaded/ebin/prim_socket.beam
Binary files differ
diff --git a/erts/preloaded/src/prim_socket.erl b/erts/preloaded/src/prim_socket.erl
index 68c950331a..fd4432cbc9 100644
--- a/erts/preloaded/src/prim_socket.erl
+++ b/erts/preloaded/src/prim_socket.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2018-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -512,6 +512,8 @@ send(SockRef, Bin, EFlags, SendRef) when is_integer(EFlags) ->
{select, Written} ->
<<_:Written/binary, RestBin/binary>> = Bin,
{select, RestBin, EFlags};
+ completion ->
+ completion;
{error, _Reason} = Result ->
Result
end;
@@ -621,6 +623,7 @@ sendmsg_result(
sendmsg_result(
SockRef, RestIOV, Cont, SendRef, true,
nif_sendmsg(SockRef, EMsg, EFlags, SendRef, RestIOV));
+
select ->
if
HasWritten ->
@@ -631,6 +634,11 @@ sendmsg_result(
{select, Written} ->
RestIOV = rest_iov(Written, IOV),
{select, RestIOV, Cont};
+
+ %% Either the message was written or not. No half ways...
+ completion = C ->
+ C;
+
{error, Reason} = Error->
if
HasWritten ->