mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-21 11:30:01 +00:00
Reimplement BSD layer
This commit is contained in:
1 parent
aa1dfe8000
commit
31c6487070
43 files changed
+3908
-1955
No files matched your search
+30
-5
@@ -13,18 +13,21 @@ else
|
||||
C_WARN ?= -Wno-deprecated
|
||||
endif
|
||||
|
||||
.PHONY: all bsd_test bsd_client shm_tap_bridge shm_port_bridge bsd_s390 clean
|
||||
.PHONY: all bsd bsd_test bsd_client bsd_nortos bsd_nortos_test shm_tap_bridge \
|
||||
shm_port_bridge bsd_s390 bsd_nortos_s390 clean
|
||||
|
||||
TEST_SOURCES = bsd_shm_test.c
|
||||
# A C++ application compiles its sources as C++, linking Mongoose and FreeRTOS as C.
|
||||
# FreeRTOS does not build as C++, so C sources are compiled separately.
|
||||
C_SOURCES = ../../mongoose.c
|
||||
C_SOURCES += FreeRTOS-Kernel/portable/MemMang/heap_4.c
|
||||
C_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/port.c
|
||||
C_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
|
||||
C_SOURCES += $(wildcard FreeRTOS-Kernel/*.c)
|
||||
FREERTOS_SOURCES = FreeRTOS-Kernel/portable/MemMang/heap_4.c
|
||||
FREERTOS_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/port.c
|
||||
FREERTOS_SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
|
||||
FREERTOS_SOURCES += $(wildcard FreeRTOS-Kernel/*.c)
|
||||
C_OBJECTS = $(notdir $(C_SOURCES:.c=.o))
|
||||
FREERTOS_OBJECTS = $(notdir $(FREERTOS_SOURCES:.c=.o))
|
||||
C_INPUTS = $(foreach S,$(C_SOURCES),-x c $(S))
|
||||
FREERTOS_INPUTS = $(foreach S,$(FREERTOS_SOURCES),-x c $(S))
|
||||
CFLAGS += -IFreeRTOS-Kernel/include
|
||||
CFLAGS += -IFreeRTOS-Kernel/portable/ThirdParty/GCC/Posix -Wno-conversion -Wno-undef
|
||||
|
||||
@@ -44,11 +47,24 @@ DOCKER ?= $(DOCKER_BIN) run --platform linux/amd64 --net=host --rm $(ENV) -v $(R
|
||||
CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM
|
||||
|
||||
|
||||
bsd: CFLAGS_MONGOOSE += -I../../src/bsd
|
||||
all bsd: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test
|
||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS)
|
||||
|
||||
# also
|
||||
bsd_nortos: CFLAGS_MONGOOSE += -I../../src/bsd
|
||||
bsd_nortos: shm_tap_bridge bsd_client bsd_nortos_test
|
||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_nortos_test $(ARGS)
|
||||
|
||||
# requires shm_tap_bridge to be running
|
||||
bsd_test: CFLAGS_MONGOOSE += -DMG_ENABLE_FREERTOS=1
|
||||
bsd_test: FreeRTOS-Kernel
|
||||
$(CC) $(C_INPUTS) $(FREERTOS_INPUTS) -c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
|
||||
$(CC) -x $(TEST_LANGUAGE) -c $(TEST_SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
|
||||
$(CC) $(C_OBJECTS) $(FREERTOS_OBJECTS) $(TEST_SOURCES:.c=.o) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@
|
||||
|
||||
bsd_nortos_test: CFLAGS_MONGOOSE += -DMG_ENABLE_FREERTOS=0
|
||||
bsd_nortos_test:
|
||||
$(CC) $(C_INPUTS) -c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
|
||||
$(CC) -x $(TEST_LANGUAGE) -c $(TEST_SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA)
|
||||
$(CC) $(C_OBJECTS) $(TEST_SOURCES:.c=.o) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@
|
||||
@@ -61,12 +77,21 @@ shm_tap_bridge: shm_tap_bridge.c shm_queue.h
|
||||
$(CC) shm_tap_bridge.c -pthread -o $@
|
||||
|
||||
# requires test/port_tap_bridge to be running
|
||||
bsd_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
|
||||
bsd_s390: ASAN=
|
||||
bsd_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||
bsd_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||
bsd_s390: shm_port_bridge bsd_client FreeRTOS-Kernel bsd_test
|
||||
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
||||
|
||||
#also
|
||||
bsd_nortos_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
|
||||
bsd_nortos_s390: ASAN=
|
||||
bsd_nortos_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||
bsd_nortos_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||
bsd_nortos_s390: shm_port_bridge bsd_client bsd_nortos_test
|
||||
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_nortos_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
||||
|
||||
shm_port_bridge: shm_port_bridge.c shm_queue.h
|
||||
$(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@
|
||||
|
||||
|
||||
+64
-5
@@ -1,4 +1,5 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -91,22 +92,80 @@ fail:
|
||||
exit(1);
|
||||
}
|
||||
|
||||
enum wait_mode { WAIT_ABORT, WAIT_DRAIN, WAIT_HOLD, WAIT_CLOSE };
|
||||
|
||||
// Wait for output, optionally draining through the final marker.
|
||||
static int wait_data(const char *host, enum wait_mode mode) {
|
||||
struct sockaddr_in sa;
|
||||
struct timeval timeout;
|
||||
char buf[512];
|
||||
ssize_t n;
|
||||
size_t i;
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (fd < 0) return 1;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(PORT);
|
||||
if (inet_pton(AF_INET, host, &sa.sin_addr) != 1) goto fail;
|
||||
timeout.tv_sec = 5, timeout.tv_usec = 0;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0)
|
||||
goto fail;
|
||||
if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) != 0) goto fail;
|
||||
if (mode == WAIT_HOLD) {
|
||||
usleep(100000); // Keep the peer connected while the server fails TX
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
if (mode == WAIT_CLOSE) {
|
||||
n = recv(fd, buf, sizeof(buf), 0);
|
||||
close(fd);
|
||||
return n == 0 || (n < 0 && errno == ECONNRESET) ? 0 : 1;
|
||||
}
|
||||
for (;;) {
|
||||
n = recv(fd, buf, sizeof(buf), 0);
|
||||
if (n <= 0) goto fail;
|
||||
if (mode == WAIT_ABORT) break;
|
||||
for (i = 0; i < (size_t) n; i++) {
|
||||
if (buf[i] == '\1') break;
|
||||
}
|
||||
if (i < (size_t) n) break;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
pthread_t threads[NUM_CONNECTIONS];
|
||||
int i;
|
||||
int connections = NUM_CONNECTIONS, i;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s IP\n", argv[0]);
|
||||
if (argc == 3 && strcmp(argv[2], "waitandabort") == 0)
|
||||
return wait_data(argv[1], WAIT_ABORT);
|
||||
if (argc == 3 && strcmp(argv[2], "waitanddrain") == 0)
|
||||
return wait_data(argv[1], WAIT_DRAIN);
|
||||
if (argc == 3 && strcmp(argv[2], "hold") == 0)
|
||||
return wait_data(argv[1], WAIT_HOLD);
|
||||
if (argc == 3 && strcmp(argv[2], "expectclose") == 0)
|
||||
return wait_data(argv[1], WAIT_CLOSE);
|
||||
if (argc == 3) connections = atoi(argv[2]);
|
||||
if ((argc != 2 && argc != 3) || connections < 1 ||
|
||||
connections > NUM_CONNECTIONS) {
|
||||
fprintf(stderr, "usage: %s IP [connections|waitandabort|waitanddrain|hold|expectclose]\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
srandom(1);
|
||||
|
||||
for (i = 0; i < NUM_CONNECTIONS; i++) {
|
||||
for (i = 0; i < connections; i++) {
|
||||
if (pthread_create(&threads[i], NULL, worker, argv[1]) != 0) return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_CONNECTIONS; i++) {
|
||||
for (i = 0; i < connections; i++) {
|
||||
if (pthread_join(threads[i], NULL) != 0) return 1;
|
||||
}
|
||||
|
||||
|
||||
+814
-71
@@ -1,11 +1,11 @@
|
||||
#define BSDTEST_USING_DHCP 1
|
||||
|
||||
#include <errno.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int s_num_tests = 0;
|
||||
static bool s_error = false;
|
||||
static volatile bool s_done = false, s_wait;
|
||||
|
||||
#ifdef NO_ABORT
|
||||
static int s_abort = 0;
|
||||
@@ -30,15 +30,177 @@ static int s_abort = 0;
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DASHBOARD(x) \
|
||||
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
|
||||
|
||||
static pid_t start_client(const char *ip, const char *connections) {
|
||||
char *cmd[4];
|
||||
pid_t pid;
|
||||
extern char **environ;
|
||||
|
||||
cmd[0] = (char *) "./bsd_client";
|
||||
cmd[1] = (char *) ip;
|
||||
cmd[2] = (char *) connections;
|
||||
cmd[3] = NULL;
|
||||
ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0);
|
||||
return pid;
|
||||
}
|
||||
|
||||
static bool client_done(pid_t pid, int *status) {
|
||||
pid_t r = waitpid(pid, status, WNOHANG);
|
||||
ASSERT(r >= 0);
|
||||
return r == pid;
|
||||
}
|
||||
|
||||
static void check_client(int status) {
|
||||
ASSERT(WIFEXITED(status));
|
||||
ASSERT(WEXITSTATUS(status) == 0);
|
||||
}
|
||||
|
||||
static int open_listener(int backlog, bool nonblock) {
|
||||
int lfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(1234);
|
||||
sa.sin_addr.s_addr = INADDR_ANY;
|
||||
ASSERT(lfd >= 0);
|
||||
ASSERT(bind(lfd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
|
||||
ASSERT(listen(lfd, backlog) == 0);
|
||||
if (nonblock) ASSERT(fcntl(lfd, F_SETFL, O_NONBLOCK) == 0);
|
||||
else ASSERT((fcntl(lfd, F_GETFL, 0) & O_NONBLOCK) == 0);
|
||||
MG_INFO(("BSD echo server on :1234"));
|
||||
return lfd;
|
||||
}
|
||||
|
||||
static void wait_ready(struct mg_mgr *mgr) {
|
||||
// Stack initialization, Network configuration (DHCP lease, ...)
|
||||
#if BSDTEST_USING_DHCP == 0
|
||||
MG_INFO(("MIF configuration: Static IP"));
|
||||
ASSERT(mgr->ifp->ip != 0); // Check we have a satic IP assigned
|
||||
mg_mgr_poll(mgr, 0); // For initialisation
|
||||
#else
|
||||
MG_INFO(("MIF configuration: DHCP"));
|
||||
ASSERT(mgr->ifp->ip == 0); // Check we are set for DHCP
|
||||
while (mgr->ifp->ip == 0 && mg_millis() < 5000) mg_mgr_poll(mgr, 0);
|
||||
if (mgr->ifp->ip == 0) MG_ERROR(("No ip assigned (DHCP lease may have failed).\n"));
|
||||
ASSERT(mgr->ifp->ip != 0); // We have an IP (lease or static)
|
||||
#endif
|
||||
while (mgr->ifp->state != MG_TCPIP_STATE_READY) mg_mgr_poll(mgr, 0);
|
||||
MG_INFO(("Interface started, starting main loop"));
|
||||
}
|
||||
|
||||
#if !MG_ENABLE_FREERTOS
|
||||
static bool s_fail_alloc;
|
||||
|
||||
void *mg_calloc(size_t count, size_t size) {
|
||||
return s_fail_alloc ? NULL : calloc(count, size);
|
||||
}
|
||||
|
||||
void mg_free(void *ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static uint64_t s_bootmillis = 0;
|
||||
|
||||
uint64_t mg_millis(void) {
|
||||
struct timespec ts = {0, 0};
|
||||
// See #1615 - prefer monotonic clock
|
||||
#if defined(CLOCK_MONOTONIC_RAW)
|
||||
// Raw hardware-based time that is not subject to NTP adjustment
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
#elif defined(CLOCK_MONOTONIC)
|
||||
// Affected by the incremental adjustments performed by adjtime and NTP
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
#else
|
||||
// Affected by discontinuous jumps in the system time and by the incremental
|
||||
// adjustments performed by adjtime and NTP
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
#endif
|
||||
return ((uint64_t) ts.tv_sec * 1000 + (uint64_t) ts.tv_nsec / 1000000) - s_bootmillis;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MG_ENABLE_FREERTOS
|
||||
static volatile bool s_done = false, s_wait, s_accept_waiting;
|
||||
static volatile bool s_accept_done, s_dontwait_done, s_nonblock_done;
|
||||
static volatile bool s_select_waiting, s_select_done;
|
||||
static volatile bool s_poll_waiting, s_poll_done;
|
||||
static volatile bool s_ready_waiting, s_ready_done;
|
||||
static volatile bool s_backlog_ready, s_backlog_full, s_backlog_accept;
|
||||
static volatile bool s_tx_held, s_tx_waits, s_tx_fail, s_tx_error;
|
||||
static volatile bool s_send_waiting;
|
||||
static volatile bool s_send_done, s_send_expect_error;
|
||||
static volatile bool s_recv_start, s_recv_waiting, s_recv_done;
|
||||
static volatile bool s_recv_expect_error;
|
||||
static size_t (*s_tx)(const void *, size_t, struct mg_tcpip_if *);
|
||||
|
||||
struct client_data {
|
||||
char hostname[64];
|
||||
uint32_t ip;
|
||||
bool done;
|
||||
};
|
||||
|
||||
static void echo_task(void *args);
|
||||
static void echo_dontwait_task(void *args);
|
||||
static void echo_nonblock_task(void *args);
|
||||
static void send_task(void *args);
|
||||
static void recv_wait_task(void *args);
|
||||
static void select_task(void *args);
|
||||
static void readiness_task(void *args);
|
||||
static void backlog_task(void *args);
|
||||
static void tx_gate_task(void *args);
|
||||
|
||||
struct atask_args {
|
||||
bool listener_nonblock;
|
||||
bool worker_nonblock;
|
||||
bool once;
|
||||
bool receiver;
|
||||
TaskFunction_t task;
|
||||
TaskFunction_t gate;
|
||||
};
|
||||
|
||||
static const struct atask_args s_echo_server = {true, false, false,
|
||||
false, echo_task, NULL};
|
||||
static const struct atask_args s_accept_server = {false, false, true,
|
||||
false, echo_task, NULL};
|
||||
static const struct atask_args s_nonblock_accept_server = {
|
||||
true, false, true, false, echo_task, NULL};
|
||||
static const struct atask_args s_dontwait_server = {
|
||||
true, false, false, false, echo_dontwait_task, NULL};
|
||||
static const struct atask_args s_nonblock_server = {
|
||||
true, true, false, false, echo_nonblock_task, NULL};
|
||||
static const struct atask_args s_send_server = {true, false, false,
|
||||
true, send_task, tx_gate_task};
|
||||
static const struct atask_args s_send_error_server = {
|
||||
true, false, false, false, send_task, tx_gate_task};
|
||||
|
||||
static size_t tx_gate(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||
if (s_tx_fail) return (size_t) -1; // Force MG_IO_ERR after send() waits
|
||||
if (s_tx_held) {
|
||||
s_tx_waits = true;
|
||||
return 0; // Gate TX: MG_IO_WAIT leaves c->send queued
|
||||
}
|
||||
return s_tx(buf, len, ifp);
|
||||
}
|
||||
|
||||
static void run_client(const char *ip, const char *connections) {
|
||||
int status;
|
||||
pid_t pid = start_client(ip, connections);
|
||||
|
||||
while (!client_done(pid, &status)) {
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
check_client(status);
|
||||
}
|
||||
|
||||
// client_task: connects to hostname:80, fetches "/", logs response length.
|
||||
static void client_task(void *args) {
|
||||
int fd, c;
|
||||
fd_set w, e;
|
||||
struct pollfd p;
|
||||
int fd;
|
||||
struct client_data *d = (struct client_data *) args;
|
||||
char req[256];
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
@@ -47,14 +209,20 @@ static void client_task(void *args) {
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(80);
|
||||
memcpy(&sa.sin_addr, &d->ip, 4);
|
||||
c = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
|
||||
ASSERT(c >= 0);
|
||||
if (c < 0) {
|
||||
MG_ERROR(("connect failed"));
|
||||
close(fd);
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||
ASSERT(connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 &&
|
||||
errno == EINPROGRESS);
|
||||
FD_ZERO(&w);
|
||||
FD_ZERO(&e);
|
||||
FD_SET(fd, &w);
|
||||
FD_SET(fd, &e);
|
||||
ASSERT(select(fd + 1, NULL, &w, &e, NULL) == 1);
|
||||
ASSERT(FD_ISSET(fd, &w));
|
||||
ASSERT(!FD_ISSET(fd, &e));
|
||||
p.fd = fd, p.events = POLLOUT, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, 0) == 1);
|
||||
ASSERT(p.revents == POLLOUT);
|
||||
ASSERT(fcntl(fd, F_SETFL, 0) == 0);
|
||||
snprintf(req, sizeof(req),"GET / HTTP/1.0\r\nHost: %s\r\n\r\n", d->hostname);
|
||||
send(fd, req, strlen(req), 0);
|
||||
size_t total = 0;
|
||||
@@ -69,84 +237,475 @@ static void client_task(void *args) {
|
||||
(void) args;
|
||||
}
|
||||
|
||||
// echo_task: one per accepted connection. Echoes data back.
|
||||
static void echo_task(void *args) {
|
||||
int fd = (int) (uintptr_t) args;
|
||||
// Echo data back from one accepted connection.
|
||||
static void echo_task_common(int fd, int flags, volatile bool *done) {
|
||||
bool nonblock = (fcntl(fd, F_GETFL, 0) & O_NONBLOCK) != 0;
|
||||
bool dontwait = (flags & MSG_DONTWAIT) != 0;
|
||||
bool echoed = false, eagain = false;
|
||||
static const size_t sizes[] = {1, 7, 17, 255, 256, 257, 511, 512};
|
||||
char buf[512];
|
||||
size_t i = 0;
|
||||
ssize_t n;
|
||||
|
||||
MG_INFO(("Echo task started: %d", fd));
|
||||
while ((n = recv(fd, buf, sizes[i++ % (sizeof(sizes) / sizeof(sizes[0]))], 0)) > 0)
|
||||
send(fd, buf, (size_t) n, 0);
|
||||
for (;;) {
|
||||
n = recv(fd, buf, sizes[i++ % (sizeof(sizes) / sizeof(sizes[0]))], flags);
|
||||
if (n > 0) {
|
||||
if (!nonblock) {
|
||||
ssize_t w = send(fd, buf, (size_t) n, 0);
|
||||
if (done != NULL) ASSERT(w == n);
|
||||
echoed = w == n;
|
||||
} else {
|
||||
size_t sent = 0;
|
||||
while (sent < (size_t) n) {
|
||||
ssize_t w = send(fd, buf + sent, (size_t) n - sent, 0);
|
||||
if (w > 0) sent += (size_t) w;
|
||||
else if (w < 0 && errno == EAGAIN) vTaskDelay(1);
|
||||
else {
|
||||
ASSERT(w > 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sent != (size_t) n) break;
|
||||
echoed = true;
|
||||
}
|
||||
} else if ((nonblock || dontwait) && n < 0 && errno == EAGAIN) {
|
||||
if (echoed) eagain = true;
|
||||
vTaskDelay(1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (done != NULL) {
|
||||
ASSERT(eagain);
|
||||
*done = true;
|
||||
}
|
||||
close(fd);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void echo_task(void *args) {
|
||||
echo_task_common((int) (uintptr_t) args, 0, NULL);
|
||||
}
|
||||
|
||||
static void echo_dontwait_task(void *args) {
|
||||
echo_task_common((int) (uintptr_t) args, MSG_DONTWAIT, &s_dontwait_done);
|
||||
}
|
||||
|
||||
static void echo_nonblock_task(void *args) {
|
||||
echo_task_common((int) (uintptr_t) args, 0, &s_nonblock_done);
|
||||
}
|
||||
|
||||
static void tx_gate_task(void *args) {
|
||||
// receiver/sender > network task > gate: final send is waiting
|
||||
while (!s_send_waiting) vTaskDelay(1);
|
||||
ASSERT(!s_send_done);
|
||||
if (s_send_expect_error) s_tx_error = s_tx_fail = true;
|
||||
else s_tx_held = false;
|
||||
vTaskDelete(NULL);
|
||||
(void) args;
|
||||
}
|
||||
|
||||
static void recv_wait_task(void *args) {
|
||||
char buf;
|
||||
int fd = (int) (uintptr_t) args;
|
||||
ssize_t n;
|
||||
|
||||
while (!s_recv_start) vTaskDelay(1);
|
||||
s_recv_waiting = true; // send_task starts after recv() is waiting
|
||||
n = recv(fd, &buf, sizeof(buf), 0);
|
||||
if (s_recv_expect_error) {
|
||||
ASSERT(n < 0 && errno == EIO); // bsd_client aborts with unread data
|
||||
} else {
|
||||
ASSERT(n == 0); // bsd_client drains, then closes
|
||||
}
|
||||
s_recv_done = true;
|
||||
close(fd); // Receiver owns the shared socket close
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void send_task(void *args) {
|
||||
static const char buf[512] = {0};
|
||||
static const char marker = '\1';
|
||||
int fd = (int) (uintptr_t) args;
|
||||
ssize_t n;
|
||||
|
||||
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||
s_tx_held = true; // Gate TX until c->send fills
|
||||
do {
|
||||
n = send(fd, buf, sizeof(buf), 0);
|
||||
} while (n == (ssize_t) sizeof(buf));
|
||||
ASSERT(n < 0 && errno == EAGAIN); // Full c->send: O_NONBLOCK
|
||||
while (!s_tx_waits) vTaskDelay(1); // Next write_conn hits the gate
|
||||
|
||||
ASSERT(fcntl(fd, F_SETFL, 0) == 0);
|
||||
n = send(fd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
ASSERT(n < 0 && errno == EAGAIN); // Same full c->send: MSG_DONTWAIT
|
||||
|
||||
if (!s_send_expect_error) {
|
||||
s_recv_start = true; // O_NONBLOCK is per socket; now start the receiver
|
||||
while (!s_recv_waiting) vTaskDelay(1);
|
||||
}
|
||||
s_send_waiting = true; // Gate task releases TX after this send blocks
|
||||
n = send(fd, &marker, sizeof(marker), 0);
|
||||
if (s_send_expect_error) ASSERT(n < 0 && errno == EPIPE);
|
||||
else ASSERT(n == (ssize_t) sizeof(marker));
|
||||
s_send_done = true;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
// accept loop. Waits for incoming connections on port 1234 and spawns
|
||||
// an echo_task for each one, allowing concurrent clients.
|
||||
static void atask(void *args) {
|
||||
const struct atask_args *cfg = (const struct atask_args *) args;
|
||||
BaseType_t result;
|
||||
mg_bsd_init();
|
||||
int lfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(1234);
|
||||
sa.sin_addr.s_addr = INADDR_ANY;
|
||||
bind(lfd, (struct sockaddr *) &sa, sizeof(sa));
|
||||
listen(lfd, 5);
|
||||
fcntl(lfd, F_SETFL, O_NONBLOCK);
|
||||
MG_INFO(("BSD echo server on :1234"));
|
||||
int lfd = open_listener(5, cfg->listener_nonblock);
|
||||
if (cfg->once && cfg->listener_nonblock) {
|
||||
ASSERT(accept(lfd, NULL, NULL) < 0 && errno == EAGAIN);
|
||||
s_accept_waiting = true;
|
||||
}
|
||||
while (!s_wait) {
|
||||
int fd = accept(lfd, NULL, NULL);
|
||||
if (fd < 0) { vTaskDelay(pdMS_TO_TICKS(10)); continue; }
|
||||
// echo_task at higher priority than task1
|
||||
result = xTaskCreate(echo_task, "echo", 512, (void *) (uintptr_t) fd,
|
||||
int fd;
|
||||
|
||||
if (cfg->once && !cfg->listener_nonblock) s_accept_waiting = true;
|
||||
fd = accept(lfd, NULL, NULL);
|
||||
if (fd < 0) {
|
||||
if (!cfg->listener_nonblock) {
|
||||
ASSERT(fd >= 0);
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
continue;
|
||||
}
|
||||
// Accepted sockets start blocking, regardless of listener flags
|
||||
ASSERT((fcntl(fd, F_GETFL, 0) & O_NONBLOCK) == 0);
|
||||
if (cfg->worker_nonblock) ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||
// Worker tasks run at higher priority than task1
|
||||
if (cfg->receiver) {
|
||||
result = xTaskCreate(recv_wait_task, "recv", 512,
|
||||
(void *) (uintptr_t) fd,
|
||||
configMAX_PRIORITIES - 1, NULL);
|
||||
ASSERT(result == pdPASS);
|
||||
}
|
||||
if (cfg->gate != NULL) {
|
||||
result = xTaskCreate(cfg->gate, "gate", 256, NULL,
|
||||
configMAX_PRIORITIES - 3, NULL);
|
||||
ASSERT(result == pdPASS);
|
||||
}
|
||||
result = xTaskCreate(cfg->task, "echo", 512, (void *) (uintptr_t) fd,
|
||||
configMAX_PRIORITIES - 1, NULL);
|
||||
MG_INFO(("Echo task %d: %s", fd, result == pdPASS ? "created" : "FAILED"));
|
||||
ASSERT(result == pdPASS);
|
||||
if (cfg->once) break;
|
||||
}
|
||||
close(lfd);
|
||||
s_wait = false;
|
||||
if (cfg->once) s_accept_done = true;
|
||||
else s_wait = false;
|
||||
vTaskDelete(NULL);
|
||||
(void) args;
|
||||
}
|
||||
|
||||
static void select_task(void *args) {
|
||||
struct timeval zero = {0, 0};
|
||||
fd_set r, w, e;
|
||||
int fd, lfd, n;
|
||||
|
||||
(void) args;
|
||||
lfd = open_listener(5, true);
|
||||
FD_ZERO(&r);
|
||||
FD_SET(lfd, &r);
|
||||
ASSERT(select(lfd, &r, NULL, NULL, &zero) == 0);
|
||||
ASSERT(!FD_ISSET(lfd, &r));
|
||||
FD_ZERO(&r);
|
||||
FD_SET(lfd + 1, &r);
|
||||
ASSERT(select(lfd + 2, &r, NULL, NULL, &zero) < 0 && errno == EBADF);
|
||||
FD_ZERO(&r);
|
||||
FD_ZERO(&w);
|
||||
FD_ZERO(&e);
|
||||
FD_SET(lfd, &r);
|
||||
FD_SET(lfd, &w);
|
||||
FD_SET(lfd, &e);
|
||||
s_select_waiting = true;
|
||||
n = select(lfd + 1, &r, &w, &e, NULL);
|
||||
ASSERT(n == 1);
|
||||
ASSERT(FD_ISSET(lfd, &r));
|
||||
ASSERT(!FD_ISSET(lfd, &w));
|
||||
ASSERT(!FD_ISSET(lfd, &e));
|
||||
fd = accept(lfd, NULL, NULL);
|
||||
ASSERT(fd >= 0);
|
||||
ASSERT(close(fd) == 0);
|
||||
ASSERT(close(lfd) == 0);
|
||||
s_select_done = true;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void poll_task(void *args) {
|
||||
struct pollfd p;
|
||||
int fd, lfd;
|
||||
|
||||
(void) args;
|
||||
lfd = open_listener(5, true);
|
||||
p.fd = lfd, p.events = POLLIN, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, 0) == 0);
|
||||
ASSERT(p.revents == 0);
|
||||
p.fd = lfd + 1, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, 0) == 1);
|
||||
ASSERT(p.revents == POLLNVAL);
|
||||
p.fd = lfd, p.revents = 0;
|
||||
s_poll_waiting = true;
|
||||
ASSERT(poll(&p, 1, -1) == 1);
|
||||
ASSERT(p.revents == POLLIN);
|
||||
fd = accept(lfd, NULL, NULL);
|
||||
ASSERT(fd >= 0);
|
||||
ASSERT(close(fd) == 0);
|
||||
ASSERT(close(lfd) == 0);
|
||||
s_poll_done = true;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void readiness_task(void *args) {
|
||||
struct pollfd p;
|
||||
fd_set r, w;
|
||||
int fd, lfd;
|
||||
|
||||
(void) args;
|
||||
lfd = open_listener(5, true);
|
||||
s_ready_waiting = true;
|
||||
while ((fd = accept(lfd, NULL, NULL)) < 0) {
|
||||
ASSERT(errno == EAGAIN);
|
||||
vTaskDelay(1);
|
||||
}
|
||||
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||
FD_ZERO(&r);
|
||||
FD_SET(fd, &r);
|
||||
ASSERT(select(fd + 1, &r, NULL, NULL, NULL) == 1);
|
||||
ASSERT(FD_ISSET(fd, &r));
|
||||
p.fd = fd, p.events = POLLIN, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, 0) == 1);
|
||||
ASSERT(p.revents == POLLIN);
|
||||
FD_ZERO(&w);
|
||||
FD_SET(fd, &w);
|
||||
ASSERT(select(fd + 1, NULL, &w, NULL, NULL) == 1);
|
||||
ASSERT(FD_ISSET(fd, &w));
|
||||
p.events = POLLOUT, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, 0) == 1);
|
||||
ASSERT(p.revents == POLLOUT);
|
||||
ASSERT(close(lfd) == 0);
|
||||
echo_task_common(fd, 0, &s_ready_done);
|
||||
}
|
||||
|
||||
static void backlog_task(void *args) {
|
||||
struct pollfd p;
|
||||
BaseType_t result;
|
||||
int fd, lfd;
|
||||
|
||||
(void) args;
|
||||
lfd = open_listener(1, true);
|
||||
p.fd = lfd, p.events = POLLIN, p.revents = 0;
|
||||
s_backlog_ready = true;
|
||||
while (poll(&p, 1, 0) == 0) vTaskDelay(1);
|
||||
ASSERT(p.revents == POLLIN);
|
||||
s_backlog_full = true; // One orphan occupies the BSD backlog
|
||||
while (!s_backlog_accept) vTaskDelay(1);
|
||||
ASSERT((fd = accept(lfd, NULL, NULL)) >= 0);
|
||||
result = xTaskCreate(echo_task, "echo", 512, (void *) (uintptr_t) fd,
|
||||
configMAX_PRIORITIES - 1, NULL);
|
||||
ASSERT(result == pdPASS);
|
||||
ASSERT(close(lfd) == 0);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
// task2: tests owner.
|
||||
static void task2(void *args) {
|
||||
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
||||
char *ip, *cmd[3];
|
||||
pid_t pid;
|
||||
char *ip;
|
||||
int status;
|
||||
extern char **environ;
|
||||
|
||||
#define DASHBOARD(x) printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true");
|
||||
pid_t first, second;
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
||||
s_error = false;
|
||||
s_wait = false;
|
||||
// atask at higher priority than task1
|
||||
xTaskCreate(atask, "atask", 256, NULL, configMAX_PRIORITIES - 1, NULL);
|
||||
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
|
||||
cmd[0] = (char *) "./bsd_client";
|
||||
cmd[1] = ip;
|
||||
cmd[2] = NULL;
|
||||
ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0);
|
||||
for (;;) {
|
||||
pid_t r = waitpid(pid, &status, WNOHANG);
|
||||
ASSERT(r >= 0);
|
||||
if (r == pid) break;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
ASSERT(WIFEXITED(status));
|
||||
ASSERT(WEXITSTATUS(status) == 0);
|
||||
mg_free(ip);
|
||||
s_poll_waiting = s_poll_done = false;
|
||||
ASSERT(xTaskCreate(poll_task, "poll", 512, NULL,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
while (!s_poll_waiting) vTaskDelay(1);
|
||||
ASSERT(!s_poll_done);
|
||||
run_client(ip, "hold");
|
||||
while (!s_poll_done) vTaskDelay(1);
|
||||
DASHBOARD("poll_read");
|
||||
|
||||
s_select_waiting = s_select_done = false;
|
||||
ASSERT(xTaskCreate(select_task, "select", 512, NULL,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
while (!s_select_waiting) vTaskDelay(1);
|
||||
ASSERT(!s_select_done);
|
||||
run_client(ip, "hold");
|
||||
while (!s_select_done) vTaskDelay(1);
|
||||
DASHBOARD("select_read");
|
||||
|
||||
s_error = false;
|
||||
s_ready_waiting = s_ready_done = false;
|
||||
ASSERT(xTaskCreate(readiness_task, "ready", 512, NULL,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
while (!s_ready_waiting) vTaskDelay(1);
|
||||
run_client(ip, "1");
|
||||
while (!s_ready_done) vTaskDelay(1);
|
||||
DASHBOARD("select_child_read");
|
||||
DASHBOARD("poll_child_read");
|
||||
DASHBOARD("select_child_write");
|
||||
DASHBOARD("poll_child_write");
|
||||
|
||||
#if 0
|
||||
s_error = false;
|
||||
s_backlog_ready = s_backlog_full = s_backlog_accept = false;
|
||||
ASSERT(xTaskCreate(backlog_task, "backlog", 512, NULL,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
while (!s_backlog_ready) vTaskDelay(1);
|
||||
first = start_client(ip, "1"); // Fills listen(fd, 1)'s pending accept
|
||||
while (!s_backlog_full) vTaskDelay(1);
|
||||
second = start_client(ip, "expectclose"); // Rejected over the backlog
|
||||
while (!client_done(second, &status)) vTaskDelay(1);
|
||||
check_client(status);
|
||||
s_backlog_accept = true;
|
||||
while (!client_done(first, &status)) vTaskDelay(1);
|
||||
check_client(status);
|
||||
DASHBOARD("listen_backlog");
|
||||
#else
|
||||
(void) first;
|
||||
(void) second;
|
||||
(void) status;
|
||||
(void) backlog_task;
|
||||
#endif
|
||||
|
||||
s_error = false;
|
||||
// atask at higher priority than task1
|
||||
s_accept_waiting = s_accept_done = false;
|
||||
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_accept_server,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
while (!s_accept_waiting) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
ASSERT(!s_accept_done);
|
||||
run_client(ip, "1");
|
||||
while (!s_accept_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
DASHBOARD("blocking_accept");
|
||||
|
||||
s_error = false;
|
||||
s_wait = false;
|
||||
s_accept_waiting = s_accept_done = false;
|
||||
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_nonblock_accept_server,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
while (!s_accept_waiting) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
run_client(ip, "1");
|
||||
while (!s_accept_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
DASHBOARD("nonblocking_accept");
|
||||
|
||||
s_error = false;
|
||||
s_wait = false;
|
||||
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_echo_server,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
run_client(ip, NULL);
|
||||
s_wait = true;
|
||||
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
DASHBOARD("accept");
|
||||
|
||||
s_error = false;
|
||||
s_wait = false;
|
||||
s_dontwait_done = false;
|
||||
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_dontwait_server,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
run_client(ip, "1");
|
||||
while (!s_dontwait_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
s_wait = true;
|
||||
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
DASHBOARD("dontwait_recv");
|
||||
|
||||
s_error = false;
|
||||
s_wait = false;
|
||||
s_nonblock_done = false;
|
||||
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_nonblock_server,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
run_client(ip, "1");
|
||||
while (!s_nonblock_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
s_wait = true;
|
||||
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
DASHBOARD("nonblocking_recv");
|
||||
|
||||
s_error = false;
|
||||
s_wait = s_tx_held = s_tx_waits = s_tx_fail = s_tx_error = false;
|
||||
s_send_waiting = false;
|
||||
s_send_done = s_send_expect_error = false;
|
||||
s_recv_start = s_recv_waiting = s_recv_done = s_recv_expect_error = false;
|
||||
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_send_server,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
run_client(ip, "waitanddrain"); // Peer drains through the final marker
|
||||
ASSERT(s_send_done);
|
||||
DASHBOARD("nonblocking_send");
|
||||
DASHBOARD("blocking_send");
|
||||
while (!s_recv_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
DASHBOARD("blocking_recv");
|
||||
DASHBOARD("shared_socket");
|
||||
s_wait = true;
|
||||
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
s_error = false;
|
||||
s_wait = s_tx_held = s_tx_waits = s_tx_fail = s_tx_error = false;
|
||||
s_send_waiting = false;
|
||||
s_send_done = s_send_expect_error = false;
|
||||
s_recv_start = s_recv_waiting = s_recv_done = false;
|
||||
s_recv_expect_error = true;
|
||||
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_send_server,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
run_client(ip, "waitandabort"); // Peer leaves data unread and sends RST
|
||||
ASSERT(s_send_done);
|
||||
while (!s_recv_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
DASHBOARD("error_wakeup");
|
||||
s_wait = true;
|
||||
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
s_error = false;
|
||||
s_wait = s_tx_held = s_tx_waits = s_tx_fail = s_tx_error = false;
|
||||
s_send_waiting = false;
|
||||
s_send_done = false;
|
||||
s_send_expect_error = true;
|
||||
ASSERT(xTaskCreate(atask, "atask", 256, (void *) &s_send_error_server,
|
||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||
run_client(ip, "hold"); // Peer stays connected while the driver fails TX
|
||||
while (!s_tx_error) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
ASSERT(s_send_done);
|
||||
DASHBOARD("send_error_wakeup");
|
||||
s_wait = true;
|
||||
while (s_wait) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
s_tx_held = s_tx_fail = s_tx_error = s_send_expect_error = false;
|
||||
|
||||
mg_free(ip);
|
||||
|
||||
s_error = false;
|
||||
{
|
||||
fd_set e;
|
||||
struct pollfd p;
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(1234);
|
||||
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||
ASSERT(connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 &&
|
||||
errno == EINPROGRESS);
|
||||
FD_ZERO(&e);
|
||||
FD_SET(fd, &e);
|
||||
ASSERT(select(fd + 1, NULL, NULL, &e, NULL) == 1);
|
||||
ASSERT(FD_ISSET(fd, &e));
|
||||
p.fd = fd, p.events = 0, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, -1) == 1);
|
||||
ASSERT((p.revents & POLLERR) != 0);
|
||||
p.events = POLLOUT, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, 0) == 1);
|
||||
ASSERT((p.revents & POLLERR) != 0);
|
||||
ASSERT((p.revents & POLLOUT) == 0);
|
||||
ASSERT(close(fd) == 0);
|
||||
}
|
||||
DASHBOARD("select_error");
|
||||
DASHBOARD("poll_error");
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
||||
s_error = false;
|
||||
{
|
||||
@@ -188,6 +747,7 @@ static void task2(void *args) {
|
||||
while (!cd[0].done || !cd[1].done || !cd[2].done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
DASHBOARD("connect");
|
||||
DASHBOARD("select_write");
|
||||
// End task 1
|
||||
s_done = true;
|
||||
while (s_done) vTaskDelay(pdMS_TO_TICKS(10));
|
||||
@@ -206,35 +766,205 @@ static void task2(void *args) {
|
||||
static void task1(void *args) {
|
||||
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
||||
|
||||
// Stack initialization, Network configuration (DHCP lease, ...)
|
||||
#if BSDTEST_USING_DHCP == 0
|
||||
MG_INFO(("MIF configuration: Static IP"));
|
||||
ASSERT(mif.ip != 0); // Check we have a satic IP assigned
|
||||
mg_mgr_poll(&mgr, 0); // For initialisation
|
||||
#else
|
||||
MG_INFO(("MIF configuration: DHCP"));
|
||||
ASSERT(mgr->ifp->ip == 0); // Check we are set for DHCP
|
||||
while (mgr->ifp->ip == 0 && mg_millis() < 5000) {
|
||||
mg_mgr_poll(mgr, 0);
|
||||
}
|
||||
if (mgr->ifp->ip == 0) MG_ERROR(("No ip assigned (DHCP lease may have failed).\n"));
|
||||
ASSERT(mgr->ifp->ip != 0); // We have an IP (lease or static)
|
||||
#endif
|
||||
while (mgr->ifp->state != MG_TCPIP_STATE_READY) {
|
||||
mg_mgr_poll(mgr, 0);
|
||||
}
|
||||
MG_INFO(("Interface started, starting main loop"));
|
||||
wait_ready(mgr);
|
||||
xTaskCreate(task2, "task2", 256, mgr, configMAX_PRIORITIES - 2, NULL);
|
||||
|
||||
while (!s_done) {
|
||||
mg_mgr_poll(mgr, 0);
|
||||
mg_bsd_poll(mgr);
|
||||
vTaskDelay(1); // Let idle reclaim deleted echo tasks
|
||||
}
|
||||
s_done = false;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define NORTOS_ECHO_CLIENTS 3
|
||||
|
||||
struct nortos_echo_client {
|
||||
int fd;
|
||||
size_t len, sent;
|
||||
bool eof;
|
||||
char buf[512];
|
||||
};
|
||||
|
||||
static int nortos_accept(struct mg_mgr *mgr, int lfd) {
|
||||
int fd;
|
||||
|
||||
while ((fd = accept(lfd, NULL, NULL)) < 0) {
|
||||
ASSERT(errno == EAGAIN);
|
||||
mg_mgr_poll(mgr, 0);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void nortos_wait_client(struct mg_mgr *mgr, pid_t pid) {
|
||||
int status;
|
||||
|
||||
while (!client_done(pid, &status)) mg_mgr_poll(mgr, 0);
|
||||
check_client(status);
|
||||
}
|
||||
|
||||
static void nortos_echo(struct mg_mgr *mgr, struct nortos_echo_client *clients,
|
||||
size_t count, pid_t pid) {
|
||||
struct pollfd p[NORTOS_ECHO_CLIENTS];
|
||||
fd_set r;
|
||||
size_t i, live = count;
|
||||
int maxfd, result;
|
||||
ssize_t n;
|
||||
|
||||
while (live > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
struct nortos_echo_client *c = &clients[i];
|
||||
if (!c->eof && c->sent < c->len) {
|
||||
n = send(c->fd, c->buf + c->sent, c->len - c->sent, 0);
|
||||
if (n > 0) c->sent += (size_t) n;
|
||||
else ASSERT(n < 0 && errno == EAGAIN);
|
||||
}
|
||||
}
|
||||
|
||||
FD_ZERO(&r);
|
||||
maxfd = -1;
|
||||
for (i = 0; i < count; i++) {
|
||||
struct nortos_echo_client *c = &clients[i];
|
||||
p[i].fd = -1, p[i].events = POLLIN, p[i].revents = 0;
|
||||
if (!c->eof && c->sent == c->len) {
|
||||
p[i].fd = c->fd;
|
||||
FD_SET(c->fd, &r);
|
||||
if (c->fd > maxfd) maxfd = c->fd;
|
||||
}
|
||||
}
|
||||
if (maxfd >= 0) {
|
||||
result = poll(p, (nfds_t) count, 0);
|
||||
ASSERT(result >= 0);
|
||||
if (result > 0) {
|
||||
ASSERT(select(maxfd + 1, &r, NULL, NULL, NULL) == result);
|
||||
for (i = 0; i < count; i++) {
|
||||
struct nortos_echo_client *c = &clients[i];
|
||||
bool readable = (p[i].revents & POLLIN) != 0;
|
||||
|
||||
ASSERT(p[i].revents == (readable ? POLLIN : 0));
|
||||
ASSERT(FD_ISSET(c->fd, &r) == readable);
|
||||
if (readable) {
|
||||
n = recv(c->fd, c->buf, sizeof(c->buf), 0);
|
||||
ASSERT(n >= 0);
|
||||
if (n > 0) c->len = (size_t) n, c->sent = 0;
|
||||
else c->eof = true, live--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mg_mgr_poll(mgr, 0);
|
||||
}
|
||||
nortos_wait_client(mgr, pid);
|
||||
}
|
||||
|
||||
static void nortos_test(struct mg_mgr *mgr) {
|
||||
static const char bootstrap = '\0';
|
||||
static const char grow[512] = {0};
|
||||
static const char marker = '\1';
|
||||
struct sockaddr_in sa;
|
||||
struct timeval zero = {0, 0};
|
||||
struct pollfd p;
|
||||
fd_set r, w, e;
|
||||
char buf;
|
||||
char *ip;
|
||||
pid_t pid;
|
||||
size_t i, j;
|
||||
int fd, lfd, n;
|
||||
struct nortos_echo_client clients[NORTOS_ECHO_CLIENTS];
|
||||
|
||||
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
|
||||
ASSERT(ip != NULL);
|
||||
lfd = open_listener(5, true);
|
||||
p.fd = lfd, p.events = POLLIN, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, 10) == 0);
|
||||
ASSERT(p.revents == 0);
|
||||
p.fd = lfd + 1, p.revents = 0;
|
||||
ASSERT(poll(&p, 1, 10) == 1);
|
||||
ASSERT(p.revents == POLLNVAL);
|
||||
p.fd = lfd, p.revents = 0;
|
||||
FD_ZERO(&r);
|
||||
FD_SET(lfd, &r);
|
||||
ASSERT(select(lfd, &r, NULL, NULL, &zero) == 0);
|
||||
ASSERT(!FD_ISSET(lfd, &r));
|
||||
FD_ZERO(&r);
|
||||
FD_SET(lfd + 1, &r);
|
||||
ASSERT(select(lfd + 2, &r, NULL, NULL, &zero) < 0 && errno == EBADF);
|
||||
ASSERT(accept(lfd, NULL, NULL) < 0 && errno == EAGAIN);
|
||||
DASHBOARD("nortos_nonblocking_accept");
|
||||
|
||||
pid = start_client(ip, "waitanddrain");
|
||||
do {
|
||||
p.revents = 0;
|
||||
n = poll(&p, 1, 10);
|
||||
if (n == 0) mg_mgr_poll(mgr, 0);
|
||||
} while (n == 0);
|
||||
ASSERT(n == 1);
|
||||
ASSERT(p.revents == POLLIN);
|
||||
DASHBOARD("nortos_poll_read");
|
||||
do {
|
||||
FD_ZERO(&r);
|
||||
FD_ZERO(&w);
|
||||
FD_ZERO(&e);
|
||||
FD_SET(lfd, &r);
|
||||
FD_SET(lfd, &w);
|
||||
FD_SET(lfd, &e);
|
||||
n = select(lfd + 1, &r, &w, &e, NULL);
|
||||
if (n == 0) mg_mgr_poll(mgr, 0);
|
||||
} while (n == 0);
|
||||
ASSERT(n == 1);
|
||||
ASSERT(FD_ISSET(lfd, &r));
|
||||
ASSERT(!FD_ISSET(lfd, &w));
|
||||
ASSERT(!FD_ISSET(lfd, &e));
|
||||
DASHBOARD("nortos_select_read");
|
||||
fd = nortos_accept(mgr, lfd);
|
||||
ASSERT(recv(fd, &buf, sizeof(buf), MSG_DONTWAIT) < 0 && errno == EAGAIN);
|
||||
DASHBOARD("nortos_dontwait_recv");
|
||||
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||
ASSERT(recv(fd, &buf, sizeof(buf), 0) < 0 && errno == EAGAIN);
|
||||
DASHBOARD("nortos_nonblocking_recv");
|
||||
|
||||
ASSERT(send(fd, &bootstrap, sizeof(bootstrap), 0) ==
|
||||
(ssize_t) sizeof(bootstrap));
|
||||
s_fail_alloc = true; // Fail growth after c->send has been bootstrapped
|
||||
ASSERT(send(fd, grow, sizeof(grow), 0) < 0 && errno == EAGAIN);
|
||||
s_fail_alloc = false;
|
||||
ASSERT(send(fd, &marker, sizeof(marker), 0) == (ssize_t) sizeof(marker));
|
||||
nortos_wait_client(mgr, pid);
|
||||
ASSERT(close(fd) == 0);
|
||||
DASHBOARD("nortos_nonblocking_send");
|
||||
|
||||
memset(clients, 0, sizeof(clients));
|
||||
pid = start_client(ip, "3");
|
||||
for (i = 0; i < NORTOS_ECHO_CLIENTS; i++) {
|
||||
clients[i].fd = nortos_accept(mgr, lfd);
|
||||
ASSERT(fcntl(clients[i].fd, F_SETFL, O_NONBLOCK) == 0);
|
||||
for (j = 0; j < i; j++) ASSERT(clients[i].fd != clients[j].fd);
|
||||
}
|
||||
nortos_echo(mgr, clients, NORTOS_ECHO_CLIENTS, pid);
|
||||
for (i = 0; i < NORTOS_ECHO_CLIENTS; i++)
|
||||
ASSERT(close(clients[i].fd) == 0);
|
||||
ASSERT(close(lfd) == 0);
|
||||
DASHBOARD("nortos_select_children");
|
||||
DASHBOARD("nortos_poll_children");
|
||||
DASHBOARD("nortos_echo");
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(1234);
|
||||
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||
ASSERT(connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 &&
|
||||
errno == EINPROGRESS);
|
||||
ASSERT(close(fd) == 0);
|
||||
mg_mgr_poll(mgr, 0);
|
||||
DASHBOARD("nortos_nonblocking_connect");
|
||||
mg_free(ip);
|
||||
}
|
||||
|
||||
#endif // MG_ENABLE_FREERTOS
|
||||
|
||||
static bool bsd_x_test(struct mg_tcpip_driver *driver, void *driver_data, const char *mac, const char *debug_level) {
|
||||
if (debug_level == NULL) debug_level = "3";
|
||||
mg_log_set(atoi(debug_level));
|
||||
@@ -263,8 +993,21 @@ static bool bsd_x_test(struct mg_tcpip_driver *driver, void *driver_data, const
|
||||
MG_INFO(("Init done, starting interface"));
|
||||
usleep(200000); // 200 ms
|
||||
|
||||
#if MG_ENABLE_FREERTOS
|
||||
s_tx = driver->tx;
|
||||
driver->tx = tx_gate;
|
||||
xTaskCreate(task1, "task1", 2048, &mgr, configMAX_PRIORITIES - 2, NULL);
|
||||
vTaskStartScheduler();
|
||||
driver->tx = s_tx;
|
||||
#else
|
||||
s_bootmillis = mg_millis();
|
||||
wait_ready(&mgr);
|
||||
nortos_test(&mgr);
|
||||
s_error = false;
|
||||
mg_mgr_free(&mgr);
|
||||
ASSERT(mgr.conns == NULL);
|
||||
printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false" : "true");
|
||||
#endif
|
||||
|
||||
#ifdef NO_ABORT
|
||||
if (s_abort != 0) return false;
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// See https://mongoose.ws/documentation/#build-options
|
||||
//#undef MG_ARCH
|
||||
//#define MG_ARCH MG_ARCH_UNIX
|
||||
#define MG_ENABLE_FREERTOS 1
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
#define MG_ENABLE_BSD_SOCKETS 1
|
||||
#define MG_ENABLE_BSD_LOG 1
|
||||
#define MG_ENABLE_LINES 1
|
||||
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||
|
||||
//#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
@@ -24,7 +19,16 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if MG_ENABLE_FREERTOS
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#define MG_ENABLE_CUSTOM_CALLOC 1
|
||||
#define MG_ENABLE_CUSTOM_MILLIS 1
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user