summaryrefslogtreecommitdiffstats
path: root/Modules/socketmodule.c
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-04-01 19:57:09 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2015-04-01 19:57:09 (GMT)
commit31bf2d50735f7d0eb1407fe450e878b7f6e7715c (patch)
treeebea5c3ccb2c157e89efb10b4793649759945753 /Modules/socketmodule.c
parentc7489a5595293153205a20d77a667a0260b6e96d (diff)
downloadcpython-31bf2d50735f7d0eb1407fe450e878b7f6e7715c.zip
cpython-31bf2d50735f7d0eb1407fe450e878b7f6e7715c.tar.gz
cpython-31bf2d50735f7d0eb1407fe450e878b7f6e7715c.tar.bz2
Issue #23834: Add sock_call() helper function
The BEGIN_SELECT_LOOP and END_SELECT_LOOP macros of socketmodule.c don't handle EINTR. Functions using these macros use an inner loop to handle EINTR, but they don't recompute the timeout. This changes replaces the two macros with a new sock_call() function which takes a function as a parameter. sock_call() recomputes the timeout, handle false positive and handle EINTR.
Diffstat (limited to 'Modules/socketmodule.c')
-rw-r--r--Modules/socketmodule.c579
1 files changed, 323 insertions, 256 deletions
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 64db006..38879c1 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -683,45 +683,113 @@ internal_connect_select(PySocketSockObject *s)
return internal_select_impl(s, 1, s->sock_timeout, 1);
}
-/*
- Two macros for automatic retry of select() in case of false positives
- (for example, select() could indicate a socket is ready for reading
- but the data then discarded by the OS because of a wrong checksum).
- Here is an example of use:
+/* Call a socket function.
- BEGIN_SELECT_LOOP(s)
+ Raise an exception and return -1 on error, return 0 on success.
- timeout = internal_select(s, 0, interval);
+ If the socket has a timeout, wait until the socket is ready before calling
+ the function: wait until the socket is writable if writing is nonzero, wait
+ until the socket received data otherwise.
- if (!timeout) {
- Py_BEGIN_ALLOW_THREADS
- outlen = recv(s->sock_fd, cbuf, len, flags);
- Py_END_ALLOW_THREADS
- }
- if (timeout == 1) {
- PyErr_SetString(socket_timeout, "timed out");
+ If the function func is interrupted by a signal (failed with EINTR): retry
+ the function, except if the signal handler raised an exception (PEP 475).
+
+ When the function is retried, recompute the timeout using a monotonic clock.
+
+ sock_call() must be called with the GIL held. The function func is called
+ with the GIL released. */
+static int
+sock_call(PySocketSockObject *s,
+ int writing,
+ int (*func) (PySocketSockObject *s, void *data),
+ void *data)
+{
+ int has_timeout = (s->sock_timeout > 0);
+ _PyTime_t deadline = 0;
+ int deadline_initialized = 0;
+ int res;
+
+ /* sock_call() must be called with the GIL held. */
+ assert(PyGILState_Check());
+
+ /* outer loop to retry select() when select() is interrupted by a signal
+ or to retry select()+func() on false positive (see above) */
+ while (1) {
+ if (has_timeout) {
+ _PyTime_t interval;
+
+ if (deadline_initialized) {
+ /* recompute the timeout */
+ interval = deadline - _PyTime_GetMonotonicClock();
+ }
+ else {
+ deadline = _PyTime_GetMonotonicClock() + s->sock_timeout;
+ interval = s->sock_timeout;
+ }
+
+ res = internal_select(s, writing, interval);
+ if (res == -1) {
+ if (CHECK_ERRNO(EINTR)) {
+ /* select() was interrupted by a signal */
+ if (PyErr_CheckSignals())
+ return -1;
+
+ /* retry select() */
+ continue;
+ }
+
+ /* select() failed */
+ s->errorhandler();
+ return -1;
+ }
+
+ if (res == 1) {
+ PyErr_SetString(socket_timeout, "timed out");
+ return -1;
+ }
+
+ /* the socket is ready */
+ }
+
+ /* inner loop to retry func() when func() is interrupted by a signal */
+ while (1) {
+ Py_BEGIN_ALLOW_THREADS
+ res = func(s, data);
+ Py_END_ALLOW_THREADS
+
+ if (res) {
+ /* func() succeeded */
+ return 0;
+ }
+
+ if (!CHECK_ERRNO(EINTR))
+ break;
+
+ /* func() was interrupted by a signal */
+ if (PyErr_CheckSignals())
+ return -1;
+
+ /* retry func() */
+ }
+
+ if (s->sock_timeout > 0
+ && (CHECK_ERRNO(EWOULDBLOCK) || CHECK_ERRNO(EAGAIN))) {
+ /* False positive: func() failed with EWOULDBLOCK or EAGAIN.
+
+ For example, select() could indicate a socket is ready for
+ reading, but the data then discarded by the OS because of a
+ wrong checksum.
+
+ Loop on select() to recheck for socket readyness. */
+ continue;
+ }
+
+ /* func() failed */
+ s->errorhandler();
return -1;
}
- END_SELECT_LOOP(s)
-*/
+}
-#define BEGIN_SELECT_LOOP(s) \
- { \
- _PyTime_t deadline = 0; \
- _PyTime_t interval = s->sock_timeout; \
- int has_timeout = (s->sock_timeout > 0); \
- if (has_timeout) \
- deadline = _PyTime_GetMonotonicClock() + interval; \
- while (1) { \
- errno = 0; \
-
-#define END_SELECT_LOOP(s) \
- if (!has_timeout || \
- (!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \
- break; \
- interval = deadline - _PyTime_GetMonotonicClock(); \
- } \
- } \
/* Initialize a new socket object. */
@@ -2061,23 +2129,51 @@ get_cmsg_data_len(struct msghdr *msg, struct cmsghdr *cmsgh, size_t *data_len)
#endif /* CMSG_LEN */
+struct sock_accept {
+ socklen_t *addrlen;
+ sock_addr_t *addrbuf;
+ SOCKET_T result;
+};
+
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+/* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
+static int accept4_works = -1;
+#endif
+
+static int
+sock_accept_impl(PySocketSockObject *s, void *data)
+{
+ struct sock_accept *ctx = data;
+
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+ if (accept4_works != 0) {
+ ctx->result = accept4(s->sock_fd, SAS2SA(ctx->addrbuf), ctx->addrlen,
+ SOCK_CLOEXEC);
+ if (ctx->result == INVALID_SOCKET && accept4_works == -1) {
+ /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
+ accept4_works = (errno != ENOSYS);
+ }
+ }
+ if (accept4_works == 0)
+ ctx->result = accept(s->sock_fd, SAS2SA(ctx->addrbuf), ctx->addrlen);
+#else
+ ctx->result = accept(s->sock_fd, SAS2SA(ctx->addrbuf), ctx->addrlen);
+#endif
+ return (ctx->result >= 0);
+}
+
/* s._accept() -> (fd, address) */
static PyObject *
sock_accept(PySocketSockObject *s)
{
sock_addr_t addrbuf;
- SOCKET_T newfd = INVALID_SOCKET;
+ SOCKET_T newfd;
socklen_t addrlen;
PyObject *sock = NULL;
PyObject *addr = NULL;
PyObject *res = NULL;
- int timeout;
- int async_err = 0;
-#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
- /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
- static int accept4_works = -1;
-#endif
+ struct sock_accept ctx;
if (!getsockaddrlen(s, &addrlen))
return NULL;
@@ -2086,37 +2182,11 @@ sock_accept(PySocketSockObject *s)
if (!IS_SELECTABLE(s))
return select_error();
- BEGIN_SELECT_LOOP(s)
- do {
- timeout = internal_select(s, 0, interval);
-
- if (!timeout) {
- Py_BEGIN_ALLOW_THREADS
-#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
- if (accept4_works != 0) {
- newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen,
- SOCK_CLOEXEC);
- if (newfd == INVALID_SOCKET && accept4_works == -1) {
- /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
- accept4_works = (errno != ENOSYS);
- }
- }
- if (accept4_works == 0)
- newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
-#else
- newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
-#endif
- Py_END_ALLOW_THREADS
- }
- } while (newfd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
- if (timeout == 1) {
- PyErr_SetString(socket_timeout, "timed out");
+ ctx.addrlen = &addrlen;
+ ctx.addrbuf = &addrbuf;
+ if (sock_call(s, 0, sock_accept_impl, &ctx) < 0)
return NULL;
- }
- END_SELECT_LOOP(s)
-
- if (newfd == INVALID_SOCKET)
- return (!async_err) ? s->errorhandler() : NULL;
+ newfd = ctx.result;
#ifdef MS_WINDOWS
if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) {
@@ -2690,6 +2760,28 @@ at least 0 (if it is lower, it is set to 0); it specifies the number of\n\
unaccepted connections that the system will allow before refusing new\n\
connections. If not specified, a default reasonable value is chosen.");
+struct sock_recv {
+ char *cbuf;
+ Py_ssize_t len;
+ int flags;
+ Py_ssize_t result;
+};
+
+static int
+sock_recv_impl(PySocketSockObject *s, void *data)
+{
+ struct sock_recv *ctx = data;
+
+#ifdef MS_WINDOWS
+ if (ctx->len > INT_MAX)
+ ctx->len = INT_MAX;
+ ctx->result = recv(s->sock_fd, ctx->cbuf, (int)ctx->len, ctx->flags);
+#else
+ ctx->result = recv(s->sock_fd, ctx->cbuf, ctx->len, ctx->flags);
+#endif
+ return (ctx->result >= 0);
+}
+
/*
* This is the guts of the recv() and recv_into() methods, which reads into a
@@ -2703,9 +2795,7 @@ connections. If not specified, a default reasonable value is chosen.");
static Py_ssize_t
sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags)
{
- Py_ssize_t outlen = -1;
- int timeout;
- int async_err = 0;
+ struct sock_recv ctx;
if (!IS_SELECTABLE(s)) {
select_error();
@@ -2716,36 +2806,13 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags)
return 0;
}
- BEGIN_SELECT_LOOP(s)
- do {
- timeout = internal_select(s, 0, interval);
-
- if (!timeout) {
- Py_BEGIN_ALLOW_THREADS
-#ifdef MS_WINDOWS
- if (len > INT_MAX)
- len = INT_MAX;
- outlen = recv(s->sock_fd, cbuf, (int)len, flags);
-#else
- outlen = recv(s->sock_fd, cbuf, len, flags);
-#endif
- Py_END_ALLOW_THREADS
- }
- } while (outlen < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
-
- if (timeout == 1) {
- PyErr_SetString(socket_timeout, "timed out");
+ ctx.cbuf = cbuf;
+ ctx.len = len;
+ ctx.flags = flags;
+ if (sock_call(s, 0, sock_recv_impl, &ctx) < 0)
return -1;
- }
- END_SELECT_LOOP(s)
- if (outlen < 0) {
- /* Note: the call to errorhandler() ALWAYS indirectly returned
- NULL, so ignore its return value */
- if (!async_err)
- s->errorhandler();
- return -1;
- }
- return outlen;
+
+ return ctx.result;
}
@@ -2859,6 +2926,34 @@ is not specified (or 0), receive up to the size available in the given buffer.\n
\n\
See recv() for documentation about the flags.");
+struct sock_recvfrom {
+ char* cbuf;
+ Py_ssize_t len;
+ int flags;
+ socklen_t *addrlen;
+ sock_addr_t *addrbuf;
+ Py_ssize_t result;
+};
+
+static int
+sock_recvfrom_impl(PySocketSockObject *s, void *data)
+{
+ struct sock_recvfrom *ctx = data;
+
+ memset(ctx->addrbuf, 0, *ctx->addrlen);
+
+#ifdef MS_WINDOWS
+ if (ctx->len > INT_MAX)
+ ctx->len = INT_MAX;
+ ctx->result = recvfrom(s->sock_fd, ctx->cbuf, (int)ctx->len, ctx->flags,
+ SAS2SA(ctx->addrbuf), ctx->addrlen);
+#else
+ ctx->result = recvfrom(s->sock_fd, ctx->cbuf, ctx->len, ctx->flags,
+ SAS2SA(ctx->addrbuf), ctx->addrlen);
+#endif
+ return (ctx->result >= 0);
+}
+
/*
* This is the guts of the recvfrom() and recvfrom_into() methods, which reads
@@ -2876,10 +2971,8 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags,
PyObject** addr)
{
sock_addr_t addrbuf;
- int timeout;
- Py_ssize_t n = -1;
socklen_t addrlen;
- int async_err = 0;
+ struct sock_recvfrom ctx;
*addr = NULL;
@@ -2891,42 +2984,20 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags,
return -1;
}
- BEGIN_SELECT_LOOP(s)
- do {
- memset(&addrbuf, 0, addrlen);
- timeout = internal_select(s, 0, interval);
-
- if (!timeout) {
- Py_BEGIN_ALLOW_THREADS
-#ifdef MS_WINDOWS
- if (len > INT_MAX)
- len = INT_MAX;
- n = recvfrom(s->sock_fd, cbuf, (int)len, flags,
- (void *) &addrbuf, &addrlen);
-#else
- n = recvfrom(s->sock_fd, cbuf, len, flags,
- SAS2SA(&addrbuf), &addrlen);
-#endif
- Py_END_ALLOW_THREADS
- }
- } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
-
- if (timeout == 1) {
- PyErr_SetString(socket_timeout, "timed out");
+ ctx.cbuf = cbuf;
+ ctx.len = len;
+ ctx.flags = flags;
+ ctx.addrbuf = &addrbuf;
+ ctx.addrlen = &addrlen;
+ if (sock_call(s, 0, sock_recvfrom_impl, &ctx) < 0)
return -1;
- }
- END_SELECT_LOOP(s)
- if (n < 0) {
- if (!async_err)
- s->errorhandler();
- return -1;
- }
- if (!(*addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- addrlen, s->sock_proto)))
+ *addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen,
+ s->sock_proto);
+ if (*addr == NULL)
return -1;
- return n;
+ return ctx.result;
}
/* s.recvfrom(nbytes [,flags]) method */
@@ -3037,6 +3108,21 @@ PyDoc_STRVAR(recvfrom_into_doc,
\n\
Like recv_into(buffer[, nbytes[, flags]]) but also return the sender's address info.");
+struct sock_recvmsg {
+ struct msghdr *msg;
+ int flags;
+ ssize_t result;
+};
+
+static int
+sock_recvmsg_impl(PySocketSockObject *s, void *data)
+{
+ struct sock_recvmsg *ctx = data;
+
+ ctx->result = recvmsg(s->sock_fd, ctx->msg, ctx->flags);
+ return (ctx->result >= 0);
+}
+
/* The sendmsg() and recvmsg[_into]() methods require a working
CMSG_LEN(). See the comment near get_CMSG_LEN(). */
@@ -3056,9 +3142,6 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
int flags, Py_ssize_t controllen,
PyObject *(*makeval)(ssize_t, void *), void *makeval_data)
{
- ssize_t bytes_received = -1;
- int timeout;
- int async_err = 0;
sock_addr_t addrbuf;
socklen_t addrbuflen;
struct msghdr msg = {0};
@@ -3067,6 +3150,7 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
struct cmsghdr *cmsgh;
size_t cmsgdatalen = 0;
int cmsg_status;
+ struct sock_recvmsg ctx;
/* XXX: POSIX says that msg_name and msg_namelen "shall be
ignored" when the socket is connected (Linux fills them in
@@ -3093,35 +3177,17 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
goto finally;
}
- BEGIN_SELECT_LOOP(s)
- do {
- msg.msg_name = SAS2SA(&addrbuf);
- msg.msg_namelen = addrbuflen;
- msg.msg_iov = iov;
- msg.msg_iovlen = iovlen;
- msg.msg_control = controlbuf;
- msg.msg_controllen = controllen;
- timeout = internal_select(s, 0, interval);
-
- if (timeout == 1) {
- PyErr_SetString(socket_timeout, "timed out");
- goto finally;
- }
-
- if (!timeout) {
- Py_BEGIN_ALLOW_THREADS;
- bytes_received = recvmsg(s->sock_fd, &msg, flags);
- Py_END_ALLOW_THREADS;
- }
- } while (bytes_received < 0 && errno == EINTR &&
- !(async_err = PyErr_CheckSignals()));
- END_SELECT_LOOP(s)
+ msg.msg_name = SAS2SA(&addrbuf);
+ msg.msg_namelen = addrbuflen;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = iovlen;
+ msg.msg_control = controlbuf;
+ msg.msg_controllen = controllen;
- if (bytes_received < 0) {
- if (!async_err)
- s->errorhandler();
+ ctx.msg = &msg;
+ ctx.flags = flags;
+ if (sock_call(s, 0, sock_recvmsg_impl, &ctx) < 0)
goto finally;
- }
/* Make list of (level, type, data) tuples from control messages. */
if ((cmsg_list = PyList_New(0)) == NULL)
@@ -3163,7 +3229,7 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
}
retval = Py_BuildValue("NOiN",
- (*makeval)(bytes_received, makeval_data),
+ (*makeval)(ctx.result, makeval_data),
cmsg_list,
(int)msg.msg_flags,
makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
@@ -3371,16 +3437,36 @@ SCM_RIGHTS mechanism.");
#endif /* CMSG_LEN */
+struct sock_send {
+ char *buf;
+ Py_ssize_t len;
+ int flags;
+ Py_ssize_t result;
+};
+
+static int
+sock_send_impl(PySocketSockObject *s, void *data)
+{
+ struct sock_send *ctx = data;
+
+#ifdef MS_WINDOWS
+ if (ctx->len > INT_MAX)
+ ctx->len = INT_MAX;
+ ctx->result = send(s->sock_fd, ctx->buf, (int)ctx->len, ctx->flags);
+#else
+ ctx->result = send(s->sock_fd, ctx->buf, ctx->len, ctx->flags);
+#endif
+ return (ctx->result >= 0);
+}
+
/* s.send(data [,flags]) method */
static PyObject *
sock_send(PySocketSockObject *s, PyObject *args)
{
- char *buf;
- Py_ssize_t len, n = -1;
- int async_err = 0;
- int flags = 0, timeout;
+ int flags = 0;
Py_buffer pbuf;
+ struct sock_send ctx;
if (!PyArg_ParseTuple(args, "y*|i:send", &pbuf, &flags))
return NULL;
@@ -3389,36 +3475,16 @@ sock_send(PySocketSockObject *s, PyObject *args)
PyBuffer_Release(&pbuf);
return select_error();
}
- buf = pbuf.buf;
- len = pbuf.len;
-
- BEGIN_SELECT_LOOP(s)
- do {
- timeout = internal_select(s, 1, interval);
-
- if (!timeout) {
- Py_BEGIN_ALLOW_THREADS
-#ifdef MS_WINDOWS
- if (len > INT_MAX)
- len = INT_MAX;
- n = send(s->sock_fd, buf, (int)len, flags);
-#else
- n = send(s->sock_fd, buf, len, flags);
-#endif
- Py_END_ALLOW_THREADS
- }
- } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
- if (timeout == 1) {
+ ctx.buf = pbuf.buf;
+ ctx.len = pbuf.len;
+ ctx.flags = flags;
+ if (sock_call(s, 1, sock_send_impl, &ctx) < 0) {
PyBuffer_Release(&pbuf);
- PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
- END_SELECT_LOOP(s)
-
PyBuffer_Release(&pbuf);
- if (n < 0)
- return (!async_err) ? s->errorhandler() : NULL;
- return PyLong_FromSsize_t(n);
+
+ return PyLong_FromSsize_t(ctx.result);
}
PyDoc_STRVAR(send_doc,
@@ -3494,6 +3560,32 @@ until all data is sent. If an error occurs, it's impossible\n\
to tell how much data has been sent.");
+struct sock_sendto {
+ char *buf;
+ Py_ssize_t len;
+ int flags;
+ int addrlen;
+ sock_addr_t *addrbuf;
+ Py_ssize_t result;
+};
+
+static int
+sock_sendto_impl(PySocketSockObject *s, void *data)
+{
+ struct sock_sendto *ctx = data;
+
+#ifdef MS_WINDOWS
+ if (ctx->len > INT_MAX)
+ ctx->len = INT_MAX;
+ ctx->result = sendto(s->sock_fd, ctx->buf, (int)ctx->len, ctx->flags,
+ SAS2SA(ctx->addrbuf), ctx->addrlen);
+#else
+ ctx->result = sendto(s->sock_fd, ctx->buf, ctx->len, ctx->flags,
+ SAS2SA(ctx->addrbuf), ctx->addrlen);
+#endif
+ return (ctx->result >= 0);
+}
+
/* s.sendto(data, [flags,] sockaddr) method */
static PyObject *
@@ -3501,11 +3593,10 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
{
Py_buffer pbuf;
PyObject *addro;
- char *buf;
- Py_ssize_t len, arglen;
+ Py_ssize_t arglen;
sock_addr_t addrbuf;
- int addrlen, n = -1, flags, timeout;
- int async_err = 0;
+ int addrlen, flags;
+ struct sock_sendto ctx;
flags = 0;
arglen = PyTuple_Size(args);
@@ -3526,9 +3617,6 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
if (PyErr_Occurred())
return NULL;
- buf = pbuf.buf;
- len = pbuf.len;
-
if (!IS_SELECTABLE(s)) {
PyBuffer_Release(&pbuf);
return select_error();
@@ -3539,35 +3627,18 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
return NULL;
}
- BEGIN_SELECT_LOOP(s)
- do {
- timeout = internal_select(s, 1, interval);
-
- if (!timeout) {
- Py_BEGIN_ALLOW_THREADS
-#ifdef MS_WINDOWS
- if (len > INT_MAX)
- len = INT_MAX;
- n = sendto(s->sock_fd, buf, (int)len, flags,
- SAS2SA(&addrbuf), addrlen);
-#else
- n = sendto(s->sock_fd, buf, len, flags,
- SAS2SA(&addrbuf), addrlen);
-#endif
- Py_END_ALLOW_THREADS
- }
- } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
-
- if (timeout == 1) {
+ ctx.buf = pbuf.buf;
+ ctx.len = pbuf.len;
+ ctx.flags = flags;
+ ctx.addrlen = addrlen;
+ ctx.addrbuf = &addrbuf;
+ if (sock_call(s, 1, sock_sendto_impl, &ctx) < 0) {
PyBuffer_Release(&pbuf);
- PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
- END_SELECT_LOOP(s)
PyBuffer_Release(&pbuf);
- if (n < 0)
- return (!async_err) ? s->errorhandler() : NULL;
- return PyLong_FromSsize_t(n);
+
+ return PyLong_FromSsize_t(ctx.result);
}
PyDoc_STRVAR(sendto_doc,
@@ -3577,6 +3648,21 @@ Like send(data, flags) but allows specifying the destination address.\n\
For IP sockets, the address is a pair (hostaddr, port).");
+struct sock_sendmsg {
+ struct msghdr *msg;
+ int flags;
+ ssize_t result;
+};
+
+static int
+sock_sendmsg_impl(PySocketSockObject *s, void *data)
+{
+ struct sock_sendmsg *ctx = data;
+
+ ctx->result = sendmsg(s->sock_fd, ctx->msg, ctx->flags);
+ return (ctx->result >= 0);
+}
+
/* The sendmsg() and recvmsg[_into]() methods require a working
CMSG_LEN(). See the comment near get_CMSG_LEN(). */
#ifdef CMSG_LEN
@@ -3597,11 +3683,10 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args)
} *cmsgs = NULL;
void *controlbuf = NULL;
size_t controllen, controllen_last;
- ssize_t bytes_sent = -1;
- int async_err = 0;
- int addrlen, timeout, flags = 0;
+ int addrlen, flags = 0;
PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL, *data_fast = NULL,
*cmsg_fast = NULL, *retval = NULL;
+ struct sock_sendmsg ctx;
if (!PyArg_ParseTuple(args, "O|OiO:sendmsg",
&data_arg, &cmsg_arg, &flags, &addr_arg))
@@ -3755,30 +3840,12 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args)
goto finally;
}
- BEGIN_SELECT_LOOP(s)
- do {
- timeout = internal_select(s, 1, interval);
-
- if (!timeout) {
- Py_BEGIN_ALLOW_THREADS;
- bytes_sent = sendmsg(s->sock_fd, &msg, flags);
- Py_END_ALLOW_THREADS;
- }
-
- if (timeout == 1) {
- PyErr_SetString(socket_timeout, "timed out");
- goto finally;
- }
- } while (bytes_sent < 0 && errno == EINTR &&
- !(async_err = PyErr_CheckSignals()));
- END_SELECT_LOOP(s)
-
- if (bytes_sent < 0) {
- if (!async_err)
- s->errorhandler();
+ ctx.msg = &msg;
+ ctx.flags = flags;
+ if (sock_call(s, 1, sock_sendmsg_impl, &ctx) < 0)
goto finally;
- }
- retval = PyLong_FromSsize_t(bytes_sent);
+
+ retval = PyLong_FromSsize_t(ctx.result);
finally:
PyMem_Free(controlbuf);