Posting to main loop before main loop is run

I am fairly new to GTK and need some help. I have an application where I am using the GTK event loop. I am creating my own threads during initialization of the application. After that, the main thread is entering the event loop using g_main_loop_run.

Now, on my worker threads, something can go wrong and I want to exit the event loop. I am using g_main_loop_quit to do that.

According to the documentation, the main loop will run till g_main_loop_quit is called. Any calls to g_main_loop_run will return.

GMainLoop * gMainLoopPtr = nullptr;

void * ThreadFunc (void * pParms)
{

bool success = DoSomeWork ();

if (!success) {

  // I want main thread to exit the event loop

  g_main_loop_quit (gMainLoopPtr);

  return nullptr;

} else {

  DoSomeOtherWork ();

}

}

void Initialize (int pArgc, char** pArgv)
{

gtk_init (&pArgc, &pArgv);

gMainLoopPtr = g_main_loop_new (nullptr, false);

pthread_create (ThreadFunc, nullptr);
}

void Run ()
{
Log (“Entering Event Loop”); // Logs to a file

g_main_loop_run (gMainLoopPtr);

Log (“Exited Event Loop”);
}

int main (int pArgc, char** pArgv)
{
Initialize (pArgc, pArgv);

Run ();

Finalize ();

return 0;
}

Now there is a rare race condition in the above code. Before the main thread could invoke g_main_loop_run, the worker thread could invoke g_main_loop_quit. I simulated this (by waiting for the worker thread after calling Initialize and before calling Run). The log for “Entering Event Loop” gets written, but the “Exited Event Loop” does not get written.

I modified the code a bit to post a function to the event loop using g_idle_add_full, and the function (expected to be invoked on main thread) will invoke g_main_loop_quit:

GMainLoop * gMainLoopPtr = nullptr;

gboolean PostQuit (gpointer pData)
{
Log (“Posting Quit”);

g_main_loop_quit (gMainLoopPtr);
}

void * ThreadFunc (void * pParms)
{
bool success = DoSomeWork ();

if (!success) {

  // I want main thread to exit the event loop

  g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, PostQuit, nullptr, nullptr);

  return nullptr;

} else {

  DoSomeOtherWork ();

}

}

void Initialize (int pArgc, char** pArgv)
{
gtk_init (&pArgc, &pArgv);

gMainLoopPtr = g_main_loop_new (nullptr, false);

pthread_create (ThreadFunc, nullptr);
}

void Run ()
{
Log (“Entering Event Loop”);

g_main_loop_run (gMainLoopPtr);

Log (“Exited Event Loop”);
}

int main (int pArgc, char** pArgv)
{
Initialize (pArgc, pArgv);

Run ();

Finalize ();

return 0;
}

Here also, the same race condition exists. When simulated, the Log “Posting Quit” does not get written, nor does the “Exited Event Loop”.

Any ideas on how to handle this race condition?

1 Like

Hi, you have multiple options:

  • Start the main loop before creating threads (e.g. by creating the threads in a callback). This is probably your best option.
  • Get rid of GMainLoop and instead call g_main_context_iteration(NULL, TRUE) directly in your own while or for loop. Use an atomic int as a flag for the secondary threads to tell the main thread to stop iterating your loop.

That said, since you are using GTK, actually you should use gtk_application_run() to run the main loop. If you create your secondary threads in a GApplication::startup callback then you shouldn’t have any issues.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.