Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

add callback for separate g_main_loop

Tags:

loops

glib

I have some misunderstanding about how GMainLoop work. Main thing - API which add some callbacks into g_main_loop (like g_timeout_add_seconds()) don't take pointer to which loop you want to add that callback.

It looks like you add callback's for all g_main_loop instances. Even if you have not yet created. Simple example for this:

#include <glib.h>

gboolean callback(gpointer data)
{
  static guint16 i=0; 
  g_print("Iter=%"G_GUINT16_FORMAT"\n",i++);
  if(i%5==0){
    g_print("try to stop loop1\n");
    g_main_loop_quit((GMainLoop*)data);
  }
  return TRUE;
}

int main()
{
  GMainLoop* loop1 = NULL;
  GMainLoop* loop2 = NULL;
  loop1 = g_main_loop_new (NULL, FALSE);

  g_timeout_add_seconds(1, callback,loop1);

  loop2 = g_main_loop_new (NULL, FALSE);
  g_print("run loop1\n");
  g_main_loop_run(loop1);
  g_free(loop1);
  g_print("run loop2\n");
  g_main_loop_run(loop2);
  g_free(loop2);
  return 0;
}

Result is:

run loop1
Iter=0
Iter=1
Iter=2
Iter=3
Iter=4
try to stop loop1
run loop2
Iter=5
Iter=6
Iter=7
Iter=8
Iter=9
try to stop loop1
Segmentation fault (core dumped)

Is it possible add callback() to loop1, and don't add it to loop2?

like image 995
Stepan Loginov Avatar asked Oct 16 '14 17:10

Stepan Loginov


1 Answers

A quick look at the documentation for g_idle_add(), g_idle_add_full(), g_timeout_add(), or g_timeout_add_full() will tell you:

This internally creates a main loop source using g_timeout_source_new() and attaches it to the main loop context using g_source_attach(). You can do these steps manually if you need greater control.

Note that it says it attaches the source to the main loop contexti.e., a GMainContext, not a GMainLoop. When you create your GMainLoop instances you're passing NULL for the first argument. According to the the g_main_loop_new() documentation, that argument is

a GMainContext (if NULL, the default context will be used).

So, you are creating two main loops, both using the same context (which is the default context).

To get the result I think you're expecting, you should do something like:

#include <glib.h>

gboolean callback(gpointer data)
{
  static guint16 i=0; 
  g_print("Iter=%"G_GUINT16_FORMAT"\n",i++);
  if(i%5==0){
    g_print("try to stop loop1\n", data);
    g_main_loop_quit((GMainLoop*)data);
  }
  return TRUE;
}

int main()
{
  GMainContext* con1 = NULL;
  GMainContext* con2 = NULL;
  GMainLoop* loop1 = NULL;
  GMainLoop* loop2 = NULL;
  GSource* source1 = NULL;

  con1 = g_main_context_new ();
  con2 = g_main_context_new ();

  loop1 = g_main_loop_new (con1, FALSE);
  loop2 = g_main_loop_new (con2, FALSE);

  source1 = g_timeout_source_new_seconds (1);
  g_source_set_callback (source1, callback, loop1, NULL);
  g_source_attach (source1, con1);

  // We don't need the GMainContext anymore—the loop has an internal
  // reference so we'll drop ours.
  g_main_context_unref (con1);
  con1 = NULL;

  // Ditto for the GSource
  g_source_unref (source1);
  source1 = NULL;

  g_main_context_unref (con2);
  con2 = NULL;

  g_print("run loop1\n");
  g_main_loop_run(loop1);
  // Use g_main_loop_unref, not g_free
  g_main_loop_unref(loop1);
  loop1 = NULL;

  g_print("run loop2\n");
  // Note that there is no longer a callback attached here, so it will
  // run forever.
  g_main_loop_run(loop2);
  g_main_loop_unref(loop2);
  loop2 = NULL;

  return 0;
}
like image 191
nemequ Avatar answered Oct 18 '22 05:10

nemequ