Immediatelly stop a libuv event loop from another thread

1,095 views
Skip to first unread message

Thiago Arruda

unread,
Mar 5, 2014, 5:04:19 AM3/5/14
to li...@googlegroups.com
I have a libuv event loop running in a background thread, it's reading from stdin and filling a buffer read by the main thread.

There's an 'exit' function called by the main thread that does some cleanup including these lines:

    /* stop libuv loop */
    uv_async_send(&stop_loop_async);
    /* wait for the event loop thread */
    uv_thread_join(&io_thread);

The 'stop_loop_async' is configured to call a function that will call 'uv_stop' on the background thread. That wont work when libuv is polling for data and the program will block forever in `uv_thread_join`. Here's the backtrace of the hanging program:

thread 1:

#0  0xb77a4424 in __kernel_vsyscall ()
#1  0xb7751e1c in pthread_join (threadid=3073854272, thread_return=0x0) at pthread_join.c:89
#2  0x081fc309 in uv_thread_join (tid=0x826b5a0) at src/unix/thread.c:35

thread 2(the event loop thread):

#0  0xb77a4424 in __kernel_vsyscall ()
#1  0xb77579db in read () at ../sysdeps/unix/syscall-template.S:82
#2  0x081fa4c3 in read (__nbytes=<optimized out>, __buf=<optimized out>, __fd=<optimized out>) at /usr/include/i386-linux-gnu/bits/unistd.h:45
#3  uv__read (stream=0x826b680) at src/unix/stream.c:1001
#4  uv__stream_io (loop=0xb6a00468, w=0x826b6b8, events=1) at src/unix/stream.c:1146
#5  0x081ff35e in uv__io_poll (loop=0xb6a00468, timeout=-1) at src/unix/linux-core.c:271
#6  0x081f6252 in uv_run (loop=0xb6a00468, mode=UV_RUN_DEFAULT) at src/unix/core.c:284

The libuv version is 0.11.19 and operating system is linux 32 bits.

What would be the best way to deal with the problem?

Saúl Ibarra Corretgé

unread,
Mar 5, 2014, 5:14:52 AM3/5/14
to li...@googlegroups.com
Hi,

On 03/05/2014 11:04 AM, Thiago Arruda wrote:
> I have a libuv event loop running in a background thread, it's reading
> from stdin and filling a buffer read by the main thread.
>
> There's an 'exit' function called by the main thread that does some
> cleanup including these lines:
>
> /* stop libuv loop */
> uv_async_send(&stop_loop_async);
> /* wait for the event loop thread */
> uv_thread_join(&io_thread);
>
> The 'stop_loop_async' is configured to call a function that will call
> 'uv_stop' on the background thread. That wont work when libuv is polling
> for data and the program will block forever in `uv_thread_join`. Here's
> the backtrace of the hanging program:
>

What do you mean by "won't work"? Is the async callback executed? If so,
libuv is executing that callback and not polling for i/o, and when
uv_stop is called the next time a 0 timeout poll will be performed.

> thread 1:
>
> #0 0xb77a4424 in __kernel_vsyscall ()
> #1 0xb7751e1c in pthread_join (threadid=3073854272, thread_return=0x0)
> at pthread_join.c:89
> #2 0x081fc309 in uv_thread_join (tid=0x826b5a0) at src/unix/thread.c:35
>
> thread 2(the event loop thread):
>
> #0 0xb77a4424 in __kernel_vsyscall ()
> #1 0xb77579db in read () at ../sysdeps/unix/syscall-template.S:82
> #2 0x081fa4c3 in read (__nbytes=<optimized out>, __buf=<optimized out>,
> __fd=<optimized out>) at /usr/include/i386-linux-gnu/bits/unistd.h:45
> #3 uv__read (stream=0x826b680) at src/unix/stream.c:1001
> #4 uv__stream_io (loop=0xb6a00468, w=0x826b6b8, events=1) at
> src/unix/stream.c:1146
> #5 0x081ff35e in uv__io_poll (loop=0xb6a00468, timeout=-1) at
> src/unix/linux-core.c:271
> #6 0x081f6252 in uv_run (loop=0xb6a00468, mode=UV_RUN_DEFAULT) at
> src/unix/core.c:284
>
> The libuv version is 0.11.19 and operating system is linux 32 bits.
>
> What would be the best way to deal with the problem?

Do you have a test case showing this behavior? By looking at the code I
don't see how this could happen.


Cheers,

--
Saúl Ibarra Corretgé
bettercallsaghul.com

Thiago Padilha

