[ WARNING: Long and ardorous - difficult to trace behaviour ]
Behaviour described is without EV_PERSIST on internal signal handler (as it's
been for months):
1. signal comes in, we jump to our internal signal handler, it writes to pipe.
2. epoll_wait() returns -1/EINTR because a signal handler was called.
3. we do not process epoll events here, we call evsignal_process() and return
from dispatch.
4. our user-side event is deleted from queues, marked active, and processed
normally BY evsignal_process().
5. user-side event then adds itself back normally to handle signals again.
6. we head back into epoll_dispatch(), where epoll_wait() immediately returns
because
signal pipe is ready for reading (from step 1).
7. we implicitly delete the internal signal event (remember, this is without
EV_PERSIST on it) and
mark is as active. however, since it is an internal event, we do not increment
event_count_active.
8. we return to our main event loop within event.c, and do not call
event_process_active() because
event_count_active == 0. this is where the problems really start.
9. we head back into epoll_dispatch() and wait normally. however, since we
previously deleted
our internal signal pipe fd from the epoll set, and did NOT call it from
event_process_active() in
the previous step - the pipe never ends up being read. but since the user
callback was executed
properly, everything looks "normal."
10. we send the same signal again. epoll_wait() returns, and we call
evsignal_process() again.
11. here's the funny part: evsignal_process() increments event_count_active -
but since our
internal signal event is actually first on the active queue at this point, when
we go to
process events in the main loop - we actually end up processing our internal
signal
event here. it reads from the pipe, and then re-adds itself back into epoll's
watch set.
but since it's internal, we do not decrement event_count_active - guaranteeing
we'll
get to our user event when we process the active events from the main loop.
this is
the "flip-flop".
12. repeat.
Now with EV_PERSIST on the internal signal event, steps 7-12 do not happen the
first
time the signal is called. So not only is the bug still there, it's super bad
when
it happens as epoll_wait() returns immediately. By sending a signal again - we
can flip
it from spin to wait.
So this is a bug that needs to be fixed anyways. The EV_PERSIST change just
made it more visible. I'm not so sure how to make a regression test for this
other
than a specific test to make sure that our signal pipe is empty AFTER returning
from
event_process_active().
--
test case:
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <event.h>
void scb(int sig, short event, void *a)
{
write(2, ".\n", 2);
event_add(a, NULL);
return;
}
int main(void)
{
struct event sig;
event_init();
event_set(&sig, SIGTSTP, EV_SIGNAL, scb, &sig);
event_add(&sig, NULL);
event_dispatch();
return 0;
}
--
debug log (unpatched):
$ ./sigtest
[debug] event_add: event: 0x7fff762c0500, call 0x400768
[debug] event_queue_insert: 0x7fff762c0500: docount == 1, event_count == 1,
queue == 4
[debug] evsignal_add: evsignal > sh_old_max, s == 20, max == 0
[debug] event_add: event: 0x602050, EV_READ call 0x2ac034a166d4
[debug] event_queue_insert: 0x602050: docount == 0, event_count == 1, queue == 2
<wait, signal>
[debug] evsignal_handler: wake up
[debug] epoll_dispatch: epoll_wait() res == -1, errno = 4
[debug] evsignal_process: processing signals, caught == 1
[debug] event_del: 0x7fff762c0500, callback 0x400768
[debug] evsignal_del: 0x7fff762c0500: restoring previous handler
[debug] event_active: 0x7fff762c0500: adding to active queue, ev_res == 0, res
== 8
[debug] event_queue_insert: 0x7fff762c0500: docount == 1, event_count == 1,
queue == 8
[debug] event_base_loop: event_count_active == 1
.
[debug] event_add: event: 0x7fff762c0500, call 0x400768
[debug] event_queue_insert: 0x7fff762c0500: docount == 1, event_count == 1,
queue == 4
[debug] epoll_dispatch: epoll_wait reports 1
[debug] event_del: 0x602050, callback 0x2ac034a166d4
[debug] event_active: 0x602050: adding to active queue, ev_res == 0, res == 2
[debug] event_queue_insert: 0x602050: docount == 0, event_count == 1, queue == 8
[debug] event_base_loop: event_count_active == 0
<wait, signal>
(notice how we immediately call our internal event from event_base_loop?)
[debug] evsignal_handler: wake up
[debug] epoll_dispatch: epoll_wait() res == -1, errno = 4
[debug] evsignal_process: processing signals, caught == 1
[debug] event_del: 0x7fff762c0500, callback 0x400768
[debug] evsignal_del: 0x7fff762c0500: restoring previous handler
[debug] event_active: 0x7fff762c0500: adding to active queue, ev_res == 8, res
== 8
[debug] event_queue_insert: 0x7fff762c0500: docount == 1, event_count == 1,
queue == 8
[debug] event_base_loop: event_count_active == 1
[debug] evsignal_cb: n == 2
^^^^^^^^
[debug] event_add: event: 0x602050, EV_READ call 0x2ac034a166d4
[debug] event_queue_insert: 0x602050: docount == 0, event_count == 1, queue == 2
.
[debug] event_add: event: 0x7fff762c0500, call 0x400768
[debug] event_queue_insert: 0x7fff762c0500: docount == 1, event_count == 1,
queue == 4
--
debug log (patched):
$ ./sigtest
[debug] event_add: event: 0x7fff05e0b050, call 0x400768
[debug] event_queue_insert: 0x7fff05e0b050: docount == 1, event_count == 1,
queue == 4
[debug] evsignal_add: evsignal > sh_old_max, s == 20, max == 0
[debug] event_add: event: 0x602050, EV_READ call 0x2b07a4ece6c4
[debug] event_queue_insert: 0x602050: docount == 0, event_count == 1, queue == 2
<wait, signal>
[debug] evsignal_handler: wake up
[debug] epoll_dispatch: epoll_wait() res == -1, errno = 4
[debug] evsignal_process: processing signals, caught == 1
[debug] event_del: 0x7fff05e0b050, callback 0x400768
[debug] evsignal_del: 0x7fff05e0b050: restoring previous handler
[debug] event_active: 0x7fff05e0b050: adding to active queue, ev_res == 0, res
== 8
[debug] event_queue_insert: 0x7fff05e0b050: docount == 1, event_count == 1,
queue == 8
[debug] event_base_loop: event_count_active == 1
.
[debug] event_add: event: 0x7fff05e0b050, call 0x400768
[debug] event_queue_insert: 0x7fff05e0b050: docount == 1, event_count == 1,
queue == 4
[debug] epoll_dispatch: epoll_wait reports 1
[debug] event_active: 0x602050: adding to active queue, ev_res == 0, res == 2
[debug] event_queue_insert: 0x602050: docount == 0, event_count == 1, queue == 8
[debug] event_base_loop: event_count_active == 1
[debug] evsignal_cb: n == 1
<wait, signal>
[debug] evsignal_handler: wake up
[debug] epoll_dispatch: epoll_wait() res == -1, errno = 4
[debug] evsignal_process: processing signals, caught == 1
[debug] event_del: 0x7fff05e0b050, callback 0x400768
[debug] evsignal_del: 0x7fff05e0b050: restoring previous handler
[debug] event_active: 0x7fff05e0b050: adding to active queue, ev_res == 8, res
== 8
[debug] event_queue_insert: 0x7fff05e0b050: docount == 1, event_count == 1,
queue == 8
[debug] event_base_loop: event_count_active == 1
.
[debug] event_add: event: 0x7fff05e0b050, call 0x400768
[debug] event_queue_insert: 0x7fff05e0b050: docount == 1, event_count == 1,
queue == 4
[debug] epoll_dispatch: epoll_wait reports 1
[debug] event_active: 0x602050: adding to active queue, ev_res == 2, res == 2
[debug] event_queue_insert: 0x602050: docount == 0, event_count == 1, queue == 8
[debug] event_base_loop: event_count_active == 1
[debug] evsignal_cb: n == 1
<repeats consistently>
--
patch:
Index: event.c
===================================================================
--- event.c (revision 507)
+++ event.c (working copy)
@@ -844,8 +844,7 @@
ev->ev_flags &= ~queue;
switch (queue) {
case EVLIST_ACTIVE:
- if (docount)
- base->event_count_active--;
+ base->event_count_active--;
TAILQ_REMOVE(base->activequeues[ev->ev_pri],
ev, ev_active_next);
break;
@@ -886,8 +885,7 @@
ev->ev_flags |= queue;
switch (queue) {
case EVLIST_ACTIVE:
- if (docount)
- base->event_count_active++;
+ base->event_count_active++;
TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
ev,ev_active_next);
break;
_______________________________________________
Libevent-users mailing list
[email protected]
http://monkeymail.org/mailman/listinfo/libevent-users