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
SW Task Framework

Build Status Documentation License: MIT C++20

Advanced C++ Event Loop Framework for High-Performance Asynchronous Task Management with Coroutines

SW Task Framework is a modern event loop framework designed to efficiently and safely manage asynchronous tasks. The framework provides a flexible architecture for handling messages, timers, promises, and coroutines in multi-threaded C++ applications.

Table of Contents

  • Features
  • Architecture
  • API Overview
  • Examples
  • Building
  • Testing
  • Performance
  • Contributing
  • License

Features

Core Components

Component Description Key Features
SLLooper Main event loop coordinator Thread-safe posting, coroutine integration
EventQueue High-performance Event queue Priority handling, delayed execution, templates
TimerManager Precise timer management One-shot & periodic timers, microsecond precision
Promise System Modern async pattern (JS A+) Chainable continuations, error handling
Coroutines System C++20 coroutines integration Natural async/await syntax, suspend/resume
Handler Pattern Message-based communication Android-style messaging, flexible dispatching
CPU Task Executor CPU-bound task isolation Timeout protection

Advanced Features

  • C++20 Coroutines Support: Native co_await integration with awaitWork, awaitPost, awaitDelay
  • Dual Async Paradigm: Both Promise chains and coroutines for different use cases
  • Asynchronous Task Management: Post functions with futures, delayed execution, promise-based workflows
  • High-Precision Timers: Microsecond accuracy, chrono duration support, RAII timer management
  • Thread-Safe Operations: Lock-free queues, atomic operations, safe cross-thread communication
  • CPU-Bound Task Isolation: Separate CPU-bound task thread for intensive operations
  • Android-Style Messaging: Familiar Handler/Message pattern for structured communication
  • Modern C++ Design: C++20 features, RAII principles, smart pointer management

Architecture

System Overview

System Overview

Thread Model with Coroutines

Main Thread Event Loop Thread CPU-bound Task thread
│ │ │
│ post(normal_task) │ │
├─────────────────────────►│ │
│ │ process tasks │
│ co_await awaitWork() │ delegate │
├─────────────────────────►│ ─────────────────────────►│
│ (coroutine suspended) │ │ execute
│ │◄──────────────────────────┤
│◄─────────────────────────┤ resume coroutine │
│ (coroutine resumed) │ │

API Overview

1. Function Posting & Promise

Category API Name Description
Function Posting post(func, args...) Post a function or lambda to the event loop for immediate execution. Returns a std::future for result retrieval.
postDelayed(delayMs, func, args...) Post a function or lambda to the event loop to be executed after a specified delay in milliseconds.
postWithTimeout(func, timeout_ms) Post a function to be executed after a timeout. Returns a Timer object for cancellation.
Promise createPromise<T>() Create a manual promise object of type T. Allows explicit fulfillment or rejection from any thread.
postWork(func) Run a CPU-bound function in a dedicated worker thread. Returns a Promise for result and chaining.
postWork(func, timeout) Run a CPU-bound function with a timeout. If the function does not complete in time, the promise is rejected.
Promise::set_value(value) Fulfill a promise with a value, triggering any chained continuations.
Promise::then(callback) Chain a callback to be executed when the promise is fulfilled. Supports type transformation.
Promise::catchError(callback) Chain an error handler to be executed if the promise is rejected with an exception.

2. Coroutines (co_await)

Category API Name Description
Coroutine Creation Task<T> function_name() Create a coroutine function that returns Task<T>. Use co_return to return values.
task.start() Start execution of a coroutine task. Must be called after task. creation.
task.done() Check if coroutine has completed execution.
co_await Operations co_await awaitWork(func) Execute a CPU-bound function on background thread. Suspends coroutine until completion.
co_await awaitPost(func) Execute a function on main event loop thread. Suspends coroutine until completion.
co_await awaitDelay(milliseconds) Suspend coroutine for specified time duration. Non-blocking delay operation.
Error Handling try/catch with co_await Use standard C++ exception handling with coroutines. Exceptions propagate naturally.

3. Timer & Timer Control

Category API Name Description
Timer addTimer(cb, delay) Create a one-shot timer that executes a callback after a specified delay (ms).
addPeriodicTimer(cb, interval) Create a periodic timer that executes a callback repeatedly at the given interval (ms).
Timer Control Timer::cancel() Cancel the timer, preventing further callbacks.
Timer::isActive() Check if the timer is currently active and not cancelled.
Timer::restart(delay) Restart a one-shot timer with a new delay.

