This is the mail archive of the
libc-help@sourceware.org
mailing list for the glibc project.
Re: losing events with EPOLLONESHOT and cancellation
- From: Eric Wong <normalperson at yhbt dot net>
- To: Carlos O'Donell <carlos at systemhalted dot org>
- Cc: libc-help at sourceware dot org
- Date: Wed, 12 Jun 2013 16:56:01 +0000
- Subject: Re: losing events with EPOLLONESHOT and cancellation
- References: <20130612003848 dot GA17829 at dcvr dot yhbt dot net> <CAE2sS1gxQkqmcywQ07pmgNHM+CyqzMkuASVjmWDL+hgaTMURWQ at mail dot gmail dot com>
Carlos O'Donell <carlos@systemhalted.org> wrote:
> On Tue, Jun 11, 2013 at 8:38 PM, Eric Wong <normalperson@yhbt.net> wrote:
> > I seem to be encountering lost events with epoll_wait/epoll_pwait when
> > using EPOLLONESHOT and pthreads cancellation.
>
> You are correct.
>
> POSIX has this to say about the cancellation and the waiting event:
> ~~~
> ... if the thread is suspended at a cancellation point and the
> event for which it is waiting occurs before the cancellation request
> is acted upon, it is unspecified whether the cancellation request is
> acted upon or whether the cancellation request remains pending
> and the thread resumes normal execution. ...
> ~~~
> Therefore you must accept that it could be possible to get the data
> and then be cancelled.
>
> The only recourse that I am aware of is to use a cancellation cleanup
> handler to attempt to restore the lost data.
>
> See pthread_cleanup_push and pthread_cleanup_pop:
> http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cleanup_push.html
>
> Does that answer your question?
Thanks. With pthread_cleanup_*, I can't see the return value of the
cancelled syscall. So I think my alternative is to not rely on normal
syscalls being cancellable and explicitly put cancellation points in my
program around interruptible syscalls, using pthread_kill in a loop in
addition to pthread_cancel.
Something like this:
thread_1
--------
pthread_cancel(thread_2);
/*
* trigger EINTR in any interruptible syscall
* the loop seems icky, but this will be a rare code path
*/
while (pthread_kill(thread_2, ...) == 0)
sched_yield();
thread_2
--------
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
for (;;) {
/* open a brief window for async cancellation */
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
/*
* We could lose cancellation requests here, so we loop in thread_1.
* I also have a similar loop doing blocking accept4 in a
* different thread.
*/
rc = epoll_wait(...);
if (rc < 0 && errno == EINTR) {
/* open a brief window for async cancellation */
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
}
...
}