From cacdbb526241fd67df9b35a99a93dbd8c5ca3000 Mon Sep 17 00:00:00 2001 From: sg Date: Wed, 10 Dec 2014 20:45:01 +0100 Subject: [PATCH] added option LWIP_NETCONN_SEM_PER_THREAD to use a semaphore per thread instead of using one per netconn and per select call --- CHANGELOG | 4 ++ src/api/api_lib.c | 27 +++++++++++- src/api/api_msg.c | 63 +++++++++++++++++----------- src/api/sockets.c | 84 +++++++++++++++++++++++++++++--------- src/api/tcpip.c | 7 +++- src/include/lwip/api.h | 14 ++++++- src/include/lwip/api_msg.h | 10 +++++ src/include/lwip/opt.h | 14 +++++++ src/include/lwip/sockets.h | 4 ++ src/include/lwip/tcpip.h | 8 +++- 10 files changed, 187 insertions(+), 48 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f49d444d..8bfae789 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,10 @@ HISTORY ++ New features: + 2014-12-10: Simon Goldschmidt + * api: added option LWIP_NETCONN_SEM_PER_THREAD to use a semaphore per thread + instead of using one per netconn and per select call + 2014-12-08: Simon Goldschmidt * ip6.h: fixed bug #43778: IPv6 header version not set on 16-bit platform (macro IP6H_VTCFL_SET()) diff --git a/src/api/api_lib.c b/src/api/api_lib.c index 8c7226fa..97177206 100644 --- a/src/api/api_lib.c +++ b/src/api/api_lib.c @@ -86,12 +86,14 @@ netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_cal API_MSG_VAR_FREE(msg); if (err != ERR_OK) { LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); - LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed)); LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox)); #if LWIP_TCP LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox)); #endif /* LWIP_TCP */ +#if !LWIP_NETCONN_SEM_PER_THREAD + LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed)); sys_sem_free(&conn->op_completed); +#endif /* !LWIP_NETCONN_SEM_PER_THREAD */ sys_mbox_free(&conn->recvmbox); memp_free(MEMP_NETCONN, conn); return NULL; @@ -827,7 +829,7 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr) LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;); LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;); #if LWIP_MPU_COMPATIBLE - if (strlen(name >= DNS_MAX_NAME_LENGTH) { + if (strlen(name) >= DNS_MAX_NAME_LENGTH) { return ERR_ARG; } #endif @@ -862,4 +864,25 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr) } #endif /* LWIP_DNS*/ +#if LWIP_NETCONN_SEM_PER_THREAD +void netconn_thread_init(void) +{ + sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET(); + if (sem == SYS_SEM_NULL) { + /* call alloc only once */ + LWIP_NETCONN_THREAD_SEM_ALLOC(); + LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", LWIP_NETCONN_THREAD_SEM_GET() != SYS_SEM_NULL); + } +} + +void netconn_thread_cleanup(void) +{ + sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET(); + if (sem == SYS_SEM_NULL) { + /* call free only once */ + LWIP_NETCONN_THREAD_SEM_FREE(); + } +} +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + #endif /* LWIP_NETCONN */ diff --git a/src/api/api_msg.c b/src/api/api_msg.c index 2a90e93e..3528a461 100644 --- a/src/api/api_msg.c +++ b/src/api/api_msg.c @@ -70,7 +70,7 @@ static void lwip_netconn_do_close_internal(struct netconn *conn); #if LWIP_RAW /** * Receive callback function for RAW netconns. - * Doesn't 'eat' the packet, only references it and sends it to + * Doesn't 'eat' the packet, only copies it and sends it to * conn->recvmbox * * @see raw.h (struct raw_pcb.recv) for parameters and return value @@ -365,8 +365,9 @@ err_tcp(void *arg, err_t err) old_state = conn->state; conn->state = NETCONN_NONE; - /* Notify the user layer about a connection error. Used to signal - select. */ + /* @todo: the type of NETCONN_EVT created should depend on 'old_state' */ + + /* Notify the user layer about a connection error. Used to signal select. */ API_EVENT(conn, NETCONN_EVT_ERROR, 0); /* Try to release selects pending on 'read' or 'write', too. They will get an error if they actually try to read or write. */ @@ -392,12 +393,15 @@ err_tcp(void *arg, err_t err) SET_NONBLOCKING_CONNECT(conn, 0); if (!was_nonblocking_connect) { + sys_sem_t* op_completed_sem; /* set error return code */ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); conn->current_msg->err = err; + op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); + LWIP_ASSERT("inavlid op_completed_sem", op_completed_sem != SYS_SEM_NULL); conn->current_msg = NULL; /* wake up the waiting task */ - sys_sem_signal(&conn->op_completed); + sys_sem_signal(op_completed_sem); } } else { LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL); @@ -553,7 +557,7 @@ void lwip_netconn_do_newconn(struct api_msg_msg *msg) { msg->err = ERR_OK; - if(msg->conn->pcb.tcp == NULL) { + if (msg->conn->pcb.tcp == NULL) { pcb_new(msg); } /* Else? This "new" connection already has a PCB allocated. */ @@ -588,7 +592,7 @@ netconn_alloc(enum netconn_type t, netconn_callback callback) conn->type = t; conn->pcb.tcp = NULL; - /* If all sizes are the same, every compiler should optimize this switch to nothing, */ + /* If all sizes are the same, every compiler should optimize this switch to nothing */ switch(NETCONNTYPE_GROUP(t)) { #if LWIP_RAW case NETCONN_RAW: @@ -610,13 +614,15 @@ netconn_alloc(enum netconn_type t, netconn_callback callback) goto free_and_return; } - if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { - goto free_and_return; - } if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { - sys_sem_free(&conn->op_completed); goto free_and_return; } +#if !LWIP_NETCONN_SEM_PER_THREAD + if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { + sys_mbox_free(&conn->recvmbox); + goto free_and_return; + } +#endif #if LWIP_TCP sys_mbox_set_invalid(&conn->acceptmbox); @@ -665,8 +671,10 @@ netconn_free(struct netconn *conn) !sys_mbox_valid(&conn->acceptmbox)); #endif /* LWIP_TCP */ +#if !LWIP_NETCONN_SEM_PER_THREAD sys_sem_free(&conn->op_completed); sys_sem_set_invalid(&conn->op_completed); +#endif memp_free(MEMP_NETCONN, conn); } @@ -790,6 +798,7 @@ lwip_netconn_do_close_internal(struct netconn *conn) } if (err == ERR_OK) { /* Closing succeeded */ + sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); conn->current_msg->err = ERR_OK; conn->current_msg = NULL; conn->state = NETCONN_NONE; @@ -807,7 +816,7 @@ lwip_netconn_do_close_internal(struct netconn *conn) API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); } /* wake up the application task */ - sys_sem_signal(&conn->op_completed); + sys_sem_signal(op_completed_sem); } else { /* Closing failed, restore some of the callbacks */ /* Closing of listen pcb will never fail! */ @@ -884,8 +893,8 @@ lwip_netconn_do_delconn(struct api_msg_msg *msg) API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); } - if (sys_sem_valid(&msg->conn->op_completed)) { - sys_sem_signal(&msg->conn->op_completed); + if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) { + sys_sem_signal(LWIP_API_MSG_SEM(msg)); } } @@ -940,6 +949,7 @@ lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err) { struct netconn *conn; int was_blocking; + sys_sem_t* op_completed_sem = NULL; LWIP_UNUSED_ARG(pcb); @@ -955,12 +965,16 @@ lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err) if (conn->current_msg != NULL) { conn->current_msg->err = err; + op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); } if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) { setup_tcp(conn); } was_blocking = !IN_NONBLOCKING_CONNECT(conn); SET_NONBLOCKING_CONNECT(conn, 0); + LWIP_ASSERT("blocking connect state error", + (was_blocking && op_completed_sem != NULL) || + (!was_blocking && op_completed_sem == NULL)); conn->current_msg = NULL; conn->state = NETCONN_NONE; if (!was_blocking) { @@ -969,7 +983,7 @@ lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err) API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); if (was_blocking) { - sys_sem_signal(&conn->op_completed); + sys_sem_signal(op_completed_sem); } return ERR_OK; } @@ -990,7 +1004,7 @@ lwip_netconn_do_connect(struct api_msg_msg *msg) msg->err = ERR_CLSD; if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ - sys_sem_signal(&msg->conn->op_completed); + sys_sem_signal(LWIP_API_MSG_SEM(msg)); return; } } else { @@ -1029,7 +1043,7 @@ lwip_netconn_do_connect(struct api_msg_msg *msg) } } /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ - sys_sem_signal(&msg->conn->op_completed); + sys_sem_signal(LWIP_API_MSG_SEM(msg)); return; #endif /* LWIP_TCP */ default: @@ -1286,7 +1300,7 @@ lwip_netconn_do_writemore(struct netconn *conn) if (available < len) { /* don't try to write more than sendbuf */ len = available; - if (dontblock){ + if (dontblock) { if (!len) { err = ERR_WOULDBLOCK; goto err_mem; @@ -1347,6 +1361,7 @@ err_mem: if (write_finished) { /* everything was written: set back connection state and back to application task */ + sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); conn->current_msg->err = err; conn->current_msg = NULL; conn->state = NETCONN_NONE; @@ -1354,12 +1369,13 @@ err_mem: if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0) #endif { - sys_sem_signal(&conn->op_completed); + sys_sem_signal(op_completed_sem); } } #if LWIP_TCPIP_CORE_LOCKING - else + else { return ERR_MEM; + } #endif return ERR_OK; } @@ -1395,9 +1411,9 @@ lwip_netconn_do_write(struct api_msg_msg *msg) if (lwip_netconn_do_writemore(msg->conn) != ERR_OK) { LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); UNLOCK_TCPIP_CORE(); - sys_arch_sem_wait(&msg->conn->op_completed, 0); + sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); LOCK_TCPIP_CORE(); - LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE); + LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE); } #else /* LWIP_TCPIP_CORE_LOCKING */ lwip_netconn_do_writemore(msg->conn); @@ -1484,8 +1500,9 @@ lwip_netconn_do_getaddr(struct api_msg_msg *msg) } /** - * Close a TCP pcb contained in a netconn + * Close or half-shutdown a TCP pcb contained in a netconn * Called from netconn_close + * In contrast to closing sockets, the netconn is not deallocated. * * @param msg the api_msg_msg pointing to the connection */ @@ -1521,7 +1538,7 @@ lwip_netconn_do_close(struct api_msg_msg *msg) { msg->err = ERR_VAL; } - sys_sem_signal(&msg->conn->op_completed); + sys_sem_signal(LWIP_API_MSG_SEM(msg)); } #if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) diff --git a/src/api/sockets.c b/src/api/sockets.c index 847d23a0..71060fbe 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -125,7 +125,7 @@ #define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \ name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \ if (name == NULL) { \ - sock_set_errno(sock, ERR_MEM); \ + sock_set_errno(sock, ENOMEM); \ return -1; \ } }while(0) #else /* LWIP_MPU_COMPATIBLE */ @@ -135,6 +135,13 @@ #define NUM_SOCKETS MEMP_NUM_NETCONN +/** This is overridable for the rare case where more than 255 threads + * select on the same socket... + */ +#ifndef SELWAIT_T +#define SELWAIT_T u8_t +#endif + /** Contains all internal pointers and states used for a socket */ struct lwip_sock { /** sockets currently are built on netconns, each socket has one netconn */ @@ -150,13 +157,21 @@ struct lwip_sock { tested by select */ u16_t sendevent; /** error happened for this socket, set by event_callback(), tested by select */ - u16_t errevent; - /** last error that occurred on this socket */ - int err; + u16_t errevent; + /** last error that occurred on this socket (in fact, all our errnos fit into an u8_t) */ + u8_t err; /** counter of how many threads are waiting for this socket using select */ - int select_waiting; + SELWAIT_T select_waiting; }; +#if LWIP_NETCONN_SEM_PER_THREAD +#define SELECT_SEM_T sys_sem_t* +#define SELECT_SEM_PTR(sem) (sem) +#else /* LWIP_NETCONN_SEM_PER_THREAD */ +#define SELECT_SEM_T sys_sem_t +#define SELECT_SEM_PTR(sem) (&(sem)) +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + /** Description for a task waiting in select */ struct lwip_select_cb { /** Pointer to the next waiting task */ @@ -172,7 +187,7 @@ struct lwip_select_cb { /** don't signal the same semaphore twice: set to 1 when signalled */ int sem_signalled; /** semaphore to wake up a task waiting for select */ - sys_sem_t sem; + SELECT_SEM_T sem; }; /** A struct sockaddr replacement that has the same alignment as sockaddr_in/ @@ -537,8 +552,8 @@ lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) { /* sockaddr does not match socket type (IPv4/IPv6) */ - sock_set_errno(sock, err_to_errno(ERR_VAL)); - return -1; + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; } LWIP_UNUSED_ARG(namelen); @@ -1153,7 +1168,6 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, fd_set lreadset, lwriteset, lexceptset; u32_t msectimeout; struct lwip_select_cb select_cb; - err_t err; int i; SYS_ARCH_DECL_PROTECT(lev); @@ -1186,12 +1200,15 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, select_cb.writeset = writeset; select_cb.exceptset = exceptset; select_cb.sem_signalled = 0; - err = sys_sem_new(&select_cb.sem, 0); - if (err != ERR_OK) { +#if LWIP_NETCONN_SEM_PER_THREAD + select_cb.sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else /* LWIP_NETCONN_SEM_PER_THREAD */ + if (sys_sem_new(&select_cb.sem, 0) != ERR_OK) { /* failed to create semaphore */ set_errno(ENOMEM); return -1; } +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ /* Protect the select_cb_list */ SYS_ARCH_PROTECT(lev); @@ -1217,7 +1234,7 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, LWIP_ASSERT("sock != NULL", sock != NULL); SYS_ARCH_PROTECT(lev); sock->select_waiting++; - LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + LWIP_ASSERT("sock->select_waiting overflow", sock->select_waiting > 0); SYS_ARCH_UNPROTECT(lev); } } @@ -1238,7 +1255,7 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, } } - waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout); + waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout); } /* Increase select_waiting for each socket we are interested in */ for(i = 0; i < maxfdp1; i++) { @@ -1269,8 +1286,11 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, select_cb_ctr++; SYS_ARCH_UNPROTECT(lev); +#if !LWIP_NETCONN_SEM_PER_THREAD sys_sem_free(&select_cb.sem); - if (waitres == SYS_ARCH_TIMEOUT) { +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + if (waitres == SYS_ARCH_TIMEOUT) { /* Timeout */ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); /* This is OK as the local fdsets are empty and nready is zero, @@ -1403,7 +1423,7 @@ again: scb->sem_signalled = 1; /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might lead to the select thread taking itself off the list, invalidating the semaphore. */ - sys_sem_signal(&scb->sem); + sys_sem_signal(SELECT_SEM_PTR(scb->sem)); } } /* unlock interrupts with each step */ @@ -1742,8 +1762,19 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval = optval; LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen; LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = err; +#if LWIP_TCPIP_CORE_LOCKING + LOCK_TCPIP_CORE(); + lwip_getsockopt_internal(&LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); + UNLOCK_TCPIP_CORE(); +#else /* LWIP_TCPIP_CORE_LOCKING */ +#if LWIP_NETCONN_SEM_PER_THREAD + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed; +#endif tcpip_callback(lwip_getsockopt_internal, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); - sys_arch_sem_wait(&sock->conn->op_completed, 0); + sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0); +#endif /* LWIP_TCPIP_CORE_LOCKING */ /* maybe lwip_getsockopt_internal has changed err */ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err; LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data); @@ -1993,7 +2024,9 @@ lwip_getsockopt_internal(void *arg) LWIP_ASSERT("unhandled level", 0); break; } /* switch (level) */ - sys_sem_signal(&sock->conn->op_completed); +#if !LWIP_TCPIP_CORE_LOCKING + sys_sem_signal((sys_sem_t*)(data->completed_sem)); +#endif } int @@ -2253,8 +2286,19 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval = (void*)optval; LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = &optlen; LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = err; +#if LWIP_TCPIP_CORE_LOCKING + LOCK_TCPIP_CORE(); + lwip_setsockopt_internal(&LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); + UNLOCK_TCPIP_CORE(); +#else /* LWIP_TCPIP_CORE_LOCKING */ +#if LWIP_NETCONN_SEM_PER_THREAD + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed; +#endif tcpip_callback(lwip_setsockopt_internal, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); - sys_arch_sem_wait(&sock->conn->op_completed, 0); + sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0); +#endif /* LWIP_TCPIP_CORE_LOCKING */ /* maybe lwip_setsockopt_internal has changed err */ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err; LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data); @@ -2510,7 +2554,9 @@ lwip_setsockopt_internal(void *arg) LWIP_ASSERT("unhandled level", 0); break; } /* switch (level) */ - sys_sem_signal(&sock->conn->op_completed); +#if !LWIP_TCPIP_CORE_LOCKING + sys_sem_signal((sys_sem_t*)(data->completed_sem)); +#endif } int diff --git a/src/api/tcpip.c b/src/api/tcpip.c index d0923df6..452616cf 100644 --- a/src/api/tcpip.c +++ b/src/api/tcpip.c @@ -338,8 +338,13 @@ tcpip_apimsg(struct api_msg *apimsg) TCPIP_MSG_VAR_ALLOC(msg); TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API; TCPIP_MSG_VAR_REF(msg).msg.apimsg = apimsg; +#if LWIP_NETCONN_SEM_PER_THREAD + apimsg->msg.op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); + LWIP_ASSERT("netconn semaphore not initialized", + apimsg->msg.op_completed_sem != SYS_SEM_NULL); +#endif sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg)); - sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0); + sys_arch_sem_wait(LWIP_API_MSG_SEM(&apimsg->msg), 0); TCPIP_MSG_VAR_FREE(msg); return apimsg->msg.err; } diff --git a/src/include/lwip/api.h b/src/include/lwip/api.h index 74379c84..cc4fe25f 100644 --- a/src/include/lwip/api.h +++ b/src/include/lwip/api.h @@ -172,8 +172,10 @@ struct netconn { } pcb; /** the last error this netconn had */ err_t last_err; +#if !LWIP_NETCONN_SEM_PER_THREAD /** sem that is used to synchronously execute functions in the core context */ sys_sem_t op_completed; +#endif /** mbox where received packets are stored until they are fetched by the netconn application thread (can grow quite big) */ sys_mbox_t recvmbox; @@ -188,11 +190,11 @@ struct netconn { #endif /* LWIP_SOCKET */ #if LWIP_SO_SNDTIMEO /** timeout to wait for sending data (which means enqueueing data for sending - in internal buffers) */ + in internal buffers) in milliseconds */ s32_t send_timeout; #endif /* LWIP_SO_RCVTIMEO */ #if LWIP_SO_RCVTIMEO - /** timeout to wait for new data to be received + /** timeout in milliseconds to wait for new data to be received (or connections to arrive for listening netconns) */ int recv_timeout; #endif /* LWIP_SO_RCVTIMEO */ @@ -329,6 +331,14 @@ err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); #define netconn_get_recvbufsize(conn) ((conn)->recv_bufsize) #endif /* LWIP_SO_RCVBUF*/ +#if LWIP_NETCONN_SEM_PER_THREAD +void netconn_thread_init(void); +void netconn_thread_cleanup(void); +#else /* LWIP_NETCONN_SEM_PER_THREAD */ +#define netconn_thread_init() +#define netconn_thread_cleanup() +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + #ifdef __cplusplus } #endif diff --git a/src/include/lwip/api_msg.h b/src/include/lwip/api_msg.h index c796a553..ffef8609 100644 --- a/src/include/lwip/api_msg.h +++ b/src/include/lwip/api_msg.h @@ -121,8 +121,18 @@ struct api_msg_msg { } lb; #endif /* TCP_LISTEN_BACKLOG */ } msg; +#if LWIP_NETCONN_SEM_PER_THREAD + sys_sem_t* op_completed_sem; +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ }; +#if LWIP_NETCONN_SEM_PER_THREAD +#define LWIP_API_MSG_SEM(msg) ((msg)->op_completed_sem) +#else /* LWIP_NETCONN_SEM_PER_THREAD */ +#define LWIP_API_MSG_SEM(msg) (&(msg)->conn->op_completed) +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + /** This struct contains a function to execute in another thread context and a struct api_msg_msg that serves as an argument for this function. This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */ diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index e0adf77e..f3dd8aa7 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -1473,6 +1473,20 @@ #define LWIP_TCPIP_TIMEOUT 1 #endif +/** LWIP_NETCONN_SEM_PER_THREAD==1: Use one (thread-local) semaphore per + * thread calling socket/netconn functions instead of allocating one + * semaphore per netconn (and per select etc.) + * ATTENTION: a thread-local semaphore for API calls is needed: + * - LWIP_NETCONN_THREAD_SEM_GET() returning a sys_sem_t* + * - LWIP_NETCONN_THREAD_SEM_ALLOC() creating the semaphore + * - LWIP_NETCONN_THREAD_SEM_FREE() freeing the semaphore + * The latter 2 can be invoked up by calling netconn_thread_init()/netconn_thread_cleanup(). + * Ports may call these for threads created with sys_thread_new(). + */ +#ifndef LWIP_NETCONN_SEM_PER_THREAD +#define LWIP_NETCONN_SEM_PER_THREAD 0 +#endif + /* ------------------------------------ ---------- Socket options ---------- diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h index 556f7021..53204b34 100644 --- a/src/include/lwip/sockets.h +++ b/src/include/lwip/sockets.h @@ -128,6 +128,10 @@ struct lwip_setgetsockopt_data { socklen_t *optlen; /** if an error occurs, it is temporarily stored here */ err_t err; +#if !LWIP_TCPIP_CORE_LOCKING + /** semaphore to wake up the calling task */ + void* completed_sem; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ }; /* Socket protocol types (TCP/UDP/RAW) */ diff --git a/src/include/lwip/tcpip.h b/src/include/lwip/tcpip.h index c512a8a2..81090456 100644 --- a/src/include/lwip/tcpip.h +++ b/src/include/lwip/tcpip.h @@ -65,8 +65,14 @@ extern sys_mutex_t lock_tcpip_core; #else #define TCIP_APIMSG_SET_ERR(m, e) #endif +#if LWIP_NETCONN_SEM_PER_THREAD +#define TCPIP_APIMSG_SET_SEM(m) ((m)->msg.op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET()) +#else +#define TCPIP_APIMSG_SET_SEM(m) +#endif #define TCPIP_APIMSG_NOERR(m,f) do { \ TCIP_APIMSG_SET_ERR(m, ERR_VAL); \ + TCPIP_APIMSG_SET_SEM(m); \ LOCK_TCPIP_CORE(); \ f(&((m)->msg)); \ UNLOCK_TCPIP_CORE(); \ @@ -85,7 +91,7 @@ extern sys_mutex_t lock_tcpip_core; #define UNLOCK_TCPIP_CORE() #define TCPIP_APIMSG_NOERR(m,f) do { (m)->function = f; tcpip_apimsg(m); } while(0) #define TCPIP_APIMSG(m,f,e) do { (m)->function = f; (e) = tcpip_apimsg(m); } while(0) -#define TCPIP_APIMSG_ACK(m) sys_sem_signal(&m->conn->op_completed) +#define TCPIP_APIMSG_ACK(m) sys_sem_signal(LWIP_API_MSG_SEM(m)) #define TCPIP_NETIFAPI(m) tcpip_netifapi(m) #define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(&m->sem) #define TCPIP_PPPAPI(m) tcpip_pppapi(m)