From 1686346d53051d77daa662f7cc85614cc4ada06a Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 20 Jun 2017 16:52:39 +1000 Subject: [PATCH] cc3200: Make non-zero socket timeout work with connect/accept/send. The CC3100 only allows to set a timeout for receiving data, not for accept, connect or send. But it can set non-blocking for all these operations and this patch uses that feature to implement socket timeout in terms of non- blocking behaviour combined with a loop. --- cc3200/mods/modnetwork.h | 2 +- cc3200/mods/modusocket.c | 171 +++++++++++++++++++++++---------------- 2 files changed, 103 insertions(+), 70 deletions(-) diff --git a/cc3200/mods/modnetwork.h b/cc3200/mods/modnetwork.h index 7ecb8becb5..8a886c3e40 100644 --- a/cc3200/mods/modnetwork.h +++ b/cc3200/mods/modnetwork.h @@ -52,7 +52,7 @@ typedef struct _mod_network_socket_base_t { } u_param; int16_t sd; }; - bool has_timeout; + uint32_t timeout_ms; // 0 for no timeout bool cert_req; } mod_network_socket_base_t; diff --git a/cc3200/mods/modusocket.c b/cc3200/mods/modusocket.c index 40a53b404c..bef421a305 100644 --- a/cc3200/mods/modusocket.c +++ b/cc3200/mods/modusocket.c @@ -34,6 +34,7 @@ #include "py/objstr.h" #include "py/runtime.h" #include "py/stream.h" +#include "py/mphal.h" #include "lib/netutils/netutils.h" #include "modnetwork.h" #include "modusocket.h" @@ -68,6 +69,31 @@ ip[2] = addr.sa_data[3]; \ ip[3] = addr.sa_data[2]; +#define SOCKET_TIMEOUT_QUANTA_MS (20) + +STATIC int convert_sl_errno(int sl_errno) { + return -sl_errno; +} + +// This function is left as non-static so it's not inlined. +int check_timedout(mod_network_socket_obj_t *s, int ret, uint32_t *timeout_ms, int *_errno) { + if (*timeout_ms == 0 || ret != SL_EAGAIN) { + if (s->sock_base.timeout_ms > 0 && ret == SL_EAGAIN) { + *_errno = MP_ETIMEDOUT; + } else { + *_errno = convert_sl_errno(ret); + } + return -1; + } + mp_hal_delay_ms(SOCKET_TIMEOUT_QUANTA_MS); + if (*timeout_ms < SOCKET_TIMEOUT_QUANTA_MS) { + *timeout_ms = 0; + } else { + *timeout_ms -= SOCKET_TIMEOUT_QUANTA_MS; + } + return 0; +} + STATIC int wlan_gethostbyname(const char *name, mp_uint_t len, uint8_t *out_ip, uint8_t family) { uint32_t ip; int result = sl_NetAppDnsGetHostByName((_i8 *)name, (_u16)len, (_u32*)&ip, (_u8)family); @@ -122,70 +148,93 @@ STATIC int wlan_socket_accept(mod_network_socket_obj_t *s, mod_network_socket_ob SlSockAddr_t addr; SlSocklen_t addr_len = sizeof(addr); - sd = sl_Accept(s->sock_base.sd, &addr, &addr_len); - // save the socket descriptor - s2->sock_base.sd = sd; - if (sd < 0) { - *_errno = sd; - return -1; + uint32_t timeout_ms = s->sock_base.timeout_ms; + for (;;) { + sd = sl_Accept(s->sock_base.sd, &addr, &addr_len); + if (sd >= 0) { + // save the socket descriptor + s2->sock_base.sd = sd; + // return ip and port + UNPACK_SOCKADDR(addr, ip, *port); + return 0; + } + if (check_timedout(s, sd, &timeout_ms, _errno)) { + return -1; + } } - - // return ip and port - UNPACK_SOCKADDR(addr, ip, *port); - return 0; } STATIC int wlan_socket_connect(mod_network_socket_obj_t *s, byte *ip, mp_uint_t port, int *_errno) { MAKE_SOCKADDR(addr, ip, port) - int ret = sl_Connect(s->sock_base.sd, &addr, sizeof(addr)); - if (ret != 0) { - *_errno = ret; - return -1; + uint32_t timeout_ms = s->sock_base.timeout_ms; + for (;;) { + int ret = sl_Connect(s->sock_base.sd, &addr, sizeof(addr)); + if (ret == 0) { + return 0; + } + if (check_timedout(s, ret, &timeout_ms, _errno)) { + return -1; + } } - return 0; } STATIC int wlan_socket_send(mod_network_socket_obj_t *s, const byte *buf, mp_uint_t len, int *_errno) { - mp_int_t bytes = 0; - if (len > 0) { - bytes = sl_Send(s->sock_base.sd, (const void *)buf, len, 0); + if (len == 0) { + return 0; } - if (bytes <= 0) { - *_errno = bytes; - return -1; + uint32_t timeout_ms = s->sock_base.timeout_ms; + for (;;) { + int ret = sl_Send(s->sock_base.sd, (const void *)buf, len, 0); + if (ret > 0) { + return ret; + } + if (check_timedout(s, ret, &timeout_ms, _errno)) { + return -1; + } } - return bytes; } STATIC int wlan_socket_recv(mod_network_socket_obj_t *s, byte *buf, mp_uint_t len, int *_errno) { - int ret = sl_Recv(s->sock_base.sd, buf, MIN(len, WLAN_MAX_RX_SIZE), 0); - if (ret < 0) { - *_errno = ret; - return -1; + uint32_t timeout_ms = s->sock_base.timeout_ms; + for (;;) { + int ret = sl_Recv(s->sock_base.sd, buf, MIN(len, WLAN_MAX_RX_SIZE), 0); + if (ret >= 0) { + return ret; + } + if (check_timedout(s, ret, &timeout_ms, _errno)) { + return -1; + } } - return ret; } STATIC int wlan_socket_sendto( mod_network_socket_obj_t *s, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) { MAKE_SOCKADDR(addr, ip, port) - int ret = sl_SendTo(s->sock_base.sd, (byte*)buf, len, 0, (SlSockAddr_t*)&addr, sizeof(addr)); - if (ret < 0) { - *_errno = ret; - return -1; + uint32_t timeout_ms = s->sock_base.timeout_ms; + for (;;) { + int ret = sl_SendTo(s->sock_base.sd, (byte*)buf, len, 0, (SlSockAddr_t*)&addr, sizeof(addr)); + if (ret >= 0) { + return ret; + } + if (check_timedout(s, ret, &timeout_ms, _errno)) { + return -1; + } } - return ret; } STATIC int wlan_socket_recvfrom(mod_network_socket_obj_t *s, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) { SlSockAddr_t addr; SlSocklen_t addr_len = sizeof(addr); - mp_int_t ret = sl_RecvFrom(s->sock_base.sd, buf, MIN(len, WLAN_MAX_RX_SIZE), 0, &addr, &addr_len); - if (ret < 0) { - *_errno = ret; - return -1; + uint32_t timeout_ms = s->sock_base.timeout_ms; + for (;;) { + int ret = sl_RecvFrom(s->sock_base.sd, buf, MIN(len, WLAN_MAX_RX_SIZE), 0, &addr, &addr_len); + if (ret >= 0) { + UNPACK_SOCKADDR(addr, ip, *port); + return ret; + } + if (check_timedout(s, ret, &timeout_ms, _errno)) { + return -1; + } } - UNPACK_SOCKADDR(addr, ip, *port); - return ret; } STATIC int wlan_socket_setsockopt(mod_network_socket_obj_t *s, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno) { @@ -198,10 +247,8 @@ STATIC int wlan_socket_setsockopt(mod_network_socket_obj_t *s, mp_uint_t level, } STATIC int wlan_socket_settimeout(mod_network_socket_obj_t *s, mp_uint_t timeout_s, int *_errno) { - int ret; - bool has_timeout; + SlSockNonblocking_t option; if (timeout_s == 0 || timeout_s == -1) { - SlSockNonblocking_t option; if (timeout_s == 0) { // set non-blocking mode option.NonblockingEnabled = 1; @@ -209,23 +256,19 @@ STATIC int wlan_socket_settimeout(mod_network_socket_obj_t *s, mp_uint_t timeout // set blocking mode option.NonblockingEnabled = 0; } - ret = sl_SetSockOpt(s->sock_base.sd, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &option, sizeof(option)); - has_timeout = false; + timeout_s = 0; } else { - // set timeout - struct SlTimeval_t timeVal; - timeVal.tv_sec = timeout_s; // seconds - timeVal.tv_usec = 0; // microseconds. 10000 microseconds resolution - ret = sl_SetSockOpt(s->sock_base.sd, SL_SOL_SOCKET, SL_SO_RCVTIMEO, &timeVal, sizeof(timeVal)); - has_timeout = true; + // synthesize timeout via non-blocking behaviour with a loop + option.NonblockingEnabled = 1; } + int ret = sl_SetSockOpt(s->sock_base.sd, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &option, sizeof(option)); if (ret != 0) { *_errno = ret; return -1; } - s->sock_base.has_timeout = has_timeout; + s->sock_base.timeout_ms = timeout_s * 1000; return 0; } @@ -379,7 +422,7 @@ STATIC mp_obj_t socket_make_new(const mp_obj_type_t *type, size_t n_args, size_t s->sock_base.u_param.type = SL_SOCK_STREAM; s->sock_base.u_param.proto = SL_IPPROTO_TCP; s->sock_base.u_param.fileno = -1; - s->sock_base.has_timeout = false; + s->sock_base.timeout_ms = 0; s->sock_base.cert_req = false; if (n_args > 0) { @@ -462,7 +505,7 @@ STATIC mp_obj_t socket_accept(mp_obj_t self_in) { mp_uint_t port = 0; int _errno = 0; if (wlan_socket_accept(self, socket2, ip, &port, &_errno) != 0) { - mp_raise_OSError(-_errno); + mp_raise_OSError(_errno); } // add the socket to the list @@ -490,7 +533,7 @@ STATIC mp_obj_t socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { if (!self->sock_base.cert_req && _errno == SL_ESECSNOVERIFY) { return mp_const_none; } - mp_raise_OSError(-_errno); + mp_raise_OSError(_errno); } return mp_const_none; } @@ -504,7 +547,7 @@ STATIC mp_obj_t socket_send(mp_obj_t self_in, mp_obj_t buf_in) { int _errno; mp_int_t ret = wlan_socket_send(self, bufinfo.buf, bufinfo.len, &_errno); if (ret < 0) { - mp_raise_OSError(-_errno); + mp_raise_OSError(_errno); } return mp_obj_new_int_from_uint(ret); } @@ -519,10 +562,7 @@ STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { int _errno; mp_int_t ret = wlan_socket_recv(self, (byte*)vstr.buf, len, &_errno); if (ret < 0) { - if (_errno == MP_EAGAIN && self->sock_base.has_timeout) { - mp_raise_OSError(MP_ETIMEDOUT); - } - mp_raise_OSError(-_errno); + mp_raise_OSError(_errno); } if (ret == 0) { return mp_const_empty_bytes; @@ -549,7 +589,7 @@ STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_ int _errno = 0; mp_int_t ret = wlan_socket_sendto(self, bufinfo.buf, bufinfo.len, ip, port, &_errno); if (ret < 0) { - mp_raise_OSError(-_errno); + mp_raise_OSError(_errno); } return mp_obj_new_int(ret); } @@ -565,10 +605,7 @@ STATIC mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { int _errno = 0; mp_int_t ret = wlan_socket_recvfrom(self, (byte*)vstr.buf, vstr.len, ip, &port, &_errno); if (ret < 0) { - if (_errno == MP_EAGAIN && self->sock_base.has_timeout) { - mp_raise_OSError(MP_ETIMEDOUT); - } - mp_raise_OSError(-_errno); + mp_raise_OSError(_errno); } mp_obj_t tuple[2]; if (ret == 0) { @@ -666,7 +703,7 @@ STATIC const mp_map_elem_t socket_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_makefile), (mp_obj_t)&socket_makefile_obj }, // stream methods - { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read1_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj }, @@ -680,10 +717,8 @@ STATIC mp_uint_t socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *e if (ret < 0) { // we need to ignore the socket closed error here because a read() without params // only returns when the socket is closed by the other end - if (*errcode != SL_ESECCLOSED) { + if (*errcode != -SL_ESECCLOSED) { ret = MP_STREAM_ERROR; - // needed to convert simplelink's negative error codes to POSIX - (*errcode) *= -1; } else { ret = 0; } @@ -696,8 +731,6 @@ STATIC mp_uint_t socket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, mp_int_t ret = wlan_socket_send(self, buf, size, errcode); if (ret < 0) { ret = MP_STREAM_ERROR; - // needed to convert simplelink's negative error codes to POSIX - (*errcode) *= -1; } return ret; }