Skip to content

Commit

Permalink
Async: Add Linux backend using io_uring
Browse files Browse the repository at this point in the history
  • Loading branch information
Pagghiu committed Feb 14, 2024
1 parent 64c6c6c commit d970016
Show file tree
Hide file tree
Showing 7 changed files with 592 additions and 18 deletions.
25 changes: 25 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,30 @@
"command source ${workspaceRoot}/Support/DebugVisualizers/LLDB/.lldbinit"
],
},
{
"name": "SCTest [linux][io_uring] (gdb)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/_Build/Output/Posix-Any-Any-Makefile-Debug/SCTest",
"args": [],
"stopAtEntry": false,
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build SCTest Debug [linux][io_uring]",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
]
}
33 changes: 27 additions & 6 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
"label": "Build SCTest Debug",
"type": "shell",
"osx": {
"command": "make -j`sysctl -n hw.physicalcpu` -C _Build/Projects/Make"
"command": "make -j -C _Build/Projects/Make"
},
"linux": {
"command": "make -j`nproc` -C _Build/Projects/Make"
"command": "make -j -C _Build/Projects/Make"
},
"windows": {
"command": "${env:ProgramFiles}/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe",
Expand Down Expand Up @@ -41,14 +41,35 @@
"isDefault": true
}
},
{
"label": "Build SCTest Debug [linux][io_uring]",
"type": "shell",
"linux": {
"command": "make -j -C _Build/Projects/Make CPPFLAGS=-DSC_ASYNC_USE_IO_URING=1 LDFLAGS=-luring"
},
"problemMatcher": [
"$gcc"
],
"presentation": {
"echo": true,
"reveal": "never",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"group": {
"kind": "build",
}
},
{
"label": "Build SCTest Debug ARM64",
"type": "shell",
"osx": {
"command": "make -j`sysctl -n hw.physicalcpu` -C _Build/Projects/Make"
"command": "make -j -C _Build/Projects/Make"
},
"linux": {
"command": "make -j`nproc` -C _Build/Projects/Make"
"command": "make -j -C _Build/Projects/Make",
},
"windows": {
"command": "${env:ProgramFiles}/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe",
Expand Down Expand Up @@ -79,10 +100,10 @@
"label": "Build SCTest Release",
"type": "shell",
"osx": {
"command": "make -j`sysctl -n hw.physicalcpu` -C _Build/Projects/Make CONFIG=Release",
"command": "make -j -C _Build/Projects/Make CONFIG=Release",
},
"linux": {
"command": "make -j`nproc` -C _Build/Projects/Make CONFIG=Release"
"command": "make -j -C _Build/Projects/Make CONFIG=Release"
},
"windows": {
"command": "${env:ProgramFiles}/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe",
Expand Down
13 changes: 7 additions & 6 deletions Documentation/Libraries/Async.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This is the list of supported async operations:
| [AsyncFileClose](@ref SC::AsyncFileClose) | @copybrief SC::AsyncFileClose |
| [AsyncLoopTimeout](@ref SC::AsyncLoopTimeout) | @copybrief SC::AsyncLoopTimeout |
| [AsyncLoopWakeUp](@ref SC::AsyncLoopWakeUp) | @copybrief SC::AsyncLoopWakeUp |
| [AsyncWindowsPoll](@ref SC::AsyncWindowsPoll) | @copybrief SC::AsyncWindowsPoll |
| [AsyncFilePoll](@ref SC::AsyncFilePoll) | @copybrief SC::AsyncFilePoll |

# Status
🟨 MVP
Expand Down Expand Up @@ -79,14 +79,15 @@ Event loop can be run in different ways to allow integrated it in multiple ways
## AsyncFileClose
@copydoc SC::AsyncFileClose

## AsyncWindowsPoll
@copydoc SC::AsyncWindowsPoll
## AsycFilePoll
@copydoc SC::AsycFilePoll

# Implementation

Library abstracts async operations by exposing a completion based mechanism.
This mechanism currently maps on `kqueue` on macOS and `OVERLAPPED` on Windows.
It currently uses `epoll` on Linux but it should efficiently map over `io_uring`, that will be implemented in the future, as the API is centered around completions and not readiness.

It currently uses by default `epoll` on Linux but an experimental `io_uring` backend can be enabled by defining `SC_ASYNC_USE_IO_URING=1` and linking `liburing` (with `-luring`).

The api works on file and socket descriptors, that can be obtained from the [File](@ref library_file) and [Socket](@ref library_socket) libraries.

Expand All @@ -100,10 +101,10 @@ SC::ArenaMap from the [Containers](@ref library_containers) can be used to preal
🟩 Usable Features:
- Use a thread pool to execute File Operations actually asynchronously.
- Implement option to do blocking poll check without dispatching callbacks (needed for efficient gui event loop integration)
- More comprehensive test suite, testing all cancellations

🟦 Complete Features:
- Implement `io_uring` backend for Linux
- Implement FS operations (open stat read write unlink copyfile mkdir chmod etc.)

💡 Unplanned Features:
- Additional async operation
- Additional async operations
4 changes: 4 additions & 0 deletions Libraries/Async/Async.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
#define SC_ASYNC_USE_EPOLL 0 // uses kqueue
#include "Internal/AsyncPosix.inl"
#elif SC_PLATFORM_LINUX
#if SC_ASYNC_USE_IO_URING
#include "Internal/AsyncLinux.inl" // uses io_uring
#else
#define SC_ASYNC_USE_EPOLL 1 // uses epoll
#include "Internal/AsyncPosix.inl"
#endif
#elif SC_PLATFORM_EMSCRIPTEN
#include "Internal/AsyncEmscripten.inl"
#endif
Expand Down
27 changes: 21 additions & 6 deletions Libraries/Async/Async.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,23 @@
//! Async is a multi-platform / event-driven asynchronous I/O library.
//!
/// It exposes async programming model for common IO operations like reading / writing to / from a file or tcp socket.
///
/// Synchronous I/O operations could block the current thread of execution for an undefined amount of time, making it
/// difficult to scale an application to a large number of concurrent operations, or to coexist with other even loop,
/// like for example a GUI event loop. Such async programming model uses a common pattern, where the call fills an
/// AsyncRequest with the required data. The AsyncRequest is added to an AsyncEventLoop that will queue the request to
/// some low level OS IO queue. The event loop can then monitor all the requests in a single call to
/// SC::AsyncEventLoop::run, SC::AsyncEventLoop::runOnce or SC::AsyncEventLoop::runNoWait. These three different run
/// methods cover different integration use cases of the event loop inside of an applications.

///
/// The kernel Async API used on each operating systems are the following:
/// - `IOCP` on Windows
/// - `kqueue` on macOS
/// - `epoll` on Linux
/// - `io_uring` on Linux (experimental).
///
/// Note: To enable `io_uring` backend set `SC_ASYNC_USE_IO_URING=1` and link `liburing` (`-luring`).
///
namespace SC
{
struct EventObject;
Expand Down Expand Up @@ -109,7 +118,7 @@ struct SC::AsyncRequest
#if SC_CONFIGURATION_DEBUG
void setDebugName(const char* newDebugName) { debugName = newDebugName; }
#else
void setDebugName(const char* newDebugName) { SC_COMPILER_UNUSED(newDebugName); }
void setDebugName(const char* newDebugName) { SC_COMPILER_UNUSED(newDebugName); }
#endif

/// @brief Get the event loop associated with this AsyncRequest
Expand Down Expand Up @@ -312,6 +321,8 @@ struct AsyncProcessExit : public AsyncRequest
#if SC_PLATFORM_WINDOWS
detail::WinOverlappedOpaque overlapped;
detail::WinWaitHandle waitHandle;
#elif SC_PLATFORM_LINUX
FileDescriptor pidFd;
#endif
};

Expand Down Expand Up @@ -361,6 +372,9 @@ struct AsyncSocketAccept : public AsyncRequest

SocketDescriptor clientSocket;
uint8_t acceptBuffer[288];
#elif SC_PLATFORM_LINUX
AlignedStorage<28> sockAddrHandle;
uint32_t sockAddrLen;
#endif
};

Expand Down Expand Up @@ -554,7 +568,7 @@ struct AsyncFileRead : public AsyncRequest
#if SC_PLATFORM_WINDOWS
detail::WinOverlappedOpaque overlapped;
#elif SC_PLATFORM_LINUX
size_t syncReadBytes = 0;
size_t syncReadBytes = 0;
#endif
};

Expand Down Expand Up @@ -624,8 +638,9 @@ struct AsyncFileClose : public AsyncRequest
FileDescriptor::Handle fileDescriptor;
};

/// @brief Starts a file poll operation, using `GetOverlappedResult` (windows), kevent (macOS) and epoll (Linux).
/// Callback will be called when any of the three API signals readiness events on the given file descriptor.
/// @brief Starts an handle polling operation.
/// Uses `GetOverlappedResult` (windows), `kevent` (macOS), `epoll` (Linux) and `io_uring` (Linux).
/// Callback will be called when any of the three API signals readiness events on the given file descriptor.
/// Check @ref library_file_system_watcher for an example usage of this notification.
struct AsyncFilePoll : public AsyncRequest
{
Expand Down Expand Up @@ -732,7 +747,7 @@ struct SC::AsyncEventLoop
{
static constexpr int Windows = 160;
static constexpr int Apple = 88;
static constexpr int Default = 168;
static constexpr int Default = 304;

static constexpr size_t Alignment = alignof(void*);

Expand Down
Loading

0 comments on commit d970016

Please sign in to comment.