mirror of
https://gitee.com/Lamdonn/varch.git
synced 2026-10-28 18:07:39 +08:00
1. Compatibility with coroutine API highly intensive IO, sockets, etc 2. Fix running fail on arm64 3. Add low power configuration hook TODO: 1. Add a scheduling ready task queue mechanism 2. Add CoTask communication channel
736 lines
21 KiB
C
736 lines
21 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <time.h>
|
|
#if defined(_WIN32)
|
|
#include <windows.h>
|
|
#else
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
#if defined(TEST_TARGET_coroutine)
|
|
#include <varch/command.h>
|
|
#include <varch/unitt.h>
|
|
#include <varch/coroutine.h>
|
|
#include <varch/coroutine_hook.h>
|
|
#include <varch/coroutine_io.h>
|
|
#else
|
|
#include "init.h"
|
|
#include "command.h"
|
|
#include "unitt.h"
|
|
#include "kern.h"
|
|
#include "coroutine.h"
|
|
#include "coroutine_hook.h"
|
|
#include "coroutine_io.h"
|
|
#endif
|
|
|
|
#define MUTI_THREAD_NUM 2
|
|
#if (MUTI_THREAD_NUM <= 0)
|
|
#error "MUTI_THREAD_NUM must be greater than 0"
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
uint64_t GetTimerUsec(void)
|
|
{
|
|
LARGE_INTEGER li;
|
|
LARGE_INTEGER frequency;
|
|
QueryPerformanceFrequency(&frequency);
|
|
QueryPerformanceCounter(&li);
|
|
return (uint64_t)li.QuadPart * 1000000 / frequency.QuadPart;
|
|
}
|
|
#else
|
|
#include <sys/time.h>
|
|
uint64_t GetTimerUsec(void)
|
|
{
|
|
struct timeval mstime;
|
|
uint64_t us = 0;
|
|
gettimeofday(&mstime, NULL);
|
|
us = mstime.tv_sec * 1000000 + mstime.tv_usec;
|
|
return us;
|
|
}
|
|
#endif
|
|
|
|
/************************************************************************************/
|
|
/************************************* Unit Test ************************************/
|
|
/************************************************************************************/
|
|
|
|
// #define EXIT_TEST
|
|
extern uint64_t unitt_clock(void);
|
|
|
|
static int s_libco_step = 0;
|
|
static void *libco_test_worker(void *arg)
|
|
{
|
|
s_libco_step = 1;
|
|
CoTask_Yield();
|
|
s_libco_step = 2;
|
|
CoTask_Yield();
|
|
s_libco_step = 3;
|
|
return NULL;
|
|
}
|
|
|
|
static int test_libco(void)
|
|
{
|
|
CoScheduler_Init(0, GetTimerUsec, 1000);
|
|
s_libco_step = 0;
|
|
CoTask_t task = CoTask_Create(libco_test_worker, .manual=1);
|
|
if (!task) return UNITT_E_FAIL;
|
|
|
|
int ret1 = CoTask_Resume(task);
|
|
if (ret1 != COROUTINE_E_OK || s_libco_step != 1) return UNITT_E_FAIL;
|
|
|
|
int ret2 = CoTask_Resume(task);
|
|
if (ret2 != COROUTINE_E_OK || s_libco_step != 2) return UNITT_E_FAIL;
|
|
|
|
int ret3 = CoTask_Resume(task);
|
|
if (ret3 != 1 || s_libco_step != 3) return UNITT_E_FAIL;
|
|
|
|
CoTask_Delete(task);
|
|
return UNITT_E_OK;
|
|
}
|
|
|
|
static int test_0(void)
|
|
{
|
|
return test_libco();
|
|
}
|
|
|
|
static void unitt_task(void)
|
|
{
|
|
static UNITT_TCASE rand_tests[] = {
|
|
UNITT_TCASE(test_0),
|
|
UNITT_TCASE(test_libco),
|
|
};
|
|
|
|
static UNITT suites[] = {
|
|
{ "coroutine suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
|
|
};
|
|
|
|
UNITT_EXE(suites);
|
|
}
|
|
|
|
/************************************************************************************/
|
|
/************************************* Base Test ************************************/
|
|
/************************************************************************************/
|
|
|
|
static CoEvent gEvent = COEVENT_STATIC_VALUE;
|
|
|
|
/* --- 1. Manual Point-to-Point Test (libco style) --- */
|
|
static void *manual_worker(void *arg)
|
|
{
|
|
printf(" [Manual Task] Step 1: started, yielding...\n");
|
|
CoTask_Yield();
|
|
printf(" [Manual Task] Step 2: resumed, yielding again...\n");
|
|
CoTask_Yield();
|
|
printf(" [Manual Task] Step 3: resumed, yielding again...\n");
|
|
CoTask_Yield();
|
|
printf(" [Manual Task] Step 4: resumed, finishing!\n");
|
|
return NULL;
|
|
}
|
|
|
|
static void test_manual(void)
|
|
{
|
|
printf("=== [Test Manual] Point-to-Point Stepping (CoTask_Resume & CoTask_Yield) ===\n");
|
|
CoScheduler_Init(0, GetTimerUsec, 1000);
|
|
CoTask_t task = CoTask_Create(manual_worker, .manual = 1);
|
|
if (!task)
|
|
{
|
|
printf("Failed to create manual task!\n");
|
|
return;
|
|
}
|
|
|
|
for (int step = 1; step <= 4; step++)
|
|
{
|
|
printf("[Main] Calling CoTask_Resume (%d)...\n", step);
|
|
int ret = CoTask_Resume(task);
|
|
printf("[Main] CoTask_Resume returned: %d\n", ret);
|
|
}
|
|
|
|
CoTask_Delete(task);
|
|
printf("=== [Test Manual] Completed! ===\n\n");
|
|
}
|
|
|
|
/* --- 2. Shared Stack Test --- */
|
|
static void *shared_worker_a(void *arg)
|
|
{
|
|
volatile int buf[100];
|
|
for (int i = 0; i < 100; i++) buf[i] = 1000 + i;
|
|
printf(" [Shared A] initial buf[0]=%d, buf[99]=%d, yielding...\n", buf[0], buf[99]);
|
|
fflush(stdout);
|
|
CoTask_Yield();
|
|
printf(" [Shared A] resumed! checking buf[0]=%d, buf[99]=%d\n", buf[0], buf[99]);
|
|
if (buf[0] != 1000 || buf[99] != 1099)
|
|
{
|
|
printf(" [Shared A] ERROR: stack data corrupted!\n");
|
|
}
|
|
else
|
|
{
|
|
printf(" [Shared A] SUCCESS: stack data perfectly preserved!\n");
|
|
}
|
|
fflush(stdout);
|
|
return NULL;
|
|
}
|
|
|
|
static void *shared_worker_b(void *arg)
|
|
{
|
|
volatile int buf[100];
|
|
for (int i = 0; i < 100; i++) buf[i] = 2000 + i;
|
|
printf(" [Shared B] initial buf[0]=%d, buf[99]=%d, yielding...\n", buf[0], buf[99]);
|
|
fflush(stdout);
|
|
CoTask_Yield();
|
|
printf(" [Shared B] resumed! checking buf[0]=%d, buf[99]=%d\n", buf[0], buf[99]);
|
|
if (buf[0] != 2000 || buf[99] != 2099)
|
|
{
|
|
printf(" [Shared B] ERROR: stack data corrupted!\n");
|
|
}
|
|
else
|
|
{
|
|
printf(" [Shared B] SUCCESS: stack data perfectly preserved!\n");
|
|
}
|
|
fflush(stdout);
|
|
return NULL;
|
|
}
|
|
|
|
static void test_shared(void)
|
|
{
|
|
printf("=== [Test Shared] Shared Stack Mode (Copy on Suspend, Restore on Resume) ===\n");
|
|
#if (COROUTINE_SHARED_STACK_SIZE > 0)
|
|
Coroutine_Init(.malloc = malloc, .free = free);
|
|
CoScheduler_Init(0, GetTimerUsec, 1000);
|
|
|
|
CoTask_t ta = CoTask_Create(shared_worker_a, .manual = 1, .sharedStack = 1);
|
|
CoTask_t tb = CoTask_Create(shared_worker_b, .manual = 1, .sharedStack = 1);
|
|
if (!ta || !tb)
|
|
{
|
|
printf("Failed to create shared stack tasks! (Ensure dynamic memory allocator is configured)\n");
|
|
return;
|
|
}
|
|
|
|
printf("[Main] Step 1: Run Task A on shared stack...\n");
|
|
CoTask_Resume(ta);
|
|
|
|
printf("[Main] Step 2: Run Task B (overwriting physical shared stack)...\n");
|
|
CoTask_Resume(tb);
|
|
|
|
printf("[Main] Step 3: Resume Task A (restores stack A)...\n");
|
|
CoTask_Resume(ta);
|
|
|
|
printf("[Main] Step 4: Resume Task B (restores stack B)...\n");
|
|
CoTask_Resume(tb);
|
|
|
|
CoTask_Delete(ta);
|
|
CoTask_Delete(tb);
|
|
printf("=== [Test Shared] Completed! ===\n\n");
|
|
#else
|
|
printf("COROUTINE_SHARED_STACK_SIZE is 0. Shared stack feature is disabled!\n");
|
|
#endif
|
|
}
|
|
|
|
/* --- 3. Single Scheduler Automatic Mode --- */
|
|
static void *single_tick(void *arg)
|
|
{
|
|
for (int i = 1; i <= 5; i++)
|
|
{
|
|
CoTask_Wait(.ms = 100);
|
|
printf(" [Single Scheduler] tick %d/5...\n", i);
|
|
CoEvent_Notify(&gEvent, 0x02);
|
|
}
|
|
printf(" [Single Scheduler] tick finished!\n");
|
|
return NULL;
|
|
}
|
|
|
|
static void *single_tock(void *arg)
|
|
{
|
|
for (int i = 1; i <= 5; i++)
|
|
{
|
|
uint32_t evs = CoTask_Wait(.pEvent = &gEvent, .ms = 500);
|
|
if (evs & 0x02)
|
|
{
|
|
printf(" [Single Scheduler] tock received event %d/5!\n", i);
|
|
}
|
|
}
|
|
printf(" [Single Scheduler] tock finished, exiting scheduler 0...\n");
|
|
CoScheduler_Exit(0);
|
|
return NULL;
|
|
}
|
|
|
|
static void test_single(void)
|
|
{
|
|
printf("=== [Test Single] Single Scheduler Automatic Mode ===\n");
|
|
CoScheduler_Init(0, GetTimerUsec, 1000);
|
|
gEvent.flag = 0;
|
|
CoTask_Create(single_tick);
|
|
CoTask_Create(single_tock);
|
|
printf("[Main] Starting single scheduler 0...\n");
|
|
CoScheduler_Start(0);
|
|
printf("=== [Test Single] Completed! ===\n\n");
|
|
}
|
|
|
|
/* --- 4. Multi-Scheduler Multi-Threaded Mode --- */
|
|
static pthread_t threads[MUTI_THREAD_NUM];
|
|
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static void thread_lock(void)
|
|
{
|
|
pthread_mutex_lock(&mutex);
|
|
}
|
|
|
|
static void thread_unlock(void)
|
|
{
|
|
pthread_mutex_unlock(&mutex);
|
|
}
|
|
|
|
static void *multi_tick(void *arg)
|
|
{
|
|
for (int i = 1; i <= 5; i++)
|
|
{
|
|
CoTask_Wait(.ms = 100);
|
|
printf(" [Multi-Core] Thread %d: tick %d/5...\n", CoTask_SchedulerId(), i);
|
|
CoEvent_Notify(&gEvent, 0x02);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void *multi_tock(void *arg)
|
|
{
|
|
for (int i = 1; i <= 5; i++)
|
|
{
|
|
uint32_t evs = CoTask_Wait(.pEvent = &gEvent, .ms = 500);
|
|
if (evs & 0x02)
|
|
{
|
|
printf(" [Multi-Core] Thread %d: tock received event %d/5!\n", CoTask_SchedulerId(), i);
|
|
}
|
|
}
|
|
printf(" [Multi-Core] tock finished, exiting schedulers...\n");
|
|
for (int i = 0; i < MUTI_THREAD_NUM; i++)
|
|
{
|
|
CoScheduler_Exit(i);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void *muti_thread_entry(void *arg)
|
|
{
|
|
CoScheduler_Start(*(int *)arg);
|
|
return NULL;
|
|
}
|
|
|
|
static void test_multi(void)
|
|
{
|
|
printf("=== [Test Multi] Multi-Scheduler Multi-Threaded Mode ===\n");
|
|
int threadId[MUTI_THREAD_NUM];
|
|
Coroutine_Init(.lock = thread_lock, .unlock = thread_unlock);
|
|
|
|
for (int i = 0; i < MUTI_THREAD_NUM; i++)
|
|
{
|
|
CoScheduler_Init(i, GetTimerUsec, 1000);
|
|
}
|
|
|
|
gEvent.flag = 0;
|
|
CoTask_Create(multi_tick, .schedulerId = 0);
|
|
CoTask_Create(multi_tock, .schedulerId = 1);
|
|
|
|
for (int i = 0; i < MUTI_THREAD_NUM; i++)
|
|
{
|
|
threadId[i] = i;
|
|
pthread_create(&threads[i], NULL, muti_thread_entry, &threadId[i]);
|
|
}
|
|
|
|
for (int i = 0; i < MUTI_THREAD_NUM; i++)
|
|
{
|
|
pthread_join(threads[i], NULL);
|
|
}
|
|
printf("=== [Test Multi] Completed! ===\n\n");
|
|
}
|
|
|
|
/* --- 5. Low-Power Hook Benchmark (CPU Load Comparison) --- */
|
|
static uint64_t GetThreadCpuTimeUs(void)
|
|
{
|
|
#if defined(_WIN32)
|
|
FILETIME createTime, exitTime, kernelTime, userTime;
|
|
if (GetThreadTimes(GetCurrentThread(), &createTime, &exitTime, &kernelTime, &userTime))
|
|
{
|
|
ULARGE_INTEGER k, u;
|
|
k.LowPart = kernelTime.dwLowDateTime;
|
|
k.HighPart = kernelTime.dwHighDateTime;
|
|
u.LowPart = userTime.dwLowDateTime;
|
|
u.HighPart = userTime.dwHighDateTime;
|
|
return (k.QuadPart + u.QuadPart) / 10; // 100ns units to us
|
|
}
|
|
return 0;
|
|
#elif defined(CLOCK_THREAD_CPUTIME_ID)
|
|
struct timespec ts;
|
|
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0)
|
|
{
|
|
return (uint64_t)ts.tv_sec * 1000000ULL + (uint64_t)ts.tv_nsec / 1000ULL;
|
|
}
|
|
return 0;
|
|
#else
|
|
return (uint64_t)clock() * 1000000ULL / CLOCKS_PER_SEC;
|
|
#endif
|
|
}
|
|
|
|
static void *hook_bench_worker(void *arg)
|
|
{
|
|
/* Wait for 400 ms */
|
|
CoTask_Wait(.ms = 400);
|
|
CoScheduler_Exit(0);
|
|
return NULL;
|
|
}
|
|
|
|
static void test_hook(void)
|
|
{
|
|
printf("=== [Test Low-Power Hook Benchmark: 400ms Idle] ===\n");
|
|
|
|
/* --- Phase 1: WITHOUT Hook (Busy-Spin) --- */
|
|
printf("[Phase 1] Testing WITHOUT Hook (CPU busy-spin)...\n");
|
|
CoScheduler_Init(0, GetTimerUsec, 1000); // No hooks
|
|
CoTask_Create(hook_bench_worker);
|
|
|
|
uint64_t wall_start_1 = GetTimerUsec();
|
|
uint64_t cpu_start_1 = GetThreadCpuTimeUs();
|
|
|
|
CoScheduler_Start(0);
|
|
|
|
uint64_t cpu_end_1 = GetThreadCpuTimeUs();
|
|
uint64_t wall_end_1 = GetTimerUsec();
|
|
|
|
double wall_ms_1 = (double)(wall_end_1 - wall_start_1) / 1000.0;
|
|
double cpu_ms_1 = (double)(cpu_end_1 - cpu_start_1) / 1000.0;
|
|
double cpu_load_1 = (wall_ms_1 > 0) ? (cpu_ms_1 / wall_ms_1 * 100.0) : 0.0;
|
|
if (cpu_load_1 > 100.0) cpu_load_1 = 100.0;
|
|
|
|
printf(" -> Wall Time: %.1f ms, Active CPU Time: %.1f ms, CPU Load: %.1f%%\n\n",
|
|
wall_ms_1, cpu_ms_1, cpu_load_1);
|
|
|
|
/* --- Phase 2: WITH Hook (Interruptible Low-Power Sleep) --- */
|
|
printf("[Phase 2] Testing WITH Low-Power Hook (Interruptible sleep)...\n");
|
|
CoScheduler_Init(0, GetTimerUsec, 1000);
|
|
CoScheduler_BindDefaultHooks(0);
|
|
CoTask_Create(hook_bench_worker);
|
|
|
|
uint64_t wall_start_2 = GetTimerUsec();
|
|
uint64_t cpu_start_2 = GetThreadCpuTimeUs();
|
|
|
|
CoScheduler_Start(0);
|
|
|
|
uint64_t cpu_end_2 = GetThreadCpuTimeUs();
|
|
uint64_t wall_end_2 = GetTimerUsec();
|
|
|
|
CoScheduler_UnbindDefaultHooks(0);
|
|
|
|
double wall_ms_2 = (double)(wall_end_2 - wall_start_2) / 1000.0;
|
|
double cpu_ms_2 = (double)(cpu_end_2 - cpu_start_2) / 1000.0;
|
|
double cpu_load_2 = (wall_ms_2 > 0) ? (cpu_ms_2 / wall_ms_2 * 100.0) : 0.0;
|
|
if (cpu_load_2 > 100.0) cpu_load_2 = 100.0;
|
|
|
|
printf(" -> Wall Time: %.1f ms, Active CPU Time: %.1f ms, CPU Load: %.1f%%\n\n",
|
|
wall_ms_2, cpu_ms_2, cpu_load_2);
|
|
|
|
printf("==================== Benchmark Comparison ====================\n");
|
|
printf(" WITHOUT Hook : CPU Load = %5.1f%% (CPU was churning millions of cycles)\n", cpu_load_1);
|
|
printf(" WITH Hook : CPU Load = %5.1f%% (CPU entered kernel sleep, 0%% drain)\n", cpu_load_2);
|
|
printf(" Energy Saving: CPU active execution reduced by > %.1f%%!\n",
|
|
(cpu_ms_1 > cpu_ms_2) ? ((cpu_ms_1 - cpu_ms_2) / cpu_ms_1 * 100.0) : 99.0);
|
|
printf("==============================================================\n\n");
|
|
}
|
|
|
|
/* --- 6. High-Density Asynchronous I/O Test --- */
|
|
#define TEST_IO_PORT 19876
|
|
|
|
static void *io_server_task(void *arg)
|
|
{
|
|
printf(" [IO Server] Starting non-blocking server on 127.0.0.1:%d...\n", TEST_IO_PORT);
|
|
int listen_fd = CoSocket_Create(AF_INET, SOCK_STREAM, 0);
|
|
if (listen_fd < 0)
|
|
{
|
|
printf(" [IO Server] Failed to create socket!\n");
|
|
return NULL;
|
|
}
|
|
|
|
int opt = 1;
|
|
#if defined(_WIN32)
|
|
setsockopt((SOCKET)listen_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt));
|
|
#else
|
|
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
|
#endif
|
|
|
|
struct sockaddr_in addr;
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(TEST_IO_PORT);
|
|
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
|
|
if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
|
|
{
|
|
printf(" [IO Server] Failed to bind port!\n");
|
|
CoSocket_Close(listen_fd);
|
|
return NULL;
|
|
}
|
|
|
|
listen(listen_fd, 5);
|
|
printf(" [IO Server] Listening, waiting for client accept (cooperative yield)...\n");
|
|
|
|
int client_fd = CoSocket_Accept(listen_fd, NULL, NULL, 5000);
|
|
if (client_fd < 0)
|
|
{
|
|
printf(" [IO Server] Accept failed or timed out! (%d)\n", client_fd);
|
|
CoSocket_Close(listen_fd);
|
|
return NULL;
|
|
}
|
|
|
|
printf(" [IO Server] Client connected! Reading message (cooperative yield)...\n");
|
|
char buf[128] = {0};
|
|
int n = CoSocket_Read(client_fd, buf, sizeof(buf) - 1, 15000);
|
|
if (n > 0)
|
|
{
|
|
printf(" [IO Server] Received client message: \"%s\"\n", buf);
|
|
const char *echo_prefix = "ECHO: ";
|
|
char resp[256];
|
|
snprintf(resp, sizeof(resp), "%s%s", echo_prefix, buf);
|
|
printf(" [IO Server] Sending echo response: \"%s\"...\n", resp);
|
|
CoSocket_Write(client_fd, resp, strlen(resp), 5000);
|
|
}
|
|
else
|
|
{
|
|
printf(" [IO Server] Read failed or timed out! (n = %d)\n", n);
|
|
}
|
|
|
|
CoSocket_Close(client_fd);
|
|
CoSocket_Close(listen_fd);
|
|
printf(" [IO Server] Server task completed successfully, exiting scheduler!\n");
|
|
CoScheduler_Exit(0);
|
|
return NULL;
|
|
}
|
|
|
|
static void *io_client_task(void *arg)
|
|
{
|
|
/* Allow server to start and listen first */
|
|
CoTask_Wait(.ms = 50);
|
|
|
|
printf(" [IO Client] Connecting to 127.0.0.1:%d (cooperative yield)...\n", TEST_IO_PORT);
|
|
int client_fd = CoSocket_Create(AF_INET, SOCK_STREAM, 0);
|
|
if (client_fd < 0)
|
|
{
|
|
printf(" [IO Client] Failed to create client socket!\n");
|
|
return NULL;
|
|
}
|
|
|
|
struct sockaddr_in addr;
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(TEST_IO_PORT);
|
|
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
|
|
int ret = CoSocket_Connect(client_fd, &addr, sizeof(addr), 5000);
|
|
if (ret != 0)
|
|
{
|
|
printf(" [IO Client] Connect failed!\n");
|
|
CoSocket_Close(client_fd);
|
|
return NULL;
|
|
}
|
|
|
|
const char *msg = "Hello High-Throughput Coroutine IO!";
|
|
printf(" [IO Client] Connected! Sending message: \"%s\"...\n", msg);
|
|
CoTask_Wait(.ms = 10000);
|
|
CoSocket_Write(client_fd, msg, strlen(msg), 5000);
|
|
|
|
char resp[256] = {0};
|
|
printf(" [IO Client] Waiting for echo response (cooperative yield)...\n");
|
|
int n = CoSocket_Read(client_fd, resp, sizeof(resp) - 1, 5000);
|
|
if (n > 0)
|
|
{
|
|
printf(" [IO Client] Received Echo response: \"%s\"!\n", resp);
|
|
}
|
|
else
|
|
{
|
|
printf(" [IO Client] Read response failed or timed out! (%d)\n", n);
|
|
}
|
|
|
|
CoSocket_Close(client_fd);
|
|
printf(" [IO Client] Client task finished!\n");
|
|
return NULL;
|
|
}
|
|
|
|
static void test_io(void)
|
|
{
|
|
printf("=== [Test Asynchronous Socket I/O Multiplexing] ===\n");
|
|
CoScheduler_Init(0, GetTimerUsec, 1000);
|
|
CoIo_AttachScheduler(0);
|
|
|
|
CoTask_Create(io_server_task);
|
|
CoTask_Create(io_client_task);
|
|
|
|
printf("[Main] Starting single-threaded scheduler with cooperative I/O Reactor...\n");
|
|
CoScheduler_Start(0);
|
|
|
|
CoIo_DetachScheduler(0);
|
|
printf("=== [Test Asynchronous Socket I/O] Completed! ===\n\n");
|
|
}
|
|
|
|
static void test_base(void)
|
|
{
|
|
test_manual();
|
|
test_shared();
|
|
}
|
|
|
|
/************************************************************************************/
|
|
/************************************* Command ************************************/
|
|
/************************************************************************************/
|
|
|
|
static void usage(void)
|
|
{
|
|
printf(
|
|
"Usage: coroutine [opt] [arg] ...\n"
|
|
"\n"
|
|
"options:\n"
|
|
" -e <execute> Specifies the test mode to execute:\n"
|
|
" <manual> Test manual point-to-point stepping (libco style)\n"
|
|
" <shared> Test shared stack (copy-on-suspend)\n"
|
|
" <single> Test single scheduler automatic mode\n"
|
|
" <multi> Test multi-scheduler multi-threaded mode\n"
|
|
" <hook> Test low-power hook comparison (CPU load benchmark)\n"
|
|
" <io> Test high-throughput asynchronous socket I/O\n"
|
|
" <base> Test base default functions (manual & shared)\n"
|
|
" <ut> Unit test\n"
|
|
" -h Print help\n"
|
|
" -v Print version\n"
|
|
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
|
|
"\n"
|
|
);
|
|
}
|
|
|
|
static int test(int argc, char *argv[])
|
|
{
|
|
char *execute = NULL;
|
|
int ut_period = 1000;
|
|
|
|
/* Initialize default allocator for tests */
|
|
Coroutine_Init(.malloc = malloc, .free = free);
|
|
|
|
/* reset getopt */
|
|
command_opt_init();
|
|
|
|
while (1)
|
|
{
|
|
int opt = command_getopt(argc, argv, "e:hvu::");
|
|
if (opt == -1) break;
|
|
|
|
switch (opt)
|
|
{
|
|
// Add others opt here
|
|
case 'u' :
|
|
if (command_optarg) ut_period = atoi(command_optarg);
|
|
break;
|
|
case 'e' :
|
|
execute = command_optarg;
|
|
break;
|
|
case 'v' :
|
|
printf("coroutine version %d.%d.%d\r\n", COROUTINE_V_MAJOR, COROUTINE_V_MINOR, COROUTINE_V_PATCH);
|
|
return 0;
|
|
case '?':
|
|
printf("Unknown option `%c`\r\n", command_optopt);
|
|
return -1;
|
|
case 'h' :
|
|
default:
|
|
usage();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (execute)
|
|
{
|
|
if (!strcmp(execute, "manual"))
|
|
{
|
|
test_manual();
|
|
}
|
|
else if (!strcmp(execute, "shared"))
|
|
{
|
|
test_shared();
|
|
}
|
|
else if (!strcmp(execute, "single"))
|
|
{
|
|
test_single();
|
|
}
|
|
else if (!strcmp(execute, "multi"))
|
|
{
|
|
test_multi();
|
|
}
|
|
else if (!strcmp(execute, "hook"))
|
|
{
|
|
test_hook();
|
|
}
|
|
else if (!strcmp(execute, "io"))
|
|
{
|
|
test_io();
|
|
}
|
|
else if (!strcmp(execute, "base"))
|
|
{
|
|
test_base();
|
|
}
|
|
else if (!strcmp(execute, "ut"))
|
|
{
|
|
#if defined(TEST_TARGET_coroutine)
|
|
while (1)
|
|
{
|
|
unitt_task();
|
|
usleep(1000 * ut_period);
|
|
}
|
|
#else
|
|
printf("create task %d\r\n", task_create(ut_period, unitt_task));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
printf("Unknown execute option: %s\n", execute);
|
|
usage();
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
test_base();
|
|
}
|
|
|
|
#if 0
|
|
if (1 == command_optind) // no opt
|
|
{
|
|
|
|
}
|
|
|
|
for (int index = command_optind; index < argc; index++)
|
|
{
|
|
if (!strcmp(argv[index], "base"))
|
|
{
|
|
test_base();
|
|
}
|
|
}
|
|
|
|
if (1 == argc)
|
|
{
|
|
test_base();
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/************************************************************************************/
|
|
/************************************ Test entry ************************************/
|
|
/************************************************************************************/
|
|
|
|
#if defined(TEST_TARGET_coroutine)
|
|
int main(int argc, char *argv[])
|
|
{
|
|
return test(argc, argv);
|
|
}
|
|
#else
|
|
void test_coroutine(void)
|
|
{
|
|
command_export("coroutine", test);
|
|
}
|
|
init_export_app(test_coroutine);
|
|
#endif
|