diff options
author | Raimo Niskanen <raimo@erlang.org> | 2020-11-30 12:25:01 +0100 |
---|---|---|
committer | Raimo Niskanen <raimo@erlang.org> | 2020-12-04 10:21:23 +0100 |
commit | 5dbd5c5af324926b93b6f3265213e0cb3911f69d (patch) | |
tree | 3048c539b4179a1bdc8d5796f6c5e9cc5e2298c7 | |
parent | 6f3ebb6c8a6c120f2349777cdb04106081759cb5 (diff) | |
download | erlang-5dbd5c5af324926b93b6f3265213e0cb3911f69d.tar.gz |
Optimize recv timeout 0
-rw-r--r-- | erts/emulator/nifs/common/prim_socket_nif.c | 245 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_util.c | 2 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_util.h | 2 | ||||
-rw-r--r-- | erts/preloaded/ebin/prim_socket.beam | bin | 27116 -> 27116 bytes | |||
-rw-r--r-- | erts/preloaded/src/prim_socket.erl | 18 | ||||
-rw-r--r-- | lib/kernel/doc/src/socket.xml | 174 | ||||
-rw-r--r-- | lib/kernel/src/socket.erl | 397 | ||||
-rw-r--r-- | lib/kernel/test/socket_SUITE.erl | 101 |
8 files changed, 587 insertions, 352 deletions
diff --git a/erts/emulator/nifs/common/prim_socket_nif.c b/erts/emulator/nifs/common/prim_socket_nif.c index 905dff630a..3390f61e41 100644 --- a/erts/emulator/nifs/common/prim_socket_nif.c +++ b/erts/emulator/nifs/common/prim_socket_nif.c @@ -3679,6 +3679,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') LOCAL_ATOM_DECL(write_pkg_max); \ LOCAL_ATOM_DECL(write_tries); \ LOCAL_ATOM_DECL(write_waits); \ + LOCAL_ATOM_DECL(zero); \ LOCAL_ATOM_DECL(zerocopy) /* Local error reason atoms */ @@ -3750,7 +3751,7 @@ static ESOCK_INLINE ErlNifEnv* esock_alloc_env(const char* slogan) * nif_send(Sock, SendRef, Data, Flags) * nif_sendto(Sock, SendRef, Data, Dest, Flags) * nif_sendmsg(Sock, SendRef, Msg, Flags) - * nif_recv(Sock, RecvRef, Length, Flags) + * nif_recv(Sock, Length, Flags, RecvRef) * nif_recvfrom(Sock, RecvRef, BufSz, Flags) * nif_recvmsg(Sock, RecvRef, BufSz, CtrlSz, Flags) * nif_close(Sock) @@ -4665,7 +4666,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, if (! GET_INT(env, argv[0], &fd)) { if (IS_INTEGER(env, argv[0])) - return esock_make_error_invalid_integer(env, argv[0]); + return esock_make_error_integer_range(env, argv[0]); else return enif_make_badarg(env); } @@ -4693,7 +4694,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, if (! GET_INT(env, argv[2], &proto)) { if (IS_INTEGER(env, argv[2])) - return esock_make_error_invalid_integer(env, argv[2]); + return esock_make_error_integer_range(env, argv[2]); else return enif_make_badarg(env); } @@ -5640,7 +5641,7 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, } if (! GET_INT(env, argv[1], &backlog)) { if (IS_INTEGER(env, argv[1])) - return esock_make_error_invalid_integer(env, argv[1]); + return esock_make_error_integer_range(env, argv[1]); else return enif_make_badarg(env); } @@ -6272,7 +6273,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, if (! GET_INT(env, argv[2], &flags)) { SGDBG( ("SOCKET", "nif_send -> argv decode failed\r\n") ); if (IS_INTEGER(env, argv[2])) - return esock_make_error_invalid_integer(env, argv[2]); + return esock_make_error_integer_range(env, argv[2]); else return enif_make_badarg(env); } @@ -6423,7 +6424,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, if (! GET_INT(env, argv[3], &flags)) { SGDBG( ("SOCKET", "nif_sendto -> argv decode failed\r\n") ); if (IS_INTEGER(env, argv[3])) - return esock_make_error_invalid_integer(env, argv[3]); + return esock_make_error_integer_range(env, argv[3]); else return enif_make_badarg(env); } @@ -6573,7 +6574,7 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, } if (! GET_INT(env, argv[2], &flags)) { if (IS_INTEGER(env, argv[2])) - return esock_make_error_invalid_integer(env, argv[2]); + return esock_make_error_integer_range(env, argv[2]); else return enif_make_badarg(env); } @@ -6908,10 +6909,10 @@ ERL_NIF_TERM nwritev(ErlNifEnv* env, * whatever is in the buffers (everything it got). * * Arguments: - * Socket (ref) - Points to the socket descriptor. - * RecvRef - A unique id for this (send) request. - * Length - The number of bytes to receive. - * Flags - Receive flags. + * Socket (ref) - NIF resource reference() to the socket descriptor. + * Length - The number of bytes to receive; integer(). + * Flags - Receive flags; integer(). + * RecvRef - A unique reference() id for this (send) request | 'poll' */ static @@ -6932,31 +6933,32 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, ESOCK_ASSERT( argc == 4 ); sockRef = argv[0]; // We need this in case we send abort (to the caller) - recvRef = argv[1]; + recvRef = argv[3]; if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) { return enif_make_badarg(env); } - if (! enif_is_ref(env, recvRef)) { + if ((! enif_is_ref(env, recvRef)) && + (COMPARE(recvRef, atom_zero) != 0)) { return enif_make_badarg(env); } - if ((! GET_UINT64(env, argv[2], &elen)) || - (! GET_INT(env, argv[3], &flags))) { - BOOLEAN_T argv2_is_integer = IS_INTEGER(env, argv[2]); + if ((! GET_UINT64(env, argv[1], &elen)) || + (! GET_INT(env, argv[2], &flags))) { + BOOLEAN_T argv1_is_integer = IS_INTEGER(env, argv[1]); - if ((! argv2_is_integer) || - (! IS_INTEGER(env, argv[3]))) + if ((! argv1_is_integer) || + (! IS_INTEGER(env, argv[2]))) return enif_make_badarg(env); - if (argv2_is_integer) - return esock_make_error_invalid_integer(env, argv[2]); + if (argv1_is_integer) + return esock_make_error_integer_range(env, argv[1]); return - esock_make_error_invalid_integer(env, argv[3]); + esock_make_error_integer_range(env, argv[2]); } len = (ssize_t) elen; if (elen != (ErlNifUInt64) len) - return esock_make_error_invalid_integer(env, elen); + return esock_make_error_integer_range(env, elen); MLOCK(descP->readMtx); @@ -7056,12 +7058,8 @@ ERL_NIF_TERM esock_recv(ErlNifEnv* env, "esock_recv {%d} -> read: %ld (%d)\r\n", descP->sock, (long) read, save_errno) ); - return recv_check_result(env, descP, - read, len, - save_errno, - &buf, - sockRef, - recvRef); + return recv_check_result(env, descP, read, len, save_errno, + &buf, sockRef, recvRef); } #endif // #ifndef __WIN32__ @@ -7077,10 +7075,11 @@ ERL_NIF_TERM esock_recv(ErlNifEnv* env, * buffer size for this socket (whatever has been configured). * * Arguments: - * Socket (ref) - Points to the socket descriptor. - * RecvRef - A unique id for this (send) request. - * BufSz - Size of the buffer into which we put the received message. - * Flags - Receive flags. + * Socket (ref) - NIF resource reference() to the socket descriptor. + * BufSz - integer() ize of the buffer + * into which we put the received message. + * Flags - Receive flags; integer(). + * RecvRef - A unique reference() id for this recv request. * * <KOLLA> * @@ -7111,7 +7110,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, SGDBG( ("SOCKET", "nif_recvfrom -> entry with argc: %d\r\n", argc) ); sockRef = argv[0]; // We need this in case we send abort (to the caller) - recvRef = argv[1]; + recvRef = argv[3]; if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) { return enif_make_badarg(env); @@ -7119,25 +7118,27 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, /* Extract arguments and perform preliminary validation */ - if (! enif_is_ref(env, recvRef)) + if ((! enif_is_ref(env, recvRef)) && + (COMPARE(recvRef, atom_zero) != 0)) { return enif_make_badarg(env); + } - if ((! GET_UINT64(env, argv[2], &elen)) || - (! GET_INT(env, argv[3], &flags))) { - BOOLEAN_T argv2_is_integer = IS_INTEGER(env, argv[2]); + if ((! GET_UINT64(env, argv[1], &elen)) || + (! GET_INT(env, argv[2], &flags))) { + BOOLEAN_T argv1_is_integer = IS_INTEGER(env, argv[1]); - if ((! argv2_is_integer) || - (! IS_INTEGER(env, argv[3]))) + if ((! argv1_is_integer) || + (! IS_INTEGER(env, argv[2]))) return enif_make_badarg(env); - if (argv2_is_integer) - return esock_make_error_invalid_integer(env, argv[2]); + if (argv1_is_integer) + return esock_make_error_integer_range(env, argv[1]); return - esock_make_error_invalid_integer(env, argv[3]); + esock_make_error_integer_range(env, argv[2]); } len = (ssize_t) elen; if (elen != (ErlNifUInt64) len) - return esock_make_error_invalid_integer(env, elen); + return esock_make_error_integer_range(env, elen); MLOCK(descP->readMtx); @@ -7197,7 +7198,7 @@ ERL_NIF_TERM esock_recvfrom(ErlNifEnv* env, int save_errno; ErlNifBinary buf; ERL_NIF_TERM readerCheck; - size_t bufSz = (len ? len : descP->rBufSz); + size_t bufSz = (len != 0 ? len : descP->rBufSz); SSDBG( descP, ("SOCKET", "esock_recvfrom {%d} -> entry with" "\r\n bufSz: %d" @@ -7239,13 +7240,9 @@ ERL_NIF_TERM esock_recvfrom(ErlNifEnv* env, else save_errno = 0; // The value does not actually matter in this case - return recvfrom_check_result(env, descP, - read, - save_errno, - &buf, - &fromAddr, addrLen, - sockRef, - recvRef); + return recvfrom_check_result(env, descP, read, save_errno, + &buf, &fromAddr, addrLen, + sockRef, recvRef); } #endif // #ifndef __WIN32__ @@ -7263,12 +7260,13 @@ ERL_NIF_TERM esock_recvfrom(ErlNifEnv* env, * (buffer) size is used (1024). * * Arguments: - * Socket (ref) - Points to the socket descriptor. - * RecvRef - A unique id for this (send) request. - * BufSz - Size of the buffer into which we put the received message. + * Socket (ref) - NIF resource reference() to the socket descriptor. + * BufSz - Size of the buffer into which we put the received message; + * integer(). * CtrlSz - Size of the ctrl (buffer) into which we put the received - * ancillary data. - * Flags - Receive flags. + * ancillary data; integer(). + * Flags - Receive flags; integer(). + * RecvRef - A unique reference() id for this (send) request. * * <KOLLA> * @@ -7299,7 +7297,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, SGDBG( ("SOCKET", "nif_recvmsg -> entry with argc: %d\r\n", argc) ); sockRef = argv[0]; // We need this in case we send abort (to the caller) - recvRef = argv[1]; + recvRef = argv[4]; if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) { return enif_make_badarg(env); @@ -7307,36 +7305,38 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, /* Extract arguments and perform preliminary validation */ - if (! enif_is_ref(env, recvRef)) + if ((! enif_is_ref(env, recvRef)) && + (COMPARE(recvRef, atom_zero) != 0)) { return enif_make_badarg(env); + } - if ((! GET_UINT64(env, argv[2], &eBufSz)) || - (! GET_UINT64(env, argv[3], &eCtrlSz)) || - (! GET_INT(env, argv[4], &flags))) { + if ((! GET_UINT64(env, argv[1], &eBufSz)) || + (! GET_UINT64(env, argv[2], &eCtrlSz)) || + (! GET_INT(env, argv[3], &flags))) { BOOLEAN_T - argv2_is_integer = IS_INTEGER(env, argv[2]), - argv3_is_integer; + argv1_is_integer = IS_INTEGER(env, argv[1]), + argv2_is_integer; - if ((! argv2_is_integer) || - (! (argv3_is_integer = IS_INTEGER(env, argv[3]))) || - (! IS_INTEGER(env, argv[4]))) + if ((! argv1_is_integer) || + (! (argv2_is_integer = IS_INTEGER(env, argv[2]))) || + (! IS_INTEGER(env, argv[3]))) return enif_make_badarg(env); + if (argv1_is_integer) + return esock_make_error_integer_range(env, argv[1]); if (argv2_is_integer) - return esock_make_error_invalid_integer(env, argv[2]); - if (argv3_is_integer) - return esock_make_error_invalid_integer(env, argv[3]); + return esock_make_error_integer_range(env, argv[2]); return - esock_make_error_invalid_integer(env, argv[4]); + esock_make_error_integer_range(env, argv[3]); } bufSz = (ssize_t) eBufSz; if (eBufSz != (ErlNifUInt64) bufSz) - return esock_make_error_invalid_integer(env, eBufSz); + return esock_make_error_integer_range(env, eBufSz); ctrlSz = (ssize_t) eCtrlSz; if (eCtrlSz != (ErlNifUInt64) ctrlSz) - return esock_make_error_invalid_integer(env, eCtrlSz); + return esock_make_error_integer_range(env, eCtrlSz); MLOCK(descP->readMtx); @@ -7397,8 +7397,8 @@ ERL_NIF_TERM esock_recvmsg(ErlNifEnv* env, SOCKLEN_T addrLen; ssize_t read; int save_errno; - size_t bufSz = (bufLen ? bufLen : descP->rBufSz); - size_t ctrlSz = (ctrlLen ? ctrlLen : descP->rCtrlSz); + size_t bufSz = (bufLen != 0 ? bufLen : descP->rBufSz); + size_t ctrlSz = (ctrlLen != 0 ? ctrlLen : descP->rCtrlSz); struct msghdr msgHdr; struct iovec iov[1]; // Shall we always use 1? ErlNifBinary data[1]; // Shall we always use 1? @@ -7469,14 +7469,11 @@ ERL_NIF_TERM esock_recvmsg(ErlNifEnv* env, else save_errno = 0; // The value does not actually matter in this case - return recvmsg_check_result(env, descP, - read, - save_errno, + return recvmsg_check_result(env, descP, read, save_errno, &msgHdr, data, // Needed for iov encode &ctrl, // Needed for ctrl header encode - sockRef, - recvRef); + sockRef, recvRef); } #endif // #ifndef __WIN32__ @@ -8047,7 +8044,7 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, if (! IS_INTEGER(env, argv[2])) return enif_make_badarg(env); else - return esock_make_error_invalid_integer(env, argv[2]); + return esock_make_error_integer_range(env, argv[2]); } eVal = argv[3]; @@ -8069,7 +8066,7 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, SGDBG( ("SOCKET", "nif_setopt -> failed arg check\r\n") ); if (IS_INTEGER(env, argv[1])) - return esock_make_error_invalid_integer(env, argv[1]); + return esock_make_error_integer_range(env, argv[1]); else return enif_make_badarg(env); #endif // #ifdef __WIN32__ #else @@ -9731,7 +9728,7 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, if (! IS_INTEGER(env, argv[2])) return enif_make_badarg(env); else - return esock_make_error_invalid_integer(env, argv[2]); + return esock_make_error_integer_range(env, argv[2]); } if (esock_decode_level(env, argv[1], &level)) { @@ -9750,7 +9747,7 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, SGDBG( ("SOCKET", "nif_getopt -> failed args check\r\n") ); if (IS_INTEGER(env, argv[1])) - return esock_make_error_invalid_integer(env, argv[1]); + return esock_make_error_integer_range(env, argv[1]); else return enif_make_badarg(env); @@ -11692,7 +11689,7 @@ ERL_NIF_TERM esock_cancel_recv_current(ErlNifEnv* env, ("SOCKET", "esock_cancel_recv_current(%T) {%d} -> cancel res: %T" "\r\n", sockRef, descP->sock, res) ); - if (!activate_next_reader(env, descP, sockRef)) { + if (! activate_next_reader(env, descP, sockRef)) { SSDBG( descP, ("SOCKET", "esock_cancel_recv_current(%T) {%d} -> no more readers" @@ -12220,7 +12217,9 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env, "\r\n ref: %T" "\r\n", descP->sock, ref) ); - if (!reader_search4pid(env, descP, &caller)) { + if (! reader_search4pid(env, descP, &caller)) { + if (COMPARE(ref, atom_zero) == 0) + goto done_ok; reader_push(env, descP, caller, ref); *checkResult = atom_select; } else { @@ -12237,9 +12236,9 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env, } } + done_ok: // Does not actually matter in this case, but ... *checkResult = esock_atom_ok; - return TRUE; } #endif // #ifndef __WIN32__ @@ -12306,7 +12305,7 @@ ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, DEMONP("recv_update_current_reader", env, descP, &descP->currentReader.mon); - if (!activate_next_reader(env, descP, sockRef)) { + if (! activate_next_reader(env, descP, sockRef)) { SSDBG( descP, ("SOCKET", @@ -12411,7 +12410,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, * 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 from a stream socket! + * *We* do never actually try to read 0 bytes! * * We must also notify any waiting readers! */ @@ -12488,14 +12487,15 @@ ERL_NIF_TERM recv_check_full(ErlNifEnv* env, { ERL_NIF_TERM res; - if (toRead == 0) { + if ((toRead == 0) && + (descP->type == SOCK_STREAM)) { /* +++ Give us everything you have got => * * (maybe) needs to continue +++ */ /* Send up each chunk of data for each of the read - * and let the erlang code assemble it: {ok, false, Bin} - * (when complete it should return {ok, true, Bin}). + * and let the erlang code assemble it: {more, Bin} + * (when complete it should return {ok, Bin}). * We need to read atleast one more time to be sure if its * done... * @@ -12537,9 +12537,9 @@ ERL_NIF_TERM recv_check_full(ErlNifEnv* env, /* *** recv_check_full_maybe_done *** * * Send up each chunk of data for each of the read - * and let the erlang code assemble it: {ok, false, Bin} - * (when complete it should return {ok, true, Bin}). - * We need to read atleast one more time to be sure if its + * and let the erlang code assemble it: {more, Bin} + * (when complete it should return {ok, Bin}). + * We need to read at least one more time to be sure if its * done... * * Also, we need to check if the rNumCnt has reached its max (rNum), @@ -12649,11 +12649,12 @@ ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, { ERL_NIF_TERM res; - FREE_BIN(buf1P); if (buf2P != NULL) FREE_BIN(buf2P); + FREE_BIN(buf1P); + if (buf2P != NULL) FREE_BIN(buf2P); if (saveErrno == ECONNRESET) { - /* +++ Oups - closed +++ */ + /* +++ Oops - closed +++ */ SSDBG( descP, ("SOCKET", @@ -12676,7 +12677,10 @@ ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, "\r\n recvRef: %T" "\r\n", sockRef, descP->sock, recvRef) ); - res = recv_check_retry(env, descP, sockRef, recvRef); + if (COMPARE(recvRef, atom_zero) == 0) + res = esock_atom_ok; + else + res = recv_check_retry(env, descP, sockRef, recvRef); } else { @@ -12812,7 +12816,9 @@ ERL_NIF_TERM recv_check_partial(ErlNifEnv* env, { ERL_NIF_TERM res; - if (toRead == 0) { + if ((toRead == 0) || + (descP->type != SOCK_STREAM) || + (COMPARE(recvRef, atom_zero) == 0)) { /* +++ We got it all, but since we +++ * +++ did not fill the buffer, we +++ @@ -12829,6 +12835,10 @@ ERL_NIF_TERM recv_check_partial(ErlNifEnv* env, res = recv_check_partial_done(env, descP, read, bufP, sockRef); } else { + /* A stream socket with specified read size + * and not a polling read, we got a partial read + * - return a select result to initiate a retry + */ SSDBG( descP, ("SOCKET", @@ -12967,6 +12977,31 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, "\r\n", sockRef, descP->sock, (long) read, saveErrno, recvRef) ); + /* <KOLLA> + * + * We need to handle read = 0 for non_stream socket type(s) 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, + atom_read_fails, &descP->readFails, 1); + + FREE_BIN(bufP); + + return esock_make_error(env, atom_closed); + } + if (read < 0) { /* +++ Error handling +++ */ @@ -13050,19 +13085,19 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, /* <KOLLA> * - * We need to handle read = 0 for other type(s) (DGRAM) when + * We need to handle read = 0 for non_stream socket type(s) 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 from a stream socket! + * *We* do never actually try to read 0 bytes! */ ESOCK_CNT_INC(env, descP, sockRef, @@ -13071,15 +13106,9 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); return esock_make_error(env, atom_closed); - } - /* There is a special case: If the provided 'to read' value is - * zero (0). That means that we reads as much as we can, using - * the default read buffer size. - */ - if (read < 0) { /* +++ Error handling +++ */ @@ -16849,7 +16878,7 @@ void esock_down_reader(ErlNifEnv* env, "current reader - try activate next\r\n", sockRef, descP->sock) ); - if (!activate_next_reader(env, descP, sockRef)) { + if (! activate_next_reader(env, descP, sockRef)) { SSDBG( descP, ("SOCKET", diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 6c196ef6f7..ce879dbfb3 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1774,7 +1774,7 @@ ERL_NIF_TERM esock_make_error_invalid(ErlNifEnv* env, ERL_NIF_TERM what) * ERL_NIF_TERM so all we have to do is create the tuple. */ extern -ERL_NIF_TERM esock_make_error_invalid_integer(ErlNifEnv* env, ERL_NIF_TERM i) +ERL_NIF_TERM esock_make_error_integer_range(ErlNifEnv* env, ERL_NIF_TERM i) { return esock_make_invalid(env, MKT2(env, esock_atom_integer_range, i)); diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index f20b33fe7b..3a0a84a8d8 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -241,7 +241,7 @@ ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err); extern ERL_NIF_TERM esock_make_error_invalid(ErlNifEnv* env, ERL_NIF_TERM what); extern -ERL_NIF_TERM esock_make_error_invalid_integer(ErlNifEnv* env, ERL_NIF_TERM i); +ERL_NIF_TERM esock_make_error_integer_range(ErlNifEnv* env, ERL_NIF_TERM i); extern ERL_NIF_TERM esock_make_invalid(ErlNifEnv* env, ERL_NIF_TERM reason); extern diff --git a/erts/preloaded/ebin/prim_socket.beam b/erts/preloaded/ebin/prim_socket.beam Binary files differindex c616a36d62..ecaca5322d 100644 --- a/erts/preloaded/ebin/prim_socket.beam +++ b/erts/preloaded/ebin/prim_socket.beam diff --git a/erts/preloaded/src/prim_socket.erl b/erts/preloaded/src/prim_socket.erl index f3204d09ad..7efba71fe9 100644 --- a/erts/preloaded/src/prim_socket.erl +++ b/erts/preloaded/src/prim_socket.erl @@ -633,26 +633,26 @@ invalid_iov(_, N) -> %% ---------------------------------- -recv(SockRef, RecvRef, Length, Flags) -> +recv(SockRef, Length, Flags, RecvRef) -> try enc_msg_flags(Flags) of EFlags -> - nif_recv(SockRef, RecvRef, Length, EFlags) + nif_recv(SockRef, Length, EFlags, RecvRef) catch throw : Reason -> {error, Reason} end. -recvfrom(SockRef, RecvRef, Length, Flags) -> +recvfrom(SockRef, Length, Flags, RecvRef) -> try enc_msg_flags(Flags) of EFlags -> - nif_recvfrom(SockRef, RecvRef, Length, EFlags) + nif_recvfrom(SockRef, Length, EFlags, RecvRef) catch throw : Reason -> {error, Reason} end. -recvmsg(SockRef, RecvRef, BufSz, CtrlSz, Flags) -> +recvmsg(SockRef, BufSz, CtrlSz, Flags, RecvRef) -> try enc_msg_flags(Flags) of EFlags -> - case nif_recvmsg(SockRef, RecvRef, BufSz, CtrlSz, EFlags) of + case nif_recvmsg(SockRef, BufSz, CtrlSz, EFlags, RecvRef) of {ok, #{ctrl := []}} = Result -> Result; {ok, #{ctrl := Cmsgs} = Msg} -> @@ -995,9 +995,9 @@ nif_send(_SockRef, _Bin, _Flags, _SendRef) -> erlang:nif_error(undef). nif_sendto(_SRef, _Bin, _Dest, _Flags, _SendRef) -> erlang:nif_error(undef). nif_sendmsg(_SRef, _Msg, _Flags, _SendRef, _IOV) -> erlang:nif_error(undef). -nif_recv(_SRef, _RecvRef, _Length, _Flags) -> erlang:nif_error(undef). -nif_recvfrom(_SRef, _RecvRef, _Length, _Flags) -> erlang:nif_error(undef). -nif_recvmsg(_SRef, _RecvRef, _BufSz, _CtrlSz, _Flags) -> erlang:nif_error(undef). +nif_recv(_SRef, _Length, _Flags, _RecvRef) -> erlang:nif_error(undef). +nif_recvfrom(_SRef, _Length, _Flags, _RecvRef) -> erlang:nif_error(undef). +nif_recvmsg(_SRef, _BufSz, _CtrlSz, _Flags, _RecvRef) -> erlang:nif_error(undef). nif_close(_SRef) -> erlang:nif_error(undef). nif_finalize_close(_SRef) -> erlang:nif_error(undef). diff --git a/lib/kernel/doc/src/socket.xml b/lib/kernel/doc/src/socket.xml index b8f699e047..49d043f403 100644 --- a/lib/kernel/doc/src/socket.xml +++ b/lib/kernel/doc/src/socket.xml @@ -1885,23 +1885,30 @@ <func> <name name="recv" arity="1" since="OTP 22.0" anchor="recv-infinity"/> - <name name="recv" arity="2" since="OTP 22.0"/> - <name name="recv" arity="3" clause_i="1" since="OTP 22.0"/> - <name name="recv" arity="3" clause_i="4" since="OTP 22.0"/> + <name name="recv" arity="2" clause_i="1" since="OTP @OTP-16749@"/> + <name name="recv" arity="2" clause_i="2" since="OTP 22.0"/> + <name name="recv" arity="3" clause_i="3" since="OTP @OTP-16749@"/> + <name name="recv" arity="3" clause_i="5" since="OTP 22.0"/> + <name name="recv" arity="3" clause_i="8" since="OTP 22.0"/> <name name="recv" arity="4" clause_i="3" since="OTP 22.0"/> - <fsummary>Receive data from a socket.</fsummary> + <fsummary> + Receive data from a socket, with "infinite" time-out. + </fsummary> <desc> - <p>Receive data from a socket.</p> + <p> + Receives data from a socket, waiting for it to arrive. + </p> <p> The argument <c><anno>Length</anno></c> specifies how many bytes to receive, with the special case <c>0</c> meaning "all available". </p> <p> - This call will not return until all requested - data can be delivered. - For "all available" this means the first data - chunk that arrives. + For a socket of + <seetype marker="#type">type <c>stream</c></seetype> + this call will not return until all requested + data can be delivered, or if "all available" data + was requested when the first data chunk arrives. </p> <p> The message <c>Flags</c> may be symbolic @@ -1919,39 +1926,55 @@ </func> <func> - <name name="recv" arity="3" clause_i="5" since="OTP 22.0" + <name name="recv" arity="3" clause_i="4" since="OTP @OTP-16749@" anchor="recv-timeout"/> + <name name="recv" arity="3" clause_i="9" since="OTP 22.0"/> <name name="recv" arity="4" clause_i="4" since="OTP 22.0"/> - <fsummary>Receive data from a socket.</fsummary> + <fsummary>Receive data from a socket, with time-out.</fsummary> <desc> + <p> + Receives data from a socket, waiting at most + <c><anno>Timeout</anno></c> milliseconds for it to arrive. + </p> <p> The same as - <seemfa marker="#recv/1">recv/1,2,3</seemfa> + <seeerl marker="#recv-infinity"> + infinite time-out <c>recv/1,2,3,4</c> + </seeerl> but returns <c>{error, timeout}</c> or <c>{error, {timeout, <anno>Data</anno>}}</c> after <c><anno>Timeout</anno></c> milliseconds, - if the requested data could not be delivered. + if the requested data has not been delivered. </p> </desc> </func> <func> - <name name="recv" arity="3" clause_i="2" since="OTP 22.1" + <name name="recv" arity="3" clause_i="1" since="OTP @OTP-16749@" anchor="recv-nowait"/> - <name name="recv" arity="3" clause_i="3" since="OTP @OTP-16749@"/> + <name name="recv" arity="3" clause_i="2" since="OTP @OTP-16749@"/> + <name name="recv" arity="3" clause_i="6" since="OTP 22.1"/> + <name name="recv" arity="3" clause_i="7" since="OTP @OTP-16749@"/> <name name="recv" arity="4" clause_i="1" since="OTP 22.1"/> <name name="recv" arity="4" clause_i="2" since="OTP @OTP-16749@"/> - <fsummary>Receive data from a socket.</fsummary> + <fsummary>Receive data from a socket, but do not wait.</fsummary> <desc> + <p> + Receives data from a socket, + but returns a select continuation if the data + could not be returned immediately. + </p> <p> The same as - <seemfa marker="#recv/1">recv/1,2,3</seemfa> - but if the data can not be immediately delivered, + <seeerl marker="#recv-infinity"> + infinite time-out <c>recv/1,2,3,4</c> + </seeerl> + but if the data can not be delivered immediately, the function returns - <seetype marker="#select_info"><c>{select, <anno>SelectInfo</anno>}</c></seetype>, + <seetype marker="#select_info"><c>{select, <anno>SelectInfo</anno>}</c></seetype>, and the caller will then receive a select message, - <c>{'$socket', Socket, select, SelectHandle}</c> ( - with the + <c>{'$socket', Socket, select, SelectHandle}</c> + ( with the <seetype marker="socket#select_handle"><c>SelectHandle</c></seetype> contained in the <seetype marker="#select_info"> @@ -1978,7 +2001,8 @@ </seetype> generated by the call. </p> <p> - Note that if a <c><anno>Length</anno> > 0</c> + Note that for a socket of type <c>stream</c>, + if <c><anno>Length</anno> > 0</c> and only part of that amount of data is available, the function will return <seetype marker="#select_info"> @@ -1986,7 +2010,8 @@ </seetype> with partial data. If the caller doesn't want to wait for more data, it must call - <seemfa marker="#cancel/2"><c>cancel/2</c></seemfa>. + <seemfa marker="#cancel/2"><c>cancel/2</c></seemfa> + to cancel the operation. </p> </desc> </func> @@ -1994,20 +2019,26 @@ <func> <name name="recvfrom" arity="1" since="OTP 22.0" anchor="recvfrom-infinity"/> - <name name="recvfrom" arity="2" since="OTP 22.0"/> + <name name="recvfrom" arity="2" clause_i="1" since="OTP @OTP-16749@"/> + <name name="recvfrom" arity="2" clause_i="2" since="OTP 22.0"/> <name name="recvfrom" arity="3" clause_i="3" since="OTP 22.0"/> <name name="recvfrom" arity="3" clause_i="5" since="OTP 22.0"/> <name name="recvfrom" arity="3" clause_i="8" since="OTP 22.0"/> <name name="recvfrom" arity="4" clause_i="3" since="OTP 22.0"/> - <fsummary>Receive a message from a socket.</fsummary> + <fsummary> + Receive a message from a socket, with "infinite" time-out. + </fsummary> <desc> - <p>Receive a message from a socket.</p> + <p> + Receive a message from a socket, waiting for it to arrive. + </p> <p> - The function returns when a message is received. + The function returns when a message is received, + or when there is a socket error. Argument <c><anno>BufSz</anno></c> specifies the number of bytes for the receive buffer. If the buffer size is too small, - the message will be truncated... + the message will be truncated. </p> <p> If <c><anno>BufSz</anno></c> is not specified or <c>0</c>, @@ -2022,8 +2053,7 @@ <seetype marker="#msg_flag">message flag</seetype> <c>peek</c>. When this flag is used, the message is <em>not</em> "consumed" from the underlying buffers, - so another - <seemfa marker="#recvfrom/1"><c>recvfrom/1,2,3</c></seemfa> + so another <c>recvfrom/1,2,3,4</c> call is needed, possibly with an adjusted buffer size. </p> <p> @@ -2040,14 +2070,20 @@ anchor="recvfrom-timeout"/> <name name="recvfrom" arity="3" clause_i="9" since="OTP 22.0"/> <name name="recvfrom" arity="4" clause_i="4" since="OTP 22.0"/> - <fsummary>Receive a message from a socket.</fsummary> + <fsummary>Receive a message from a socket, with time-out.</fsummary> <desc> + <p> + Receives a message from a socket, waiting at most + <c><anno>Timeout</anno></c> milliseconds for it to arrive. + </p> <p> The same as - <seemfa marker="#recvfrom/1">recvfrom/1,2,3</seemfa> + <seeerl marker="#recvfrom-infinity"> + infinite time-out <c>recvfrom/1,2,3,4</c> + </seeerl> but returns <c>{error, timeout}</c> after <c><anno>Timeout</anno></c> milliseconds, - if the requested data could not be delivered. + if no message has been delivered. </p> </desc> </func> @@ -2060,17 +2096,24 @@ <name name="recvfrom" arity="3" clause_i="7" since="OTP @OTP-16749@"/> <name name="recvfrom" arity="4" clause_i="1" since="OTP 22.1"/> <name name="recvfrom" arity="4" clause_i="2" since="OTP @OTP-16749@"/> - <fsummary>Receive a message from a socket.</fsummary> + <fsummary>Receive a message from a socket, but do not wait.</fsummary> <desc> + <p> + Receives a message from a socket, + but returns a select continuation if no message + could be returned immediately. + </p> <p> The same as - <seemfa marker="#recvfrom/1">recvfrom/1,2,3</seemfa> - but if the data can not be immediately delivered, + <seeerl marker="#recvfrom-infinity"> + infinite time-out <c>recvfrom/1,2,3,4</c> + </seeerl> + but if no message can not delivered immediately, the function returns - <seetype marker="#select_info"><c>{select, <anno>SelectInfo</anno>}</c></seetype>, + <seetype marker="#select_info"><c>{select, <anno>SelectInfo</anno>}</c></seetype>, and the caller will then receive a select message, - <c>{'$socket', Socket, select, SelectHandle}</c> ( - with the + <c>{'$socket', Socket, select, SelectHandle}</c> + ( with the <seetype marker="socket#select_handle"><c>SelectHandle</c></seetype> contained in the <seetype marker="#select_info"> @@ -2078,7 +2121,7 @@ </seetype> ) when data has arrived. A subsequent call to <c>recvfrom/1,2,3,4</c> - will then return the data. + will then return the message. </p> <p> If the time-out argument is <c>SelectHandle</c>, @@ -2106,18 +2149,24 @@ <name name="recvmsg" arity="2" clause_i="4" since="OTP 22.0"/> <name name="recvmsg" arity="3" clause_i="3" since="OTP 22.0"/> <name name="recvmsg" arity="3" clause_i="5" since="OTP 22.0"/> + <name name="recvmsg" arity="4" clause_i="3" since="OTP @OTP-16749@"/> <name name="recvmsg" arity="5" clause_i="3" since="OTP 22.0"/> - <fsummary>Receive a message from a socket.</fsummary> + <fsummary> + Receive a message from a socket, with "infinite" time-out. + </fsummary> <desc> - <p>Receive a message from a socket.</p> + <p> + Receive a message from a socket, waiting for it to arrive. + </p> <p> - The function returns when a message is received. + The function returns when a message is received, + or when there is a socket error. Arguments <c><anno>BufSz</anno></c> and <c><anno>CtrlSz</anno></c> specifies the number of bytes for the receive buffer and the control message buffer. If the buffer size(s) is(are) too small, - the message and/or control message list will be truncated... + the message and/or control message list will be truncated. </p> <p> If <c><anno>BufSz</anno></c> is not specified or <c>0</c>, @@ -2137,8 +2186,7 @@ <seetype marker="#msg_flag">message flag</seetype> <c>peek</c>. When this flag is used, the message is <em>not</em> "consumed" from the underlying buffers, - so another - <seemfa marker="#recvmsg/1"><c>recvfrom/1,2,3,5</c></seemfa> + so another <c>recvfrom/1,2,3,4,5</c> call is needed, possibly with an adjusted buffer size. </p> <p> @@ -2154,15 +2202,20 @@ <name name="recvmsg" arity="2" clause_i="5" since="OTP 22.0" anchor="recvmsg-timeout"/> <name name="recvmsg" arity="3" clause_i="4" since="OTP 22.0"/> + <name name="recvmsg" arity="4" clause_i="4" since="OTP @OTP-16749@"/> <name name="recvmsg" arity="5" clause_i="4" since="OTP 22.0"/> - <fsummary>Receive a message from a socket.</fsummary> + <fsummary>Receive a message from a socket, with time-out.</fsummary> <desc> + <p> + Receives a message from a socket, waiting at most + <c><anno>Timeout</anno></c> milliseconds for it to arrive. + </p> <p> The same as - <seemfa marker="#recvmsg/1">recvmsg/1,2,3</seemfa> + <seeerl marker="#recvmsg-infinity">recvmsg/1,2,3,4,5</seeerl> but returns <c>{error, timeout}</c> after <c><anno>Timeout</anno></c> milliseconds, - if the requested data could not be delivered. + if no message has been delivered. </p> </desc> </func> @@ -2173,26 +2226,35 @@ <name name="recvmsg" arity="2" clause_i="3" since="OTP @OTP-16749@"/> <name name="recvmsg" arity="3" clause_i="1" since="OTP 22.1"/> <name name="recvmsg" arity="3" clause_i="2" since="OTP @OTP-16749@"/> + <name name="recvmsg" arity="4" clause_i="1" since="OTP @OTP-16749@"/> + <name name="recvmsg" arity="4" clause_i="2" since="OTP @OTP-16749@"/> <name name="recvmsg" arity="5" clause_i="1" since="OTP 22.1"/> <name name="recvmsg" arity="5" clause_i="2" since="OTP @OTP-16749@"/> - <fsummary>Receive a message from a socket.</fsummary> + <fsummary>Receive a message from a socket, but do not wait.</fsummary> <desc> + <p> + Receives a message from a socket, + but returns a select continuation if no message + could be returned immediately. + </p> <p> The same as - <seemfa marker="#recvmsg/1">recvmsg/1,2,3</seemfa> - but if the data can not be immediately delivered, + <seeerl marker="#recvfrom-infinity"> + infinite time-out <c>recvfrom/1,2,3,4</c> + </seeerl> + but if no message can not delivered immediately, the function returns - <seetype marker="#select_info"><c>{select, <anno>SelectInfo</anno>}</c></seetype>, + <seetype marker="#select_info"><c>{select, <anno>SelectInfo</anno>}</c></seetype>, and the caller will then receive a select message, - <c>{'$socket', Socket, select, SelectHandle}</c> ( - with the + <c>{'$socket', Socket, select, SelectHandle}</c> + ( with the <seetype marker="socket#select_handle"><c>SelectHandle</c></seetype> contained in the <seetype marker="#select_info"> <c><anno>SelectInfo</anno></c> </seetype> ) when data has arrived. - A subsequent call to <c>recvmsg/1,2,3,5</c> + A subsequent call to <c>recvmsg/1,2,3,4,5</c> will then return the data. </p> <p> diff --git a/lib/kernel/src/socket.erl b/lib/kernel/src/socket.erl index 0788af62f5..489b1578cd 100644 --- a/lib/kernel/src/socket.erl +++ b/lib/kernel/src/socket.erl @@ -46,7 +46,7 @@ recv/1, recv/2, recv/3, recv/4, recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4, - recvmsg/1, recvmsg/2, recvmsg/3, recvmsg/5, + recvmsg/1, recvmsg/2, recvmsg/3, recvmsg/4, recvmsg/5, close/1, shutdown/2, @@ -2223,148 +2223,186 @@ sendmsg_deadline_cont(SockRef, Data, Cont, Deadline, HasWritten) -> -spec recv(Socket) -> {'ok', Data} | - {'error', Reason} when + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Data :: binary(), - Reason :: - posix() | 'closed' | invalid() | - {posix() | 'closed' | invalid(), Data :: binary()}. + Reason :: posix() | 'closed' | invalid(). recv(Socket) -> - recv(Socket, 0). + recv(Socket, 0, ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT). --spec recv(Socket, Length) -> +-spec recv(Socket, Flags) -> {'ok', Data} | - {'error', Reason} when + {'error', Reason} | + {'error', {Reason, Data}} when + Socket :: socket(), + Flags :: [msg_flag() | integer()], + Data :: binary(), + Reason :: posix() | 'closed' | invalid(); + + (Socket, Length) -> + {'ok', Data} | + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Length :: non_neg_integer(), Data :: binary(), - Reason :: - posix() | 'closed' | invalid() | - {posix() | 'closed' | invalid(), Data :: binary()}. + Reason :: posix() | 'closed' | invalid(). +recv(Socket, Flags) when is_list(Flags) -> + recv(Socket, 0, Flags, ?ESOCK_RECV_TIMEOUT_DEFAULT); recv(Socket, Length) -> - recv(Socket, Length, - ?ESOCK_RECV_FLAGS_DEFAULT, - ?ESOCK_RECV_TIMEOUT_DEFAULT). + recv( + Socket, Length, + ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT). + +-spec recv(Socket, Flags, SelectHandle :: 'nowait') -> + {'ok', Data} | + {'ok', {Data, SelectInfo}} | + {'select', SelectInfo} | + {'error', Reason} | + {'error', {Reason, Data}} when + Socket :: socket(), + Flags :: [msg_flag() | integer()], + Data :: binary(), + SelectInfo :: select_info(), + Reason :: posix() | 'closed' | invalid(); --spec recv(Socket, Length, Flags) -> + (Socket, Flags, SelectHandle :: select_handle()) -> {'ok', Data} | - {'error', Reason} when + {'ok', {Data, SelectInfo}} | + {'select', SelectInfo} | + {'error', Reason} | + {'error', {Reason, Data}} when + Socket :: socket(), + Flags :: [msg_flag() | integer()], + Data :: binary(), + SelectInfo :: select_info(), + Reason :: posix() | 'closed' | invalid(); + + (Socket, Flags, Timeout :: 'infinity') -> + {'ok', Data} | + {'error', Reason} | + {'error', {Reason, Data}} when + Socket :: socket(), + Flags :: [msg_flag() | integer()], + Data :: binary(), + Reason :: posix() | 'closed' | invalid(); + + (Socket, Flags, Timeout :: non_neg_integer()) -> + {'ok', Data} | + {'error', Reason} | + {'error', {Reason, Data}} when + Socket :: socket(), + Flags :: [msg_flag() | integer()], + Data :: binary(), + Reason :: posix() | 'closed' | invalid() | 'timeout'; + + (Socket, Length, Flags) -> + {'ok', Data} | + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Length :: non_neg_integer(), Flags :: [msg_flag() | integer()], Data :: binary(), - Reason :: - posix() | 'closed' | invalid() | - {posix() | 'closed' | invalid(), Data :: binary()}; + Reason :: posix() | 'closed' | invalid(); - (Socket, Length, 'nowait') -> + (Socket, Length, SelectHandle :: 'nowait') -> {'ok', Data} | {'ok', {Data, SelectInfo}} | {'select', SelectInfo} | - {'error', Reason} when + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Length :: non_neg_integer(), Data :: binary(), SelectInfo :: select_info(), - Reason :: - posix() | 'closed' | invalid() | - {posix() | 'closed' | invalid(), Data :: binary()}; + Reason :: posix() | 'closed' | invalid(); - (Socket, Length, SelectHandle) -> + (Socket, Length, SelectHandle :: select_handle()) -> {'ok', Data} | {'ok', {Data, SelectInfo}} | {'select', SelectInfo} | - {'error', Reason} when + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Length :: non_neg_integer(), Data :: binary(), SelectInfo :: select_info(), - SelectHandle :: select_handle(), - Reason :: - posix() | 'closed' | invalid() | - {posix() | 'closed' | invalid(), Data :: binary()}; + Reason :: posix() | 'closed' | invalid(); - (Socket, Length, Timeout) -> + (Socket, Length, Timeout :: 'infinity') -> {'ok', Data} | - {'error', Reason} when + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Length :: non_neg_integer(), - Timeout :: 'infinity', Data :: binary(), - Reason :: - posix() | 'closed' | invalid() | - {posix() | 'closed' | invalid(), Data :: binary()}; + Reason :: posix() | 'closed' | invalid(); - (Socket, Length, Timeout) -> + (Socket, Length, Timeout :: non_neg_integer()) -> {'ok', Data} | - {'error', Reason} when + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Length :: non_neg_integer(), - Timeout :: non_neg_integer(), Data :: binary(), - Reason :: - posix() | 'closed' | invalid() | 'timeout' | - {posix() | 'closed' | invalid() | 'timeout', Data :: binary()}. + Reason :: posix() | 'closed' | invalid() | 'timeout'. +recv(Socket, Flags, Timeout) when is_list(Flags) -> + recv(Socket, 0, Flags, Timeout); recv(Socket, Length, Flags) when is_list(Flags) -> recv(Socket, Length, Flags, ?ESOCK_RECV_TIMEOUT_DEFAULT); recv(Socket, Length, Timeout) -> recv(Socket, Length, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout). --spec recv(Socket, Length, Flags, 'nowait') -> +-spec recv(Socket, Length, Flags, SelectHandle :: 'nowait') -> {'ok', Data} | {'ok', {Data, SelectInfo}} | {'select', SelectInfo} | - {'error', Reason} when + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Length :: non_neg_integer(), Flags :: [msg_flag() | integer()], Data :: binary(), SelectInfo :: select_info(), - Reason :: - posix() | 'closed' | invalid() | - {posix() | 'closed' | invalid(), Data :: binary()}; + Reason :: posix() | 'closed' | invalid(); - (Socket, Length, Flags, SelectHandle) -> + (Socket, Length, Flags, SelectHandle :: select_handle()) -> {'ok', Data} | {'ok', {Data, SelectInfo}} | {'select', SelectInfo} | - {'error', Reason} when + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Length :: non_neg_integer(), Flags :: [msg_flag() | integer()], Data :: binary(), SelectInfo :: select_info(), - SelectHandle :: select_handle(), - Reason :: - posix() | 'closed' | invalid() | - {posix() | 'closed' | invalid(), Data :: binary()}; + Reason :: posix() | 'closed' | invalid(); - (Socket, Length, Flags, Timeout) -> + (Socket, Length, Flags, Timeout :: 'infinity') -> {'ok', Data} | - {'error', Reason} when + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Length :: non_neg_integer(), Flags :: [msg_flag() | integer()], - Timeout :: 'infinity', Data :: binary(), - Reason :: - posix() | 'closed' | invalid() | - {posix() | 'closed' | invalid(), Data :: binary()}; + Reason :: posix() | 'closed' | invalid(); - (Socket, Length, Flags, Timeout) -> + (Socket, Length, Flags, Timeout :: non_neg_integer()) -> {'ok', Data} | - {'error', Reason} when + {'error', Reason} | + {'error', {Reason, Data}} when Socket :: socket(), Length :: non_neg_integer(), Flags :: [msg_flag() | integer()], - Timeout :: non_neg_integer(), Data :: binary(), - Reason :: - posix() | 'closed' | invalid() | 'timeout' | - {posix() | 'closed' | invalid() | 'timeout', Data :: binary()}. + Reason :: posix() | 'closed' | invalid() | 'timeout'. recv(?socket(SockRef), Length, Flags, Timeout) when is_reference(SockRef), @@ -2379,6 +2417,13 @@ recv(?socket(SockRef), Length, Flags, Timeout) select_handle -> SelectHandle = Timeout, recv_nowait(SockRef, Length, Flags, SelectHandle, <<>>); + zero -> + case prim_socket:recv(SockRef, Length, Flags, zero) of + ok -> + {error, timeout}; + Result -> + Result + end; Deadline -> recv_deadline(SockRef, Length, Flags, Deadline, <<>>) end; @@ -2389,7 +2434,7 @@ recv(Socket, Length, Flags, Timeout) -> %% so Length == 0 means to return all available data also when recursing recv_nowait(SockRef, Length, Flags, SelectHandle, Acc) -> - case prim_socket:recv(SockRef, SelectHandle, Length, Flags) of + case prim_socket:recv(SockRef, Length, Flags, SelectHandle) of {more, Bin} -> %% We got what we requested but will not waste more time %% although there might be more data available @@ -2400,7 +2445,7 @@ recv_nowait(SockRef, Length, Flags, SelectHandle, Acc) -> {ok, {bincat(Acc, Bin), ?SELECT_INFO(recv, SelectHandle)}}; select -> %% The caller will get a select message when there - %% might me data to read + %% might be data to read if byte_size(Acc) =:= 0 -> {select, ?SELECT_INFO(recv, SelectHandle)}; @@ -2413,7 +2458,7 @@ recv_nowait(SockRef, Length, Flags, SelectHandle, Acc) -> recv_deadline(SockRef, Length, Flags, Deadline, Acc) -> SelectHandle = make_ref(), - case prim_socket:recv(SockRef, SelectHandle, Length, Flags) of + case prim_socket:recv(SockRef, Length, Flags, SelectHandle) of {more, Bin} -> %% There is more data readily available %% - repeat unless time's up @@ -2510,7 +2555,9 @@ recv_error(Acc, Reason) -> %% is needed, possibly with a then adjusted buffer size. %% --spec recvfrom(Socket) -> {'ok', {Source, Data}} | {'error', Reason} when +-spec recvfrom(Socket) -> + {'ok', {Source, Data}} | + {'error', Reason} when Socket :: socket(), Source :: sockaddr_recv(), Data :: binary(), @@ -2519,19 +2566,32 @@ recv_error(Acc, Reason) -> recvfrom(Socket) -> recvfrom(Socket, 0). --spec recvfrom(Socket, BufSz) -> {'ok', {Source, Data}} | {'error', Reason} when +-spec recvfrom(Socket, Flags) -> + {'ok', {Source, Data}} | + {'error', Reason} when + Socket :: socket(), + Flags :: [msg_flag() | integer()], + Source :: sockaddr_recv(), + Data :: binary(), + Reason :: posix() | 'closed' | invalid(); + + (Socket, BufSz) -> + {'ok', {Source, Data}} | + {'error', Reason} when Socket :: socket(), BufSz :: non_neg_integer(), Source :: sockaddr_recv(), Data :: binary(), Reason :: posix() | 'closed' | invalid(). +recvfrom(Socket, Flags) when is_list(Flags) -> + recvfrom(Socket, 0, Flags, ?ESOCK_RECV_TIMEOUT_DEFAULT); recvfrom(Socket, BufSz) -> recvfrom(Socket, BufSz, ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT). --spec recvfrom(Socket, Flags, 'nowait') -> +-spec recvfrom(Socket, Flags, SelectHandle :: 'nowait') -> {'ok', {Source, Data}} | {'select', SelectInfo} | {'error', Reason} when @@ -2542,7 +2602,7 @@ recvfrom(Socket, BufSz) -> SelectInfo :: select_info(), Reason :: posix() | 'closed' | invalid(); - (Socket, Flags, SelectHandle) -> + (Socket, Flags, SelectHandle :: select_handle()) -> {'ok', {Source, Data}} | {'select', SelectInfo} | {'error', Reason} when @@ -2551,31 +2611,29 @@ recvfrom(Socket, BufSz) -> Source :: sockaddr_recv(), Data :: binary(), SelectInfo :: select_info(), - SelectHandle :: select_handle(), Reason :: posix() | 'closed' | invalid(); - (Socket, Flags, Timeout) -> + (Socket, Flags, Timeout :: 'infinity') -> {'ok', {Source, Data}} | {'error', Reason} when Socket :: socket(), Flags :: [msg_flag() | integer()], - Timeout :: 'infinity', Source :: sockaddr_recv(), Data :: binary(), Reason :: posix() | 'closed' | invalid(); - (Socket, Flags, Timeout) -> + (Socket, Flags, Timeout :: non_neg_integer()) -> {'ok', {Source, Data}} | {'error', Reason} when Socket :: socket(), Flags :: [msg_flag() | integer()], - Timeout :: non_neg_integer(), Source :: sockaddr_recv(), Data :: binary(), Reason :: posix() | 'closed' | invalid() | 'timeout'; (Socket, BufSz, Flags) -> - {'ok', {Source, Data}} | {'error', Reason} when + {'ok', {Source, Data}} | + {'error', Reason} when Socket :: socket(), BufSz :: non_neg_integer(), Flags :: [msg_flag() | integer()], @@ -2583,7 +2641,7 @@ recvfrom(Socket, BufSz) -> Data :: binary(), Reason :: posix() | 'closed' | invalid(); - (Socket, BufSz, 'nowait') -> + (Socket, BufSz, SelectHandle :: 'nowait') -> {'ok', {Source, Data}} | {'select', SelectInfo} | {'error', Reason} when @@ -2594,7 +2652,7 @@ recvfrom(Socket, BufSz) -> SelectInfo :: select_info(), Reason :: posix() | 'closed' | invalid(); - (Socket, BufSz, SelectHandle) -> + (Socket, BufSz, SelectHandle :: select_handle()) -> {'ok', {Source, Data}} | {'select', SelectInfo} | {'error', Reason} when @@ -2603,23 +2661,22 @@ recvfrom(Socket, BufSz) -> Source :: sockaddr_recv(), Data :: binary(), SelectInfo :: select_info(), - SelectHandle :: select_handle(), Reason :: posix() | 'closed' | invalid(); - (Socket, BufSz, Timeout) -> - {'ok', {Source, Data}} | {'error', Reason} when + (Socket, BufSz, Timeout :: 'infinity') -> + {'ok', {Source, Data}} | + {'error', Reason} when Socket :: socket(), BufSz :: non_neg_integer(), - Timeout :: 'infinity', Source :: sockaddr_recv(), Data :: binary(), Reason :: posix() | 'closed' | invalid(); - (Socket, BufSz, Timeout) -> - {'ok', {Source, Data}} | {'error', Reason} when + (Socket, BufSz, Timeout :: non_neg_integer()) -> + {'ok', {Source, Data}} | + {'error', Reason} when Socket :: socket(), BufSz :: non_neg_integer(), - Timeout :: non_neg_integer(), Source :: sockaddr_recv(), Data :: binary(), Reason :: posix() | 'closed' | invalid() | 'timeout'. @@ -2631,7 +2688,7 @@ recvfrom(Socket, BufSz, Flags) when is_list(Flags) -> recvfrom(Socket, BufSz, Timeout) -> recvfrom(Socket, BufSz, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout). --spec recvfrom(Socket, BufSz, Flags, 'nowait') -> +-spec recvfrom(Socket, BufSz, Flags, SelectHandle :: 'nowait') -> {'ok', {Source, Data}} | {'select', SelectInfo} | {'error', Reason} when @@ -2643,7 +2700,7 @@ recvfrom(Socket, BufSz, Timeout) -> SelectInfo :: select_info(), Reason :: posix() | 'closed' | invalid(); - (Socket, BufSz, Flags, SelectHandle) -> + (Socket, BufSz, Flags, SelectHandle :: select_handle()) -> {'ok', {Source, Data}} | {'select', SelectInfo} | {'error', Reason} when @@ -2653,27 +2710,24 @@ recvfrom(Socket, BufSz, Timeout) -> Source :: sockaddr_recv(), Data :: binary(), SelectInfo :: select_info(), - SelectHandle :: select_handle(), Reason :: posix() | 'closed' | invalid(); - (Socket, BufSz, Flags, Timeout) -> + (Socket, BufSz, Flags, Timeout :: 'infinity') -> {'ok', {Source, Data}} | {'error', Reason} when Socket :: socket(), BufSz :: non_neg_integer(), Flags :: [msg_flag() | integer()], - Timeout :: 'infinity', Source :: sockaddr_recv(), Data :: binary(), Reason :: posix() | 'closed' | invalid(); - (Socket, BufSz, Flags, Timeout) -> + (Socket, BufSz, Flags, Timeout :: non_neg_integer()) -> {'ok', {Source, Data}} | {'error', Reason} when Socket :: socket(), BufSz :: non_neg_integer(), Flags :: [msg_flag() | integer()], - Timeout :: non_neg_integer(), Source :: sockaddr_recv(), Data :: binary(), Reason :: posix() | 'closed' | invalid() | 'timeout'. @@ -2691,6 +2745,13 @@ recvfrom(?socket(SockRef), BufSz, Flags, Timeout) select_handle -> SelectHandle = Timeout, recvfrom_nowait(SockRef, BufSz, SelectHandle, Flags); + zero -> + case prim_socket:recvfrom(SockRef, BufSz, Flags, zero) of + ok -> + {error, timeout}; + Result -> + recvfrom_result(Result) + end; Deadline -> recvfrom_deadline(SockRef, BufSz, Flags, Deadline) end; @@ -2698,7 +2759,7 @@ recvfrom(Socket, BufSz, Flags, Timeout) -> erlang:error(badarg, [Socket, BufSz, Flags, Timeout]). recvfrom_nowait(SockRef, BufSz, SelectHandle, Flags) -> - case prim_socket:recvfrom(SockRef, SelectHandle, BufSz, Flags) of + case prim_socket:recvfrom(SockRef, BufSz, Flags, SelectHandle) of select -> {select, ?SELECT_INFO(recvfrom, SelectHandle)}; Result -> @@ -2707,7 +2768,7 @@ recvfrom_nowait(SockRef, BufSz, SelectHandle, Flags) -> recvfrom_deadline(SockRef, BufSz, Flags, Deadline) -> SelectHandle = make_ref(), - case prim_socket:recvfrom(SockRef, SelectHandle, BufSz, Flags) of + case prim_socket:recvfrom(SockRef, BufSz, Flags, SelectHandle) of select -> %% There is nothing just now, but we will be notified when there %% is something to read (a select message). @@ -2737,7 +2798,9 @@ recvfrom_result(Result) -> %% --------------------------------------------------------------------------- %% --spec recvmsg(Socket) -> {'ok', Msg} | {'error', Reason} when +-spec recvmsg(Socket) -> + {'ok', Msg} | + {'error', Reason} when Socket :: socket(), Msg :: msg_recv(), Reason :: posix() | 'closed' | invalid(). @@ -2746,40 +2809,44 @@ recvmsg(Socket) -> recvmsg(Socket, 0, 0, ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT). --spec recvmsg(Socket, Flags) -> {'ok', Msg} | {'error', Reason} when + +-spec recvmsg(Socket, Flags) -> + {'ok', Msg} | + {'error', Reason} when Socket :: socket(), Flags :: [msg_flag() | integer()], Msg :: msg_recv(), Reason :: posix() | 'closed' | invalid(); - (Socket, Timeout :: 'nowait') - -> {'ok', Msg} | - {'select', SelectInfo} | - {'error', Reason} when + (Socket, Timeout :: 'nowait') -> + {'ok', Msg} | + {'select', SelectInfo} | + {'error', Reason} when Socket :: socket(), Msg :: msg_recv(), SelectInfo :: select_info(), Reason :: posix() | 'closed' | invalid(); - (Socket, SelectHandle) - -> {'ok', Msg} | - {'select', SelectInfo} | - {'error', Reason} when + (Socket, SelectHandle :: select_handle()) -> + {'ok', Msg} | + {'select', SelectInfo} | + {'error', Reason} when Socket :: socket(), Msg :: msg_recv(), SelectInfo :: select_info(), - SelectHandle :: select_handle(), Reason :: posix() | 'closed' | invalid(); - (Socket, Timeout) -> {'ok', Msg} | {'error', Reason} when + (Socket, Timeout :: 'infinity') -> + {'ok', Msg} | + {'error', Reason} when Socket :: socket(), - Timeout :: 'infinity', Msg :: msg_recv(), Reason :: posix() | 'closed' | invalid(); - (Socket, Timeout) -> {'ok', Msg} | {'error', Reason} when + (Socket, Timeout :: non_neg_integer()) -> + {'ok', Msg} | + {'error', Reason} when Socket :: socket(), - Timeout :: non_neg_integer(), Msg :: msg_recv(), Reason :: posix() | 'closed' | invalid() | 'timeout'. @@ -2788,42 +2855,90 @@ recvmsg(Socket, Flags) when is_list(Flags) -> recvmsg(Socket, Timeout) -> recvmsg(Socket, 0, 0, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout). --spec recvmsg(Socket, Flags, Timeout :: 'nowait') - -> {'ok', Msg} | - {'select', SelectInfo} | - {'error', Reason} when + +-spec recvmsg(Socket, BufSz, CtrlSz, SelectHandle :: 'nowait') -> + {'ok', Msg} | + {'select', SelectInfo} | + {'error', Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + Msg :: msg_recv(), + SelectInfo :: select_info(), + Reason :: posix() | 'closed' | invalid(); + + (Socket, BufSz, CtrlSz, SelectHandle :: select_handle()) -> + {'ok', Msg} | + {'select', SelectInfo} | + {'error', Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + Msg :: msg_recv(), + SelectInfo :: select_info(), + Reason :: posix() | 'closed' | invalid(); + + (Socket, BufSz, CtrlSz, Timeout :: 'infinity') -> + {'ok', Msg} | + {'error', Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + Msg :: msg_recv(), + Reason :: posix() | 'closed' | invalid(); + + (Socket, BufSz, CtrlSz, Timeout :: non_neg_integer()) -> + {'ok', Msg} | + {'error', Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + Msg :: msg_recv(), + Reason :: posix() | 'closed' | invalid() | 'timeout'. + +recvmsg(Socket, BufSz, CtrlSz, Timeout) -> + recvmsg(Socket, BufSz, CtrlSz, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout). + + +-spec recvmsg(Socket, Flags, Timeout :: 'nowait') -> + {'ok', Msg} | + {'select', SelectInfo} | + {'error', Reason} when Socket :: socket(), Flags :: [msg_flag() | integer()], Msg :: msg_recv(), SelectInfo :: select_info(), Reason :: posix() | 'closed' | invalid(); - (Socket, Flags, SelectHandle) - -> {'ok', Msg} | - {'select', SelectInfo} | - {'error', Reason} when + (Socket, Flags, SelectHandle :: select_handle()) -> + {'ok', Msg} | + {'select', SelectInfo} | + {'error', Reason} when Socket :: socket(), Flags :: [msg_flag() | integer()], Msg :: msg_recv(), SelectInfo :: select_info(), - SelectHandle :: select_handle(), Reason :: posix() | 'closed' | invalid(); - (Socket, Flags, Timeout) -> {'ok', Msg} | {'error', Reason} when + (Socket, Flags, Timeout :: 'infinity') -> + {'ok', Msg} | + {'error', Reason} when Socket :: socket(), Flags :: [msg_flag() | integer()], - Timeout :: 'infinity', Msg :: msg_recv(), Reason :: posix() | 'closed' | invalid(); - (Socket, Flags, Timeout) -> {'ok', Msg} | {'error', Reason} when + (Socket, Flags, Timeout :: non_neg_integer()) -> + {'ok', Msg} | + {'error', Reason} when Socket :: socket(), Flags :: [msg_flag() | integer()], - Timeout :: non_neg_integer(), Msg :: msg_recv(), Reason :: posix() | 'closed' | invalid() | 'timeout'; - (Socket, BufSz, CtrlSz) -> {'ok', Msg} | {'error', Reason} when + (Socket, BufSz, CtrlSz) -> + {'ok', Msg} | + {'error', Reason} when Socket :: socket(), BufSz :: non_neg_integer(), CtrlSz :: non_neg_integer(), @@ -2837,7 +2952,7 @@ recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz), is_integer(CtrlSz) -> ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT). --spec recvmsg(Socket, BufSz, CtrlSz, Flags, 'nowait') -> +-spec recvmsg(Socket, BufSz, CtrlSz, Flags, SelectHandle :: 'nowait') -> {'ok', Msg} | {'select', SelectInfo} | {'error', Reason} when @@ -2849,7 +2964,7 @@ recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz), is_integer(CtrlSz) -> SelectInfo :: select_info(), Reason :: posix() | 'closed' | invalid(); - (Socket, BufSz, CtrlSz, Flags, SelectHandle) -> + (Socket, BufSz, CtrlSz, Flags, SelectHandle :: select_handle()) -> {'ok', Msg} | {'select', SelectInfo} | {'error', Reason} when @@ -2859,28 +2974,25 @@ recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz), is_integer(CtrlSz) -> Flags :: [msg_flag() | integer()], Msg :: msg_recv(), SelectInfo :: select_info(), - SelectHandle :: select_handle(), Reason :: posix() | 'closed' | invalid(); - (Socket, BufSz, CtrlSz, Flags, Timeout) -> + (Socket, BufSz, CtrlSz, Flags, Timeout :: 'infinity') -> {'ok', Msg} | {'error', Reason} when Socket :: socket(), BufSz :: non_neg_integer(), CtrlSz :: non_neg_integer(), Flags :: [msg_flag() | integer()], - Timeout :: 'infinity', Msg :: msg_recv(), Reason :: posix() | 'closed' | invalid(); - (Socket, BufSz, CtrlSz, Flags, Timeout) -> + (Socket, BufSz, CtrlSz, Flags, Timeout :: non_neg_integer()) -> {'ok', Msg} | {'error', Reason} when Socket :: socket(), BufSz :: non_neg_integer(), CtrlSz :: non_neg_integer(), Flags :: [msg_flag() | integer()], - Timeout :: non_neg_integer(), Msg :: msg_recv(), Reason :: posix() | 'closed' | invalid() | 'timeout'. @@ -2898,6 +3010,13 @@ recvmsg(?socket(SockRef), BufSz, CtrlSz, Flags, Timeout) select_handle -> SelectHandle = Timeout, recvmsg_nowait(SockRef, BufSz, CtrlSz, Flags, SelectHandle); + zero -> + case prim_socket:recvmsg(SockRef, BufSz, CtrlSz, Flags, zero) of + ok -> + {error, timeout}; + Result -> + recvmsg_result(Result) + end; Deadline -> recvmsg_deadline(SockRef, BufSz, CtrlSz, Flags, Deadline) end; @@ -2905,7 +3024,7 @@ recvmsg(Socket, BufSz, CtrlSz, Flags, Timeout) -> erlang:error(badarg, [Socket, BufSz, CtrlSz, Flags, Timeout]). recvmsg_nowait(SockRef, BufSz, CtrlSz, Flags, SelectHandle) -> - case prim_socket:recvmsg(SockRef, SelectHandle, BufSz, CtrlSz, Flags) of + case prim_socket:recvmsg(SockRef, BufSz, CtrlSz, Flags, SelectHandle) of select -> {select, ?SELECT_INFO(recvmsg, SelectHandle)}; Result -> @@ -2914,7 +3033,7 @@ recvmsg_nowait(SockRef, BufSz, CtrlSz, Flags, SelectHandle) -> recvmsg_deadline(SockRef, BufSz, CtrlSz, Flags, Deadline) -> SelectHandle = make_ref(), - case prim_socket:recvmsg(SockRef, SelectHandle, BufSz, CtrlSz, Flags) of + case prim_socket:recvmsg(SockRef, BufSz, CtrlSz, Flags, SelectHandle) of select -> %% There is nothing just now, but we will be notified when there %% is something to read (a select message). @@ -2929,12 +3048,6 @@ recvmsg_deadline(SockRef, BufSz, CtrlSz, Flags, Deadline) -> cancel(SockRef, recvmsg, SelectHandle), {error, timeout} end; - %% - {error, ealready = Reason} -> - %% Internal error: - %% we called recvmsg, got eagain, and called recvmsg again - %% - without waiting for select message - erlang:error(Reason); Result -> recvmsg_result(Result) end. @@ -3270,12 +3383,12 @@ deadline(Timeout) -> Timeout; infinity -> Timeout; + SelectHandle when is_reference(SelectHandle) -> + select_handle; 0 -> zero; _ when is_integer(Timeout), 0 < Timeout -> timestamp() + Timeout; - _ when is_reference(Timeout) -> - select_handle; _ -> invalid end. diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index c55f120d93..139a886aa5 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -20701,8 +20701,6 @@ api_opt_ip_recverr_udp4(doc) -> []; api_opt_ip_recverr_udp4(Config) when is_list(Config) -> ?TT(?SECS(5)), - SendRef = nowait(Config), - RecvRef = nowait(Config), tc_try(api_opt_ip_recverr_udp4, fun() -> has_support_ip_recverr() @@ -20714,21 +20712,19 @@ api_opt_ip_recverr_udp4(Config) when is_list(Config) -> Get = fun(Sock, Key) -> socket:getopt(Sock, ip, Key) end, - Send = fun(Sock, Data, Dest) -> - socket:sendto(Sock, Data, Dest, [], SendRef) + Send = fun(Sock, Data, Dest, Tmo) -> + socket:sendto(Sock, Data, Dest, [], Tmo) end, - Recv = fun(Sock) -> - socket:recvfrom(Sock, 0, [], RecvRef) + Recv = fun(Sock, Tmo) -> + socket:recvfrom(Sock, 0, [], Tmo) end, InitState = #{domain => inet, proto => udp, send => Send, recv => Recv, - send_sref => SendRef, - recv_sref => RecvRef, set => Set, get => Get}, - ok = api_opt_recverr_udp(InitState) + ok = api_opt_recverr_udp(Config, InitState) end). @@ -20744,8 +20740,6 @@ api_opt_ipv6_recverr_udp6(doc) -> []; api_opt_ipv6_recverr_udp6(Config) when is_list(Config) -> ?TT(?SECS(5)), - SendRef = nowait(Config), - RecvRef = nowait(Config), tc_try(api_opt_ipv6_recverr_udp6, fun() -> has_support_ipv6(), @@ -20758,27 +20752,25 @@ api_opt_ipv6_recverr_udp6(Config) when is_list(Config) -> Get = fun(Sock, Key) -> socket:getopt(Sock, ipv6, Key) end, - Send = fun(Sock, Data, Dest) -> - socket:sendto(Sock, Data, Dest, [], SendRef) + Send = fun(Sock, Data, Dest, Tmo) -> + socket:sendto(Sock, Data, Dest, [], Tmo) end, - Recv = fun(Sock) -> - socket:recvfrom(Sock, 0, [], SendRef) + Recv = fun(Sock, Tmo) -> + socket:recvfrom(Sock, 0, [], Tmo) end, InitState = #{domain => inet6, proto => udp, send => Send, recv => Recv, - send_sref => SendRef, - recv_sref => RecvRef, set => Set, get => Get}, - ok = api_opt_recverr_udp(InitState) + ok = api_opt_recverr_udp(Config, InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -api_opt_recverr_udp(InitState) -> +api_opt_recverr_udp(Config, InitState) -> Seq = [ #{desc => "create socket", @@ -20792,13 +20784,19 @@ api_opt_recverr_udp(InitState) -> end end}, #{desc => "bind (to loopback)", - cmd => fun(#{sock := Sock} = _State) -> + cmd => fun(#{sock := Sock} = State) -> case socket:bind(Sock, loopback) of ok -> - ?SEV_IPRINT("bound"), - ok; - {error, _} = ERROR -> - ERROR + case socket:sockname(Sock) of + {ok, Addr} -> + ?SEV_IPRINT( + "bound to ~p", [Addr]), + {ok, State#{addr => Addr}}; + {error, _} = ERROR_1 -> + ERROR_1 + end; + {error, _} = ERROR_2 -> + ERROR_2 end end}, @@ -20814,9 +20812,9 @@ api_opt_recverr_udp(InitState) -> #{desc => "try (async) read (=> select)", cmd => fun(#{sock := Sock, - recv := Recv, - recv_sref := RecvRef} = State) -> - case Recv(Sock) of + recv := Recv} = State) -> + RecvRef = nowait(Config), + case Recv(Sock, RecvRef) of {select, SelectInfo} when RecvRef =:= nowait -> ?SEV_IPRINT("expected select nowait: " "~n ~p", [SelectInfo]), @@ -20836,11 +20834,11 @@ api_opt_recverr_udp(InitState) -> end end}, - #{desc => "try (dummy) send", + #{desc => "try send to nowhere", cmd => fun(#{domain := Domain, sock := Sock, - send := Send, - send_sref := SendRef} = State) -> + send := Send} = State) -> + SendRef = nowait(Config), Dest = #{family => Domain, addr => if (Domain =:= inet) -> @@ -20848,8 +20846,8 @@ api_opt_recverr_udp(InitState) -> (Domain =:= inet6) -> {0,0,0,0,0,0,0,1} end, - port => 1234}, - case Send(Sock, <<"ping">>, Dest) of + port => 44444}, + case Send(Sock, <<"ping">>, Dest, SendRef) of ok -> ?SEV_IPRINT("sent"), ok; @@ -20871,7 +20869,7 @@ api_opt_recverr_udp(InitState) -> end end}, - #{desc => "await select message", + #{desc => "await receive select message", cmd => fun(#{sock := Sock, rselect := {select_info, _, Ref}} = _State) -> receive @@ -20884,7 +20882,7 @@ api_opt_recverr_udp(InitState) -> #{desc => "try recv - expect econnrefused", cmd => fun(#{sock := Sock, recv := Recv} = _State) -> - case Recv(Sock) of + case Recv(Sock, infinity) of {error, econnrefused = Reason} -> ?SEV_IPRINT("expected failure: ~p", [Reason]), ok; @@ -20902,6 +20900,39 @@ api_opt_recverr_udp(InitState) -> end end}, + #{desc => "send to self", + cmd => + fun(#{sock := Sock, + send := Send, + addr := Addr} = _State) -> + case Send(Sock, <<"ring">>, Addr, infinity) of + ok -> + ?SEV_IPRINT("sent to self"), + ok; + {error, _} = Error -> + Error + end + end}, + + #{desc => "try recv - expect data sent to self", + cmd => fun(#{sock := Sock, + addr := Addr, + recv := Recv} = _State) -> + case Recv(Sock, infinity) of + {ok, {Addr, <<"ring">>}} -> + ?SEV_IPRINT("receive expected"), + ok; + {select, SelectInfo} -> + ?SEV_EPRINT("unexpected select: ~p", + [SelectInfo]), + {error, unexpected_success}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("unexpected error: ~p", + [Reason]), + ERROR + end + end}, + #{desc => "try recv error queue", cmd => fun(#{domain := Domain, sock := Sock}) -> %% Note that not all platforms that support @@ -20915,7 +20946,7 @@ api_opt_recverr_udp(InitState) -> if (Domain =:= inet) -> ip; (Domain =:= inet6) -> ipv6 end, - case socket:recvmsg(Sock, [errqueue]) of + case socket:recvmsg(Sock, [errqueue], 0) of {ok, #{addr := #{family := Domain, addr := Addr}, flags := [errqueue], |