MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Dispatch.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <future>
4
5#ifdef MAYAFLUX_PLATFORM_MACOS
6#include <CoreFoundation/CoreFoundation.h>
7#include <dispatch/dispatch.h>
8#include <pthread.h>
9#endif
10
11#ifdef MAYAFLUX_PLATFORM_WINDOWS
12#include <windows.h>
13#ifdef ERROR
14#undef ERROR
15#endif // ERROR
16
17#define MAYAFLUX_WM_DISPATCH (WM_USER + 0x0001)
18#endif // MAYAFLUX_PLATFORM_WINDOWS
19
20namespace MayaFlux::Parallel {
21
22#ifdef MAYAFLUX_PLATFORM_MACOS
23/**
24 * @brief Execute a function on the main dispatch queue (macOS only)
25 * @tparam Func Callable type
26 * @tparam Args Argument types
27 * @param func Function to execute
28 * @param args Arguments to forward to the function
29 *
30 * Schedules work on the main queue asynchronously. Returns immediately.
31 * Use this for AppKit operations that must execute on the main thread.
32 */
33template <typename Func, typename... Args>
34void dispatch_main_async(Func&& func, Args&&... args)
35{
36 dispatch_async(dispatch_get_main_queue(), ^{
37 std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
38 });
39}
40
41/**
42 * @brief Execute a function on the main dispatch queue and wait (macOS only)
43 * @tparam Func Callable type that returns a result
44 * @tparam Args Argument types
45 * @param func Function to execute
46 * @param args Arguments to forward to the function
47 * @return The result returned by func
48 *
49 * Runs directly when called on the main thread, otherwise schedules work on
50 * the main queue synchronously and blocks until complete.
51 * WARNING: Can deadlock during Cocoa modal loops.
52 */
53template <typename Func, typename... Args>
54auto dispatch_main_sync(Func&& func, Args&&... args) -> decltype(auto)
55{
56 using ResultType = std::invoke_result_t<Func, Args...>;
57
58 if constexpr (std::is_void_v<ResultType>) {
59 if (pthread_main_np() != 0) {
60 std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
61 return;
62 }
63 dispatch_sync(dispatch_get_main_queue(), ^{
64 std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
65 });
66 } else {
67 if (pthread_main_np() != 0) {
68 return static_cast<ResultType>(std::invoke(std::forward<Func>(func), std::forward<Args>(args)...));
69 }
70 __block ResultType result;
71 dispatch_sync(dispatch_get_main_queue(), ^{
72 result = std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
73 });
74 return result;
75 }
76}
77
78/**
79 * @brief Execute a function on the main queue with timeout (macOS only)
80 * @tparam Func Callable type
81 * @tparam Args Argument types
82 * @param timeout_ms Timeout in milliseconds
83 * @param func Function to execute
84 * @param args Arguments to forward to the function
85 * @return true if completed before timeout, false if timed out
86 *
87 * Schedules work asynchronously and waits with timeout.
88 * Returns false if the operation doesn't complete in time.
89 */
90template <typename Func, typename... Args>
91bool dispatch_main_async_with_timeout(std::chrono::milliseconds timeout_ms, Func&& func, Args&&... args)
92{
93 auto completed = std::make_shared<std::atomic<bool>>(false); // Shared pointer for capture
94
95 dispatch_async(dispatch_get_main_queue(), ^{
96 std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
97 completed->store(true, std::memory_order_release);
98 });
99
100 auto start = std::chrono::steady_clock::now();
101 while (!completed->load(std::memory_order_acquire)) {
102 if (std::chrono::steady_clock::now() - start > timeout_ms) {
103 return false;
104 }
105 std::this_thread::sleep_for(std::chrono::milliseconds(1));
106 }
107
108 return true;
109}
110
111/**
112 * @brief Wait for a future, keeping the main run loop alive on the main thread
113 * @tparam T Future value type
114 * @param future Future to wait on
115 * @return The value held by the future
116 *
117 * Work queued to the main queue, such as window creation, cannot run while
118 * the main thread is blocked in future.get(). On the main thread this pumps
119 * the default run loop until the future is ready, on any other thread it
120 * simply waits. Other platforms just wait.
121 */
122template <typename T>
123T await_future(std::future<T> future)
124{
125 if (pthread_main_np() != 0) {
126 while (future.wait_for(std::chrono::milliseconds(0)) != std::future_status::ready) {
127 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.01, false);
128 }
129 }
130 return future.get();
131}
132#else
133// On linux platforms, these just execute directly
134template <typename Func, typename... Args>
135void dispatch_main_async(Func&& func, Args&&... args)
136{
137 std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
138}
139
140template <typename Func, typename... Args>
141auto dispatch_main_sync(Func&& func, Args&&... args) -> decltype(auto)
142{
143 return std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
144}
145
146template <typename Func, typename... Args>
147bool dispatch_main_async_with_timeout(std::chrono::milliseconds /*timeout_ms*/, Func&& func, Args&&... args)
148{
149 std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
150 return true;
151}
152
153template <typename T>
154T await_future(std::future<T> future)
155{
156 return future.get();
157}
158#endif
159
160} // namespace MayaFlux::Parallel
T await_future(std::future< T > future)
Definition Dispatch.hpp:154
auto dispatch_main_sync(Func &&func, Args &&... args) -> decltype(auto)
Definition Dispatch.hpp:141
void dispatch_main_async(Func &&func, Args &&... args)
Definition Dispatch.hpp:135
bool dispatch_main_async_with_timeout(std::chrono::milliseconds, Func &&func, Args &&... args)
Definition Dispatch.hpp:147