diff options
| author | Garrett D'Amore <garrett@damore.org> | 2018-04-18 20:38:00 -0700 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2018-04-20 07:34:16 -0700 |
| commit | 5902d02ad0a056a146231568f1293ffbcd59f61c (patch) | |
| tree | be38584c02d703ec2322ab941d4d723c752fe187 /src/core/aio.h | |
| parent | 40542e7af0f5003d7ad67876ea580a59174031ca (diff) | |
| download | nng-5902d02ad0a056a146231568f1293ffbcd59f61c.tar.gz nng-5902d02ad0a056a146231568f1293ffbcd59f61c.tar.bz2 nng-5902d02ad0a056a146231568f1293ffbcd59f61c.zip | |
fixes #346 nng_recv() sometimes acts on null `msg` pointer
This closes a fundamental flaw in the way aio structures were
handled. In paticular, aio expiration could race ahead, and
fire before the aio was properly registered by the provider.
This ultimately led to the possibility of duplicate completions
on the same aio.
The solution involved breaking up nni_aio_start into two functions.
nni_aio_begin (which can be run outside of external locks) simply
validates that nni_aio_fini() has not been called, and clears certain
fields in the aio to make it ready for use by the provider.
nni_aio_schedule does the work to register the aio with the expiration
thread, and should only be called when the aio is actually scheduled
for asynchronous completion. nni_aio_schedule_verify does the same thing,
but returns NNG_ETIMEDOUT if the aio has a zero length timeout.
This change has a small negative performance impact. We have plans to
rectify that by converting nni_aio_begin to use a locklesss flag for
the aio->a_fini bit.
While we were here, we fixed some error paths in the POSIX subsystem,
which would have returned incorrect error codes, and we made some
optmizations in the message queues to reduce conditionals while holding
locks in the hot code path.
Diffstat (limited to 'src/core/aio.h')
| -rw-r--r-- | src/core/aio.h | 20 |
1 files changed, 19 insertions, 1 deletions
diff --git a/src/core/aio.h b/src/core/aio.h index a0f4934f..d23ddf5b 100644 --- a/src/core/aio.h +++ b/src/core/aio.h @@ -123,7 +123,14 @@ extern void nni_aio_finish_msg(nni_aio *, nni_msg *); // with the indicated result (NNG_ECLOSED or NNG_ECANCELED is recommended.) extern void nni_aio_abort(nni_aio *, int rv); -extern int nni_aio_start(nni_aio *, nni_aio_cancelfn, void *); +// nni_aio_begin is called by a provider to indicate it is starting the +// operation, and to check that the aio has not already been marked for +// teardown. It returns 0 on success, or NNG_ECANCELED if the aio is being +// torn down. (In that case, no operation should be aborted without any +// call to any other functions on this AIO, most especially not the +// nng_aio_finish family of functions.) +extern int nni_aio_begin(nni_aio *); + extern void *nni_aio_get_prov_data(nni_aio *); extern void nni_aio_set_prov_data(nni_aio *, void *); extern void *nni_aio_get_prov_extra(nni_aio *, unsigned); @@ -143,6 +150,17 @@ extern void nni_aio_get_iov(nni_aio *, unsigned *, nni_iov **); extern void nni_aio_normalize_timeout(nni_aio *, nng_duration); extern void nni_aio_bump_count(nni_aio *, size_t); +// nni_aio_schedule indicates that the AIO has begun, and is scheduled for +// asychronous completion. This also starts the expiration timer. Note that +// prior to this, the aio is uncancellable. +extern void nni_aio_schedule(nni_aio *, nni_aio_cancelfn, void *); + +// nni_aio_schedule_verify is like nni_aio_schedule, except that if the +// operation has been run with a zero time (NNG_FLAG_NONBLOCK), then it +// returns NNG_ETIMEDOUT. This is done to permit bypassing scheduling +// if the operation could not be immediately completed. +extern int nni_aio_schedule_verify(nni_aio *, nni_aio_cancelfn, void *); + extern void nni_sleep_aio(nni_duration, nni_aio *); extern int nni_aio_sys_init(void); |