unread,
Mar 5, 2014, 5:35:39 AM3/5/14
to li...@googlegroups.com
On Wed, Mar 5, 2014 at 7:14 AM, Saúl Ibarra Corretgé <sag...@gmail.com> wrote:
> What do you mean by "won't work"? Is the async callback executed? If so,
> libuv is executing that callback and not polling for i/o, and when uv_stop
> is called the next time a 0 timeout poll will be performed.

The async callback is never called so the loop isn't stopped

> Do you have a test case showing this behavior? By looking at the code I
> don't see how this could happen.

Yes, I've pushed a branch to github. If you are on a linux machine you
can probably copy/paste these to setup everything(need cmake
installed):

git clone git://github.com/tarruda/neovim
cd neovim
git checkout uv_async_send-hang
make cmake
make

The above should compile './build/bin/nvim'. To reproduce the problem
enter the editor:

./build/bin/nvim -u NONE

and quit it with ':q<enter>'

You should see a message 'calling async uv_stop'. The relevant code is
at src/os/io.c .

Saúl Ibarra Corretgé

unread,
Mar 5, 2014, 5:38:23 AM3/5/14
to li...@googlegroups.com
On 03/05/2014 11:35 AM, Thiago Padilha wrote:
> On Wed, Mar 5, 2014 at 7:14 AM, Saúl Ibarra Corretgé <sag...@gmail.com> wrote:
>> What do you mean by "won't work"? Is the async callback executed? If so,
>> libuv is executing that callback and not polling for i/o, and when uv_stop
>> is called the next time a 0 timeout poll will be performed.
>
> The async callback is never called so the loop isn't stopped
>

Hum, that's weird. I assume it was initialized, right?

>> Do you have a test case showing this behavior? By looking at the code I
>> don't see how this could happen.
>
> Yes, I've pushed a branch to github. If you are on a linux machine you
> can probably copy/paste these to setup everything(need cmake
> installed):
>
> git clone git://github.com/tarruda/neovim
> cd neovim
> git checkout uv_async_send-hang
> make cmake
> make
>
> The above should compile './build/bin/nvim'. To reproduce the problem
> enter the editor:
>
> ./build/bin/nvim -u NONE
>
> and quit it with ':q<enter>'
>
> You should see a message 'calling async uv_stop'. The relevant code is
> at src/os/io.c .
>

I'll have a look!

Thiago Padilha

unread,
Mar 5, 2014, 5:42:18 AM3/5/14
to li...@googlegroups.com
Btw, I'm pretty newbie to libuv, can you suggest a better way of doing
what I'm trying to do? (Read user input from a background thread)

I'm using a pipe for reading backed by a static buffer, but I have to
return buf->len == 1 to ensure libuv calls my read_cb everytime a new
character is typed.

Also, everything works perfectly if I set BUF_SIZE to 1(makes sense
since libuv won't be polling when I call uv_async_send)

Thanks
> --
> You received this message because you are subscribed to the Google Groups
> "libuv" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to libuv+un...@googlegroups.com.
> To post to this group, send email to li...@googlegroups.com.
> Visit this group at http://groups.google.com/group/libuv.
> For more options, visit https://groups.google.com/groups/opt_out.

Saúl Ibarra Corretgé

unread,
Mar 5, 2014, 5:42:57 AM3/5/14
to li...@googlegroups.com
Ah, I think I found it. You are using a uv_pipe_t to read stdin using
uv_pipe_open. Currently uv_pipe_open doesn't set the file descriptor in
non-blocking mode, so the loop thread is probably blocked in the
blocking call to read.

Set the fd to be non-blocking and it should work. Another approach is to
use a uv_tty_t handle, which does non-blocking reads.


Cheers,

PS: As an everyday vim user for years, I'm really excited for neovim :-)

Thiago Padilha

unread,
Mar 5, 2014, 12:27:05 PM3/5/14
to li...@googlegroups.com
Thanks Saúl.

I've done as you suggested, set the fd to non-blocking and it solved
the deadlock problem.

Btw, it's good to have a libuv maintainer as possible neovim user :)

Saúl Ibarra Corretgé

unread,
Mar 5, 2014, 3:50:39 PM3/5/14
to li...@googlegroups.com
On 03/05/2014 06:27 PM, Thiago Padilha wrote:
> Thanks Saúl.
>
> I've done as you suggested, set the fd to non-blocking and it solved
> the deadlock problem.
>

That's great news!

> Btw, it's good to have a libuv maintainer as possible neovim user :)
>

Count me in ;-)
Reply all
Reply to author
Forward
0 new messages