Re: glib: processing events in multiple threads



On Tue, 2013-04-30 at 10:42 +0200, Patrick Ohly wrote:
A quick and dirty workaround would be to ensure that at regular
intervals a "watch dog" timer wakes up whatever thread currently owns
the main context. But what's the right solution?

I thought that g_main_loop_run() with separate GMainLoop instances each
time a threads needs to wait might work, but I can see at least one race
condition with that. The code would look like this, for the simple "time
out after certain time" case:

    gboolean timed_out(gpointer data) {
            GMainLoop *loop = (GMainLoop *)data;
            g_main_loop_quit(loop);
            return G_SOURCE_REMOVE;
        }

    ....
        GMainLoop *loop = g_main_loop_new(NULL, TRUE /* running */);
        g_timeout_add(1 /*ms*/, timed_out, loop);
        g_main_loop_run(loop);

The problem with that is:
     1. Thread 1 is inside g_main_loop_run().
     2. Thread 2 sets the timeout, gets stopped after g_timeout_add()
        and before g_main_loop_run().
     3. Thread 1 calls timed_out(), which stops thread 2's event loop
        and removes the source.
     4. Thread 2 enters g_main_loop_run() which sets "is running" to
        true and is stuck forever.

It's a bit contrived because it's unlikely to happen when the timeout is
large, but it may be more likely with other kinds of events.

It may further reduce the risk to do:
        if (!g_main_loop_is_running(loop)) {
            g_main_loop_run(loop);
        }
but because that code can be interrupted, too, it just shifts the
problem. The real solution would be a g_main_loop_run_if_running() which
atomically checks the flag and returns immediately if false. Is
something like that possible with the current API, or are there other
solutions to the problem?

One more potential problem with such a g_main_loop_run_if_running() if
implemented as g_main_loop_run() without the "loop->is_running = TRUE"
parts: if thread 1 keeps owning the main context forever, will thread 2
get a chance to check it's is_running flag?

In the current implementation of g_main_loop_run(), thread 2 would wait
for ownership of the context here:

      while (loop->is_running && !got_ownership)
        got_ownership = g_main_context_wait (loop->context,
                                             &loop->context->cond,
                                             &loop->context->mutex);

Will g_main_context_wait() ever return if thread 1 doesn't release
ownership? It doesn't look like this will happen.

-- 
Best Regards, Patrick Ohly

The content of this message is my personal opinion only and although
I am an employee of Intel, the statements I make here in no way
represent Intel's position on the issue, nor am I authorized to speak
on behalf of Intel on this matter.




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]