I am trying to pass member function pointers to g_source_set_callback
to queue them. This is a dummy code that I wrote.
#include<glib.h>
#include<iostream>
#include<thread>
#include<chrono>
using namespace std;
using namespace std::chrono_literals;
class executor
{
private:
GMainLoop* main_loop;
GMainContext* worker_context;
thread worker_thread;
void worker_loop()
{
g_main_context_push_thread_default(worker_context);
cout << "Starting main loop" << endl;
g_main_loop_run(main_loop);
cout << "Finished main loop" << endl;
g_main_context_pop_thread_default(worker_context);
}
void queue_callback(int (*callback)(void))
{
GSource* idle_source = g_idle_source_new();
g_source_set_callback(idle_source, (GSourceFunc)callback, NULL, NULL);
g_source_attach(idle_source, worker_context);
g_source_unref(idle_source);
}
int func1()
{
cout << "func1 started" << endl;
this_thread::sleep_for(5s);
cout << "func1 finished waiting" << endl;
return 0;
}
int func2()
{
cout << "func2 started" << endl;
this_thread::sleep_for(1s);
cout << "func2 finished waiting" << endl;
return 0;
}
public:
executor()
{
worker_context = g_main_context_new();
main_loop = g_main_loop_new(worker_context, false);
worker_thread = thread(&executor::worker_loop, this);
}
~executor()
{
cout << "Stopping main loop" << endl;
GSource* idle_source = g_idle_source_new();
g_source_set_callback(idle_source, (GSourceFunc)g_main_loop_quit, main_loop, NULL);
g_source_attach(idle_source, worker_context);
g_source_unref(idle_source);
if (worker_thread.joinable())
{
worker_thread.join();
}
cout << "Removing references to main loop and context" << endl;
g_main_loop_unref(main_loop);
g_main_context_unref(worker_context);
}
void start()
{
queue_callback(func1);
queue_callback(func2);
}
};
int main()
{
executor e;
e.start();
return 0;
}
I know that you cannot pass non-static member function like that, so, I get following compilation error as expected.
test.cpp: In member function ‘void executor::start()’:
test.cpp:79:37: error: invalid use of non-static member function ‘int executor::func1()’
queue_callback(func1);
^
test.cpp:35:13: note: declared here
int func1()
^~~~~
test.cpp:80:37: error: invalid use of non-static member function ‘int executor::func2()’
queue_callback(func2);
^
test.cpp:43:13: note: declared here
int func2()
^~~~~
I saw someone using function objects to wrap the member functions. So, I tried this.
#include<glib.h>
#include<iostream>
#include<thread>
#include<chrono>
#include<functional>
using namespace std;
using namespace std::chrono_literals;
class executor
{
private:
GMainLoop* main_loop;
GMainContext* worker_context;
thread worker_thread;
void worker_loop()
{
g_main_context_push_thread_default(worker_context);
cout << "Starting main loop" << endl;
g_main_loop_run(main_loop);
cout << "Finished main loop" << endl;
g_main_context_pop_thread_default(worker_context);
}
void queue_callback(const function<int()>* callback)
{
GSource* idle_source = g_idle_source_new();
g_source_set_callback(idle_source, reinterpret_cast<GSourceFunc>(&on_callback), const_cast<function<int()>*>(callback), NULL);
g_source_attach(idle_source, worker_context);
g_source_unref(idle_source);
}
static int on_callback(const function<int()>* callback)
{
return (*callback)();
}
int func1()
{
cout << "func1 started" << endl;
this_thread::sleep_for(5s);
cout << "func1 finished waiting" << endl;
return 0;
}
int func2()
{
cout << "func2 started" << endl;
this_thread::sleep_for(1s);
cout << "func2 finished waiting" << endl;
return 0;
}
public:
executor()
{
worker_context = g_main_context_new();
main_loop = g_main_loop_new(worker_context, false);
worker_thread = thread(&executor::worker_loop, this);
}
~executor()
{
cout << "Stopping main loop" << endl;
GSource* idle_source = g_idle_source_new();
g_source_set_callback(idle_source, (GSourceFunc)g_main_loop_quit, main_loop, NULL);
g_source_attach(idle_source, worker_context);
g_source_unref(idle_source);
if (worker_thread.joinable())
{
worker_thread.join();
}
cout << "Removing references to main loop and context" << endl;
g_main_loop_unref(main_loop);
g_main_context_unref(worker_context);
}
void start()
{
cout << "Submitting func1 callback" << endl;
function<int()> callback1 = [this]() {
return func1();
};
queue_callback(&callback1);
cout << "Submitting func2 callback" << endl;
function<int()> callback2 = [this]() {
return func2();
};
queue_callback(&callback2);
}
};
int main()
{
executor e;
e.start();
return 0;
}
This code compiles, but I keep getting either segmentation fault or bad_function_call exception.
Starting main loop
Submitting func1 callback
Submitting func2 callback
Stopping main loop
func1 started
func1 finished waiting
terminate called after throwing an instance of 'std::bad_function_call'
what(): bad_function_call
Aborted (core dumped)
I think I am getting error because callback1
and callback2
are local objects and by the time they are executed, memory for them is freed.
How do I fix this? I thought of using unique_ptr
s, but couldn't figure out how as g_source_set_callback
takes int (*GSourceFunc) void
as second argument and void*
as third.
I figured it out.
I create a new std::function
object and pass it to g_source_set_callback
. When on_callback
is called, I typecast the void *
to std::function<int()>*
, call it and delete it.
Here is the working code.
#include<glib.h>
#include<iostream>
#include<thread>
#include<chrono>
#include<functional>
using namespace std;
using namespace std::chrono_literals;
class executor
{
private:
GMainLoop* main_loop;
GMainContext* worker_context;
thread worker_thread;
void worker_loop()
{
g_main_context_push_thread_default(worker_context);
cout << "Starting main loop" << endl;
g_main_loop_run(main_loop);
cout << "Finished main loop" << endl;
g_main_context_pop_thread_default(worker_context);
}
void queue_callback(function<int()>&& callback)
{
GSource* idle_source = g_idle_source_new();
g_source_set_callback(idle_source, on_callback, new function<int()>(callback), NULL);
g_source_attach(idle_source, worker_context);
g_source_unref(idle_source);
}
static int on_callback(void* ptr)
{
function<int()>* callback = static_cast<function<int()>*>(ptr);
int ret = (*callback)();
delete callback;
return ret;
}
int func1()
{
cout << "func1 started" << endl;
this_thread::sleep_for(5s);
cout << "func1 finished waiting" << endl;
return 0;
}
int func2()
{
cout << "func2 started" << endl;
this_thread::sleep_for(1s);
cout << "func2 finished waiting" << endl;
return 0;
}
public:
executor()
{
worker_context = g_main_context_new();
main_loop = g_main_loop_new(worker_context, false);
worker_thread = thread(&executor::worker_loop, this);
}
~executor()
{
cout << "Stopping main loop" << endl;
GSource* idle_source = g_idle_source_new();
g_source_set_callback(idle_source, (GSourceFunc)g_main_loop_quit, main_loop, NULL);
g_source_attach(idle_source, worker_context);
g_source_unref(idle_source);
if (worker_thread.joinable())
{
worker_thread.join();
}
cout << "Removing references to main loop and context" << endl;
g_main_loop_unref(main_loop);
g_main_context_unref(worker_context);
}
void start()
{
cout << "Submitting func1 callback" << endl;
queue_callback([this]() { return func1(); });
cout << "Submitting func2 callback" << endl;
queue_callback([this]() { return func2(); });
}
};
int main()
{
executor e;
e.start();
return 0;
}