summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrédéric Desbiens <[email protected]>2026-03-06 19:06:56 +0100
committerFrédéric Desbiens <[email protected]>2026-03-06 19:06:56 +0100
commit63cfc6d8a5b156c243f011a02df838a359d3fa38 (patch)
tree270745b33503342e86ecc1d25603838df8bdf900
parent735022d510d5c433a246c8024f4b04de55d227e4 (diff)
Added regression test and enhanced select fix for Issue #366
-rw-r--r--addons/BSD/nxd_bsd.c7
-rw-r--r--test/cmake/netxduo/regression/CMakeLists.txt3
-rw-r--r--test/regression/bsd_test/netx_bsd_select_spurious_event_test.c282
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