After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 606950 - Attach thread-default-context to Sources to make async work with multithreading
Attach thread-default-context to Sources to make async work with multithreading
Status: RESOLVED INVALID
Product: vala
Classification: Core
Component: Async
unspecified
Other Linux
: Normal normal
: ---
Assigned To: Vala maintainers
Vala maintainers
Depends on:
Blocks:
 
 
Reported: 2010-01-14 12:22 UTC by Michael 'Mickey' Lauer
Modified: 2010-02-08 07:00 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Michael 'Mickey' Lauer 2010-01-14 12:22:59 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
Comment 1 Jürg Billeter 2010-02-08 07:00:43 UTC
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 );
    }
}