1

gtk4 removed the blocking behavior of GtkDialogs: int res = gtk_dialog_run(GTK_DIALOG(dialog));

The proposed alternative is, to make the Dialog modal and connect the "response" signal. But this has a big draw back: Some old code bases have a linear and deterministic CFG. Changing only the way all dialogs are called and handled leads to the fact, that code can be wrong after leaving the scope, if the processing of the dialog was expected in the outer scopes.

To circumvent this, I used std::promise and std::future but this will block the gtk-event loop :/.

My proposed solution would be, to "join" the gtk-event-loop until the result is ready. This will have the advantages of both aproaches: "Do not stop the world" / handle event-loop and keep the linear CFG. But it has the disadvantage, that it can lead to a stack overflow or starvation when calling the join recursively.

Pseudocode of proposed/searched solution:

auto future_result = generate_future_for_dialog_result(dialog);
while(!future_result.isDone()){
    g_join();
}

My first not working approach:

    auto wait_for_gtk_dialog_result =
            [](GtkDialog* dialog) {
                std::promise<int> promise;
                auto future = promise.get_future();
                g_signal_connect(dialog, "result",
                                 GCallback(+[](GtkDialog* /*self*/, int result, std::promise<int>* promise) {
                                     promise->set_value(result);
                                 }),
                                 &promise);
                return future.get();
            };

Is there a function to join the event loop?
If not; is there a good alternative to achieve similar?

Edit: Found a promising approach in the documentation: Look at the g_main_context_iteration.

    auto wait_for_gtk_dialog_result =
            [](GtkDialog* dialog) {
                std::promise<int> promise;
                auto future = promise.get_future();
                g_signal_connect(dialog, "result",
                                 GCallback(+[](GtkDialog* /*self*/, int result, std::promise<int>* promise) {
                                     promise->set_value(result);
                                 }),
                                 &promise);
                do {
                    g_main_context_iteration(g_main_context_default(), false);
                } while (future.wait_for(std::chrono::milliseconds(1)) != std::future_status::ready);
                return future.get();
            };
Fabian Keßler
  • 563
  • 3
  • 12
  • What's a `CFG`? A [mre] would likely help explain your problem better – Alan Birtles Sep 06 '21 at 21:59
  • A `CFG` is a control flow graph. Perhaps a call graph would be a better term. – Fabian Keßler Sep 06 '21 at 22:24
  • 1
    See: https://discourse.gnome.org/t/how-should-i-replace-a-gtk-dialog-run-in-gtk-4/3501 – Craig Estey Sep 06 '21 at 22:27
  • 1
    @AlanBirtles a minimal example of a gui-program is still too huge for stack overflow and a simple question like that. But I could provide other snippets. Also, this is a problem which does not appear in minimal code examples. To simulate a huge codebase, I would have to add some blocking waits. – Fabian Keßler Sep 06 '21 at 22:31
  • I suspect you may mean "yield" (wait and allow another thread to run) rather than "join" (block until the other thread ends)? – Galik Sep 07 '21 at 00:49
  • A general approach to remove "join recursively" is to emulate the join by adding the code to run to some external data structure, like a queue, and have a monitor job that is triggered by your event loop to process the queue. – jxh Sep 07 '21 at 01:01
  • @Galik I mean the ["join"](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinTask.html#join) concept of Fork-And-Join thread pools (FJP), not the one of a thread. The difference between the join of an FJP and thread-yield is, that the current-thread rents his resources to the task-supplier to do some tasks until the result is done. – Fabian Keßler Sep 07 '21 at 13:29

0 Answers0