4. Message & Handler

Category API Name Description
Message Creation obtainMessage() Create an empty message object.
obtainMessage(what, ...) Create a message with a type code and optional arguments.
Message Sending sendMessage(msg) Send a message for immediate processing by the handler.
sendMessageDelayed(msg, delay) Send a message to the handler after a specified delay (ms).
Message Management hasMessages(what) Check if there are pending messages of a given type.
removeMessages(what) Remove all messages of a given type from the queue.
Processing dispatchMessage(msg) Dispatch a message to the handler for processing.
handleMessage(msg) Override this method in your handler to process messages.

Examples

1. Function Posting & Promise

Chained asynchronous workflow with error handling:

#include "SLLooper.h"
#include <iostream>
#include <stdexcept>
#include <thread>
int computeHeavy(int x) {
if (x < 0) throw std::runtime_error("Negative input!");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return x * x;
}
int main() {
auto looper = std::make_shared<SLLooper>();
looper->postWork([] { return 10; })
.then(looper, [looper](int v) {
std::cout << "First result: " << v << std::endl;
return looper->postWork([v] { return computeHeavy(v); });
})
.then(looper, [](int squared) {
std::cout << "Squared: " << squared << std::endl;
return squared / 2;
})
.catchError(looper, [](std::exception_ptr ex) {
try { if (ex) std::rethrow_exception(ex); }
catch (const std::exception& e) {
std::cout << "Error: " << e.what() << std::endl;
}
return -1;
})
.then(looper, [](int final) {
std::cout << "Final result: " << final << std::endl;
return 0;
});
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return 0;
}
Main event loop coordinator for asynchronous task management and timer operations.

2. Coroutines (co_await)

Sequential async operations with natural syntax:

#include "SLLooper.h"
#include "Task.h"
#include <iostream>
#include <stdexcept>
#include <thread>
using namespace swt;
int fetchUserData(int userId) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return userId * 10; // Simulate fetched data
}
void processUserData(int data) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
std::cout << "Processed data: " << data << std::endl;
}
Task<void> userWorkflow(std::shared_ptr<SLLooper> looper, int userId) {
try {
std::cout << "Starting workflow for user " << userId << std::endl;
// Fetch data on background thread
int userData = co_await looper->awaitWork([userId]() {
return fetchUserData(userId);
});
std::cout << "User data fetched: " << userData << std::endl;
// Wait a bit
co_await looper->awaitDelay(200);
// Process on main thread
co_await looper->awaitPost([userData]() {
processUserData(userData);
});
std::cout << "Workflow completed!" << std::endl;
} catch (const std::exception& e) {
std::cout << "Workflow error: " << e.what() << std::endl;
}
}
int main() {
auto looper = std::make_shared<SLLooper>();
auto task = userWorkflow(looper, 123);
task.start();
// Wait for completion
while (!task.done()) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
return 0;
}
Coroutine-based Task implementation for asynchronous execution.
RAII wrapper for coroutines with proper continuation chaining.
Definition Task.h:303
Software Timer namespace containing all timer-related classes.
Definition Awaitable.h:21

Error handling with coroutines:

#include "SLLooper.h"
#include "Task.h"
#include <iostream>
#include <stdexcept>
using namespace swt;
Task<void> errorHandlingExample(std::shared_ptr<SLLooper> looper) {
try {
int result = co_await looper->awaitWork([]() -> int {
throw std::runtime_error("Something went wrong!");
return 42;
});
std::cout << "This won't be reached: " << result << std::endl;
} catch (const std::exception& e) {
std::cout << "Caught error: " << e.what() << std::endl;
// Recovery operation
co_await looper->awaitDelay(100);
std::cout << "Recovery completed" << std::endl;
}
}
int main() {
auto looper = std::make_shared<SLLooper>();
auto task = errorHandlingExample(looper);
task.start();
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}

3. Timer & Timer Control

Periodic timer with dynamic restart and cancellation:

#include "SLLooper.h"
#include <iostream>
#include <atomic>
#include <thread>
int main() {
auto looper = std::make_shared<SLLooper>();
std::atomic<int> count{0};
auto timer = looper->addPeriodicTimer([&]() {
std::cout << "Tick: " << ++count << std::endl;
if (count == 3) {
std::cout << "Restarting timer with new interval..." << std::endl;
timer.restart(200);
}
if (count == 6) {
std::cout << "Cancelling timer." << std::endl;
timer.cancel();
}
}, 100);
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
return 0;
}

