diff --git a/src/api/tcpip.c b/src/api/tcpip.c index 07b2f984..645e5ae0 100644 --- a/src/api/tcpip.c +++ b/src/api/tcpip.c @@ -73,6 +73,8 @@ sys_mutex_t lock_tcpip_core; #define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg) #endif /* LWIP_TIMERS */ +static void tcpip_thread_handle_msg(struct tcpip_msg *msg); + /** * The main lwIP thread. This thread has exclusive access to lwIP core functions * (unless access to them is not locked). Other threads communicate with this @@ -105,59 +107,89 @@ tcpip_thread(void *arg) LWIP_ASSERT("tcpip_thread: invalid message", 0); continue; } - switch (msg->type) { + tcpip_thread_handle_msg(msg); + } +} + +/* Handle a single tcpip_msg + * This is in its own function for access by tests only. + */ +static void +tcpip_thread_handle_msg(struct tcpip_msg *msg) +{ + switch (msg->type) { #if !LWIP_TCPIP_CORE_LOCKING - case TCPIP_MSG_API: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg)); - msg->msg.api_msg.function(msg->msg.api_msg.msg); - break; - case TCPIP_MSG_API_CALL: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg)); - msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg); - sys_sem_signal(msg->msg.api_call.sem); - break; + case TCPIP_MSG_API: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg)); + msg->msg.api_msg.function(msg->msg.api_msg.msg); + break; + case TCPIP_MSG_API_CALL: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg)); + msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg); + sys_sem_signal(msg->msg.api_call.sem); + break; #endif /* !LWIP_TCPIP_CORE_LOCKING */ #if !LWIP_TCPIP_CORE_LOCKING_INPUT - case TCPIP_MSG_INPKT: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); - msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif); - memp_free(MEMP_TCPIP_MSG_INPKT, msg); - break; + case TCPIP_MSG_INPKT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); + msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif); + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + break; #endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */ #if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS - case TCPIP_MSG_TIMEOUT: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); - sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); - memp_free(MEMP_TCPIP_MSG_API, msg); - break; - case TCPIP_MSG_UNTIMEOUT: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg)); - sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg); - memp_free(MEMP_TCPIP_MSG_API, msg); - break; + case TCPIP_MSG_TIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); + sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + case TCPIP_MSG_UNTIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg)); + sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; #endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */ - case TCPIP_MSG_CALLBACK: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); - msg->msg.cb.function(msg->msg.cb.ctx); - memp_free(MEMP_TCPIP_MSG_API, msg); - break; + case TCPIP_MSG_CALLBACK: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; - case TCPIP_MSG_CALLBACK_STATIC: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg)); - msg->msg.cb.function(msg->msg.cb.ctx); - break; + case TCPIP_MSG_CALLBACK_STATIC: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + break; - default: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type)); - LWIP_ASSERT("tcpip_thread: invalid message", 0); - break; - } + default: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type)); + LWIP_ASSERT("tcpip_thread: invalid message", 0); + break; } } +#ifdef TCPIP_THREAD_TEST +/** Work on queued items in single-threaded test mode */ +int +tcpip_thread_poll_one(void) +{ + int ret = 0; + struct tcpip_msg *msg; + + /* wait for a message, timeouts are processed while waiting */ + if (sys_arch_mbox_tryfetch(&mbox, (void **)&msg) != SYS_ARCH_TIMEOUT) { + LOCK_TCPIP_CORE(); + if (msg != NULL) { + tcpip_thread_handle_msg(msg); + ret = 1; + } + UNLOCK_TCPIP_CORE(); + } + return ret; +} +#endif + /** * Pass a received packet to tcpip_thread for input processing * diff --git a/src/include/lwip/tcpip.h b/src/include/lwip/tcpip.h index fb55cebb..a6c826ad 100644 --- a/src/include/lwip/tcpip.h +++ b/src/include/lwip/tcpip.h @@ -50,14 +50,12 @@ extern "C" { #endif #if LWIP_TCPIP_CORE_LOCKING -#ifndef LOCK_TCPIP_CORE /** The global semaphore to lock the stack. */ extern sys_mutex_t lock_tcpip_core; /** Lock lwIP core mutex (needs @ref LWIP_TCPIP_CORE_LOCKING 1) */ #define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core) /** Unlock lwIP core mutex (needs @ref LWIP_TCPIP_CORE_LOCKING 1) */ #define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core) -#endif #else /* LWIP_TCPIP_CORE_LOCKING */ #define LOCK_TCPIP_CORE() #define UNLOCK_TCPIP_CORE() @@ -99,6 +97,10 @@ err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); err_t tcpip_untimeout(sys_timeout_handler h, void *arg); #endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */ +#ifdef TCPIP_THREAD_TEST +int tcpip_thread_poll_one(void); +#endif + #ifdef __cplusplus } #endif diff --git a/test/unit/api/test_sockets.c b/test/unit/api/test_sockets.c index f0d889c3..9a3c6ff9 100644 --- a/test/unit/api/test_sockets.c +++ b/test/unit/api/test_sockets.c @@ -7,10 +7,15 @@ /* Setups/teardown functions */ +static int tcpip_init_called; static void sockets_setup(void) { + if (!tcpip_init_called) { + tcpip_init_called = 1; + tcpip_init(NULL, NULL); + } } static void @@ -22,6 +27,17 @@ sockets_teardown(void) #define NUM_SOCKETS MEMP_NUM_NETCONN #endif +static int +test_sockets_alloc_socket_nonblocking(int domain, int type) +{ + int s = lwip_socket(domain, type, 0); + if (s >= 0) { + int ret = lwip_fcntl(s, F_SETFL, O_NONBLOCK); + fail_unless(ret == 0); + } + return s; +} + /* Verify basic sockets functionality */ START_TEST(test_sockets_basics) @@ -57,12 +73,79 @@ START_TEST(test_sockets_basics) } END_TEST +static void test_sockets_allfunctions_basic_domain(int domain) +{ + int s, s2, s3, ret; + struct sockaddr_storage addr, addr2; + socklen_t addrlen, addr2len; + /* listen socket */ + s = lwip_socket(domain, SOCK_STREAM, 0); + fail_unless(s >= 0); + + ret = lwip_listen(s, 0); + fail_unless(ret == 0); + + addrlen = sizeof(addr); + ret = lwip_getsockname(s, (struct sockaddr*)&addr, &addrlen); + fail_unless(ret == 0); + + s2 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM); + fail_unless(s2 >= 0); + /* nonblocking connect s2 to s (but use loopback address) */ + if (domain == AF_INET) { +#if LWIP_IPV4 + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; + addr4->sin_addr.s_addr = inet_addr("127.0.0.1"); +#endif + } else { +#if LWIP_IPV6 +#endif + } + ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen); + fail_unless(ret == -1); + fail_unless(errno == EINPROGRESS); + ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen); + fail_unless(ret == -1); + fail_unless(errno == EALREADY); + + while(tcpip_thread_poll_one()); + + s3 = lwip_accept(s, (struct sockaddr*)&addr2, &addr2len); + fail_unless(s3 >= 0); + + ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen); + fail_unless(ret == -1); + fail_unless(errno == EISCONN); + + ret = lwip_close(s); + fail_unless(ret == 0); + ret = lwip_close(s2); + fail_unless(ret == 0); + ret = lwip_close(s3); + fail_unless(ret == 0); +} + +/* Try to step through all sockets functions once... + */ +START_TEST(test_sockets_allfunctions_basic) +{ + LWIP_UNUSED_ARG(_i); +#if LWIP_IPV4 + test_sockets_allfunctions_basic_domain(AF_INET); +#endif +#if LWIP_IPV6 + test_sockets_allfunctions_basic_domain(AF_INET6); +#endif +} +END_TEST + /** Create the suite including all tests for this module */ Suite * sockets_suite(void) { testfunc tests[] = { - TESTFUNC(test_sockets_basics) + TESTFUNC(test_sockets_basics), + TESTFUNC(test_sockets_allfunctions_basic) }; return create_suite("SOCKETS", tests, sizeof(tests)/sizeof(testfunc), sockets_setup, sockets_teardown); } diff --git a/test/unit/arch/sys_arch.c b/test/unit/arch/sys_arch.c index 85c62159..b4a6f904 100644 --- a/test/unit/arch/sys_arch.c +++ b/test/unit/arch/sys_arch.c @@ -115,7 +115,7 @@ u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) } } LWIP_ASSERT("*sem > 0", *sem > 0); - *sem--; + (*sem)--; LWIP_ASSERT("*sem > 0", *sem > 0); /* return the time we waited for the sem */ return ret; @@ -125,7 +125,7 @@ void sys_sem_signal(sys_sem_t *sem) { LWIP_ASSERT("sem != NULL", sem != NULL); LWIP_ASSERT("*sem > 0", *sem > 0); - *sem++; + (*sem)++; LWIP_ASSERT("*sem > 0", *sem > 0); } @@ -156,7 +156,7 @@ void sys_mutex_lock(sys_mutex_t *mutex) LWIP_ASSERT("mutex != NULL", mutex != NULL); LWIP_ASSERT("*mutex >= 1", *mutex >= 1); /* we count up just to check the correct pairing of lock/unlock */ - *mutex++; + (*mutex)++; LWIP_ASSERT("*mutex >= 1", *mutex >= 1); } @@ -166,7 +166,7 @@ void sys_mutex_unlock(sys_mutex_t *mutex) LWIP_ASSERT("mutex != NULL", mutex != NULL); LWIP_ASSERT("*mutex >= 1", *mutex >= 1); /* we count down just to check the correct pairing of lock/unlock */ - *mutex--; + (*mutex)--; LWIP_ASSERT("*mutex >= 1", *mutex >= 1); } @@ -223,7 +223,7 @@ void sys_mbox_set_invalid(sys_mbox_t *mbox) void sys_mbox_post(sys_mbox_t *q, void *msg) { LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL); - LWIP_ASSERT("q->sem != q", q->sem != q); + LWIP_ASSERT("q->sem == q", q->sem == q); LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL); LWIP_ASSERT("q->used >= 0", q->used >= 0); LWIP_ASSERT("q->size > 0", q->size > 0); @@ -242,7 +242,7 @@ void sys_mbox_post(sys_mbox_t *q, void *msg) err_t sys_mbox_trypost(sys_mbox_t *q, void *msg) { LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL); - LWIP_ASSERT("q->sem != q", q->sem != q); + LWIP_ASSERT("q->sem == q", q->sem == q); LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL); LWIP_ASSERT("q->used >= 0", q->used >= 0); LWIP_ASSERT("q->size > 0", q->size > 0); @@ -260,7 +260,7 @@ u32_t sys_arch_mbox_fetch(sys_mbox_t *q, void **msg, u32_t timeout) u32_t ret = 0; u32_t ret2; LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL); - LWIP_ASSERT("q->sem != q", q->sem != q); + LWIP_ASSERT("q->sem == q", q->sem == q); LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL); LWIP_ASSERT("q->used >= 0", q->used >= 0); LWIP_ASSERT("q->size > 0", q->size > 0); diff --git a/test/unit/arch/sys_arch.h b/test/unit/arch/sys_arch.h index 9373c4c4..0003da9d 100644 --- a/test/unit/arch/sys_arch.h +++ b/test/unit/arch/sys_arch.h @@ -64,9 +64,5 @@ typedef u32_t sys_thread_t; typedef int (*test_sys_arch_waiting_fn)(void* wait_element); void test_sys_arch_wait_callback(test_sys_arch_waiting_fn waiting_fn); -/* no multithreading supported: define to nothing */ -#define LOCK_TCPIP_CORE() -#define UNLOCK_TCPIP_CORE() - #endif /* LWIP_HDR_TEST_SYS_ARCH_H */ diff --git a/test/unit/lwipopts.h b/test/unit/lwipopts.h index 9c30f1d4..fd9adde8 100644 --- a/test/unit/lwipopts.h +++ b/test/unit/lwipopts.h @@ -32,11 +32,14 @@ #ifndef LWIP_HDR_LWIPOPTS_H #define LWIP_HDR_LWIPOPTS_H -/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ +/* We link to special sys_arch.c (for basic non-waiting API layers unit tests) */ #define NO_SYS 0 #define SYS_LIGHTWEIGHT_PROT 0 #define LWIP_NETCONN 1 #define LWIP_SOCKET 1 +#define LWIP_NETCONN_FULLDUPLEX 1 +#define LWIP_HAVE_LOOPIF 1 +#define TCPIP_THREAD_TEST /* Enable DHCP to test it, disable UDP checksum to easier inject packets */ #define LWIP_DHCP 1