diff options
| author | Frédéric Desbiens <[email protected]> | 2026-03-06 19:06:56 +0100 |
|---|---|---|
| committer | Frédéric Desbiens <[email protected]> | 2026-03-06 19:06:56 +0100 |
| commit | 63cfc6d8a5b156c243f011a02df838a359d3fa38 (patch) | |
| tree | 270745b33503342e86ecc1d25603838df8bdf900 | |
| parent | 735022d510d5c433a246c8024f4b04de55d227e4 (diff) | |
Added regression test and enhanced select fix for Issue #366
| -rw-r--r-- | addons/BSD/nxd_bsd.c | 7 | ||||
| -rw-r--r-- | test/cmake/netxduo/regression/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | test/regression/bsd_test/netx_bsd_select_spurious_event_test.c | 282 |
3 files changed, 290 insertions, 2 deletions
diff --git a/addons/BSD/nxd_bsd.c b/addons/BSD/nxd_bsd.c index 83ca51c3..c705e53e 100644 --- a/addons/BSD/nxd_bsd.c +++ b/addons/BSD/nxd_bsd.c @@ -8240,7 +8240,12 @@ INT ret; else NX_BSD_FD_ZERO(&suspend_request.nx_bsd_socket_suspend_exception_request_fd_set); - /* Clear the actual fd sets, which will be set in nx_bsd_select_wakeup(). */ + /* Clear the actual fd sets, which will be set in nx_bsd_select_wakeup(). + Separating request filters from actual events prevents the spurious wakeup + issue where clearing one event set would leave others stale. + Note: All actual sets must be zeroed here as their fd_count contributes to + the return value of select(). The suspend_request is on the stack, so + users with high NX_BSD_MAX_SOCKETS should monitor stack usage. */ NX_BSD_FD_ZERO(&suspend_request.nx_bsd_socket_suspend_read_fd_set); NX_BSD_FD_ZERO(&suspend_request.nx_bsd_socket_suspend_write_fd_set); NX_BSD_FD_ZERO(&suspend_request.nx_bsd_socket_suspend_exception_fd_set); diff --git a/test/cmake/netxduo/regression/CMakeLists.txt b/test/cmake/netxduo/regression/CMakeLists.txt index 54685113..32b1b981 100644 --- a/test/cmake/netxduo/regression/CMakeLists.txt +++ b/test/cmake/netxduo/regression/CMakeLists.txt @@ -52,7 +52,8 @@ if("-DNX_BSD_ENABLE" IN_LIST ${CMAKE_BUILD_TYPE}) ${SOURCE_DIR}/bsd_test/netx_bsd_getaddrinfo_test.c ${SOURCE_DIR}/bsd_test/netx_bsd_tcp_getsockname_without_bind_test.c ${SOURCE_DIR}/bsd_test/netx_bsd_tcp_rcvbuf_test.c - ${SOURCE_DIR}/bsd_test/netx_bsd_tcp_fionread_test.c) + ${SOURCE_DIR}/bsd_test/netx_bsd_tcp_fionread_test.c + ${SOURCE_DIR}/bsd_test/netx_bsd_select_spurious_event_test.c) if("-DNX_BSD_RAW_SUPPORT" IN_LIST ${CMAKE_BUILD_TYPE}) list( APPEND diff --git a/test/regression/bsd_test/netx_bsd_select_spurious_event_test.c b/test/regression/bsd_test/netx_bsd_select_spurious_event_test.c new file mode 100644 index 00000000..573cf3d9 --- /dev/null +++ b/test/regression/bsd_test/netx_bsd_select_spurious_event_test.c @@ -0,0 +1,282 @@ +/* This NetX test concentrates on the BSD select spurious event bug (Issue #366). */ + +#include "tx_api.h" +#include "nx_api.h" +#if defined(NX_BSD_ENABLE) && !defined(NX_DISABLE_IPV4) +#include "nxd_bsd.h" +#define DEMO_STACK_SIZE 4096 + +/* Define the ThreadX and NetX object control blocks... */ + +static TX_THREAD ntest_0; +static TX_THREAD ntest_1; + +static NX_PACKET_POOL pool_0; +static NX_IP ip_0; +static NX_IP ip_1; +static TX_SEMAPHORE sema_0; +static TX_SEMAPHORE sema_1; + +#define BSD_THREAD_PRIORITY 2 +#define NUM_CLIENTS 20 + +/* Define the counters used in the test application... */ + +static ULONG error_counter; +static ULONG packet_pool_area[(256 + sizeof(NX_PACKET)) * (NUM_CLIENTS + 4) * 8 / 4]; + +/* Define thread prototypes. */ + +static void ntest_0_entry(ULONG thread_input); +static void ntest_1_entry(ULONG thread_input); +extern void test_control_return(UINT status); +extern void _nx_ram_network_driver_256(struct NX_IP_DRIVER_STRUCT *driver_req); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_bsd_select_spurious_event_test_application_define(void *first_unused_memory) +#endif +{ + +CHAR *pointer; +UINT status; + + + /* Setup the working pointer. */ + pointer = (CHAR *) first_unused_memory; + + error_counter = 0; + + /* Create the main thread. */ + tx_thread_create(&ntest_0, "thread 0", ntest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 3, 3, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Create the main thread. */ + tx_thread_create(&ntest_1, "thread 1", ntest_1_entry, 0, + pointer, DEMO_STACK_SIZE, + 3, 3, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + + /* Initialize the NetX system. */ + nx_system_initialize(); + + /* Create a packet pool. */ + status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", 256, packet_pool_area, sizeof(packet_pool_area)); + + if (status) + error_counter++; + + /* Create an IP instance. */ + status = nx_ip_create(&ip_0, "NetX IP Instance 0", IP_ADDRESS(1, 2, 3, 4), 0xFFFFFF00UL, &pool_0, _nx_ram_network_driver_256, + pointer, 2048, 1); + pointer = pointer + 2048; + + /* Create another IP instance. */ + status += nx_ip_create(&ip_1, "NetX IP Instance 1", IP_ADDRESS(1, 2, 3, 5), 0xFFFFFF00UL, &pool_0, _nx_ram_network_driver_256, + pointer, 2048, 2); + pointer = pointer + 2048; + if (status) + error_counter++; + + /* Enable ARP and supply ARP cache memory for IP Instance 0. */ + status = nx_arp_enable(&ip_0, (void *) pointer, 1024); + pointer = pointer + 1024; + if (status) + error_counter++; + + /* Enable ARP and supply ARP cache memory for IP Instance 1. */ + status = nx_arp_enable(&ip_1, (void *) pointer, 1024); + pointer = pointer + 1024; + if (status) + error_counter++; + + /* Enable UDP processing for both IP instances. */ + status = nx_udp_enable(&ip_0); + status += nx_udp_enable(&ip_1); + + /* Enable BSD */ + status += bsd_initialize(&ip_0, &pool_0, pointer, DEMO_STACK_SIZE, BSD_THREAD_PRIORITY); + + /* Check UDP enable and BSD init status. */ + if (status) + error_counter++; + pointer = pointer + DEMO_STACK_SIZE; + + status = tx_semaphore_create(&sema_0, "SEMA 0", 0); + status += tx_semaphore_create(&sema_1, "SEMA 1", 0); + if(status) + error_counter++; +} + +/* Define the test threads. */ +static void ntest_0_entry(ULONG thread_input) +{ +struct sockaddr_in local_addr; +int sockfd1, sockfd2, nfd; +struct timeval tv; +fd_set readfd, exceptfd; +int n; +char buffer[20]; + + printf("NetX Test: BSD Select Spurious Event Test................"); + + /* Check for earlier error. */ + if (error_counter) + { + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open two UDP sockets. */ + sockfd1 = socket(AF_INET, SOCK_DGRAM, 0); + sockfd2 = socket(AF_INET, SOCK_DGRAM, 0); + if(sockfd1 < 0 || sockfd2 < 0) + error_counter++; + + local_addr.sin_family = AF_INET; + local_addr.sin_port = htons(12345); + local_addr.sin_addr.s_addr = INADDR_ANY; + if(bind(sockfd1, (struct sockaddr*)&local_addr, sizeof(local_addr)) < 0) + error_counter++; + + local_addr.sin_port = htons(12346); + if(bind(sockfd2, (struct sockaddr*)&local_addr, sizeof(local_addr)) < 0) + error_counter++; + + /* Tell thread 1 we are ready. */ + tx_semaphore_put(&sema_1); + + /* Prepare select. */ + FD_ZERO(&readfd); + FD_ZERO(&exceptfd); + FD_SET(sockfd1, &readfd); + FD_SET(sockfd1, &exceptfd); + FD_SET(sockfd2, &readfd); + FD_SET(sockfd2, &exceptfd); + + nfd = (sockfd1 > sockfd2 ? sockfd1 : sockfd2) + 1; + tv.tv_sec = 5; + tv.tv_usec = 0; + + /* Wait for a packet on either socket. Thread 1 will send to sockfd1. */ + n = select(nfd, &readfd, NULL, &exceptfd, &tv); + + /* We expect exactly 1 event (READ on sockfd1). */ + if (n != 1) + { + error_counter++; + } + + /* Check if sockfd1 has READ set. */ + if (!FD_ISSET(sockfd1, &readfd)) + { + error_counter++; + } + + /* Check if sockfd1 has EXCEPTION set (it SHOULD NOT). */ + if (FD_ISSET(sockfd1, &exceptfd)) + { + error_counter++; + } + + /* Check if sockfd2 has READ set (it SHOULD NOT). */ + if (FD_ISSET(sockfd2, &readfd)) + { + error_counter++; + } + + /* Check if sockfd2 has EXCEPTION set (it SHOULD NOT). + This was the specific failure in Issue #366. */ + if (FD_ISSET(sockfd2, &exceptfd)) + { + error_counter++; + } + + if (n == 1 && FD_ISSET(sockfd1, &readfd)) + { + /* Consume the packet. */ + recv(sockfd1, buffer, sizeof(buffer), 0); + } + + soc_close(sockfd1); + soc_close(sockfd2); + + if(error_counter) + printf("ERROR!\n"); + else + printf("SUCCESS!\n"); + + if(error_counter) + test_control_return(1); + + test_control_return(0); +} + +static void ntest_1_entry(ULONG thread_input) +{ +UINT status; +NX_PACKET *packet_ptr; +ULONG actual_status; +NX_UDP_SOCKET udp_socket; + + /* Ensure the IP instance has been initialized. */ + status = nx_ip_status_check(&ip_1, NX_IP_INITIALIZE_DONE, &actual_status, 1 * NX_IP_PERIODIC_RATE); + if (status != NX_SUCCESS) + { + test_control_return(3); + } + + /* Create a socket. */ + status = nx_udp_socket_create(&ip_1, &udp_socket, "Sender Socket", + NX_IP_NORMAL, NX_FRAGMENT_OKAY, NX_IP_TIME_TO_LIVE, 10); + if (status) + error_counter++; + + status = nx_udp_socket_bind(&udp_socket, NX_ANY_PORT, NX_WAIT_FOREVER); + if (status) + error_counter++; + + /* Wait for thread 0 to be ready. */ + tx_semaphore_get(&sema_1, 5 * NX_IP_PERIODIC_RATE); + + /* Small delay to ensure thread 0 is in select(). */ + tx_thread_sleep(NX_IP_PERIODIC_RATE / 2); + + /* Allocate a packet. */ + status = nx_packet_allocate(&pool_0, &packet_ptr, NX_UDP_PACKET, NX_WAIT_FOREVER); + if (status) + error_counter++; + + memcpy(packet_ptr -> nx_packet_prepend_ptr, "Hello", 5); + packet_ptr -> nx_packet_length = 5; + packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_prepend_ptr + 5; + + /* Send to sockfd1 (port 12345). */ + status = nx_udp_socket_send(&udp_socket, packet_ptr, IP_ADDRESS(1,2,3,4), 12345); + if(status) + error_counter++; + + nx_udp_socket_delete(&udp_socket); +} + +#else +extern void test_control_return(UINT status); + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_bsd_select_spurious_event_test_application_define(void *first_unused_memory) +#endif +{ + printf("NetX Test: BSD Select Spurious Event Test................N/A\n"); + test_control_return(3); +} +#endif |
