Search code examples
c++multithreadingthread-safetyglib

Thread communication using C++14 and GLib (GDBus)


I'm designing a class which will create another object that runs in parallel. This parallel object will run a GMainLoop and listen for various events (DBus in this case). My issue is I'm unsure on how to communicate with a thread running in a GMainLoop. Am I forced to use the GLib methods of communication with that thread? I'd rather rely on the standard library as hopefully I will eventually be able to remove my dependency on GLib altogether. Here is a toy example of what I'm trying to accomplish:

// Example program
#include <iostream>
#include <thread>
#include <memory>

#include <glib.h>

class ParallelTask
{
public:
ParallelTask() {
    std::cout << "I am alive!\n";

    // initialize _mainLoop (allocate, add signals, set priority, etc)
    // ...

    g_main_loop_run(_mainLoop);
};
~ParallelTask() {
    std::cout << "I am dead!\n";

    // Misc cleanup
    // ...

    g_main_loop_quit(_mainLoop);
}
private:
GMainLoop* _mainLoop;
};

class TaskManager
{
public:
TaskManager() = default;
~TaskManager() {join();}

void startTask() {

    _thread = std::thread([this](){_task = std::make_unique<ParallelTask>();}); 
}
void stopTask() {
    //??? Need to send message to Parallel task telling it to shut down
    join();
}

private:
void join() {
    if (_thread.joinable()) {
        _thread.join();
    }
};

std::thread _thread;
std::unique_ptr<ParallelTask> _task;
};

int main()
{
    TaskManager taskManager;
    taskManager.startTask();

    // Do other stuff

    taskManager.stopTask();
}

Additionally, I am running on a Linux based OS. I'd even prefer a pthread based solution over aGLib one. However, if no other solution is possible, I would gladly take GLib suggestions. Thanks!


Solution

  • I've determined that for my needs I can use g_main_context_iteration instead of g_main_loop_run. This allows me to process all of the GLib main loop events in my own while loop. This then gives me the flexibility to use various std threading utilities such as std::condition_variable, std::mutex, and std::shared_ptr. Example of how to use g_main_context_iteration:

    #include <iostream>
    #include <string>
    #include <thread>
    #include <atomic>
    #include <chrono>
    
    #include <glib.h>
    
    using namespace std::chrono_literals;
    
    void concurrentTask(std::atomic_bool& process)
    {
        std::cout << "Beginning processing\n";
    
        GMainContext *context = g_main_context_default();
    
        // Register signals, etc...
    
        while (process == true)
        {
            g_main_context_iteration(context, true);
        }
    
        g_main_context_unref(context);
    }
    
    int main()
    {
        std::atomic_bool process(true);
    
        std::thread task(concurrentTask, std::ref(process));
    
        std::this_thread::sleep_for(2s);
        process = false;
        task.join();
        std::cout << "Processing complete\n";
    }