Skip to content

Commit aae361b

Browse files
nickzoicdpgeorge
authored andcommitted
esp32/modsocket.c: sockets always nonblocking, accept timeout in modsocket
1 parent 3d903f5 commit aae361b

File tree

1 file changed

+75
-63
lines changed

1 file changed

+75
-63
lines changed

esp32/modsocket.c

Lines changed: 75 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "py/mperrno.h"
4545
#include "py/mphal.h"
4646
#include "py/stream.h"
47+
#include "py/mperrno.h"
4748
#include "lib/netutils/netutils.h"
4849
#include "tcpip_adapter.h"
4950

@@ -52,12 +53,15 @@
5253
#include "lwip/ip4.h"
5354
#include "esp_log.h"
5455

56+
#define SOCKET_POLL_US (100000)
57+
5558
typedef struct _socket_obj_t {
5659
mp_obj_base_t base;
5760
int fd;
5861
uint8_t domain;
5962
uint8_t type;
6063
uint8_t proto;
64+
unsigned int retries;
6165
} socket_obj_t;
6266

6367
NORETURN static void exception_from_errno(int _errno) {
@@ -68,6 +72,14 @@ NORETURN static void exception_from_errno(int _errno) {
6872
mp_raise_OSError(_errno);
6973
}
7074

75+
void check_for_exceptions() {
76+
mp_obj_t exc = MP_STATE_VM(mp_pending_exception);
77+
if (exc != MP_OBJ_NULL) {
78+
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
79+
nlr_raise(exc);
80+
}
81+
}
82+
7183
STATIC mp_obj_t socket_close(const mp_obj_t arg0) {
7284
socket_obj_t *self = MP_OBJ_TO_PTR(arg0);
7385
if (self->fd >= 0) {
@@ -115,15 +127,17 @@ STATIC mp_obj_t socket_bind(const mp_obj_t arg0, const mp_obj_t arg1) {
115127
_socket_getaddrinfo(arg1, &res);
116128
int r = lwip_bind_r(self->fd, res->ai_addr, res->ai_addrlen);
117129
lwip_freeaddrinfo(res);
118-
return mp_obj_new_int(r);
130+
if (r < 0) exception_from_errno(errno);
131+
return mp_const_none;
119132
}
120133
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind);
121134

122135
STATIC mp_obj_t socket_listen(const mp_obj_t arg0, const mp_obj_t arg1) {
123136
socket_obj_t *self = MP_OBJ_TO_PTR(arg0);
124137
int backlog = mp_obj_get_int(arg1);
125-
int x = lwip_listen_r(self->fd, backlog);
126-
return (x == 0) ? mp_const_true : mp_const_false;
138+
int r = lwip_listen_r(self->fd, backlog);
139+
if (r < 0) exception_from_errno(errno);
140+
return mp_const_none;
127141
}
128142
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_listen_obj, socket_listen);
129143

@@ -132,15 +146,20 @@ STATIC mp_obj_t socket_accept(const mp_obj_t arg0) {
132146

133147
struct sockaddr addr;
134148
socklen_t addr_len = sizeof(addr);
135-
int x = lwip_accept_r(self->fd, &addr, &addr_len);
136-
if (x < 0) {
137-
exception_from_errno(errno);
149+
150+
int new_fd = -1;
151+
for (int i=0; i<=self->retries; i++) {
152+
new_fd = lwip_accept_r(self->fd, &addr, &addr_len);
153+
if (new_fd >= 0) break;
154+
if (errno != EAGAIN) exception_from_errno(errno);
155+
check_for_exceptions();
138156
}
157+
if (new_fd < 0) mp_raise_OSError(MP_ETIMEDOUT);
139158

140159
// create new socket object
141160
socket_obj_t *sock = m_new_obj_with_finaliser(socket_obj_t);
142161
sock->base.type = self->base.type;
143-
sock->fd = x;
162+
sock->fd = new_fd;
144163
sock->domain = self->domain;
145164
sock->type = self->type;
146165
sock->proto = self->proto;
@@ -165,6 +184,7 @@ STATIC mp_obj_t socket_connect(const mp_obj_t arg0, const mp_obj_t arg1) {
165184
if (r != 0) {
166185
exception_from_errno(errno);
167186
}
187+
168188
return mp_const_none;
169189
}
170190
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect);
@@ -191,83 +211,63 @@ STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) {
191211
}
192212
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt);
193213

194-
static void _socket_settimeout(int fd, unsigned long timeout_us) {
214+
void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms) {
215+
sock->retries = timeout_ms * 1000 / SOCKET_POLL_US;
216+
195217
struct timeval timeout = {
196-
.tv_sec = timeout_us / 1000000,
197-
.tv_usec = timeout_us % 1000000
218+
.tv_sec = 0,
219+
.tv_usec = timeout_ms ? SOCKET_POLL_US : 0
198220
};
199-
lwip_setsockopt_r(fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&timeout, sizeof(timeout));
200-
lwip_setsockopt_r(fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout));
201-
lwip_fcntl_r(fd, F_SETFL, 0);
202-
}
203-
204-
static void _socket_setnonblock(int fd) {
205-
struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
206-
lwip_setsockopt_r(fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&timeout, sizeof(timeout));
207-
lwip_setsockopt_r(fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout));
208-
lwip_fcntl_r(fd, F_SETFL, O_NONBLOCK);
221+
lwip_setsockopt_r(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&timeout, sizeof(timeout));
222+
lwip_setsockopt_r(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout));
223+
lwip_fcntl_r(sock->fd, F_SETFL, timeout_ms ? 0 : O_NONBLOCK);
209224
}
210225

