[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;
}