-
Notifications
You must be signed in to change notification settings - Fork 545
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
External Execution Interface #4616
base: main
Are you sure you want to change the base?
Changes from all commits
448184f
442c928
e0f9d13
adb9ad9
882e630
da9e0b2
94efc44
9695592
4554407
40d1e84
74835cf
f73a471
b743020
dd7abe0
be0855e
919ae67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -109,3 +109,75 @@ graph TD | |
end | ||
end | ||
``` | ||
|
||
## Custom Execution | ||
|
||
MsQuic also supports scenarios where the application layer creates the threads that MsQuic uses to execute on. | ||
In this mode, the application creates one or more execution contexts that MsQuic will use to run all its internal logic. | ||
The application is responsible for calling down into MsQuic to allow these execution contexts to run. | ||
|
||
To create an execution context, the app much first create an event queue object, which is a platform specific type: | ||
|
||
- Windows: IOCP | ||
- Linux: epoll | ||
- macOS: kqueue | ||
|
||
On Windows, the following types are defined: | ||
|
||
```c++ | ||
typedef HANDLE QUIC_EVENTQ; | ||
|
||
typedef OVERLAPPED_ENTRY CXPLAT_CQE; | ||
|
||
typedef | ||
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
void | ||
(CXPLAT_EVENT_COMPLETION)( | ||
_In_ CXPLAT_CQE* Cqe | ||
); | ||
typedef CXPLAT_EVENT_COMPLETION *CXPLAT_EVENT_COMPLETION_HANDLER; | ||
|
||
typedef struct CXPLAT_SQE { | ||
OVERLAPPED Overlapped; | ||
CXPLAT_EVENT_COMPLETION_HANDLER Completion; | ||
} CXPLAT_SQE; | ||
``` | ||
|
||
You will also notice the definiton for `QUIC_SQE` (SQE stands for submission queue entry), which defines the format that all completion events must take so they may be generically processed from the event queue (more on this below). | ||
|
||
Once the app has the event queue, it may create the execution context with the `ExecutionCreate` function: | ||
|
||
```c++ | ||
HANDLE IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); | ||
QUIC_EXECUTION_CONTEXT_CONFIG ExecConfig = { 0, &IOCP }; | ||
|
||
QUIC_EXECUTION_CONTEXT* ExecContext = nullptr; | ||
QUIC_STATUS Status = MsQuic->ExecutionCreate(QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 1, &ExecConfig, &ExecContext); | ||
``` | ||
|
||
The above code createa a new IOCP (for Windows), sets up an execution config, indicating an ideal processor of 0 and the pointer to the IOCP, and then calls MsQuic to create 1 execution context. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A processor of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One could argue that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can look around to see how other platform abstraction layers (std, boost) solve this problem. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to stackoverflow, boost sidesteps the problem by allowing you to get a native thread handle, and then go off and call platform-specific affinity routines yourself. Personally I think an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, you could |
||
An application may expand this code to create multiple execution contexts, depending on their needs. | ||
|
||
To drive this execution context, the app will need to to periodically call `ExecutionPoll` and use the platform specific function to drain completion events from the event queue. | ||
|
||
```c | ||
bool AllDone = false; | ||
while (!AllDone) { | ||
uint32_t WaitTime = MsQuic->ExecutionPoll(ExecContext); | ||
|
||
ULONG OverlappedCount = 0; | ||
OVERLAPPED_ENTRY Overlapped[8]; | ||
if (GetQueuedCompletionStatusEx(IOCP, Overlapped, ARRAYSIZE(Overlapped), &OverlappedCount, WaitTime, FALSE)) { | ||
for (ULONG i = 0; i < OverlappedCount; ++i) { | ||
QUIC_SQE* Sqe = CONTAINING_RECORD(Overlapped[i].lpOverlapped, QUIC_SQE, Overlapped); | ||
Sqe->Completion(&Overlapped[i]); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Above, you can see a simple loop that properly drives a single execution context on Windows. | ||
`OVERLAPPED_ENTRY` objects received from `GetQueuedCompletionStatusEx` are used to get the submission queue entry and then call its completion handler. | ||
|
||
In a real application, these completion events may come both from MsQuic and the application itself, therefore, this means **the application must use the same base format for its own submission entries**. | ||
This is necessary to be able to share the same event queue object. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Copyright (c) Microsoft Corporation. | ||
# Licensed under the MIT License. | ||
|
||
add_quic_tool(quicexecution execution_windows.cpp) | ||
quic_tool_warnings(quicexecution) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we expose a platform specific queue object rather than having a more abstracted interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Such as what? The reason we use the platform queue object is because that is also a requirement for other (i.e. storage) IO. So, it's not expected to be a new requirement. Also, this is all opt-in. They can continue to use MsQuic without doing this, and it will simply create the background threads still.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant a generic platform-independent interface for queue objects.
So, for the apps to opt in, they still have to create a new IOCP handle exclusively for msquic, right? i.e. they can't just share whatever IOCP handles they use for file IO for msquic unless they change all their IO logic to use the new sqe/cqe pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, they shouldn't create anything specific for MsQuic. If they have an epoll queue (on Linux) for their storage IO, they should reuse that for MsQuic.