Search code examples
multithreadinggtkglib

Glib: Calling a iterative loop function


Have a query on timeout calling and GMainContext. It is really confusing to me

Suppose I have the codes below (a bit incomplete, just for demonstration). I use normal Pthreads to create a thread. Within the thread, I run Glib functionality and created a GMainContext (stored within l_app.context).

I then created a source to run the function check_cmd iteratively at about 1 sec interval. This callback (or could we call it a thread?) will check for command from other threads( Pthreads not shown here for update in cmd status). From here onwards, there are two specific command

  • One to start a looping function
  • The other to end the looping function

I have done and thought of two ways to create the function and set them to run iteratively.

  • To create another timeout
  • using the same method of creating check_cmd

Essentially both to me are pretty much essentially the same method, when I tried both of them. Plan A (as I called it) does not work but Plan B ...actually run at least once. So I would like to know how to fix them...

Or maybe I should use g_source_add_child_source() instead?

In Summary, my question is

  • when you created a new context and push it to become the default context, do all subsequent function that require main_context will refer to this context?
  • in a nut shell, how do you add new sources when a loop is already running, ie like my cases
  • lastly, it is okay to quit the main loop within the callback you have created?

Here is my pseudocode

#include <glib.h>
#include <dirent.h>
#include <errno.h>

#include <pthread.h>

#define PLAN_A             0

typedef struct
 {
    GMainContext *context;
    GMainLoop *loop;
 }_App;


static _App l_app;
guint gID;

gboolean
time_cycle(gpointer udata)
{
    g_print("I AM THREADING");
    return true;
}

gboolean
check_cmd_session(NULL )
{
    while(alive)           /// alive is a boolean value that is shared with other threads(not shown)                     
    { 

        if(start)
        {
        /// PLAN A
        //// which context does this add to ??
#if PLAN_A
             g_timeout_add_seconds(10, (GSourceFunc)timeout, NULL);
#else
            /// or should i use PLAN B
            GSource* source = g_timeout_source_new(1000);

            gID = g_source_set_callback(source,
                            (GSourceFunc)time_cycle,
                            NULL,
                            NULL);

            g_source_attach(source, l_app.context);
#endif
        }
        else
        {
#if PLAN_A
            g_source_remove(gID);
#else           
        }

    }

    g_main_loop_quit (l_app.loop);
    return FALSE;
}



void*
liveService(Info *info)
{

    l_app.context = g_main_context_new ();
    g_main_context_push_thread_default(l_app.context);


    GSource* source = g_timeout_source_new(1000);

    g_source_set_callback(source,
                          (GSourceFunc)check_cmd_session,
                          NULL,
                          NULL);

    /// make it run
    g_source_attach(source, l_app.context);
    

    g_main_loop_run (l_app.loop);

    pthread_exit(NULL);

}



int main()
{
    pthread_t tid[2];

    int thread_counter = 0;
    err = pthread_create(&(tid[thread_counter]), NULL, &live, &info);
    if (err != 0)
    {
        printf("\n can't create live thread :[%s]", strerror(err));
    }
    else
    {
        printf("--> Thread for Live created successfully\n");
        thread_counter++;
    }


    /**** other threads are build not shown here */

    for(int i = 0; i < 2; i++)
    {
        printf("Joining the %d threads \n", i);
        pthread_join(tid[i],NULL);
    }

    return 0;

}

Solution

  • In Summary, my question is

    1. when you created a new context and push it to become the default context, do all subsequent function that require main_context will refer to this context?

    Functions that are documented as using the thread-default main context will use the GMainContext which has been most recently pushed with g_main_context_push_thread_default().

    Functions that are documented as using the global default main context will not. They will use the GMainContext which is created at init time and which is associated with the main thread.

    g_timeout_add_seconds() is documented as using the global default main context. So you need to go with plan B if you want the timeout source to be attached to a specific GMainContext.

    1. in a nut shell, how do you add new sources when a loop is already running, ie like my cases

    g_source_attach() works when a main context is being iterated.

    1. lastly, it is okay to quit the main loop within the callback you have created?

    Yes, g_main_loop_quit() can be called at any point.

    From your code, it looks like you’re not creating a new GMainLoop for each GMainContext and are instead assuming that one GMainLoop will somehow work with all GMainContexts in the process. That’s not correct. If you’re going to use GMainLoop, you need to create a new one for each GMainContext you create.


    All other things aside, you might find it easier to use GLib’s threading functions rather than using pthread directly. GLib’s threading functions are portable to other platforms and a little bit easier to use. Given that you’re already linking to libglib, using them would cost nothing extra.