I am newer in GTK and now try to find a explonation of how GTask is working.
My main question is how in main thread I can implement a waiting all tasks finished and callbacks called before exit from app?
Is it nessesary to call g_main_loop_run
after g_task_run_in_thread
and call g_main_loop_quit
in task callback in each time or not?
Is g_main_loop_run
block a thread until g_main_loop_quit
called?
What is the best way to run many task in one main loop?
Thank you, guys!
My code for test how it is working
#include <iostream>
#include <gtk/gtk.h>
#include <gio/gio.h>
typedef GObject myGObj;
static volatile int flag = 0;
GMainLoop *loop;
static int again = 0;
static void
some_blocking_function_async (GTask *task,
gpointer object,
gpointer task_data,
GCancellable *cancellable)
{
printf("%p: %s\n", g_thread_self(), __func__);
std::cout << " some_blocking_function_async sleep\n";
usleep(3000000); //emulate some work
std::cout << " some_blocking_function_async waked up\n";
//g_main_loop_quit(loop); //callback no called if loop quited here
g_task_return_boolean(task, TRUE);
}
static void
cb_func(GObject *gobject,
GAsyncResult *res,
gpointer user_data)
{
printf("%p: %s\n", g_thread_self(), __func__);
std::cout << "cb_func sleep\n";
usleep(1000000);
std::cout << "cb_func waked up\n";
flag++;
GTask *task = (GTask*)user_data;
g_main_loop_quit(loop); //task don't call a calback if main loop not quited and runned again ???
if(flag == 1 && again == 0){
again++;
std::cout << "Run 2nd Task\n";
GTask* task2 = g_task_new(NULL, NULL, cb_func, NULL);
g_task_run_in_thread(task2, some_blocking_function_async);
g_object_unref(task2);
g_main_loop_run(loop); //task don't call a calback if main loop not quited and runned again ???
}
}
int main(int, char**){
printf("%p: %s\n", g_thread_self(), __func__);
std::cout << "Hello, from testGt!\n";
loop = g_main_loop_new(NULL, FALSE);
GTask* task = g_task_new(NULL, NULL, cb_func, NULL);
g_task_run_in_thread(task, some_blocking_function_async);
std::cout << "Task runned, from testGt!\n";
g_object_unref(task);
g_main_loop_run(loop);
std::cout << "Loop runned, from testGt!\n";
while(flag != 2){
std::cout << "Sleep, from testGt!\n";
usleep(1000000);
std::cout << "Wakeup, from testGt!\n";
}
g_main_loop_quit(loop);
std::cout << flag << " Bye, from testGt!\n";
return 0;
}
Output
0x564375b85c00: main
Hello, from testGt!
Task runned, from testGt!
0x564375b68b60: some_blocking_function_async
some_blocking_function_async sleep
some_blocking_function_async waked up
0x564375b85c00: cb_func
cb_func sleep
cb_func waked up
Run 2nd Task
0x564375b68b60: some_blocking_function_async
some_blocking_function_async sleep
some_blocking_function_async waked up
0x564375b85c00: cb_func
cb_func sleep
cb_func waked up
Loop runned, from testGt!
2 Bye, from testGt!
CMakeLists.txt
cmake_minimum_required(VERSION 3.0.0)
project(testGt VERSION 0.1.0 LANGUAGES C CXX)
include(CTest)
enable_testing()
FIND_PACKAGE(PkgConfig REQUIRED)
PKG_CHECK_MODULES(GTK REQUIRED gtk+-3.0)
INCLUDE_DIRECTORIES(${GTK_INCLUDE_DIRS})
add_executable(testGt main.cpp)
TARGET_LINK_LIBRARIES(testGt ${CMAKE_THREAD_LIBS_INIT} ${GTK_LIBRARIES})
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
I am not really a glib/gio expert but, I may be able to give some tips.
You have a few options there, you could call g_task_get_completed
on the tasks you spawn to check if they are done but, as you are spawning them from the callback, you'd need to accumulate the tasks spawned in a mutex protected vector for example.
You only need to call g_main_loop_run
if you want your app to run indefinitely until quit is called otherwise, if you have your own loop already, you need to "drive" glib's loop with g_main_context_iteration
.
Yes, g_main_loop_run
will always block
You can create the tasks in a loop and glib will take care of driving them.
Here is a changed version of your code that drives glib's loop inside the while statement
--- old.cpp 2023-07-20 09:34:13
+++ a.cpp 2023-07-20 09:34:55
@@ -4,7 +4,6 @@
typedef GObject myGObj;
static volatile int flag = 0;
-GMainLoop *loop;
static int again = 0;
static void
\ No newline at end of file
@@ -32,10 +31,7 @@
usleep(1000000);
std::cout << "cb_func waked up\n";
flag++;
- GTask *task = (GTask *)user_data;
- g_main_loop_quit(loop); // task don't call a calback if main loop not quited and runned again ???
-
if (flag == 1 && again == 0)
{
again++;
\ No newline at end of file
@@ -43,8 +39,6 @@
GTask *task2 = g_task_new(NULL, NULL, cb_func, NULL);
g_task_run_in_thread(task2, some_blocking_function_async);
g_object_unref(task2);
-
- g_main_loop_run(loop); // task don't call a calback if main loop not quited and runned again ???
}
}
\ No newline at end of file
@@ -53,25 +47,24 @@
printf("%p: %s\n", g_thread_self(), __func__);
std::cout << "Hello, from testGt!\n";
- loop = g_main_loop_new(NULL, FALSE);
+ auto loop = g_main_loop_new(NULL, FALSE);
+ auto context = g_main_loop_get_context(loop);
GTask *task = g_task_new(NULL, NULL, cb_func, NULL);
g_task_run_in_thread(task, some_blocking_function_async);
std::cout << "Task runned, from testGt!\n";
g_object_unref(task);
- g_main_loop_run(loop);
std::cout << "Loop runned, from testGt!\n";
while (flag != 2)
{
std::cout << "Sleep, from testGt!\n";
+ g_main_context_iteration(context, FALSE);
usleep(1000000);
std::cout << "Wakeup, from testGt!\n";
}
- g_main_loop_quit(loop);
-
std::cout << flag << " Bye, from testGt!\n";
return 0;
\ No newline at end of file
On this code, you will see that it drives the tasks every time g_main_context_iteration
is called, and when you achieve the desired number of ran tasks, it will just move out and finish the program.