[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
Re: [Libevent-users] deadlock in bufferevent_disable
On Fri, Jan 02, 2015 at 02:35:26PM +0200, Roman Gershman wrote:
> Unfortunately I do not have 50L sample for this but the stacktrace should
> give the clear picture on how this happens.
>
> I solved the problem by moving my shutdown flow (i.e. calls like
> bufferevent_disable) into event base main thread which apparently solves
> the deadlock bug.
Okay, finally, I have some time for validating this, once and for all, and I
confirmed that for 2.0 there is deadlock and it is pretty simple to reproduce,
while for 2.1 there is no deadlock.
It took me some time, since I used bufferevent_pair firstly, but then I
realized that it uses BEV_OPT_DEFER_CALLBACKS.
2.0:
===
$ le20=libevent-2.0.20/.libs
$ gcc -Wl,-rpath=$le20 -L$le20 -g3 -pthread -levent_pthreads -levent test.c -o test
$ ./test # hanged &
$ gdb -q -p $(pgrep test) -batch -ex 'thread apply all bt' -ex detach
[New LWP 25717]
[New LWP 25716]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007f8f1604a4db in pthread_join (threadid=140252503488256, thread_return=0x0) at pthread_join.c:92
Thread 3 (Thread 0x7f8f15264700 (LWP 25716)):
#0 pthread_cond_wait@@GLIBC_2.3.2 () at pthread_cond_wait.S:185
#1 0x00007f8f164b3082 in evthread_posix_cond_wait (_cond=0x1cce560, _lock=0x1cce530, tv=0x0) at evthread_pthread.c:156
#2 0x00007f8f16270570 in event_del_internal (ev=0x1cceb40) at event.c:2211
#3 0x00007f8f162703ef in event_del (ev=0x1cceb40) at event.c:2179
#4 0x00007f8f1627d5dc in be_socket_disable (bufev=0x1cceb30, event=2) at bufferevent_sock.c:564
#5 0x00007f8f1627b8d1 in bufferevent_disable (bufev=0x1cceb30, event=2) at bufferevent.c:505
#6 0x000000000040103b in endiscb (arg=0x0) at test.c:53
#7 0x00007f8f160490a4 in start_thread (arg=0x7f8f15264700) at pthread_create.c:309
#8 0x00007f8f15d7dccd in clone () at clone.S:111
Thread 2 (Thread 0x7f8f14a63700 (LWP 25717)):
#0 __lll_lock_wait () at lowlevellock.S:135
#1 0x00007f8f1604b4d4 in _L_lock_952 () from /lib/x86_64-linux-gnu/libpthread.so.0
#2 0x00007f8f1604b336 in __GI___pthread_mutex_lock (mutex=0x1ccee70) at pthread_mutex_lock.c:114
#3 0x00007f8f164b2e80 in evthread_posix_lock (mode=0, _lock=0x1ccee70) at evthread_pthread.c:73
#4 0x00007f8f1627bc1c in _bufferevent_incref_and_lock (bufev=0x1cceb30) at bufferevent.c:582
#5 0x00007f8f1627c9a3 in bufferevent_readcb (fd=8, event=2, arg=0x1cceb30) at bufferevent_sock.c:133
#6 0x00007f8f1626d9a0 in event_persist_closure (base=0x1cce0d0, ev=0x1cceb40) at event.c:1297
#7 0x00007f8f1626daaf in event_process_active_single_queue (base=0x1cce0d0, activeq=0x1cce510) at event.c:1341
#8 0x00007f8f1626dd7c in event_process_active (base=0x1cce0d0) at event.c:1416
#9 0x00007f8f1626e3e7 in event_base_loop (base=0x1cce0d0, flags=4) at event.c:1617
#10 0x0000000000401164 in loopcb (arg=0x0) at test.c:76
#11 0x00007f8f160490a4 in start_thread (arg=0x7f8f14a63700) at pthread_create.c:309
#12 0x00007f8f15d7dccd in clone () at clone.S:111
Thread 1 (Thread 0x7f8f168a1700 (LWP 25714)):
#0 0x00007f8f1604a4db in pthread_join (threadid=140252503488256, thread_return=0x0) at pthread_join.c:92
#1 0x00000000004013a9 in main (argc=1, argv=0x7fff509b7d98) at test.c:110
2.1:
===
$ gcc -g3 -pthread test.c libevent-github/.cmake-debug/lib/libevent.a -o test
$ ./test
stat> read: 0, readed: 0, write: 1 (0), disabled: 2
stat> read: 0, readed: 0, write: 100001 (0), disabled: 94612
stat> read: 0, readed: 0, write: 200001 (0), disabled: 188108
stat> read: 0, readed: 0, write: 300001 (0), disabled: 304106
stat> read: 0, readed: 0, write: 400001 (0), disabled: 383880
stat> read: 0, readed: 0, write: 500001 (0), disabled: 493574
stat> read: 0, readed: 0, write: 600001 (0), disabled: 568470
stat> read: 1709, readed: 7000000, write: 700000 (0), disabled: 3541154
(Also I've fixed some minor bugs during digging this, IOW they don't affect
behaviour of this particular deadlock).
Cheers,
Azat.
#define _GNU_SOURCE
#include <pthread.h>
#include <assert.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/thread.h>
#include <event2/event.h>
#define ITERATIONS 700000
#define ITERATIONS_SIZE 10
struct event_base *base;
struct bufferevent *pair[2];
struct evstat
{
size_t read;
size_t readed;
size_t write;
size_t disable;
size_t free;
} stat;
void prstat()
{
fprintf(stderr, "stat> read: %zu, readed: %zu, write: %zu (%zu), disabled: %zu\n",
stat.read, stat.readed, stat.write, evbuffer_get_length(bufferevent_get_input(pair[1])), stat.disable);
}
void readcb(struct bufferevent *bev, void *arg)
{
struct evbuffer *evbuf;
assert(evbuf = evbuffer_new());
bufferevent_read_buffer(bev, evbuf);
stat.readed += evbuffer_get_length(evbuf);
evbuffer_free(evbuf);
++stat.read;
if (stat.readed == (ITERATIONS * ITERATIONS_SIZE)) {
event_base_loopexit(base, 0);
}
}
void *endiscb(void *arg)
{
size_t i;
for (i = 0; ; ++i) {
if (stat.free) {
break;
}
if (!(i % 100)) {
assert(!bufferevent_disable(pair[1], EV_READ));
assert(!bufferevent_enable(pair[1], EV_READ));
++stat.disable;
}
}
}
void *writercb(void *arg)
{
char buffer[ITERATIONS_SIZE] = { 0 };
size_t i;
for (i = 0; i < ITERATIONS; ++i) {
bufferevent_write(pair[0], buffer, sizeof(buffer));
++stat.write;
if (!(i % 100000)) {
prstat();
}
}
}
void *loopcb(void *arg)
{
event_base_loop(base, EVLOOP_NO_EXIT_ON_EMPTY);
}
void firstcb(evutil_socket_t fd , short what, void *arg)
{
sleep(5);
}
int main(int argc, char **argv)
{
pthread_t writer;
pthread_t endis;
pthread_t loop;
evutil_socket_t pipe[2];
evthread_use_pthreads();
assert(base = event_base_new());
event_base_once(base, -1, EV_TIMEOUT, firstcb, NULL, NULL);
evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pipe);
evutil_make_socket_nonblocking(pipe[0]);
evutil_make_socket_nonblocking(pipe[1]);
pair[0] = bufferevent_socket_new(base, pipe[0], BEV_OPT_THREADSAFE);
pair[1] = bufferevent_socket_new(base, pipe[1], BEV_OPT_THREADSAFE);
bufferevent_setcb(pair[1], readcb, NULL, NULL, NULL);
assert(!bufferevent_disable(pair[0], EV_READ));
assert(!bufferevent_enable(pair[1], EV_READ));
assert(!pthread_create(&writer, NULL, writercb, NULL));
assert(!pthread_create(&endis, NULL, endiscb, NULL));
assert(!pthread_create(&loop, NULL, loopcb, NULL));
assert(!pthread_join(loop, NULL));
assert(!pthread_join(writer, NULL));
stat.free = 1;
assert(!pthread_join(endis, NULL));
prstat();
bufferevent_free(pair[0]);
bufferevent_free(pair[1]);
event_base_free(base);
#ifdef LIBEVENT_2_1
libevent_global_shutdown();
#endif
return 0;
}