GNOME Bugzilla – Bug 606950
Attach thread-default-context to Sources to make async work with multithreading
Last modified: 2010-02-08 07:00:43 UTC
Async currently does not work with multithreading, as it is using idle_add to schedule the continuation, which in turn is always using the main thread's main loop. This means we can't use async with multithreading and multiple mainloop. glib has g_attach_source, which we could use to attach the thread-default-context instead of the global-default-context to make the continuations run in the right context. The following test program can be used: ================================================== void* thread_func_1() { var loop = new MainLoop(); async_func_1(); loop.run(); return null; } void* thread_func_2() { var loop = new MainLoop(); async_func_2(); loop.run(); return null; } async void async_func_1() { while ( true ) { message( "thread %d", (int)Linux.gettid() ); Timeout.add_seconds( 1, async_func_1.callback ); yield; } } async void async_func_2() { while ( true ) { message( "thread %d", (int)Linux.gettid() ); Timeout.add_seconds( 1, async_func_2.callback ); yield; } } void main() { Thread.create( thread_func_1, false ); Thread.create( thread_func_2, false ); while ( true ) { message( "main thread %d", (int)Linux.gettid() ); Thread.usleep( 1000 * 1000 * 2 ); } } ============================================= Right now it outputs: ** Message: thread.vala:43: main thread 14350 ** Message: thread.vala:31: thread 14352 ** Message: thread.vala:21: thread 14351 ** Message: thread.vala:31: thread 14352 ** Message: thread.vala:21: thread 14352 ** Message: thread.vala:31: thread 14352 ** Message: thread.vala:21: thread 14352 ** Message: thread.vala:43: main thread 14350 ** Message: thread.vala:31: thread 14352 ** Message: thread.vala:21: thread 14352 which means that after the first 'yield' the 2nd async function continues in the thread context of the 1st one. A correct output would look like that: ** Message: thread.vala:43: main thread 14350 ** Message: thread.vala:31: thread 14352 ** Message: thread.vala:21: thread 14351 ** Message: thread.vala:31: thread 14352 ** Message: thread.vala:21: thread 14351 ** Message: thread.vala:31: thread 14352 ** Message: thread.vala:21: thread 14351 ** Message: thread.vala:43: main thread 14350 ** Message: thread.vala:31: thread 14352 ** Message: thread.vala:21: thread 14351
As discussed at FOSDEM, Vala doesn't use idle_add internally, this is a bug in the test program. I've pushed bindings for the g_main_context_*_thread_default functions and it works as in the following adapted test program: void* thread_func_1() { var context = new MainContext (); context.push_thread_default (); var loop = new MainLoop(context); async_func_1(); loop.run(); return null; } void* thread_func_2() { var context = new MainContext (); context.push_thread_default (); var loop = new MainLoop(context); async_func_2(); loop.run(); return null; } async void async_func_1() { while ( true ) { message( "thread %d", (int)Linux.gettid() ); var source = new TimeoutSource.seconds (1); source.set_callback (async_func_1.callback); source.attach (MainContext.get_thread_default ()); yield; } } async void async_func_2() { while ( true ) { message( "thread %d", (int)Linux.gettid() ); var source = new TimeoutSource.seconds (1); source.set_callback (async_func_2.callback); source.attach (MainContext.get_thread_default ()); yield; } } void main() { Thread.create( thread_func_1, false ); Thread.create( thread_func_2, false ); while ( true ) { message( "main thread %d", (int)Linux.gettid() ); Thread.usleep( 1000 * 1000 * 2 ); } }