Discussion:
infinite loop in evmap_io_active
Mike Cui
2011-04-11 10:34:10 UTC
Permalink
I'm also seeing an infinite loop in evmap_io_active:

TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {
if (ev->ev_events & events)
event_active_nolock(ev, ev->ev_events & events, 1);
}

It looks like the ev_io_next pointer points back to itself :(

Here is a disassembly of that loop:

0x280ab670 <evmap_io_active+64>: mov 0x20(%esi),%esi
0x280ab673 <evmap_io_active+67>: test %esi,%esi
0x280ab675 <evmap_io_active+69>: je 0x280ab69d <evmap_io_active+109>
0x280ab677 <evmap_io_active+71>: mov 0x30(%esi),%eax
0x280ab67a <evmap_io_active+74>: and %edi,%eax
0x280ab67c <evmap_io_active+76>: cwtl
0x280ab67d <evmap_io_active+77>: test %eax,%eax
0x280ab67f <evmap_io_active+79>: je 0x280ab670 <evmap_io_active+64>

And here is what I see from GDB (%esi points to ev, and (%esi + 0x20)
is ev_io_next):

(gdb) p/x $esi
$2 = 0x2835fad0
(gdb) p/x *(void **)($esi + 0x20)
$3 = 0x2835fad0

I guess that would happen if I added the same event twice? But I'm not
doing that. Here is the pattern of event_add() and event_callbacks()
for each fd and struct ev * that I add. I'm adding events as EV_READ
or EV_WRITE, not EV_PERSIST, and with no timeout:

event_add(fd=9, ev=0x2831fec0)
event_callback(fd=9, ev=0x2831fec0)
event_add(fd=9, ev=0x2831fec0)
event_add(fd=11, ev=0x2833dcd0)
event_callback(fd=11, ev=0x2833dcd0)
event_add(fd=11, ev=0x2835fad0)
event_add(fd=10, ev=0x2833da80)
event_callback(fd=10, ev=0x2833da80)
event_add(fd=10, ev=0x2833da80)
event_callback(fd=11, ev=0x2835fad0)
event_add(fd=11, ev=0x2835fad0)

What am I doing wrong here? The only thing I can think of is that it
might not be OK to re-add the same event in the callback? But I copied
this code right out of event-test.c.

My program is single threaded, and I can deterministically get it
stuck in this state immediately using just one connection.

Thanks for your help!
***********************************************************************
To unsubscribe, send an e-mail to ***@freehaven.net with
unsubscribe libevent-users in the body.
Nick Mathewson
2011-04-11 17:21:15 UTC
Permalink
       TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {
               if (ev->ev_events & events)
                       event_active_nolock(ev, ev->ev_events & events, 1);
       }
It looks like the ev_io_next pointer points back to itself :(
So, the likeliest reason for this to happen is a corrupt event, as I
just mentioned in my email to Sherif:

"One reason that can happen
is if you add an event, then re-assign it and re-add it without first
deleting it. To debug that, try enabling debug mode by calling
event_enable_debug_mode();
near the start of your program (before you construct any event_bases).

It should detect any attempts to modify an event that's currently pending."

HTH,
--
Nick
***********************************************************************
To unsubscribe, send an e-mail to ***@freehaven.net with
unsubscribe libevent-users in the body.
Mike Cui
2011-04-11 19:16:26 UTC
Permalink
Post by Nick Mathewson
"One reason that can happen
is if you add an event, then re-assign it and re-add it without first
deleting it.  To debug that, try enabling debug mode by calling
   event_enable_debug_mode();
near the start of your program (before you construct any event_bases).
It should detect any attempts to modify an event that's currently pending."
Yep. I was not calling event_del(). So question, if I want to
reschedule a event, I can call event_add() multiple times. But calling
event_del() deletes the event forever? There seems to be a mismatch in
the API here. So what exactly happens when neither event_del() nor
event_add() is called? Does the event fire again?
***********************************************************************
To unsubscribe, send an e-mail to ***@freehaven.net with
unsubscribe libevent-users in the body.
Nick Mathewson
2011-04-12 19:20:54 UTC
Permalink
Post by Mike Cui
Post by Nick Mathewson
"One reason that can happen
is if you add an event, then re-assign it and re-add it without first
deleting it.  To debug that, try enabling debug mode by calling
   event_enable_debug_mode();
near the start of your program (before you construct any event_bases).
It should detect any attempts to modify an event that's currently pending."
Yep. I was not calling event_del(). So question, if I want to
reschedule a event, I can call event_add() multiple times. But calling
event_del() deletes the event forever?
No; event_del() makes the event non-pending. I've tried to explain
this in the documentation at
http://www.wangafu.net/~nickm/libevent-book/Ref4_event.html , and in
the doxygen documentation.

Here's how it works: a chunk of memory that you get from
malloc(sizeof(struct event)) or by declaring "struct event x;" starts
out as uninitialized RAM. You can turn this memory into an event with
event_assign(). You can also get a new event with event_new().

These "newly initialized" events are not "pending" (that is, libevent
is not watching for events on them), and they are not "active" (that
is, libevent is not planning to run their callbacks). This is the
only state in which it is safe to reinitialize an event with
event_assign(), or to free malloc'd memory with free().

Calling event_active() on an event makes it "active": libevent will
schedule its callback to be run in the event loop.

Calling event_add() on an event makes it "pending": libevent will
start its timer (if it has one), and start watching its fd and/or
signal (if it has one). "Pending" events are also called "added"
events sometimes.

An event can be "active" and "pending" at the same time.

Calling event_del() on an event returns it to the "newly initialized"
state. If it was active and the callback hasn't been run it, it won't
get run.

If an event is pending, and the event triggers (that is, its fd/signal
becomes active or its fd expires), then the event will become
"active". If the EV_PERSIST flag was set on the event, then it will
still be pending; otherwise, it will no longer be pending.

When an active event's callback is run, it becomes inactive.

It is okay to free any initialized event with event_free(), since
event_free() includes an event_del() operation.

Here is are some things that it is _not_ okay to do:
* Call event_set or event_assign() on an event that is pending or active.
* Call free() on an event that is still pending or active, or allow
a stack-allocated event that is pending or active to go out of scope.
* Call event_free() on an event that was not returned from event_new().

I've drawn up a draft state transition diagram. It's not pretty, but
it might help:
Loading Image...
Please let me know if I've got anything wrong; I want to include it in
the book if I can figure out how. That URL isn't permanent.

Did this answer your questions?

yrs,
--
Nick
***********************************************************************
To unsubscribe, send an e-mail to ***@freehaven.net with
unsubscribe libevent-users in the body.
Mike Cui
2011-04-12 23:37:46 UTC
Permalink
I've drawn up a draft state transition diagram.  It's not pretty, but
  http://www.wangafu.net/~nickm/volatile/event_states.png
Please let me know if I've got anything wrong; I want to include it in
the book if I can figure out how.  That URL isn't permanent.
Did this answer your questions?
Yes, thanks!
***********************************************************************
To unsubscribe, send an e-mail to ***@freehaven.net with
unsubscribe libevent-users in the body.

Loading...