diff options
Diffstat (limited to 'test/regression/bsd_test/netx_bsd_select_improvements_test.c')
| -rw-r--r-- | test/regression/bsd_test/netx_bsd_select_improvements_test.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/test/regression/bsd_test/netx_bsd_select_improvements_test.c b/test/regression/bsd_test/netx_bsd_select_improvements_test.c new file mode 100644 index 00000000..86aba67f --- /dev/null +++ b/test/regression/bsd_test/netx_bsd_select_improvements_test.c @@ -0,0 +1,316 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +/* This test covers select() improvements from PR #375: + * + * 1. TCP readfds + exceptfds in one select() call — normal (non-urgent) data + * must set readfds but MUST NOT set exceptfds. + * + * 2. select() with zero timeout and no ready sockets must return 0 and must + * clear all output fd sets (TX_NO_EVENTS path). + * + * 3. A socket that is not in use must be reported in writefds (regression for + * the writefds "not in use" restore). + */ + +#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 BSD_THREAD_PRIORITY 2 + +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; + +static ULONG error_counter; + +static ULONG packet_pool_area[(512 + sizeof(NX_PACKET)) * 32 / 4]; + +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); + +static ULONG bsd_thread_area[DEMO_STACK_SIZE / sizeof(ULONG)]; + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_bsd_select_improvements_test_application_define(void *first_unused_memory) +#endif +{ +CHAR *pointer = (CHAR *)first_unused_memory; +UINT status; + + error_counter = 0; + + 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 += DEMO_STACK_SIZE; + + 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 += DEMO_STACK_SIZE; + + nx_system_initialize(); + + status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", 512, + packet_pool_area, sizeof(packet_pool_area)); + if (status) + error_counter++; + + 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 += 2048; + if (status) + error_counter++; + + 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, 1); + pointer += 2048; + if (status) + error_counter++; + + status = nx_arp_enable(&ip_0, (void *)pointer, 1024); + pointer += 1024; + if (status) + error_counter++; + + status = nx_arp_enable(&ip_1, (void *)pointer, 1024); + pointer += 1024; + if (status) + error_counter++; + + status = nx_tcp_enable(&ip_0); + status += nx_tcp_enable(&ip_1); + if (status) + error_counter++; + + status = bsd_initialize(&ip_0, &pool_0, (CHAR *)&bsd_thread_area[0], + sizeof(bsd_thread_area), BSD_THREAD_PRIORITY); + if (status) + error_counter++; + + status = tx_semaphore_create(&sema_0, "SEMA 0", 0); + status += tx_semaphore_create(&sema_1, "SEMA 1", 0); + if (status) + error_counter++; +} + +/* Server thread: exercises select() with readfds + exceptfds. */ +static void ntest_0_entry(ULONG thread_input) +{ +struct sockaddr_in server_addr; +struct sockaddr_in client_addr; +INT server_sock; +INT conn_sock; +INT client_addr_len; +INT n; +INT nfd; +struct timeval tv; +fd_set readfds; +fd_set writefds; +fd_set exceptfds; +char buf[64]; + + NX_PARAMETER_NOT_USED(thread_input); + + /* ================================================================ + * Set up TCP server socket. + * ================================================================ */ + server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (server_sock < 0) + error_counter++; + + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(5555); + server_addr.sin_addr.s_addr = INADDR_ANY; + + if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) + error_counter++; + + if (listen(server_sock, 1) < 0) + error_counter++; + + /* Signal client thread to connect. */ + tx_semaphore_put(&sema_1); + + /* Accept the connection. */ + client_addr_len = sizeof(client_addr); + conn_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_addr_len); + if (conn_sock < 0) + error_counter++; + + /* ================================================================ + * Test 1: zero-timeout select with no data ready must return 0 + * and clear all output fd sets. + * ================================================================ */ + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + FD_SET(conn_sock, &readfds); + FD_SET(conn_sock, &exceptfds); + + tv.tv_sec = 0; + tv.tv_usec = 0; + nfd = conn_sock + 1; + + n = select(nfd, &readfds, &writefds, &exceptfds, &tv); + + if (n != 0) + error_counter++; + + /* All sets must be cleared when select returns 0 (timeout). */ + if (FD_ISSET(conn_sock, &readfds)) + error_counter++; + if (FD_ISSET(conn_sock, &exceptfds)) + error_counter++; + + /* ================================================================ + * Test 2: normal TCP data must set readfds and NOT exceptfds. + * ================================================================ */ + FD_ZERO(&readfds); + FD_ZERO(&exceptfds); + FD_SET(conn_sock, &readfds); + FD_SET(conn_sock, &exceptfds); + + tv.tv_sec = 5; + tv.tv_usec = 0; + + /* Tell the client we are ready to receive. */ + tx_semaphore_put(&sema_0); + + n = select(nfd, &readfds, NX_NULL, &exceptfds, &tv); + + if (n <= 0) + error_counter++; + + /* readfds must be set (data arrived). */ + if (!FD_ISSET(conn_sock, &readfds)) + error_counter++; + + /* exceptfds must NOT be set (no urgent data). */ + if (FD_ISSET(conn_sock, &exceptfds)) + error_counter++; + + /* Consume the data. */ + recv(conn_sock, buf, sizeof(buf), 0); + + /* ================================================================ + * Test 3: a socket that is not in use must appear in writefds. + * ================================================================ */ + { + INT closed_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + INT open_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + INT max_fd; + + if (closed_sock < 0 || open_sock < 0) + error_counter++; + + /* Close one socket so its slot is free. */ + soc_close(closed_sock); + + FD_ZERO(&writefds); + FD_SET(closed_sock, &writefds); + FD_SET(open_sock, &writefds); + + max_fd = (closed_sock > open_sock ? closed_sock : open_sock) + 1; + tv.tv_sec = 0; + tv.tv_usec = 0; + + n = select(max_fd, NX_NULL, &writefds, NX_NULL, &tv); + + /* The not-in-use socket must be reported writable. */ + if (!FD_ISSET(closed_sock, &writefds)) + error_counter++; + + soc_close(open_sock); + } + + soc_close(conn_sock); + soc_close(server_sock); + + if (error_counter) + test_control_return(1); + else + test_control_return(0); +} + +/* Client thread: connects and sends normal (non-urgent) TCP data. */ +static void ntest_1_entry(ULONG thread_input) +{ +struct sockaddr_in server_addr; +INT client_sock; +ULONG actual_status; + + NX_PARAMETER_NOT_USED(thread_input); + + nx_ip_status_check(&ip_1, NX_IP_INITIALIZE_DONE, &actual_status, + 5 * NX_IP_PERIODIC_RATE); + + /* Wait for server to be ready. */ + tx_semaphore_get(&sema_1, 5 * NX_IP_PERIODIC_RATE); + + client_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (client_sock < 0) + error_counter++; + + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(5555); + server_addr.sin_addr.s_addr = htonl(IP_ADDRESS(1, 2, 3, 4)); + + if (connect(client_sock, (struct sockaddr *)&server_addr, + sizeof(server_addr)) < 0) + error_counter++; + + /* Wait for server to enter select(). */ + tx_semaphore_get(&sema_0, 5 * NX_IP_PERIODIC_RATE); + + /* Send normal (non-urgent) data. */ + if (send(client_sock, "Hello", 5, 0) < 0) + error_counter++; + + soc_close(client_sock); +} + +#else + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_bsd_select_improvements_test_application_define(void *first_unused_memory) +#endif +{ + NX_PARAMETER_NOT_USED(first_unused_memory); + test_control_return(3); +} + +#endif /* NX_BSD_ENABLE && !NX_DISABLE_IPV4 */ |
