Skip to content

Commit

Permalink
Updates to latest design
Browse files Browse the repository at this point in the history
  • Loading branch information
nibanks committed Jan 4, 2025
1 parent be0855e commit 919ae67
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 29 deletions.
30 changes: 20 additions & 10 deletions docs/Execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,23 @@ On Windows, the following types are defined:
```c++
typedef HANDLE QUIC_EVENTQ;

typedef struct QUIC_CQE {
OVERLAPPED_ENTRY Overlapped;
void (*Completion)(struct QUIC_CQE *Cqe);
} QUIC_CQE;
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_CQE` (CQE stands for completion queue event), which defines the format that all completion events must take so they may be generically processed from the event queue (more on this below).
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:
Expand All @@ -150,7 +160,7 @@ An application may expand this code to create multiple execution contexts, depen

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++
```c
bool AllDone = false;
while (!AllDone) {
uint32_t WaitTime = MsQuic->ExecutionPoll(ExecContext);
Expand All @@ -159,15 +169,15 @@ while (!AllDone) {
OVERLAPPED_ENTRY Overlapped[8];
if (GetQueuedCompletionStatusEx(IOCP, Overlapped, ARRAYSIZE(Overlapped), &OverlappedCount, WaitTime, FALSE)) {
for (ULONG i = 0; i < OverlappedCount; ++i) {
QUIC_CQE* Cqe = CONTAINING_RECORD(Overlapped[i].lpOverlapped, QUIC_CQE, Overlapped);
Cqe->Completion(Cqe);
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 completion queue event and then call its completion handler.
`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 completion events**.
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.
34 changes: 26 additions & 8 deletions src/inc/msquic_posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,10 +526,19 @@ QuicAddrToString(

typedef int QUIC_EVENTQ;

typedef struct QUIC_CQE {
struct epoll_event Event;
void (*Completion)(struct QUIC_CQE *Cqe);
} QUIC_CQE;
typedef struct epoll_event QUIC_CQE;

typedef
void
(QUIC_EVENT_COMPLETION)(
_In_ QUIC_CQE* Cqe
);
typedef QUIC_EVENT_COMPLETION *QUIC_EVENT_COMPLETION_HANDLER;

typedef struct QUIC_SQE {
int fd;
QUIC_EVENT_COMPLETION_HANDLER Completion;
} QUIC_SQE;

#elif __APPLE__ || __FreeBSD__ // kqueue

Expand All @@ -538,10 +547,19 @@ typedef struct QUIC_CQE {

typedef int QUIC_EVENTQ;

typedef struct QUIC_CQE {
struct kevent Event;
void (*Completion)(struct QUIC_CQE *Cqe);
} QUIC_CQE;
typedef struct kevent QUIC_CQE;

typedef
void
(QUIC_EVENT_COMPLETION)(
_In_ QUIC_CQE* Cqe
);
typedef QUIC_EVENT_COMPLETION *QUIC_EVENT_COMPLETION_HANDLER;

typedef struct QUIC_SQE {
uintptr_t Handle;
QUIC_EVENT_COMPLETION_HANDLER Completion;
} QUIC_SQE;

#else

Expand Down
21 changes: 17 additions & 4 deletions src/inc/msquic_winuser.h
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,22 @@ QuicAddrToString(

typedef HANDLE QUIC_EVENTQ;

typedef struct QUIC_CQE {
OVERLAPPED_ENTRY Overlapped;
void (*Completion)(struct QUIC_CQE *Cqe);
} QUIC_CQE;
typedef OVERLAPPED_ENTRY QUIC_CQE;

typedef
_IRQL_requires_max_(PASSIVE_LEVEL)
void
(QUIC_EVENT_COMPLETION)(
_In_ QUIC_CQE* Cqe
);
typedef QUIC_EVENT_COMPLETION *QUIC_EVENT_COMPLETION_HANDLER;

typedef struct QUIC_SQE {
OVERLAPPED Overlapped;
QUIC_EVENT_COMPLETION_HANDLER Completion;
#if DEBUG
BOOLEAN IsQueued; // Debug flag to catch double queueing.
#endif
} QUIC_SQE;

#endif // _MSQUIC_WINUSER_
14 changes: 7 additions & 7 deletions src/tools/execution/execution_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ main(
struct ConnectionCallback {
static QUIC_STATUS MsQuicConnectionCallback(_In_ struct MsQuicConnection* Connection, _In_opt_ void* Context, _Inout_ QUIC_CONNECTION_EVENT* Event) {
if (Event->Type == QUIC_CONNECTION_EVENT_CONNECTED) {
auto Cqe = new(std::nothrow) QUIC_CQE;
ZeroMemory(&Cqe->Overlapped, sizeof(Cqe->Overlapped));
Cqe->Completion = [](QUIC_CQE* _Cqe) {
auto Sqe = new(std::nothrow) QUIC_SQE;
ZeroMemory(&Sqe->Overlapped, sizeof(Sqe->Overlapped));
Sqe->Completion = [](QUIC_CQE* Cqe) {
printf("Connected.\n");
delete _Cqe;
delete CONTAINING_RECORD(Cqe->lpOverlapped, QUIC_SQE, Overlapped);
};
PostQueuedCompletionStatus(IOCP, 0, 0, &Cqe->Overlapped);
PostQueuedCompletionStatus(IOCP, 0, 0, &Sqe->Overlapped);
Connection->Shutdown(0);
} else if (Event->Type == QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE) {
*((bool*)Context) = true;
Expand All @@ -89,8 +89,8 @@ main(
OVERLAPPED_ENTRY Overlapped[8];
if (GetQueuedCompletionStatusEx(IOCP, Overlapped, ARRAYSIZE(Overlapped), &OverlappedCount, WaitTime, FALSE)) {
for (ULONG i = 0; i < OverlappedCount; ++i) {
QUIC_CQE* Cqe = CONTAINING_RECORD(Overlapped[i].lpOverlapped, QUIC_CQE, Overlapped);
Cqe->Completion(Cqe);
QUIC_SQE* Sqe = CONTAINING_RECORD(Overlapped[i].lpOverlapped, QUIC_SQE, Overlapped);
Sqe->Completion(&Overlapped[i]);
}
}
}
Expand Down

0 comments on commit 919ae67

Please sign in to comment.