211226
STATIC mp_obj_t socket_settimeout(const mp_obj_t arg0, const mp_obj_t arg1) {
212227
socket_obj_t *self = MP_OBJ_TO_PTR(arg0);
213-
if (arg1 == mp_const_none) {
214-
_socket_settimeout(self->fd, 0);
215-
} else {
216-
unsigned long timeout_us = (unsigned long)(mp_obj_get_float(arg1) * 1000000);
217-
if (timeout_us == 0) _socket_setnonblock(self->fd);
218-
else _socket_settimeout(self->fd, timeout_us);
219-
}
228+
if (arg1 == mp_const_none) _socket_settimeout(self, UINT_MAX);
229+
else _socket_settimeout(self, mp_obj_get_float(arg1) * 1000L);
220230
return mp_const_none;
221231
}
222232
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_settimeout_obj, socket_settimeout);
223233

224234
STATIC mp_obj_t socket_setblocking(const mp_obj_t arg0, const mp_obj_t arg1) {
225235
socket_obj_t *self = MP_OBJ_TO_PTR(arg0);
226-
if (mp_obj_is_true(arg1)) _socket_settimeout(self->fd, 0);
227-
else _socket_setnonblock(self->fd);
236+
if (mp_obj_is_true(arg1)) _socket_settimeout(self, UINT_MAX);
237+
else _socket_settimeout(self, 0);
228238
return mp_const_none;
229239
}
230240
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
231-
232-
STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
233-
socket_obj_t *self = MP_OBJ_TO_PTR(self_in);
241+
242+
mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in,
243+
struct sockaddr *from, socklen_t *from_len) {
244+
socket_obj_t *sock = MP_OBJ_TO_PTR(self_in);
234245
size_t len = mp_obj_get_int(len_in);
235246
vstr_t vstr;
236247
vstr_init_len(&vstr, len);
237-
int x = lwip_recvfrom_r(self->fd, vstr.buf, len, 0, NULL, NULL);
238-
if (x >= 0) {
239-
vstr.len = x;
240-
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
248+
249+
// XXX Would be nicer to use RTC to handle timeouts
250+
for (int i=0; i<=sock->retries; i++) {
251+
int r = lwip_recvfrom_r(sock->fd, vstr.buf, len, 0, from, from_len);
252+
if (r >= 0) { vstr.len = r; return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); }
253+
if (errno != EWOULDBLOCK) exception_from_errno(errno);
254+
check_for_exceptions();
241255
}
242-
if (errno == EWOULDBLOCK) return mp_const_empty_bytes;
243-
exception_from_errno(errno);
256+
mp_raise_OSError(MP_ETIMEDOUT);
257+
}
258+
259+
STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
260+
return _socket_recvfrom(self_in, len_in, NULL, NULL);
244261
}
245262
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv);
246263

247264
STATIC mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
248-
socket_obj_t *self = MP_OBJ_TO_PTR(self_in);
249-
250-
// create the destination buffer
251-
mp_int_t len = mp_obj_get_int(len_in);
252-
vstr_t vstr;
253-
vstr_init_len(&vstr, len);
254-
255-
// do the receive
256265
struct sockaddr from;
257266
socklen_t fromlen = sizeof(from);
258-
int ret = lwip_recvfrom_r(self->fd, vstr.buf, len, 0, &from, &fromlen);
259-
if (ret == -1) {
260-
exception_from_errno(errno);
261-
}
262267

263-
// make the return value
264268
mp_obj_t tuple[2];
265-
if (ret == 0) {
266-
tuple[0] = mp_const_empty_bytes;
267-
} else {
268-
vstr.len = ret;
269-
tuple[0] = mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
270-
}
269+
tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen);
270+
271271
uint8_t *ip = (uint8_t*)&((struct sockaddr_in*)&from)->sin_addr;
272272
mp_uint_t port = lwip_ntohs(((struct sockaddr_in*)&from)->sin_port);
273273
tuple[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG);
@@ -338,13 +338,22 @@ STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) {
338338
}
339339
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile);
340340

341+
342+
// XXX this can end up waiting a very long time if the content is dribbled in one character
343+
// at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not
344+
// good behaviour.
345+
341346
STATIC mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
342-
socket_obj_t *socket = self_in;
343-
int x = lwip_recvfrom_r(socket->fd, buf, size, 0, NULL, NULL);
344-
if (x >= 0) return x;
345-
if (errno == EWOULDBLOCK) return 0;
346-
*errcode = MP_EIO;
347-
return MP_STREAM_ERROR;
347+
socket_obj_t *sock = self_in;
348+
349+
// XXX Would be nicer to use RTC to handle timeouts
350+
for (int i=0; i<=sock->retries; i++) {
351+
int x = lwip_recvfrom_r(sock->fd, buf, size, 0, NULL, NULL);
352+
if (x > 0) return x;
353+
if (x < 0 && errno != EWOULDBLOCK) { *errcode = errno; return MP_STREAM_ERROR; }
354+
check_for_exceptions();
355+
}
356+
return 0; // causes a timeout error to be raised.
348357
}
349358

350359
STATIC mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
@@ -437,10 +446,13 @@ STATIC mp_obj_t get_socket(size_t n_args, const mp_obj_t *args) {
437446
}
438447
}
439448
}
449+
440450
sock->fd = lwip_socket(sock->domain, sock->type, sock->proto);
441451
if (sock->fd < 0) {
442452
exception_from_errno(errno);
443453
}
454+
_socket_settimeout(sock, UINT_MAX);
455+
444456
return MP_OBJ_FROM_PTR(sock);
445457
}
446458
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_socket_obj, 0, 3, get_socket);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy