summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Sokolovsky <pfalcon@users.sourceforge.net>2018-10-07 20:58:42 +0300
committerDamien George <damien.p.george@gmail.com>2018-10-17 14:19:06 +1100
commit0c18633ea9bed6a0c1031357c4eacbb016deb41a (patch)
tree83a28c7206034e4b8caa920c4f4d4343284a416f
parent80a25810f9b246094b31ce7d465a0f25842c9be6 (diff)
unix/modusocket: Finish socket.settimeout() implementation.
1. Return correct error code for non-blocking vs timed out socket (POSIX returns EAGAIN for both, we want ETIMEDOUT in case of timed out socket). To achieve this, blocking/non-blocking flag is added to the mp_obj_socket_t, to avoid issuing fcntl() syscall each time EAGAIN occurs. (mp_obj_socket_t used to be 8 bytes, having some room in a standard 16-byte alloc block.) 2. Handle socket.settimeout(0) properly - in Python, that means non-blocking mode, but SO_RCVTIMEO/SO_SNDTIMEO of 0 is infinite timeout. 3. Overall, make sure that socket.settimeout() call switches blocking state as expected.
-rw-r--r--ports/unix/modusocket.c54
1 files changed, 39 insertions, 15 deletions
diff --git a/ports/unix/modusocket.c b/ports/unix/modusocket.c
index 1a073ca03..61402e001 100644
--- a/ports/unix/modusocket.c
+++ b/ports/unix/modusocket.c
@@ -37,6 +37,7 @@
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
+#include <math.h>
#include "py/objtuple.h"
#include "py/objstr.h"
@@ -65,6 +66,7 @@
typedef struct _mp_obj_socket_t {
mp_obj_base_t base;
int fd;
+ bool blocking;
} mp_obj_socket_t;
const mp_obj_type_t mp_type_socket;
@@ -78,6 +80,7 @@ STATIC mp_obj_socket_t *socket_new(int fd) {
mp_obj_socket_t *o = m_new_obj(mp_obj_socket_t);
o->base.type = &mp_type_socket;
o->fd = fd;
+ o->blocking = true;
return o;
}
@@ -92,12 +95,14 @@ STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errc
mp_obj_socket_t *o = MP_OBJ_TO_PTR(o_in);
mp_int_t r = read(o->fd, buf, size);
if (r == -1) {
- *errcode = errno;
-
- if (*errcode == EAGAIN) {
- *errcode = MP_ETIMEDOUT;
+ int err = errno;
+ // On blocking socket, we get EAGAIN in case SO_RCVTIMEO/SO_SNDTIMEO
+ // timed out, and need to convert that to ETIMEDOUT.
+ if (err == EAGAIN && o->blocking) {
+ err = MP_ETIMEDOUT;
}
+ *errcode = err;
return MP_STREAM_ERROR;
}
return r;
@@ -107,12 +112,14 @@ STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, in
mp_obj_socket_t *o = MP_OBJ_TO_PTR(o_in);
mp_int_t r = write(o->fd, buf, size);
if (r == -1) {
- *errcode = errno;
-
- if (*errcode == EAGAIN) {
- *errcode = MP_ETIMEDOUT;
+ int err = errno;
+ // On blocking socket, we get EAGAIN in case SO_RCVTIMEO/SO_SNDTIMEO
+ // timed out, and need to convert that to ETIMEDOUT.
+ if (err == EAGAIN && o->blocking) {
+ err = MP_ETIMEDOUT;
}
+ *errcode = err;
return MP_STREAM_ERROR;
}
return r;
@@ -320,6 +327,7 @@ STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
}
flags = fcntl(self->fd, F_SETFL, flags);
RAISE_ERRNO(flags, errno);
+ self->blocking = val;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
@@ -327,21 +335,37 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
STATIC mp_obj_t socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) {
mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
struct timeval tv = {0,};
+ bool new_blocking = true;
+
if (timeout_in == mp_const_none) {
setsockopt(self->fd, SOL_SOCKET, SO_RCVTIMEO, NULL, 0);
setsockopt(self->fd, SOL_SOCKET, SO_SNDTIMEO, NULL, 0);
} else {
- tv.tv_sec = mp_obj_get_int(timeout_in);
-
#if MICROPY_PY_BUILTINS_FLOAT
- tv.tv_usec = (mp_obj_get_float(timeout_in) - tv.tv_sec) * 1000000;
+ mp_float_t val = mp_obj_get_float(timeout_in);
+ double ipart;
+ tv.tv_usec = round(modf(val, &ipart) * 1000000);
+ tv.tv_sec = ipart;
+ #else
+ tv.tv_sec = mp_obj_get_int(timeout_in);
#endif
- setsockopt(self->fd, SOL_SOCKET, SO_RCVTIMEO,
- &tv, sizeof(struct timeval));
- setsockopt(self->fd, SOL_SOCKET, SO_SNDTIMEO,
- &tv, sizeof(struct timeval));
+ // For SO_RCVTIMEO/SO_SNDTIMEO, zero timeout means infinity, but
+ // for Python API it means non-blocking.
+ if (tv.tv_sec == 0 && tv.tv_usec == 0) {
+ new_blocking = false;
+ } else {
+ setsockopt(self->fd, SOL_SOCKET, SO_RCVTIMEO,
+ &tv, sizeof(struct timeval));
+ setsockopt(self->fd, SOL_SOCKET, SO_SNDTIMEO,
+ &tv, sizeof(struct timeval));
+ }
}
+
+ if (self->blocking != new_blocking) {
+ socket_setblocking(self_in, mp_obj_new_bool(new_blocking));
+ }
+
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_settimeout_obj, socket_settimeout);