/*************************************************************************** * 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 */