Uvent¶
High-performance asynchronous I/O library for C++23.
Features¶
- C++23: modern standard with coroutines and atomic primitives.
- Event loop: non-blocking, scalable, based on
epoll/io_uring/kqueue/iocp. - Linux io_uring support (optional): full async accept/read/write backend.
- Windows IOCP support: automatically selected when building on Windows.
- Sockets: TCP/UDP (client and server).
- Timers: custom
TimerWheelwith minimal overhead. - Queues & synchronization: lock-free data structures, QSBR, and reference counting.
- Custom frames: ability to override coroutine promise frames (
AwaitableFrameBase), enabling custom scheduling and integration. - SO_REUSEPORT support: enables truly parallel TCP servers by binding separate listeners in each worker thread.
Build & Installation¶
git clone https://github.com/Usub-development/uvent.git
cd uvent
mkdir build && cd build
cmake ..
make -j
Quick example¶
#include "uvent/Uvent.h"
using namespace usub::uvent;
task::Awaitable<void> clientCoro(net::TCPClientSocket socket)
{
static constexpr size_t max_read_size = 64 * 1024;
utils::DynamicBuffer buffer;
buffer.reserve(max_read_size);
static const std::string_view httpResponse =
"HTTP/1.1 200 OK\r\n"
"Content-Type: application/json\r\n"
"Content-Length: 20\r\n"
"\r\n"
"{\"status\":\"success\"}";
socket.set_timeout_ms(5000);
while (true)
{
buffer.clear();
ssize_t rdsz = co_await socket.async_read(buffer, max_read_size);
socket.update_timeout(5000);
if (rdsz <= 0)
{
socket.shutdown();
break;
}
auto buf = std::make_unique<uint8_t[]>(1024);
size_t wrsz = co_await socket.async_write(
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(httpResponse.data())),
httpResponse.size()
);
if (wrsz <= 0)
{
break;
}
socket.update_timeout(5000);
}
co_return;
}
task::Awaitable<void> test_coro()
{
using namespace std::chrono_literals;
std::cout << "test_coro()" << std::endl;
co_await system::this_coroutine::sleep_for(2000ms);
std::cout << "test_coro() 2" << std::endl;
co_return;
}
task::Awaitable<void> listeningCoro()
{
auto acceptor = new net::TCPServerSocket{"0.0.0.0", 45900};
for (;;)
{
auto soc = co_await acceptor->async_accept();
co_await test_coro();
if (soc) system::co_spawn(clientCoro(std::move(soc.value())));
}
}
task::Awaitable<void> sendingCoro()
{
auto socket = net::TCPClientSocket{};
auto res = co_await socket.async_connect("example.com", "80");
if (res.has_value()) co_return;
uint8_t buffer[] =
"GET / HTTP/1.1\r\n"
"Host: example.com\r\n"
"User-Agent: test-client\r\n"
"Accept: */*\r\n"
"Connection: close\r\n\r\n";
size_t size = sizeof(buffer) - 1;
for (int i = 0; i < 2; i++)
{
auto result = co_await socket.async_send(buffer, size);
if (result.has_value())
{
std::cout << result.value() << std::endl;
}
else
{
std::cout << toString(result.error()) << std::endl;
}
}
co_return;
}
int main()
{
settings::timeout_duration_ms = 5000;
if constexpr (!system::is_reuseaddr_enabled)
{
system::co_spawn(std::move(listeningCoro()));
}
usub::Uvent uvent(4);
if constexpr (system::is_reuseaddr_enabled)
{
uvent.for_each_thread([&](int threadIndex, thread::ThreadLocalStorage* tls)
{
system::co_spawn_static(listeningCoro(), threadIndex);
});
}
uvent.run();
return 0;
}
Notes¶
-
When compiled with
SO_REUSEPORTsupport (UVENT_ENABLE_REUSEADDRdefined), each thread can bind its own listener socket viafor_each_thread, providing optimal load balancing across cores. This pattern is preferred for high-connection-rate servers. -
Without
SO_REUSEPORT, a single global listener is used — still asynchronous and non-blocking, but accepts connections sequentially. -
Both modes are fully coroutine-driven and compatible with the same I/O APIs.