From 8d8ae122ee3fe62917bd80eac70e1f95ed8ecd74 Mon Sep 17 00:00:00 2001
From: Thomas Goyne <plorkyeran@aegisub.org>
Date: Thu, 17 Jul 2014 11:23:05 -0700
Subject: [PATCH] Use wx events for invoking on the main thread on OS X

Dispatching to the main queue with GCD does not work when there is a
modal dialog active.
---
 libaegisub/osx/dispatch.mm | 99 ++++++++++++++++++++++++--------------
 1 file changed, 64 insertions(+), 35 deletions(-)

diff --git a/libaegisub/osx/dispatch.mm b/libaegisub/osx/dispatch.mm
index 6268169fe..8dc2f6680 100644
--- a/libaegisub/osx/dispatch.mm
+++ b/libaegisub/osx/dispatch.mm
@@ -17,19 +17,71 @@
 #include "libaegisub/dispatch.h"
 
 #include <dispatch/dispatch.h>
+#include <mutex>
 
 namespace {
-std::function<void (agi::dispatch::Thunk)> invoke_main;
+using namespace agi::dispatch;
+std::function<void (Thunk)> invoke_main;
 
-struct GCDQueue : agi::dispatch::Queue {
+struct OSXQueue : Queue {
+    virtual void DoSync(Thunk thunk)=0;
+};
+
+struct MainQueue final : OSXQueue {
+    void DoInvoke(Thunk thunk) override { invoke_main(thunk); }
+
+    void DoSync(Thunk thunk) {
+        std::mutex m;
+        std::condition_variable cv;
+        std::unique_lock<std::mutex> l(m);
+        std::exception_ptr e;
+        bool done = false;
+        invoke_main([&]{
+            std::unique_lock<std::mutex> l(m);
+            try {
+                thunk();
+            }
+            catch (...) {
+                e = std::current_exception();
+            }
+            done = true;
+            cv.notify_all();
+        });
+        cv.wait(l, [&]{ return done; });
+        if (e) std::rethrow_exception(e);
+    }
+};
+
+struct GCDQueue final : OSXQueue {
     dispatch_queue_t queue;
     GCDQueue(dispatch_queue_t queue) : queue(queue) { }
-    void DoInvoke(agi::dispatch::Thunk) override final { }
-};
+    ~GCDQueue() { dispatch_release(queue); }
+
+    void DoInvoke(Thunk thunk) override {
+        dispatch_async(queue, ^{
+            try {
+                thunk();
+            }
+            catch (...) {
+                auto e = std::current_exception();
+                invoke_main([=] { std::rethrow_exception(e); });
+            }
+        });
+    }
 
-struct OwningQueue final : GCDQueue {
-    using GCDQueue::GCDQueue;
-    ~OwningQueue() { dispatch_release(queue); }
+    void DoSync(Thunk thunk) override {
+        std::exception_ptr e;
+        std::exception_ptr *e_ptr = &e;
+        dispatch_sync(queue, ^{
+            try {
+                thunk();
+            }
+            catch (...) {
+                *e_ptr = std::current_exception();
+            }
+        });
+        if (e) std::rethrow_exception(e);
+    }
 };
 }
 
@@ -38,34 +90,11 @@ void Init(std::function<void (Thunk)> invoke_main) {
     ::invoke_main = std::move(invoke_main);
 }
 
-void Queue::Async(Thunk thunk) {
-    dispatch_async(static_cast<GCDQueue *>(this)->queue, ^{
-        try {
-            thunk();
-        }
-        catch (...) {
-            auto e = std::current_exception();
-            invoke_main([=] { std::rethrow_exception(e); });
-        }
-    });
-}
-
-void Queue::Sync(Thunk thunk) {
-    std::exception_ptr e;
-    std::exception_ptr *e_ptr = &e;
-    dispatch_sync(static_cast<GCDQueue *>(this)->queue, ^{
-        try {
-            thunk();
-        }
-        catch (...) {
-            *e_ptr = std::current_exception();
-        }
-    });
-    if (e) std::rethrow_exception(e);
-}
+void Queue::Async(Thunk thunk) { DoInvoke(std::move(thunk)); }
+void Queue::Sync(Thunk thunk) { static_cast<OSXQueue *>(this)->DoSync(std::move(thunk)); }
 
 Queue& Main() {
-    static GCDQueue q(dispatch_get_main_queue());
+    static MainQueue q;
     return q;
 }
 
@@ -75,7 +104,7 @@ Queue& Background() {
 }
 
 std::unique_ptr<Queue> Create() {
-    return std::unique_ptr<Queue>(new OwningQueue(dispatch_queue_create("Aegisub worker queue",
-                                                                        DISPATCH_QUEUE_SERIAL)));
+    return std::unique_ptr<Queue>(new GCDQueue(dispatch_queue_create("Aegisub worker queue",
+                                                                     DISPATCH_QUEUE_SERIAL)));
 }
 } }
-- 
GitLab