SW Task Event Loop Framework v1.0.0
High-performance C++ asynchronous event loop framework with timer management and promise-based programming
Loading...
Searching...
No Matches
EventQueue.tpp
Go to the documentation of this file.
1/**
2 * @file EventQueue.tpp
3 * @brief Template implementation for EventQueue - type-safe function enqueuing with futures
4 * @author Tran Anh Tai
5 * @date 9/2025
6 * @version 1.0.0
7 */
8
9 #pragma once
10 #include "EventQueue.h"
11
12 namespace swt {
13 /**
14 * @brief Enqueue function for immediate asynchronous execution
15 * @tparam F Function type (auto-deduced)
16 * @tparam Args Variadic argument types (auto-deduced)
17 * @param func Callable object to execute asynchronously
18 * @param args Arguments to forward to the function
19 * @return std::future<ReturnType> Future containing the function result
20 *
21 * Enqueues a function for immediate execution in the event loop thread.
22 * Uses perfect forwarding to preserve argument types and std::packaged_task
23 * to provide future-based result retrieval. The function is wrapped in a
24 * void task for uniform queue handling while preserving the original
25 * return type through the future mechanism.
26 *
27 * Key implementation details:
28 * - **Perfect forwarding**: Preserves value categories and reference types
29 * - **Type erasure**: Wraps typed task in void task for uniform storage
30 * - **Thread safety**: Protected by mutex during queue insertion
31 * - **Immediate execution**: Inserted at front of queue (whenUs = 0)
32 * - **Always notify**: Signals condition variable regardless of queue state
33 *
34 * @code{.cpp}
35 * // Enqueue lambda with capture
36 * auto future1 = queue.enqueueFunction([x = 42](int y) { return x + y; }, 8);
37 *
38 * // Enqueue free function
39 * auto future2 = queue.enqueueFunction(std::sin, 3.14159);
40 *
41 * // Get results
42 * int result1 = future1.get(); // 50
43 * double result2 = future2.get(); // ~0.0
44 * @endcode
45 *
46 * @note Thread-safe operation with automatic notification
47 * @note Function executes in event loop thread context
48 * @warning Future must be retrieved before function object destruction
49 *
50 * @see \ref swt::EventQueue "EventQueue", \ref swt::Promise "Promise"
51 */
52 template<typename F, typename... Args>
53 auto EventQueue::enqueueFunction(F&& func, Args&&... args) -> std::future<decltype(func(args...))> {
54 using ReturnType = decltype(func(args...));
55
56 // Bind arguments to function using perfect forwarding
57 auto boundTask = std::bind(std::forward<F>(func), std::forward<Args>(args)...);
58
59 // Create packaged_task for typed result handling
60 auto packagedTask = std::packaged_task<ReturnType()>(boundTask);
61 auto future = packagedTask.get_future();
62
63 // Wrap in void task for type-erased queue storage
64 auto voidTask = std::packaged_task<void()>([task = std::move(packagedTask)]() mutable {
65 task();
66 });
67
68 {
69 std::lock_guard<std::mutex> lock(iMutex);
70 int64_t whenUs = 0; // IMMEDIATE EXECUTION - front of queue
71
72 // Insert at beginning for immediate execution (highest priority)
73 mQueue.emplace_front(std::move(voidTask), whenUs);
74 }
75
76 // ALWAYS notify condition variable to wake up event loop thread
77 mQueueChanged.notify_one();
78
79 return future;
80 }
81
82 /**
83 * @brief Enqueue function for delayed asynchronous execution
84 * @tparam F Function type (auto-deduced)
85 * @tparam Args Variadic argument types (auto-deduced)
86 * @param delayMs Delay in milliseconds before execution
87 * @param func Callable object to execute asynchronously
88 * @param args Arguments to forward to the function
89 * @return std::future<ReturnType> Future containing the function result
90 *
91 * Enqueues a function for delayed execution in the event loop thread.
92 * The function will be executed after the specified delay, maintaining
93 * proper ordering with other delayed tasks. Uses binary search insertion
94 * to maintain chronological order in the queue without full sorting.
95 *
96 * Key implementation details:
97 * - **Timed execution**: Calculates absolute execution time from current uptime
98 * - **Ordered insertion**: Uses std::upper_bound for efficient sorted insertion
99 * - **Binary search**: O(log n) insertion instead of O(n log n) full sort
100 * - **Conditional notification**: Only notifies if event loop is started
101 * - **Microsecond precision**: Internal timing uses microseconds for accuracy
102 *
103 * @code{.cpp}
104 * // Execute function after 1 second delay
105 * auto future = queue.enqueueFunctionDelayed(1000, []() {
106 * return "Hello after delay!";
107 * });
108 *
109 * // Execute with arguments after 500ms delay
110 * auto future2 = queue.enqueueFunctionDelayed(500,
111 * [](int x, int y) { return x * y; }, 6, 7);
112 *
113 * // Get results (will block until execution time)
114 * std::string result1 = future.get(); // "Hello after delay!"
115 * int result2 = future2.get(); // 42
116 * @endcode
117 *
118 * @note Thread-safe operation with efficient ordered insertion
119 * @note Delay is measured from when function is enqueued
120 * @note Only notifies condition variable if event loop is started
121 *
122 * @see \ref swt::EventQueue "EventQueue", \ref swt::Promise "Promise"
123 */
124 template<typename F, typename... Args>
125 auto EventQueue::enqueueFunctionDelayed(int64_t delayMs, F&& func, Args&&... args) -> std::future<decltype(func(args...))> {
126 using ReturnType = decltype(func(args...));
127
128 // Bind arguments to function using perfect forwarding
129 auto boundTask = std::bind(std::forward<F>(func), std::forward<Args>(args)...);
130
131 // Create packaged_task for typed result handling
132 auto packagedTask = std::packaged_task<ReturnType()>(boundTask);
133 auto future = packagedTask.get_future();
134
135 // Wrap in void task for type-erased queue storage
136 auto voidTask = std::packaged_task<void()>([task = std::move(packagedTask)]() mutable {
137 task();
138 });
139
140 {
141 std::lock_guard<std::mutex> lock(iMutex);
142
143 // Calculate absolute execution time in microseconds
144 int64_t whenUs = uptimeMicros() + (delayMs * 1000);
145
146 // Insert at correct chronological position using binary search
147 auto insertPos = std::upper_bound(mQueue.begin(), mQueue.end(), whenUs,
148 [](int64_t time, const QueueItem& item) {
149 return time < item.whenUs;
150 });
151
152 // Insert at calculated position maintaining sorted order
153 mQueue.emplace(insertPos, std::move(voidTask), whenUs);
154 }
155
156 // Only notify if event loop is started to avoid spurious wakeups
157 if (mStarted) {
158 mQueueChanged.notify_one();
159 }
160
161 return future;
162 }
163
164 /**
165 * @brief Create and enqueue promise for manual resolution
166 * @tparam T Value type for the promise
167 * @return swt::Promise<T> New promise object for manual control
168 *
169 * Creates a new promise that can be resolved manually from any thread.
170 * The promise callbacks will execute in the event loop thread when
171 * the promise is resolved or rejected.
172 *
173 * @see \ref swt::Promise "Promise"
174 */
175 template<typename T>
176 Promise<T> EventQueue::enqueuePromise() {
177 return Promise<T>();
178 }
179
180 } // namespace swt