// // Copyright 2021 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this // file was obtained (LICENSE.txt). A copy of the license may also be // found online at https://opensource.org/licenses/MIT. // #include #include static void cb_done(void *p) { (*(int *) p)++; } static void sleep_done(void *arg) { *(nng_time *) arg = nng_clock(); } static void cancel(nng_aio *aio, void *arg, int rv) { *(int *) arg = rv; nng_aio_finish(aio, rv); } void test_sleep(void) { nng_time start; nng_time end = 0; nng_aio *aio; NUTS_PASS(nng_aio_alloc(&aio, sleep_done, &end)); start = nng_clock(); nng_sleep_aio(200, aio); nng_aio_wait(aio); NUTS_PASS(nng_aio_result(aio)); NUTS_TRUE(end != 0); NUTS_TRUE((end - start) >= 200); NUTS_TRUE((end - start) <= 1000); NUTS_TRUE((nng_clock() - start) >= 200); NUTS_TRUE((nng_clock() - start) <= 1000); nng_aio_free(aio); } void test_sleep_timeout(void) { nng_time start; nng_time end = 0; nng_aio *aio; NUTS_TRUE(nng_aio_alloc(&aio, sleep_done, &end) == 0); nng_aio_set_timeout(aio, 100); start = nng_clock(); nng_sleep_aio(2000, aio); nng_aio_wait(aio); NUTS_FAIL(nng_aio_result(aio), NNG_ETIMEDOUT); NUTS_TRUE(end != 0); NUTS_TRUE((end - start) >= 100); NUTS_TRUE((end - start) <= 1000); NUTS_TRUE((nng_clock() - start) >= 100); NUTS_TRUE((nng_clock() - start) <= 1000); nng_aio_free(aio); } void test_insane_nio(void) { nng_aio *aio; nng_iov iov; NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL)); NUTS_FAIL(nng_aio_set_iov(aio, 1024, &iov), NNG_EINVAL); nng_aio_free(aio); } void test_provider_cancel(void) { nng_aio *aio; int rv = 0; // We fake an empty provider that does not do anything. NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL)); NUTS_TRUE(nng_aio_begin(aio) == true); nng_aio_defer(aio, cancel, &rv); nng_aio_cancel(aio); nng_aio_wait(aio); NUTS_TRUE(rv == NNG_ECANCELED); nng_aio_free(aio); } void test_consumer_cancel(void) { nng_aio * a; nng_socket s1; int done = 0; NUTS_TRUE(nng_pair1_open(&s1) == 0); NUTS_TRUE(nng_aio_alloc(&a, cb_done, &done) == 0); nng_aio_set_timeout(a, NNG_DURATION_INFINITE); nng_recv_aio(s1, a); nng_aio_cancel(a); nng_aio_wait(a); NUTS_TRUE(done == 1); NUTS_TRUE(nng_aio_result(a) == NNG_ECANCELED); nng_aio_free(a); NUTS_TRUE(nng_close(s1) == 0); } void test_traffic(void) { nng_socket s1; nng_socket s2; nng_aio * tx_aio; nng_aio * rx_aio; int tx_done = 0; int rx_done = 0; nng_msg * m; char * addr = "inproc://traffic"; NUTS_PASS(nng_pair1_open(&s1)); NUTS_PASS(nng_pair1_open(&s2)); NUTS_PASS(nng_listen(s1, addr, NULL, 0)); NUTS_PASS(nng_dial(s2, addr, NULL, 0)); NUTS_PASS(nng_aio_alloc(&rx_aio, cb_done, &rx_done)); NUTS_PASS(nng_aio_alloc(&tx_aio, cb_done, &tx_done)); nng_aio_set_timeout(rx_aio, 1000); nng_aio_set_timeout(tx_aio, 1000); NUTS_PASS(nng_msg_alloc(&m, 0)); NUTS_PASS(nng_msg_append(m, "hello", strlen("hello"))); nng_recv_aio(s2, rx_aio); nng_aio_set_msg(tx_aio, m); nng_send_aio(s1, tx_aio); nng_aio_wait(tx_aio); nng_aio_wait(rx_aio); NUTS_PASS(nng_aio_result(rx_aio)); NUTS_PASS(nng_aio_result(tx_aio)); NUTS_TRUE((m = nng_aio_get_msg(rx_aio)) != NULL); NUTS_TRUE(nng_msg_len(m) == strlen("hello")); NUTS_TRUE(memcmp(nng_msg_body(m), "hello", strlen("hello")) == 0); nng_msg_free(m); NUTS_TRUE(rx_done == 1); NUTS_TRUE(tx_done == 1); nng_aio_free(rx_aio); nng_aio_free(tx_aio); NUTS_PASS(nng_close(s1)); NUTS_PASS(nng_close(s2)); } void test_explicit_timeout(void) { nng_socket s; nng_aio * a; int done = 0; NUTS_PASS(nng_pair1_open(&s)); NUTS_PASS(nng_aio_alloc(&a, cb_done, &done)); nng_aio_set_timeout(a, 40); nng_recv_aio(s, a); nng_aio_wait(a); NUTS_TRUE(done == 1); NUTS_FAIL(nng_aio_result(a), NNG_ETIMEDOUT); nng_aio_free(a); NUTS_PASS(nng_close(s)); } void test_inherited_timeout(void) { nng_socket s; nng_aio * a; int done = 0; NUTS_PASS(nng_pair1_open(&s)); NUTS_PASS(nng_aio_alloc(&a, cb_done, &done)); NUTS_PASS(nng_socket_set_ms(s, NNG_OPT_RECVTIMEO, 40)); nng_recv_aio(s, a); nng_aio_wait(a); NUTS_TRUE(done == 1); NUTS_FAIL(nng_aio_result(a), NNG_ETIMEDOUT); nng_aio_free(a); NUTS_PASS(nng_close(s)); } void test_zero_timeout(void) { nng_socket s; nng_aio * a; int done = 0; NUTS_PASS(nng_pair1_open(&s)); NUTS_PASS(nng_aio_alloc(&a, cb_done, &done)); nng_aio_set_timeout(a, NNG_DURATION_ZERO); nng_recv_aio(s, a); nng_aio_wait(a); NUTS_TRUE(done == 1); NUTS_FAIL(nng_aio_result(a), NNG_ETIMEDOUT); nng_aio_free(a); NUTS_PASS(nng_close(s)); } static void aio_sleep_cb(void *arg) { nng_aio *aio = *(nng_aio **) arg; nng_aio_reap(aio); } void test_aio_reap(void) { static nng_aio *a; NUTS_PASS(nng_aio_alloc(&a, aio_sleep_cb, &a)); nng_sleep_aio(10, a); nng_msleep(100); } typedef struct sleep_loop { nng_aio * aio; int limit; int count; int result; bool done; nng_duration interval; nng_cv * cv; nng_mtx * mx; } sleep_loop; static void aio_sleep_loop(void *arg) { sleep_loop *sl = arg; nng_mtx_lock(sl->mx); if (nng_aio_result(sl->aio) != 0) { sl->result = nng_aio_result(sl->aio); sl->done = true; nng_cv_wake(sl->cv); nng_mtx_unlock(sl->mx); return; } sl->count++; if (sl->count >= sl->limit) { sl->done = true; sl->result = 0; nng_cv_wake(sl->cv); nng_mtx_unlock(sl->mx); return; } nng_mtx_unlock(sl->mx); nng_sleep_aio(sl->interval, sl->aio); } static bool is_github_macos(void) { char *env; if (((env = getenv("RUNNER_OS")) != NULL) && (strcmp(env, "macOS") == 0)) { return (true); } return (true); } void test_sleep_loop(void) { sleep_loop sl; nng_time start; nng_duration dur; sl.limit = 3; sl.count = 0; sl.interval = 50; // ms sl.done = false; NUTS_PASS(nng_aio_alloc(&sl.aio, aio_sleep_loop, &sl)); NUTS_PASS(nng_mtx_alloc(&sl.mx)); NUTS_PASS(nng_cv_alloc(&sl.cv, sl.mx)); start = nng_clock(); nng_sleep_aio(100, sl.aio); nng_mtx_lock(sl.mx); while (!sl.done) { nng_cv_until(sl.cv, 2000); } nng_mtx_unlock(sl.mx); dur = (nng_duration) (nng_clock() - start); NUTS_ASSERT(dur >= 150); if (!is_github_macos()) { NUTS_ASSERT(dur <= 500); // allow for sloppy clocks NUTS_ASSERT(sl.count == 3); } NUTS_ASSERT(sl.done); NUTS_PASS(sl.result); nng_aio_free(sl.aio); nng_cv_free(sl.cv); nng_mtx_free(sl.mx); } void test_sleep_cancel(void) { sleep_loop sl; nng_time start; nng_duration dur; sl.limit = 10; sl.count = 0; sl.interval = 100; // ms sl.done = false; NUTS_PASS(nng_aio_alloc(&sl.aio, aio_sleep_loop, &sl)); NUTS_PASS(nng_mtx_alloc(&sl.mx)); NUTS_PASS(nng_cv_alloc(&sl.cv, sl.mx)); start = nng_clock(); nng_sleep_aio(100, sl.aio); nng_msleep(150); nng_aio_cancel(sl.aio); nng_mtx_lock(sl.mx); while (!sl.done) { nng_cv_until(sl.cv, 2000); } nng_mtx_unlock(sl.mx); dur = (nng_duration) (nng_clock() - start); NUTS_ASSERT(dur >= 100); if (!is_github_macos()) { NUTS_ASSERT(dur <= 500); // allow for sloppy clocks NUTS_ASSERT(sl.count == 1); } NUTS_ASSERT(sl.done); NUTS_FAIL(sl.result, NNG_ECANCELED); nng_aio_free(sl.aio); nng_cv_free(sl.cv); nng_mtx_free(sl.mx); } NUTS_TESTS = { { "sleep", test_sleep }, { "sleep timeout", test_sleep_timeout }, { "insane nio", test_insane_nio }, { "provider cancel", test_provider_cancel }, { "consumer cancel", test_consumer_cancel }, { "traffic", test_traffic }, { "explicit timeout", test_explicit_timeout }, { "inherited timeout", test_inherited_timeout }, { "zero timeout", test_zero_timeout }, { "aio reap", test_aio_reap }, { "sleep loop", test_sleep_loop }, { "sleep cancel", test_sleep_cancel }, { NULL, NULL }, };