diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-08-15 21:59:55 -0700 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-08-16 18:31:42 -0700 |
| commit | a9633313ec8e578c805cd53b37ba3360d83157bc (patch) | |
| tree | 14d32c4031ea1c8508a75469407ca77e353fa315 /tests | |
| parent | e7e2a6c14f0317eb77711951c6f1a650d4013dfe (diff) | |
| download | nng-a9633313ec8e578c805cd53b37ba3360d83157bc.tar.gz nng-a9633313ec8e578c805cd53b37ba3360d83157bc.tar.bz2 nng-a9633313ec8e578c805cd53b37ba3360d83157bc.zip | |
Provide versions of mutex, condvar, and aio init that never fail.
If the underlying platform fails (FreeBSD is the only one I'm aware
of that does this!), we use a global lock or condition variable instead.
This means that our lock initializers never ever fail.
Probably we could eliminate most of this for Linux and Darwin, since
on those platforms, mutex and condvar initialization reasonably never
fails. Initial benchmarks show little difference either way -- so we
can revisit (optimize) later.
This removes a lot of otherwise untested code in error cases and so forth,
improving coverage and resilience in the face of allocation failures.
Platforms other than POSIX should follow a similar pattern if they need
this. (VxWorks, I'm thinking of you.) Most sane platforms won't have
an issue here, since normally these initializations do not need to allocate
memory. (Reportedly, even FreeBSD has plans to "fix" this in libthr2.)
While here, some bugs were fixed in initialization & teardown.
The fallback code is properly tested with dedicated test cases.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/pair1.c | 3 | ||||
| -rw-r--r-- | tests/platform.c | 105 | ||||
| -rw-r--r-- | tests/synch.c | 292 |
4 files changed, 338 insertions, 63 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 73484a4a..754d3ab2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -85,6 +85,7 @@ add_nng_test(pubsub 5) add_nng_test(resolv 10) add_nng_test(sock 5) add_nng_test(survey 5) +add_nng_test(synch 5) add_nng_test(tcp 5) add_nng_test(scalability 20) add_nng_test(message 5) diff --git a/tests/pair1.c b/tests/pair1.c index 08691fd4..d160acdd 100644 --- a/tests/pair1.c +++ b/tests/pair1.c @@ -94,7 +94,6 @@ TestMain("PAIRv1 protocol", { }); Convey("Cannot set raw mode after connect", { - int r = 1; So(nng_listen(s1, addr, NULL, 0) == 0); So(nng_dial(c1, addr, NULL, 0) == 0); nng_usleep(100000); @@ -313,7 +312,6 @@ TestMain("PAIRv1 protocol", { int ttl; ttl = 0; - sz = sizeof(ttl); So(nng_setopt_int(s1, NNG_OPT_MAXTTL, 0) == NNG_EINVAL); @@ -423,7 +421,6 @@ TestMain("PAIRv1 protocol", { uint32_t hops; nng_pipe p1; nng_pipe p2; - size_t sz; So(nng_getopt_int(s1, NNG_OPT_POLYAMOROUS, &v) == 0); So(v == 0); diff --git a/tests/platform.c b/tests/platform.c index 5bfdc367..a28bc4e4 100644 --- a/tests/platform.c +++ b/tests/platform.c @@ -1,5 +1,6 @@ // -// Copyright 2016 Garrett D'Amore <garrett@damore.org> +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// Copyright 2017 Capitar IT Group BV <info@capitar.com> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -8,20 +9,20 @@ // #include "convey.h" -#include "nng.h" #include "core/nng_impl.h" +#include "nng.h" -#ifndef _WIN32 +#ifndef _WIN32 #include <sys/time.h> #endif uint64_t getms(void) { -#ifdef _WIN32 - return (GetTickCount64()) ; +#ifdef _WIN32 + return (GetTickCount64()); #else - static time_t epoch; + static time_t epoch; struct timeval tv; if (epoch == 0) { @@ -35,7 +36,7 @@ getms(void) return (0); } tv.tv_sec -= epoch; - return (((uint64_t)(tv.tv_sec ) * 1000) + (tv.tv_usec / 1000)); + return (((uint64_t)(tv.tv_sec) * 1000) + (tv.tv_usec / 1000)); #endif } @@ -43,15 +44,15 @@ getms(void) void add(void *arg) { - *(int *)arg += 1; + *(int *) arg += 1; } // Notify tests for verifying condvars. struct notifyarg { - int did; - int when; + int did; + int when; nni_mtx mx; - nni_cv cv; + nni_cv cv; }; void @@ -66,17 +67,10 @@ notifyafter(void *arg) nni_mtx_unlock(&na->mx); } -int -nop(void) -{ - return (0); -} +TestMain("Platform Operations", { -Main({ nni_init(); - Test("Platform Operations", { - // This is required for anything else to work Convey("The clock works", { uint64_t now = getms(); @@ -84,13 +78,13 @@ Main({ Convey("usleep works", { nni_usleep(100000); - So((getms() - now) >= 100); // cannot be *shorter*!! - So((getms() - now) < 150); // crummy clock resolution? - }) + So((getms() - now) >= 100); // cannot be *shorter*!! + So((getms() - now) < 150); // crummy clock resolution? + }); Convey("times work", { uint64_t msend; - int usdelta; - int msdelta; + int usdelta; + int msdelta; nni_time usend; nni_time usnow = nni_clock(); nni_usleep(200000); @@ -99,19 +93,17 @@ Main({ So(usend > usnow); So(msend > now); - usdelta = (int)((usend - usnow) / 1000); - msdelta = (int)((msend - now)); + usdelta = (int) ((usend - usnow) / 1000); + msdelta = (int) ((msend - now)); So(usdelta >= 200); So(usdelta < 220); So(abs(msdelta - usdelta) < 20); - }) - }) + }); + }); Convey("Mutexes work", { static nni_mtx mx; - int rv; - rv = nni_mtx_init(&mx); - So(rv == 0); + nni_mtx_init(&mx); Convey("We can lock a mutex", { nni_mtx_lock(&mx); @@ -124,40 +116,36 @@ Main({ So(1); nni_mtx_unlock(&mx); So(1); - }) - }) - }) - Convey("We can finalize it", { - nni_mtx_fini(&mx); - }) - }) + }); + }); + }); + Convey("We can finalize it", { nni_mtx_fini(&mx); }); + }); Convey("Threads work", { static nni_thr thr; - int val = 0; - int rv; + int val = 0; + int rv; Convey("We can create threads", { rv = nni_thr_init(&thr, add, &val); So(rv == 0); nni_thr_run(&thr); - Reset({ - nni_thr_fini(&thr); - }) + Reset({ nni_thr_fini(&thr); }); Convey("It ran", { - nni_usleep(50000); // for context switch + nni_usleep(50000); // for context switch So(val == 1); - }) - }) - }) + }); + }); + }); Convey("Condition variables work", { static struct notifyarg arg; - static nni_thr thr; + static nni_thr thr; - So(nni_mtx_init(&arg.mx) == 0); - So(nni_cv_init(&arg.cv, &arg.mx) == 0); + nni_mtx_init(&arg.mx); + nni_cv_init(&arg.cv, &arg.mx); So(nni_thr_init(&thr, notifyafter, &arg) == 0); Reset({ @@ -167,7 +155,7 @@ Main({ }); Convey("Notification works", { - arg.did = 0; + arg.did = 0; arg.when = 10000; nni_thr_run(&thr); @@ -178,10 +166,10 @@ Main({ nni_mtx_unlock(&arg.mx); nni_thr_wait(&thr); So(arg.did == 1); - }) + }); Convey("Timeout works", { - arg.did = 0; + arg.did = 0; arg.when = 200000; nni_thr_run(&thr); nni_mtx_lock(&arg.mx); @@ -191,10 +179,9 @@ Main({ So(arg.did == 0); nni_mtx_unlock(&arg.mx); nni_thr_wait(&thr); - }) - + }); Convey("Not running works", { - arg.did = 0; + arg.did = 0; arg.when = 1; nni_mtx_lock(&arg.mx); if (!arg.did) { @@ -202,8 +189,6 @@ Main({ } So(arg.did == 0); nni_mtx_unlock(&arg.mx); - }) - }) - }) - nni_fini(); + }); + }); }) diff --git a/tests/synch.c b/tests/synch.c new file mode 100644 index 00000000..a49e27e1 --- /dev/null +++ b/tests/synch.c @@ -0,0 +1,292 @@ +// +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// Copyright 2017 Capitar IT Group BV <info@capitar.com> +// +// 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 "convey.h" +#include "core/nng_impl.h" +#include "nng.h" + +// Notify tests for verifying condvars. +struct notifyarg { + int did; + int when; + nni_mtx mx; + nni_cv cv; +}; + +#ifdef PLATFORM_POSIX +#ifndef NDEBUG +#define SYNC_FALLBACK 1 +#endif +#endif + +void +notifyafter(void *arg) +{ + struct notifyarg *na = arg; + + nni_usleep(na->when); + nni_mtx_lock(&na->mx); + na->did = 1; + nni_cv_wake(&na->cv); + nni_mtx_unlock(&na->mx); +} + +struct notifyarg arg; +nni_thr thr; + +static void +test_sync(void) +{ + Convey("Mutexes work", { + nni_mtx mx; + + nni_mtx_init(&mx); + + Convey("We can lock a mutex", { + nni_mtx_lock(&mx); + So(1); + Convey("And we can unlock it", { + nni_mtx_unlock(&mx); + So(1); + Convey("And then lock it again", { + nni_mtx_lock(&mx); + So(1); + nni_mtx_unlock(&mx); + So(1); + }); + }); + Convey("Things block properly", { + + nni_mtx_init(&arg.mx); + nni_cv_init(&arg.cv, &arg.mx); + So(nni_thr_init(&thr, notifyafter, &arg) == 0); + arg.did = 0; + arg.when = 0; + nni_mtx_lock(&arg.mx); + nni_thr_run(&thr); + nng_usleep(10000); + So(arg.did == 0); + nni_mtx_unlock(&arg.mx); + nng_usleep(10000); + nni_mtx_lock(&arg.mx); + while (!arg.did) { + nni_cv_wait(&arg.cv); + } + So(arg.did != 0); + nni_mtx_unlock(&arg.mx); + nni_thr_fini(&thr); + nni_cv_fini(&arg.cv); + nni_mtx_fini(&arg.mx); + }) + }); + Convey("We can finalize it", { nni_mtx_fini(&mx); }); + }); + + Convey("Condition variables work", { + + nni_mtx_init(&arg.mx); + nni_cv_init(&arg.cv, &arg.mx); + So(nni_thr_init(&thr, notifyafter, &arg) == 0); + + Reset({ + nni_cv_fini(&arg.cv); + nni_mtx_fini(&arg.mx); + nni_thr_fini(&thr); + }); + + Convey("Notification works", { + arg.did = 0; + arg.when = 10000; + nni_thr_run(&thr); + + nni_mtx_lock(&arg.mx); + if (!arg.did) { + nni_cv_wait(&arg.cv); + } + nni_mtx_unlock(&arg.mx); + nni_thr_wait(&thr); + So(arg.did == 1); + }); + + Convey("Timeout works", { + arg.did = 0; + arg.when = 200000; + nni_thr_run(&thr); + nni_mtx_lock(&arg.mx); + if (!arg.did) { + nni_cv_until(&arg.cv, nni_clock() + 10000); + } + So(arg.did == 0); + nni_mtx_unlock(&arg.mx); + nni_thr_wait(&thr); + }); + + Convey("Empty timeout is EAGAIN", { + nni_mtx_lock(&arg.mx); + So(nni_cv_until(&arg.cv, 0) == NNG_EAGAIN); + nni_mtx_unlock(&arg.mx); + }); + + Convey("Not running works", { + arg.did = 0; + arg.when = 1; + nni_mtx_lock(&arg.mx); + if (!arg.did) { + nni_cv_until(&arg.cv, nni_clock() + 10000); + } + So(arg.did == 0); + nni_mtx_unlock(&arg.mx); + }); + }); +} + +#if SYNC_FALLBACK +extern int nni_plat_sync_fallback; + +#define ConveyFB(x, y) Convey(x, y) + +static void +test_sync_fallback(void) +{ + nni_plat_sync_fallback = 1; + Convey("Mutexes work", { + nni_mtx mx; + int rv; + + nni_mtx_init(&mx); + + Convey("We can lock a mutex", { + nni_mtx_lock(&mx); + So(1); + Convey("And we can unlock it", { + nni_mtx_unlock(&mx); + So(1); + Convey("And then lock it again", { + nni_mtx_lock(&mx); + So(1); + nni_mtx_unlock(&mx); + So(1); + }); + }); + Convey("Things block properly", { + + nni_mtx_init(&arg.mx); + nni_cv_init(&arg.cv, &arg.mx); + So(nni_thr_init(&thr, notifyafter, &arg) == 0); + arg.did = 0; + arg.when = 0; + nni_mtx_lock(&arg.mx); + nni_thr_run(&thr); + nng_usleep(10000); + So(arg.did == 0); + nni_mtx_unlock(&arg.mx); + nng_usleep(10000); + nni_mtx_lock(&arg.mx); + while (!arg.did) { + nni_cv_wait(&arg.cv); + } + So(arg.did != 0); + nni_mtx_unlock(&arg.mx); + nni_thr_fini(&thr); + nni_cv_fini(&arg.cv); + nni_mtx_fini(&arg.mx); + }) + }); + Convey("We can finalize it", { nni_mtx_fini(&mx); }); + }); + + Convey("Condition variables work", { + + nni_mtx_init(&arg.mx); + nni_cv_init(&arg.cv, &arg.mx); + So(nni_thr_init(&thr, notifyafter, &arg) == 0); + + Reset({ + nni_cv_fini(&arg.cv); + nni_mtx_fini(&arg.mx); + nni_thr_fini(&thr); + }); + + Convey("Notification works", { + arg.did = 0; + arg.when = 10000; + nni_thr_run(&thr); + + nni_mtx_lock(&arg.mx); + if (!arg.did) { + nni_cv_wait(&arg.cv); + } + nni_mtx_unlock(&arg.mx); + nni_thr_wait(&thr); + So(arg.did == 1); + }); + + Convey("Timeout works", { + arg.did = 0; + arg.when = 200000; + nni_thr_run(&thr); + nni_mtx_lock(&arg.mx); + if (!arg.did) { + nni_cv_until(&arg.cv, nni_clock() + 10000); + } + So(arg.did == 0); + nni_mtx_unlock(&arg.mx); + nni_thr_wait(&thr); + }); + + Convey("Empty timeout is EAGAIN", { + nni_mtx_lock(&arg.mx); + So(nni_cv_until(&arg.cv, 0) == NNG_EAGAIN); + nni_mtx_unlock(&arg.mx); + }); + + Convey("Not running works", { + arg.did = 0; + arg.when = 1; + nni_mtx_lock(&arg.mx); + if (!arg.did) { + nni_cv_until(&arg.cv, nni_clock() + 10000); + } + So(arg.did == 0); + nni_mtx_unlock(&arg.mx); + }); + }); +} +#else +#define ConveyFB(x, y) +#endif + +TestMain("Synchronization", { + nni_init(); + + Convey("Synchronization works", { test_sync(); }); + + ConveyFB("Fallback synchronization works", { test_sync_fallback(); }); + + ConveyFB("Transform works", { + nni_plat_sync_fallback = 0; + nni_mtx_init(&arg.mx); + nni_plat_sync_fallback = 1; + nni_cv_init(&arg.cv, &arg.mx); + So(nni_thr_init(&thr, notifyafter, &arg) == 0); + + arg.did = 0; + arg.when = 10000; + nni_thr_run(&thr); + + nni_mtx_lock(&arg.mx); + if (!arg.did) { + nni_cv_wait(&arg.cv); + } + nni_mtx_unlock(&arg.mx); + nni_thr_wait(&thr); + So(arg.did == 1); + }); +}) |