4. Message & Handler

Custom handler with message dispatch and delayed messaging:

#include "SLLooper.h"
#include "Handler.h"
#include <iostream>
#include <memory>
#include <thread>
class MyHandler : public Handler {
public:
MyHandler(std::shared_ptr<SLLooper> looper) : Handler(looper) {}
void handleMessage(const std::shared_ptr<Message>& msg) override {
if (msg->what == 1) {
std::cout << "Process data: " << msg->arg1 << std::endl;
} else if (msg->what == 2) {
std::cout << "Delayed event: " << msg->arg2 << std::endl;
}
}
};
int main() {
auto looper = std::make_shared<SLLooper>();
auto handler = std::make_shared<MyHandler>(looper);
auto msg1 = handler->obtainMessage(1, 42, 0);
handler->sendMessage(msg1);
auto msg2 = handler->obtainMessage(2, 0, 99);
handler->sendMessageDelayed(msg2, 200);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return 0;
}
Android-style message handler for event-driven programming.
Definition Handler.h:46

5. Combined Example: Coroutines with Timer and Promise

Workflow using both coroutines and promises:

#include "SLLooper.h"
#include "Task.h"
#include <iostream>
#include <thread>
using namespace swt;
int fetchData() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return 123;
}
Task<void> timerTriggeredWorkflow(std::shared_ptr<SLLooper> looper) {
std::cout << "Waiting for timer..." << std::endl;
// Wait for timer delay
co_await looper->awaitDelay(200);
std::cout << "Timer triggered, starting data fetch..." << std::endl;
// Fetch data using Promise system
auto promise = looper->postWork(fetchData);
// Convert promise to coroutine-compatible result
int result = co_await looper->awaitWork([promise]() mutable {
return promise.get(); // Wait for promise completion
});
std::cout << "Data received: " << result << std::endl;
// Final processing on main thread
co_await looper->awaitPost([result]() {
std::cout << "Final processing: " << result * 2 << std::endl;
});
}
int main() {
auto looper = std::make_shared<SLLooper>();
auto task = timerTriggeredWorkflow(looper);
task.start();
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}

Comparison: Promises vs Coroutines

Aspect Promises Coroutines
Syntax Callback chains with .then() Sequential with co_await
Error Handling .catchError() callbacks Standard try/catch
Readability Can become nested Linear, like sync code
Debugging Harder with callback stacks Natural debugging flow
Performance Lower overhead Slight coroutine overhead
Use Case Simple async chains Complex async workflows

Building

Requirements

  • C++20 compatible compiler (GCC 10+, Clang 10+, MSVC 2019+)
  • CMake 3.12+
  • Coroutines support: -fcoroutines (GCC) or -fcoroutines-ts (Clang)
  • Optional: Doxygen for documentation

Build Instructions

git clone https://github.com/taikt/sw_task.git
cd sw_task
mkdir build && cd build
cmake -DCMAKE_CXX_STANDARD=20 ..
make -j$(nproc)
make test
sudo make install

For coroutines support, ensure compiler flags:

target_compile_options(your_target PRIVATE -fcoroutines -std=c++20)

Testing

cd build
make test
ctest --verbose

Coroutine-specific tests:

./examples/co_await_example
./examples/fetch_coawait
./examples/coawait_simple

Performance

Operation Throughput Latency Memory
Task Posting 1M ops/sec < 1μs 64 bytes/task
Timer Creation 100K/sec < 10μs 128 bytes/timer
Promise Chain 500K/sec < 5μs 256 bytes/chain
Coroutine Task 400K/sec < 8μs 512 bytes/task
Message Send 2M ops/sec < 0.5μs 32 bytes/msg

Contributing

  • C++20 standard compliance
  • Google C++ Style Guide
  • Comprehensive unit tests for new features
  • Doxygen documentation for public APIs
  • RAII principles and smart pointer usage
  • Coroutine safety and best practices

License

MIT License

Copyright (c) 2025 Tran Anh Tai

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Author: Tran Anh Tai
Email: tai2..nosp@m.tran.nosp@m.@lge..nosp@m.com (taitr.nosp@m.anan.nosp@m.hvn@g.nosp@m.mail.nosp@m..com)
Repository: https://github.com/taikt/sw_task
Documentation: https://taikt.github.io/sw_task/