From 448184f827ea7706152a1f34a567c743f94d674f Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 16 Oct 2024 08:49:35 -0400 Subject: [PATCH 01/34] Initial External Execution Interface --- src/inc/msquic.h | 84 +++++++++++++++++++++++++++++++++++++++- src/inc/msquic_posix.h | 20 ++++++++++ src/inc/msquic_winuser.h | 7 ++++ 3 files changed, 109 insertions(+), 2 deletions(-) diff --git a/src/inc/msquic.h b/src/inc/msquic.h index d9c25b8933..f81146117a 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -264,15 +264,15 @@ typedef enum QUIC_DATAGRAM_SEND_STATE { #define QUIC_DATAGRAM_SEND_STATE_IS_FINAL(State) \ ((State) >= QUIC_DATAGRAM_SEND_LOST_DISCARDED) +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES + typedef enum QUIC_EXECUTION_CONFIG_FLAGS { QUIC_EXECUTION_CONFIG_FLAG_NONE = 0x0000, -#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES QUIC_EXECUTION_CONFIG_FLAG_QTIP = 0x0001, QUIC_EXECUTION_CONFIG_FLAG_RIO = 0x0002, QUIC_EXECUTION_CONFIG_FLAG_XDP = 0x0004, QUIC_EXECUTION_CONFIG_FLAG_NO_IDEAL_PROC = 0x0008, QUIC_EXECUTION_CONFIG_FLAG_HIGH_PRIORITY = 0x0010, -#endif } QUIC_EXECUTION_CONFIG_FLAGS; DEFINE_ENUM_FLAG_OPERATORS(QUIC_EXECUTION_CONFIG_FLAGS) @@ -293,6 +293,82 @@ typedef struct QUIC_EXECUTION_CONFIG { #define QUIC_EXECUTION_CONFIG_MIN_SIZE \ (uint32_t)FIELD_OFFSET(QUIC_EXECUTION_CONFIG, ProcessorList) +// +// Execution Context abstraction, which allows the application layer to +// completely control execution of all MsQuic work. +// + +typedef struct QUIC_EXECUTION_CONTEXT_CONFIG { + uint32_t IdealProcessor; + uint32_t PollingIdleTimeoutUs; + QUIC_EVENTQ* EventQ; +} QUIC_EXECUTION_CONTEXT_CONFIG; + +typedef struct QUIC_EXECUTION_CONTEXT QUIC_EXECUTION_CONTEXT; + +// +// This is called create the execution contexts. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_EXECUTION_CREATE_FN)( + _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type + _In_ uint32_t Count, + _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs, + _Out_writes_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + ); + +// +// This is called to delete the execution contexts. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +void +(QUIC_API * QUIC_EXECUTION_DELETE_FN)( + _In_ uint32_t Count, + _In_reads_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + ); + +// +// This is called to allow MsQuic to process any polling work. It returns the +// number of milliseconds until the next scheduled timer expiration. +// +// TODO: Should it return an indication for if we should yield? +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +uint32_t +(QUIC_API * QUIC_EXECUTION_POLL_FN)( + _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext + ); + +// +// This is called to allow MsQuic to process any completions that have occurred. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +uint32_t +(QUIC_API * QUIC_EXECUTION_PROCESS_CQE_FN)( + _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext, + _In_reads_(CqeCount) QUIC_CQE* Cqes, + _In_ uint32_t CqeCount + ); + +// +// The table of execution functions. +// +typedef struct QUIC_EXECUTION_TABLE { + + QUIC_EXECUTION_CREATE_FN ExecutionCreate; + QUIC_EXECUTION_DELETE_FN ExecutionDelete; + QUIC_EXECUTION_POLL_FN Poll; + QUIC_EXECUTION_PROCESS_CQE_FN ProcessCqe; + +} QUIC_EXECUTION_TABLE; + +#endif + typedef struct QUIC_REGISTRATION_CONFIG { // All fields may be NULL/zero. const char* AppName; QUIC_EXECUTION_PROFILE ExecutionProfile; @@ -857,6 +933,10 @@ void #endif #define QUIC_PARAM_GLOBAL_TLS_PROVIDER 0x0100000A // QUIC_TLS_PROVIDER #define QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY 0x0100000B // uint8_t[] - Array size is QUIC_STATELESS_RESET_KEY_LENGTH +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES +#define QUIC_PARAM_GLOBAL_EXECUTION_TABLE 0x0100000C // QUIC_EXECUTION_TABLE +#endif + // // Parameters for Registration. // diff --git a/src/inc/msquic_posix.h b/src/inc/msquic_posix.h index 27393e1233..87e58150fb 100644 --- a/src/inc/msquic_posix.h +++ b/src/inc/msquic_posix.h @@ -515,6 +515,26 @@ QuicAddrToString( return TRUE; } +// +// Event Queue Abstraction +// + +#if __linux__ // epoll + +typedef int QUIC_EVENTQ; +typedef struct epoll_event QUIC_CQE; + +#elif __APPLE__ || __FreeBSD__ // kqueue + +typedef int QUIC_EVENTQ; +typedef struct kevent QUIC_CQE; + +#else + +#error Unsupported Platform + +#endif + #if defined(__cplusplus) } #endif diff --git a/src/inc/msquic_winuser.h b/src/inc/msquic_winuser.h index adc2f2292e..4e0f3bac68 100644 --- a/src/inc/msquic_winuser.h +++ b/src/inc/msquic_winuser.h @@ -373,4 +373,11 @@ QuicAddrToString( #endif // WINAPI_FAMILY != WINAPI_FAMILY_GAMES +// +// Event Queue Abstraction +// + +typedef HANDLE QUIC_EVENTQ; +typedef OVERLAPPED_ENTRY QUIC_CQE; + #endif // _MSQUIC_WINUSER_ From 442c928a53afd5dbc5e0403e4053c327a7c08fd8 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 16 Oct 2024 09:04:36 -0400 Subject: [PATCH 02/34] Make .NET and Kernel mode happy --- src/cs/lib/msquic_generated.cs | 34 ++++++++++++++++++++++++++++++++++ src/inc/msquic.h | 6 +++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs index cf1477ea76..970d85153e 100644 --- a/src/cs/lib/msquic_generated.cs +++ b/src/cs/lib/msquic_generated.cs @@ -234,6 +234,37 @@ internal unsafe partial struct QUIC_EXECUTION_CONFIG internal fixed ushort ProcessorList[1]; } + internal unsafe partial struct QUIC_EXECUTION_CONTEXT_CONFIG + { + [NativeTypeName("uint32_t")] + internal uint IdealProcessor; + + [NativeTypeName("uint32_t")] + internal uint PollingIdleTimeoutUs; + + [NativeTypeName("QUIC_EVENTQ *")] + internal void** EventQ; + } + + internal partial struct QUIC_EXECUTION_CONTEXT + { + } + + internal unsafe partial struct QUIC_EXECUTION_TABLE + { + [NativeTypeName("QUIC_EXECUTION_CREATE_FN")] + internal delegate* unmanaged[Cdecl] ExecutionCreate; + + [NativeTypeName("QUIC_EXECUTION_DELETE_FN")] + internal delegate* unmanaged[Cdecl] ExecutionDelete; + + [NativeTypeName("QUIC_EXECUTION_POLL_FN")] + internal delegate* unmanaged[Cdecl] Poll; + + [NativeTypeName("QUIC_EXECUTION_PROCESS_CQE_FN")] + internal delegate* unmanaged[Cdecl] ProcessCqe; + } + internal unsafe partial struct QUIC_REGISTRATION_CONFIG { [NativeTypeName("const char *")] @@ -3345,6 +3376,9 @@ internal static unsafe partial class MsQuic [NativeTypeName("#define QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY 0x0100000B")] internal const uint QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY = 0x0100000B; + [NativeTypeName("#define QUIC_PARAM_GLOBAL_EXECUTION_TABLE 0x0100000C")] + internal const uint QUIC_PARAM_GLOBAL_EXECUTION_TABLE = 0x0100000C; + [NativeTypeName("#define QUIC_PARAM_CONFIGURATION_SETTINGS 0x03000000")] internal const uint QUIC_PARAM_CONFIGURATION_SETTINGS = 0x03000000; diff --git a/src/inc/msquic.h b/src/inc/msquic.h index f81146117a..f6941ae917 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -293,6 +293,8 @@ typedef struct QUIC_EXECUTION_CONFIG { #define QUIC_EXECUTION_CONFIG_MIN_SIZE \ (uint32_t)FIELD_OFFSET(QUIC_EXECUTION_CONFIG, ProcessorList) +#ifndef _KERNEL_MODE + // // Execution Context abstraction, which allows the application layer to // completely control execution of all MsQuic work. @@ -367,7 +369,9 @@ typedef struct QUIC_EXECUTION_TABLE { } QUIC_EXECUTION_TABLE; -#endif +#endif // _KERNEL_MODE + +#endif // QUIC_API_ENABLE_PREVIEW_FEATURES typedef struct QUIC_REGISTRATION_CONFIG { // All fields may be NULL/zero. const char* AppName; From e0f9d138b7d3e124ddb3d8bac725e74f865795cd Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 16 Oct 2024 09:08:04 -0400 Subject: [PATCH 03/34] Check function --- src/cs/lib/msquic_generated.cs | 3 +++ src/inc/msquic.h | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs index 970d85153e..1399289bbd 100644 --- a/src/cs/lib/msquic_generated.cs +++ b/src/cs/lib/msquic_generated.cs @@ -261,6 +261,9 @@ internal unsafe partial struct QUIC_EXECUTION_TABLE [NativeTypeName("QUIC_EXECUTION_POLL_FN")] internal delegate* unmanaged[Cdecl] Poll; + [NativeTypeName("QUIC_EXECUTION_CHECK_CQE_FN")] + internal delegate* unmanaged[Cdecl]<_OVERLAPPED_ENTRY*, byte> CheckCqe; + [NativeTypeName("QUIC_EXECUTION_PROCESS_CQE_FN")] internal delegate* unmanaged[Cdecl] ProcessCqe; } diff --git a/src/inc/msquic.h b/src/inc/msquic.h index f6941ae917..9eec07c4ef 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -345,6 +345,19 @@ uint32_t _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext ); +// +// This is used to check if a completion event belongs to MsQuic or not. +// +// TODO: This makes certain assumptions for the layout of completion event +// payload. How do we generalize this? +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +BOOLEAN +(QUIC_API * QUIC_EXECUTION_CHECK_CQE_FN)( + _In_ const QUIC_CQE* Cqe + ); + // // This is called to allow MsQuic to process any completions that have occurred. // @@ -365,6 +378,7 @@ typedef struct QUIC_EXECUTION_TABLE { QUIC_EXECUTION_CREATE_FN ExecutionCreate; QUIC_EXECUTION_DELETE_FN ExecutionDelete; QUIC_EXECUTION_POLL_FN Poll; + QUIC_EXECUTION_CHECK_CQE_FN CheckCqe; QUIC_EXECUTION_PROCESS_CQE_FN ProcessCqe; } QUIC_EXECUTION_TABLE; From adb9ad97180b14c5c90d2e546bf6d49ecbe4807f Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 16 Oct 2024 09:50:53 -0400 Subject: [PATCH 04/34] Incomplete example --- src/tools/execution/CMakeLists.txt | 5 + src/tools/execution/execution_windows.cpp | 109 ++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 src/tools/execution/CMakeLists.txt create mode 100644 src/tools/execution/execution_windows.cpp diff --git a/src/tools/execution/CMakeLists.txt b/src/tools/execution/CMakeLists.txt new file mode 100644 index 0000000000..dcc6c2106c --- /dev/null +++ b/src/tools/execution/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +add_quic_tool(quicexecution execution_windows.cpp) +quic_tool_warnings(quicexecution) diff --git a/src/tools/execution/execution_windows.cpp b/src/tools/execution/execution_windows.cpp new file mode 100644 index 0000000000..1eb202ce41 --- /dev/null +++ b/src/tools/execution/execution_windows.cpp @@ -0,0 +1,109 @@ +/*++ + + Copyright (c) Microsoft Corporation. + Licensed under the MIT License. + +Abstract: + + Provides simple client MsQuic example that leverages custom execution. + +--*/ + +#define _CRT_SECURE_NO_WARNINGS 1 +#define QUIC_API_ENABLE_PREVIEW_FEATURES 1 + +#include "msquic.hpp" +#include +#include + +const MsQuicApi* MsQuic; + +void PrintUsage() +{ + printf( + "\n" + "quicexec is a simple app that can connect to an HTTP/3 server.\n" + "\n" + "Usage:\n" + "\n" + " quicexec \n" + ); +} + +int +QUIC_MAIN_EXPORT +main( + _In_ int argc, + _In_reads_(argc) _Null_terminated_ char* argv[] + ) +{ + MsQuicApi _MsQuic; + if (!_MsQuic.IsValid()) { return 1; } + MsQuic = &_MsQuic; + + QUIC_STATUS Status; + QUIC_EXECUTION_TABLE MsQuicExec; + uint32_t MsQuicExecLength = sizeof(MsQuicExec); + if (QUIC_FAILED( + Status = MsQuic->GetParam( + nullptr, + QUIC_PARAM_GLOBAL_EXECUTION_TABLE, + &MsQuicExecLength, + &MsQuicExec))) { + return 1; + } + + HANDLE IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); + QUIC_EXECUTION_CONTEXT_CONFIG ExecConfig = { 0, 0, &IOCP }; + QUIC_EXECUTION_CONTEXT* ExecContext = nullptr; + if (QUIC_FAILED( + Status = MsQuicExec.ExecutionCreate( + QUIC_EXECUTION_CONFIG_FLAG_NONE, + 1, + &ExecConfig, + &ExecContext))) { + return 1; + } + + do { + MsQuicRegistration Registration("quicexec"); + MsQuicSettings Settings; + Settings.SetPeerUnidiStreamCount(3); // required for H3 + MsQuicConfiguration Configuration(Registration, "h3", Settings, MsQuicCredentialConfig()); + if (!Configuration.IsValid()) { break; } + + MsQuicConnection Connection(Registration); + if (QUIC_FAILED( + Status = Connection.Start(Configuration, argv[1], 443))) { + break; + } + + while (true) { + uint32_t WaitTime = MsQuicExec.Poll(ExecContext); + + OVERLAPPED_ENTRY Cqes[8]; + ULONG CqeCount = 0; + if (GetQueuedCompletionStatusEx(IOCP, Cqes, ARRAYSIZE(Cqes), &CqeCount, WaitTime, FALSE)) { + for (ULONG i = 0; i < CqeCount; ++i) { + if (MsQuicExec.CheckCqe(Cqes+i)) { + MsQuicExec.ProcessCqe(ExecContext, Cqes+i, 1); + } else { + // We should handle our own completions here. + } + } + } + + if (Connection.HandshakeComplete) { + Connection.Shutdown(0); + } + + // TODO - Stop once the connection is shutdown complete + } + + } while (false); + + MsQuicExec.ExecutionDelete(1, &ExecContext); + CloseHandle(IOCP); + + return 0; +} From 882e6306b2198ba1467e16b16cf0a84d23cd4647 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 16 Oct 2024 11:05:00 -0400 Subject: [PATCH 05/34] Simplify --- src/inc/msquic.h | 34 ++---------- src/inc/msquic_posix.h | 12 ++++- src/inc/msquic_winuser.h | 6 ++- src/tools/execution/execution_windows.cpp | 64 ++++++++--------------- 4 files changed, 42 insertions(+), 74 deletions(-) diff --git a/src/inc/msquic.h b/src/inc/msquic.h index 9eec07c4ef..f3729345f9 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -345,31 +345,6 @@ uint32_t _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext ); -// -// This is used to check if a completion event belongs to MsQuic or not. -// -// TODO: This makes certain assumptions for the layout of completion event -// payload. How do we generalize this? -// -typedef -_IRQL_requires_max_(PASSIVE_LEVEL) -BOOLEAN -(QUIC_API * QUIC_EXECUTION_CHECK_CQE_FN)( - _In_ const QUIC_CQE* Cqe - ); - -// -// This is called to allow MsQuic to process any completions that have occurred. -// -typedef -_IRQL_requires_max_(PASSIVE_LEVEL) -uint32_t -(QUIC_API * QUIC_EXECUTION_PROCESS_CQE_FN)( - _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext, - _In_reads_(CqeCount) QUIC_CQE* Cqes, - _In_ uint32_t CqeCount - ); - // // The table of execution functions. // @@ -378,8 +353,6 @@ typedef struct QUIC_EXECUTION_TABLE { QUIC_EXECUTION_CREATE_FN ExecutionCreate; QUIC_EXECUTION_DELETE_FN ExecutionDelete; QUIC_EXECUTION_POLL_FN Poll; - QUIC_EXECUTION_CHECK_CQE_FN CheckCqe; - QUIC_EXECUTION_PROCESS_CQE_FN ProcessCqe; } QUIC_EXECUTION_TABLE; @@ -951,9 +924,6 @@ void #endif #define QUIC_PARAM_GLOBAL_TLS_PROVIDER 0x0100000A // QUIC_TLS_PROVIDER #define QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY 0x0100000B // uint8_t[] - Array size is QUIC_STATELESS_RESET_KEY_LENGTH -#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES -#define QUIC_PARAM_GLOBAL_EXECUTION_TABLE 0x0100000C // QUIC_EXECUTION_TABLE -#endif // // Parameters for Registration. @@ -1724,6 +1694,10 @@ typedef struct QUIC_API_TABLE { QUIC_CONNECTION_COMP_RESUMPTION_FN ConnectionResumptionTicketValidationComplete; // Available from v2.2 QUIC_CONNECTION_COMP_CERT_FN ConnectionCertificateValidationComplete; // Available from v2.2 + QUIC_EXECUTION_CREATE_FN ExecutionCreate; + QUIC_EXECUTION_DELETE_FN ExecutionDelete; + QUIC_EXECUTION_POLL_FN ExecutionPoll; + } QUIC_API_TABLE; #define QUIC_API_VERSION_1 1 // Not supported any more diff --git a/src/inc/msquic_posix.h b/src/inc/msquic_posix.h index 87e58150fb..eeef69c9b1 100644 --- a/src/inc/msquic_posix.h +++ b/src/inc/msquic_posix.h @@ -522,12 +522,20 @@ QuicAddrToString( #if __linux__ // epoll typedef int QUIC_EVENTQ; -typedef struct epoll_event QUIC_CQE; + +typedef struct QUIC_CQE { + struct epoll_event Event; + void (*Completion)(struct QUIC_CQE *Cqe); +} QUIC_CQE; #elif __APPLE__ || __FreeBSD__ // kqueue typedef int QUIC_EVENTQ; -typedef struct kevent QUIC_CQE; + +typedef struct QUIC_CQE { + struct kevent Event; + void (*Completion)(struct QUIC_CQE *Cqe); +} QUIC_CQE; #else diff --git a/src/inc/msquic_winuser.h b/src/inc/msquic_winuser.h index 4e0f3bac68..0201e4e6d6 100644 --- a/src/inc/msquic_winuser.h +++ b/src/inc/msquic_winuser.h @@ -378,6 +378,10 @@ QuicAddrToString( // typedef HANDLE QUIC_EVENTQ; -typedef OVERLAPPED_ENTRY QUIC_CQE; + +typedef struct QUIC_CQE { + OVERLAPPED_ENTRY Overlapped; + void (*Completion)(struct QUIC_CQE *Cqe); +} QUIC_CQE; #endif // _MSQUIC_WINUSER_ diff --git a/src/tools/execution/execution_windows.cpp b/src/tools/execution/execution_windows.cpp index 1eb202ce41..caead4b8df 100644 --- a/src/tools/execution/execution_windows.cpp +++ b/src/tools/execution/execution_windows.cpp @@ -9,14 +9,10 @@ --*/ -#define _CRT_SECURE_NO_WARNINGS 1 #define QUIC_API_ENABLE_PREVIEW_FEATURES 1 #include "msquic.hpp" #include -#include - -const MsQuicApi* MsQuic; void PrintUsage() { @@ -42,67 +38,53 @@ main( MsQuic = &_MsQuic; QUIC_STATUS Status; - QUIC_EXECUTION_TABLE MsQuicExec; - uint32_t MsQuicExecLength = sizeof(MsQuicExec); - if (QUIC_FAILED( - Status = MsQuic->GetParam( - nullptr, - QUIC_PARAM_GLOBAL_EXECUTION_TABLE, - &MsQuicExecLength, - &MsQuicExec))) { - return 1; - } - HANDLE IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); QUIC_EXECUTION_CONTEXT_CONFIG ExecConfig = { 0, 0, &IOCP }; QUIC_EXECUTION_CONTEXT* ExecContext = nullptr; - if (QUIC_FAILED( - Status = MsQuicExec.ExecutionCreate( - QUIC_EXECUTION_CONFIG_FLAG_NONE, - 1, - &ExecConfig, - &ExecContext))) { + if (QUIC_FAILED(Status = MsQuic->ExecutionCreate(QUIC_EXECUTION_CONFIG_FLAG_NONE, 1, &ExecConfig, &ExecContext))) { return 1; } do { + bool AllDone = false; MsQuicRegistration Registration("quicexec"); MsQuicSettings Settings; Settings.SetPeerUnidiStreamCount(3); // required for H3 MsQuicConfiguration Configuration(Registration, "h3", Settings, MsQuicCredentialConfig()); if (!Configuration.IsValid()) { break; } - MsQuicConnection Connection(Registration); + 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) { + Connection->Shutdown(0); + } else if (Event->Type == QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE) { + *((bool*)Context) = true; + } + } + }; + + MsQuicConnection Connection(Registration, CleanUpManual, ConnectionCallback::MsQuicConnectionCallback, &AllDone); if (QUIC_FAILED( Status = Connection.Start(Configuration, argv[1], 443))) { break; } - while (true) { - uint32_t WaitTime = MsQuicExec.Poll(ExecContext); - - OVERLAPPED_ENTRY Cqes[8]; - ULONG CqeCount = 0; - if (GetQueuedCompletionStatusEx(IOCP, Cqes, ARRAYSIZE(Cqes), &CqeCount, WaitTime, FALSE)) { - for (ULONG i = 0; i < CqeCount; ++i) { - if (MsQuicExec.CheckCqe(Cqes+i)) { - MsQuicExec.ProcessCqe(ExecContext, Cqes+i, 1); - } else { - // We should handle our own completions here. - } - } - } + while (!AllDone) { + uint32_t WaitTime = MsQuic->ExecutionPoll(ExecContext); - if (Connection.HandshakeComplete) { - Connection.Shutdown(0); + OVERLAPPED_ENTRY Overlapped[8]; + ULONG OverlappedCount = 0; + 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); + } } - - // TODO - Stop once the connection is shutdown complete } } while (false); - MsQuicExec.ExecutionDelete(1, &ExecContext); + MsQuic->ExecutionDelete(1, &ExecContext); CloseHandle(IOCP); return 0; From da9e0b24f15f912f0965bc808344c2f8b71dc3c4 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 16 Oct 2024 11:05:34 -0400 Subject: [PATCH 06/34] Fix .net --- src/cs/lib/msquic_generated.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs index 1399289bbd..7e8e51d0a1 100644 --- a/src/cs/lib/msquic_generated.cs +++ b/src/cs/lib/msquic_generated.cs @@ -260,12 +260,6 @@ internal unsafe partial struct QUIC_EXECUTION_TABLE [NativeTypeName("QUIC_EXECUTION_POLL_FN")] internal delegate* unmanaged[Cdecl] Poll; - - [NativeTypeName("QUIC_EXECUTION_CHECK_CQE_FN")] - internal delegate* unmanaged[Cdecl]<_OVERLAPPED_ENTRY*, byte> CheckCqe; - - [NativeTypeName("QUIC_EXECUTION_PROCESS_CQE_FN")] - internal delegate* unmanaged[Cdecl] ProcessCqe; } internal unsafe partial struct QUIC_REGISTRATION_CONFIG @@ -3284,6 +3278,15 @@ internal unsafe partial struct QUIC_API_TABLE [NativeTypeName("QUIC_CONNECTION_COMP_CERT_FN")] internal delegate* unmanaged[Cdecl] ConnectionCertificateValidationComplete; + + [NativeTypeName("QUIC_EXECUTION_CREATE_FN")] + internal delegate* unmanaged[Cdecl] ExecutionCreate; + + [NativeTypeName("QUIC_EXECUTION_DELETE_FN")] + internal delegate* unmanaged[Cdecl] ExecutionDelete; + + [NativeTypeName("QUIC_EXECUTION_POLL_FN")] + internal delegate* unmanaged[Cdecl] ExecutionPoll; } internal static unsafe partial class MsQuic @@ -3379,9 +3382,6 @@ internal static unsafe partial class MsQuic [NativeTypeName("#define QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY 0x0100000B")] internal const uint QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY = 0x0100000B; - [NativeTypeName("#define QUIC_PARAM_GLOBAL_EXECUTION_TABLE 0x0100000C")] - internal const uint QUIC_PARAM_GLOBAL_EXECUTION_TABLE = 0x0100000C; - [NativeTypeName("#define QUIC_PARAM_CONFIGURATION_SETTINGS 0x03000000")] internal const uint QUIC_PARAM_CONFIGURATION_SETTINGS = 0x03000000; From 94efc4493cb3fc72804d41342ea09b3a697837a3 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 16 Oct 2024 11:06:27 -0400 Subject: [PATCH 07/34] Remove leftovers --- src/cs/lib/msquic_generated.cs | 12 ------------ src/inc/msquic.h | 11 ----------- 2 files changed, 23 deletions(-) diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs index 7e8e51d0a1..9c112f5ab1 100644 --- a/src/cs/lib/msquic_generated.cs +++ b/src/cs/lib/msquic_generated.cs @@ -250,18 +250,6 @@ internal partial struct QUIC_EXECUTION_CONTEXT { } - internal unsafe partial struct QUIC_EXECUTION_TABLE - { - [NativeTypeName("QUIC_EXECUTION_CREATE_FN")] - internal delegate* unmanaged[Cdecl] ExecutionCreate; - - [NativeTypeName("QUIC_EXECUTION_DELETE_FN")] - internal delegate* unmanaged[Cdecl] ExecutionDelete; - - [NativeTypeName("QUIC_EXECUTION_POLL_FN")] - internal delegate* unmanaged[Cdecl] Poll; - } - internal unsafe partial struct QUIC_REGISTRATION_CONFIG { [NativeTypeName("const char *")] diff --git a/src/inc/msquic.h b/src/inc/msquic.h index f3729345f9..940b70c445 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -345,17 +345,6 @@ uint32_t _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext ); -// -// The table of execution functions. -// -typedef struct QUIC_EXECUTION_TABLE { - - QUIC_EXECUTION_CREATE_FN ExecutionCreate; - QUIC_EXECUTION_DELETE_FN ExecutionDelete; - QUIC_EXECUTION_POLL_FN Poll; - -} QUIC_EXECUTION_TABLE; - #endif // _KERNEL_MODE #endif // QUIC_API_ENABLE_PREVIEW_FEATURES From 969559269b4f6c2aafc75e2e8bf29e2ae4d6d4e8 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 16 Oct 2024 11:07:59 -0400 Subject: [PATCH 08/34] fixes --- src/inc/msquic.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/inc/msquic.h b/src/inc/msquic.h index 940b70c445..4fbf31fc54 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -1683,9 +1683,11 @@ typedef struct QUIC_API_TABLE { QUIC_CONNECTION_COMP_RESUMPTION_FN ConnectionResumptionTicketValidationComplete; // Available from v2.2 QUIC_CONNECTION_COMP_CERT_FN ConnectionCertificateValidationComplete; // Available from v2.2 - QUIC_EXECUTION_CREATE_FN ExecutionCreate; - QUIC_EXECUTION_DELETE_FN ExecutionDelete; - QUIC_EXECUTION_POLL_FN ExecutionPoll; +#ifndef _KERNEL_MODE + QUIC_EXECUTION_CREATE_FN ExecutionCreate; // Available from v2.5 + QUIC_EXECUTION_DELETE_FN ExecutionDelete; // Available from v2.5 + QUIC_EXECUTION_POLL_FN ExecutionPoll; // Available from v2.5 +#endif } QUIC_API_TABLE; From 4554407185ea5d2d087a2646a6ef11449c8d350f Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Fri, 18 Oct 2024 07:57:47 -0400 Subject: [PATCH 09/34] wip --- src/inc/msquic.h | 6 ++++-- src/inc/msquic_posix.h | 6 ++++++ src/tools/execution/execution_windows.cpp | 15 ++++++++++++--- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/inc/msquic.h b/src/inc/msquic.h index 4fbf31fc54..c494eb1c7c 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -302,20 +302,20 @@ typedef struct QUIC_EXECUTION_CONFIG { typedef struct QUIC_EXECUTION_CONTEXT_CONFIG { uint32_t IdealProcessor; - uint32_t PollingIdleTimeoutUs; QUIC_EVENTQ* EventQ; } QUIC_EXECUTION_CONTEXT_CONFIG; typedef struct QUIC_EXECUTION_CONTEXT QUIC_EXECUTION_CONTEXT; // -// This is called create the execution contexts. +// This is called to create the execution contexts. // typedef _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS (QUIC_API * QUIC_EXECUTION_CREATE_FN)( _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type + _In_ uint32_t PollingIdleTimeoutUs, _In_ uint32_t Count, _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs, _Out_writes_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts @@ -1683,11 +1683,13 @@ typedef struct QUIC_API_TABLE { QUIC_CONNECTION_COMP_RESUMPTION_FN ConnectionResumptionTicketValidationComplete; // Available from v2.2 QUIC_CONNECTION_COMP_CERT_FN ConnectionCertificateValidationComplete; // Available from v2.2 +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES #ifndef _KERNEL_MODE QUIC_EXECUTION_CREATE_FN ExecutionCreate; // Available from v2.5 QUIC_EXECUTION_DELETE_FN ExecutionDelete; // Available from v2.5 QUIC_EXECUTION_POLL_FN ExecutionPoll; // Available from v2.5 #endif +#endif } QUIC_API_TABLE; diff --git a/src/inc/msquic_posix.h b/src/inc/msquic_posix.h index eeef69c9b1..67789cd45b 100644 --- a/src/inc/msquic_posix.h +++ b/src/inc/msquic_posix.h @@ -521,6 +521,9 @@ QuicAddrToString( #if __linux__ // epoll +#include +#include + typedef int QUIC_EVENTQ; typedef struct QUIC_CQE { @@ -530,6 +533,9 @@ typedef struct QUIC_CQE { #elif __APPLE__ || __FreeBSD__ // kqueue +#include +#include + typedef int QUIC_EVENTQ; typedef struct QUIC_CQE { diff --git a/src/tools/execution/execution_windows.cpp b/src/tools/execution/execution_windows.cpp index caead4b8df..1a012b3a0e 100644 --- a/src/tools/execution/execution_windows.cpp +++ b/src/tools/execution/execution_windows.cpp @@ -26,6 +26,8 @@ void PrintUsage() ); } +HANDLE IOCP; + int QUIC_MAIN_EXPORT main( @@ -38,10 +40,10 @@ main( MsQuic = &_MsQuic; QUIC_STATUS Status; - HANDLE IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); - QUIC_EXECUTION_CONTEXT_CONFIG ExecConfig = { 0, 0, &IOCP }; + IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); + QUIC_EXECUTION_CONTEXT_CONFIG ExecConfig = { 0, &IOCP }; QUIC_EXECUTION_CONTEXT* ExecContext = nullptr; - if (QUIC_FAILED(Status = MsQuic->ExecutionCreate(QUIC_EXECUTION_CONFIG_FLAG_NONE, 1, &ExecConfig, &ExecContext))) { + if (QUIC_FAILED(Status = MsQuic->ExecutionCreate(QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 1, &ExecConfig, &ExecContext))) { return 1; } @@ -56,6 +58,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) { + printf("Connected.\n"); + delete _Cqe; + }; + PostQueuedCompletionStatus(IOCP, 0, 0, &Cqe->Overlapped); Connection->Shutdown(0); } else if (Event->Type == QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE) { *((bool*)Context) = true; From 40d1e84035986062c1976a47ca0e4b959e6ae819 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Mon, 28 Oct 2024 15:18:34 -0400 Subject: [PATCH 10/34] wip --- docs/Execution.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 docs/Execution.md diff --git a/docs/Execution.md b/docs/Execution.md new file mode 100644 index 0000000000..38f5be228f --- /dev/null +++ b/docs/Execution.md @@ -0,0 +1,64 @@ +Execution +====== + +The MsQuic API takes a very difference stance when it comes to its execution model compared to BSD sockets (and most other networking libraries built on top of them). +The sections below detail the designs MsQuic uses, with some of the details as to why these design choices were made. + +## Event Model + +In the MsQuic API, all state changes and other notifications are indicated directly to the application via a callback. + +```c +typedef struct QUIC_LISTENER_EVENT { + QUIC_LISTENER_EVENT_TYPE Type; + union { + struct { ... } NEW_CONNECTION; + struct { ... } STOP_COMPLETE; + }; +} QUIC_LISTENER_EVENT; + +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +_Function_class_(QUIC_LISTENER_CALLBACK) +QUIC_STATUS +(QUIC_API QUIC_LISTENER_CALLBACK)( + _In_ HQUIC Listener, + _In_opt_ void* Context, + _Inout_ QUIC_LISTENER_EVENT* Event + ); +``` + +Above is an example of the type of callback delivered to the listener interface. +The application is requires to register a callback handler that should handle all the events MsQuic may indicate, returning a status for if it was successfully handled or not. + +This is very different from BSD sockets which required the application to make a call (e.g., `send` or `recv`) in order to determine something happened. +This difference was made for several reasons: + +- The MsQuic API **runs in-proc**, and therefore doesn't require a kernel to user mode boundary switch to indicate something to the application layer. This allows for the callback-based design which is not as practical for BSD sockets. + +- MsQuic, by virtue of the QUIC protocol itself, has a lot of different types of events. Just considering streams, the app maybe have hundreds of objects at once which may have some state change. By leveraging the callback model, the application doesn't have to manage having pending calls on each object. + +- Experience has shown it to be very difficult to write correct, performant code on top of the BSD-style interface. By leveraging callbacks (that happen at the correct time, on the correct thread/processor), it allows MsQuic to abstract a lot of complexity away from applications and make things "just work" out of the box. + +- It simplifies much of the logic in MsQuic, because it eliminates the need for a queue or cached state that needs to be indicated to the application. In the BSD model, the networking stack must wait for the top-down call from the application before it can indicate the completion. This adds increased code size, complexity and memory usage. + +### Writing Event Handlers + +Event handlers are **required** for all objects (that support them), because much of the MsQuic API happens through these callbacks. +Additionally, important events, such as "shutdown complete" events provide crucial information to the application to function properly. +Without these events, the application cannot not know when it is safe to clean up objects. + +Applicationss are expected to keep any execution time in the callbacks **to a minimum**. +MsQuic does not use separate threads for the protocol execution and upcalls to the application. +Therefore, any significant delays on the callback **will delay the protocol**. +Any significant time or work needed to be completed by the application must happen on its own thread. + +This doesn't mean the application isn't allowed to do any work in the callback handler. +In fact, many things are expressly designed to be most efficient when the application does them on the callback. +For instance, closing a handle to a connection or stream is ideally implemented in the "shutdown complete" indications. + +One important aspect of this design is that all blocking API (down) calls invoked on a callback always happen inline (to prevent deadlocks), and will supercede any calls in progress or queued from a separate thread. + +## Threading + + From 74835cf5c4d73e719b795cf5aa8d97c6811f2093 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Thu, 31 Oct 2024 08:33:35 -0400 Subject: [PATCH 11/34] docs --- docs/API.md | 10 +--------- docs/Execution.md | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/docs/API.md b/docs/API.md index 7b270b7fcc..535898cdc4 100644 --- a/docs/API.md +++ b/docs/API.md @@ -67,15 +67,7 @@ The **PATCH** version **only changes** when a servicing fix is made to an existi ## Execution Mode -In general, MsQuic uses a callback model for all asynchronous events up to the app. This includes things like connection state changes, new streams being created, stream data being received, and stream sends completing. All these events are indicated to the app via the callback handler, on a thread owned by MsQuic. - -Generally, MsQuic creates multiple threads to parallelize work, and therefore will make parallel/overlapping upcalls to the application, but not for the same connection. All upcalls to the app for a single connection and all child streams are always delivered serially. This is not to say, though, it will always be on the same thread. MsQuic does support the ability to shuffle connections around to better balance the load. - -Apps are expected to keep any execution time in the callback **to a minimum**. MsQuic does not use separate threads for the protocol execution and upcalls to the app. Therefore, any significant delays on the callback **will delay the protocol**. Any significant time or work needed to be completed by the app must happen on its own thread. - -This doesn't mean the app isn't allowed to do any work in the callback. In fact, many things are expressly designed to be most efficient when the app does them on the callback. For instance, closing a handle to a connection or stream is ideally implemented in the "shutdown complete" indications. - -One important aspect of this design is that all blocking calls invoked on a callback always happen inline (to prevent deadlocks), and will supercede any calls in progress or queued from a separate thread. +MsQuic has a very different execution model than classic BSD-style sockets. Please see [Execution](./Execution.md) for more details on how threads and upcalls are handled inside of MsQuic. ## Settings and Configuration diff --git a/docs/Execution.md b/docs/Execution.md index 38f5be228f..de79bbd320 100644 --- a/docs/Execution.md +++ b/docs/Execution.md @@ -1,12 +1,13 @@ Execution ====== -The MsQuic API takes a very difference stance when it comes to its execution model compared to BSD sockets (and most other networking libraries built on top of them). +The MsQuic API takes a very difference stance when it comes to its execution model compared to BSD-style sockets (and most other networking libraries built on top of them). The sections below detail the designs MsQuic uses, with some of the details as to why these design choices were made. ## Event Model In the MsQuic API, all state changes and other notifications are indicated directly to the application via a callback. +This includes things like connection state changes, new streams being created, stream data being received, and stream sends completing. ```c typedef struct QUIC_LISTENER_EVENT { @@ -44,7 +45,7 @@ This difference was made for several reasons: ### Writing Event Handlers -Event handlers are **required** for all objects (that support them), because much of the MsQuic API happens through these callbacks. +Event handlers are **required** for all objects (that have them), because much of the MsQuic API happens through these callbacks. Additionally, important events, such as "shutdown complete" events provide crucial information to the application to function properly. Without these events, the application cannot not know when it is safe to clean up objects. @@ -61,4 +62,22 @@ One important aspect of this design is that all blocking API (down) calls invoke ## Threading +By default, MsQuic creates its own threads to manage execution its logic. +The nature of the number and configuration of these threads depends on the configuration the apps passes to [RegistrationOpen](api/RegistrationOpen.md) or `QUIC_PARAM_GLOBAL_EXECUTION_CONFIG`. +The default behavior is to create dedicated, per-processor threads that are hard affinitized to a given NUMA-node, and soft-affinitized (set 'ideal processor') to a given processor. +These threads are then used to drive both the datapath (i.e. UDP) and QUIC layers. +Great care it taken to (try to) align the MsQuic processing logic with the rest of the networking stack (including hardware RSS) so that all processing stays at least on the same NUMA node, but ideally the same processor. + +The complexity required to achieve alignment in processing across various threads and processors is why MsQuic takes the stance of managing all its own threading by default. +The goal is to abstract all this complexity from the many applications that build on top, so every app doesn't have to build the necessary logic to do this itself. +Things should 'just work' out of the box. + +Each of these threads manage the execution of one or more connections. +All connections are spread across the various threads based on their RSS alignment, which generally should evenly spread the traffic based on the different UDP tuples used. +Each connection and all derived state (i.e., streams) are managed and executed by a single thread at a time; but may move across threads to align with any RSS changes. +This means that each connection and its streams is effectively single-threaded, including all upcalls to the application layer. +MsQuic will **never** make upcalls for a single connection or any of its streams in parallel. + +For listeners, the application callback will be called in parallel for new connections. +This allows server applications to scale efficiently with the number of processors. From b743020fa5711cf0d4e69f7ecb8645b046729cfb Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Fri, 6 Dec 2024 12:40:12 -0500 Subject: [PATCH 12/34] A bit of documentation --- docs/Execution.md | 62 +++++++++++++++++++++++ src/tools/execution/execution_windows.cpp | 10 ++-- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/docs/Execution.md b/docs/Execution.md index 7d7259e7a1..3ccf15d00f 100644 --- a/docs/Execution.md +++ b/docs/Execution.md @@ -109,3 +109,65 @@ 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 struct QUIC_CQE { + OVERLAPPED_ENTRY Overlapped; + void (*Completion)(struct QUIC_CQE *Cqe); +} QUIC_CQE; +``` + +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). + +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. +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_CQE* Cqe = CONTAINING_RECORD(Overlapped[i].lpOverlapped, QUIC_CQE, Overlapped); + Cqe->Completion(Cqe); + } + } +} +``` + +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. + +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**. +This is necessary to be able to share the same event queue object. diff --git a/src/tools/execution/execution_windows.cpp b/src/tools/execution/execution_windows.cpp index 1a012b3a0e..9ed10b794c 100644 --- a/src/tools/execution/execution_windows.cpp +++ b/src/tools/execution/execution_windows.cpp @@ -39,10 +39,14 @@ main( if (!_MsQuic.IsValid()) { return 1; } MsQuic = &_MsQuic; - QUIC_STATUS Status; IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); - QUIC_EXECUTION_CONTEXT_CONFIG ExecConfig = { 0, &IOCP }; + if (IOCP == nullptr) { + return 1; + } + + QUIC_STATUS Status; QUIC_EXECUTION_CONTEXT* ExecContext = nullptr; + QUIC_EXECUTION_CONTEXT_CONFIG ExecConfig = { 0, &IOCP }; if (QUIC_FAILED(Status = MsQuic->ExecutionCreate(QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 1, &ExecConfig, &ExecContext))) { return 1; } @@ -81,8 +85,8 @@ main( while (!AllDone) { uint32_t WaitTime = MsQuic->ExecutionPoll(ExecContext); - OVERLAPPED_ENTRY Overlapped[8]; ULONG OverlappedCount = 0; + 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); From dd7abe01a62730aea44cc6dc643d87d987b710d4 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Fri, 6 Dec 2024 12:42:29 -0500 Subject: [PATCH 13/34] c++ formatting is nicer --- docs/Execution.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Execution.md b/docs/Execution.md index 3ccf15d00f..36e764409d 100644 --- a/docs/Execution.md +++ b/docs/Execution.md @@ -124,7 +124,7 @@ To create an execution context, the app much first create an event queue object, On Windows, the following types are defined: -```c +```c++ typedef HANDLE QUIC_EVENTQ; typedef struct QUIC_CQE { @@ -137,7 +137,7 @@ You will also notice the definiton for `QUIC_CQE` (CQE stands for completion que Once the app has the event queue, it may create the execution context with the `ExecutionCreate` function: -```c +```c++ HANDLE IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); QUIC_EXECUTION_CONTEXT_CONFIG ExecConfig = { 0, &IOCP }; @@ -150,7 +150,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); From 919ae67fa97a3b26d8bfbc98d22c8b9ae0e57e30 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Sat, 4 Jan 2025 10:05:40 -0500 Subject: [PATCH 14/34] Updates to latest design --- docs/Execution.md | 30 +++++++++++++------- src/inc/msquic_posix.h | 34 +++++++++++++++++------ src/inc/msquic_winuser.h | 21 +++++++++++--- src/tools/execution/execution_windows.cpp | 14 +++++----- 4 files changed, 70 insertions(+), 29 deletions(-) diff --git a/docs/Execution.md b/docs/Execution.md index 36e764409d..8bc6566f93 100644 --- a/docs/Execution.md +++ b/docs/Execution.md @@ -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: @@ -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); @@ -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. diff --git a/src/inc/msquic_posix.h b/src/inc/msquic_posix.h index 67789cd45b..15541f0dbd 100644 --- a/src/inc/msquic_posix.h +++ b/src/inc/msquic_posix.h @@ -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 @@ -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 diff --git a/src/inc/msquic_winuser.h b/src/inc/msquic_winuser.h index 0201e4e6d6..182878d3a9 100644 --- a/src/inc/msquic_winuser.h +++ b/src/inc/msquic_winuser.h @@ -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_ diff --git a/src/tools/execution/execution_windows.cpp b/src/tools/execution/execution_windows.cpp index 9ed10b794c..cc44d897eb 100644 --- a/src/tools/execution/execution_windows.cpp +++ b/src/tools/execution/execution_windows.cpp @@ -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; @@ -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]); } } } From 94b4c3f6ab713557d2462ff73097400675a61849 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 8 Jan 2025 12:42:41 -0500 Subject: [PATCH 15/34] wip --- src/core/api.h | 26 ++++++++++++++++++++++++++ src/core/library.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/core/api.h b/src/core/api.h index 87c6631a04..c693c569da 100644 --- a/src/core/api.h +++ b/src/core/api.h @@ -5,6 +5,32 @@ --*/ +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +QUIC_API +MsQuicExecutionCreate( + _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type + _In_ uint32_t PollingIdleTimeoutUs, + _In_ uint32_t Count, + _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs, + _Out_writes_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +void +QUIC_API +MsQuicExecutionDelete( + _In_ uint32_t Count, + _In_reads_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +uint32_t +QUIC_API +MsQuicExecutionPoll( + _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext + ); + _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS QUIC_API diff --git a/src/core/library.c b/src/core/library.c index a83973b8cc..51440feca4 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -1800,6 +1800,12 @@ MsQuicOpenVersion( Api->StreamReceiveSetEnabled = MsQuicStreamReceiveSetEnabled; Api->DatagramSend = MsQuicDatagramSend; + +#ifndef _KERNEL_MODE + Api->ExecutionCreate = MsQuicExecutionCreate; + Api->ExecutionDelete = MsQuicExecutionDelete; + Api->ExecutionPoll = MsQuicExecutionPoll; +#endif *QuicApi = Api; @@ -2455,3 +2461,42 @@ QuicLibraryGenerateStatelessResetToken( } return Status; } + +#ifndef _KERNEL_MODE + +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +QUIC_API +MsQuicExecutionCreate( + _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type + _In_ uint32_t PollingIdleTimeoutUs, + _In_ uint32_t Count, + _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs, + _Out_writes_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + ) +{ + +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +void +QUIC_API +MsQuicExecutionDelete( + _In_ uint32_t Count, + _In_reads_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + ) +{ + +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +uint32_t +QUIC_API +MsQuicExecutionPoll( + _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext + ) +{ + +} + +#endif From 446a9f3ca76feb053f8adb40c612f8c56d06edde Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Wed, 22 Jan 2025 10:11:22 -0500 Subject: [PATCH 16/34] wip --- src/inc/msquic.h | 12 ++ src/inc/msquic.hpp | 53 ++++++- src/tools/execution/execution_windows.cpp | 170 ++++++++++++++-------- 3 files changed, 177 insertions(+), 58 deletions(-) diff --git a/src/inc/msquic.h b/src/inc/msquic.h index 83d6b22299..a09a132e61 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -323,6 +323,18 @@ QUIC_STATUS _Out_writes_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts ); +// +// This is called to stop any further usage of the execution contexts. MsQuic +// will no longer queue any additional work to the contexts. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +void +(QUIC_API * QUIC_EXECUTION_SHUTDOWN_FN)( + _In_ uint32_t Count, + _In_reads_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + ); + // // This is called to delete the execution contexts. // diff --git a/src/inc/msquic.hpp b/src/inc/msquic.hpp index f051e84139..529b6391b9 100644 --- a/src/inc/msquic.hpp +++ b/src/inc/msquic.hpp @@ -433,6 +433,57 @@ class MsQuicApi : public QUIC_API_TABLE { extern const MsQuicApi* MsQuic; +struct MsQuicExecutionContext : public QUIC_EXECUTION_CONTEXT { + uint32_t Poll() noexcept { + return MsQuic->ExecutionPoll(this); + } +}; + +struct MsQuicExecution { + MsQuicExecutionContext* Contexts {nullptr}; + uint32_t Count {0}; + MsQuicExecution(QUIC_EVENTQ* EventQ, QUIC_EXECUTION_CONFIG_FLAGS Flags = QUIC_EXECUTION_CONFIG_FLAG_NONE, uint32_t PollingIdleTimeoutUs = 0) noexcept : Count(1) { + QUIC_EXECUTION_CONTEXT_CONFIG Config = { 0, EventQ }; + Initialize(Flags, PollingIdleTimeoutUs, &Config); + } + MsQuicExecution(QUIC_EVENTQ** EventQ, uint32_t Count, QUIC_EXECUTION_CONFIG_FLAGS Flags = QUIC_EXECUTION_CONFIG_FLAG_NONE, uint32_t PollingIdleTimeoutUs = 0) noexcept : Count(Count) { + auto Configs = new(std::nothrow) QUIC_EXECUTION_CONTEXT_CONFIG[Count]; + if (Configs != nullptr) { + for (uint32_t i = 0; i < Count; ++i) { + Configs[i].IdealProcessor = i; + Configs[i].EventQ = EventQ[i]; + } + Initialize(Flags, PollingIdleTimeoutUs, Configs); + delete [] Configs; + } + } + void Initialize( + _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type + _In_ uint32_t PollingIdleTimeoutUs, + _In_reads_(this->Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs + ) + { + Contexts = new(std::nothrow) MsQuicExecutionContext[Count]; + if (Contexts != nullptr) { + auto Status = + MsQuic->ExecutionCreate( + Flags, + PollingIdleTimeoutUs, + Count, + Configs, + &Contexts); + if (QUIC_FAILED(Status)) { + delete [] Contexts; + Contexts = nullptr; + } + } + } + bool IsValid() const noexcept { return Contexts != nullptr; } + MsQuicExecutionContext& operator[](size_t i) const { + return *(Contexts + i); + } +}; + struct MsQuicRegistration { bool CloseAllConnectionsOnDelete {false}; HQUIC Handle {nullptr}; @@ -1687,4 +1738,4 @@ struct QuicBufferScope { ~QuicBufferScope() noexcept { if (Buffer) { delete[](uint8_t*) Buffer; } } }; -#endif // _WIN32 +#endif // _MSQUIC_HPP_ diff --git a/src/tools/execution/execution_windows.cpp b/src/tools/execution/execution_windows.cpp index cc44d897eb..6d18b40887 100644 --- a/src/tools/execution/execution_windows.cpp +++ b/src/tools/execution/execution_windows.cpp @@ -26,79 +26,135 @@ void PrintUsage() ); } -HANDLE IOCP; +struct WindowsIOCP { + HANDLE IOCP; + operator const HANDLE () const noexcept { return IOCP; } + WindowsIOCP() : IOCP(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)) { } + ~WindowsIOCP() { CloseHandle(IOCP); } + bool IsValid() const noexcept { return IOCP != nullptr; } + bool Enqueue( + _In_ LPOVERLAPPED lpOverlapped, + _In_ DWORD dwNumberOfBytesTransferred = 0, + _In_ ULONG_PTR dwCompletionKey = 0 + ) noexcept { + return PostQueuedCompletionStatus(IOCP, dwNumberOfBytesTransferred, dwCompletionKey, lpOverlapped); + } + bool Dequeue( + _Out_writes_to_(ulCount,*ulNumEntriesRemoved) LPOVERLAPPED_ENTRY lpCompletionPortEntries, + _In_ ULONG ulCount, + _Out_ PULONG ulNumEntriesRemoved, + _In_ DWORD dwMilliseconds + ) noexcept { + return GetQueuedCompletionStatusEx(IOCP, lpCompletionPortEntries, ulCount, ulNumEntriesRemoved, dwMilliseconds, FALSE); + } +}; + +char* Host = nullptr; +WindowsIOCP* IOCP; +MsQuicRegistration* Registration; +MsQuicConnection* Connection; +bool AllDone = false; + +void QueueCleanupJob() { + auto Sqe = new(std::nothrow) QUIC_SQE; + ZeroMemory(&Sqe->Overlapped, sizeof(Sqe->Overlapped)); + Sqe->Completion = [](QUIC_CQE* Cqe) { + AllDone = true; + delete CONTAINING_RECORD(Cqe->lpOverlapped, QUIC_SQE, Overlapped); + }; + IOCP->Enqueue(&Sqe->Overlapped); +} -int -QUIC_MAIN_EXPORT -main( - _In_ int argc, - _In_reads_(argc) _Null_terminated_ char* argv[] - ) -{ - MsQuicApi _MsQuic; - if (!_MsQuic.IsValid()) { return 1; } - MsQuic = &_MsQuic; +void QueueConnectedJob() { + auto Sqe = new(std::nothrow) QUIC_SQE; + ZeroMemory(&Sqe->Overlapped, sizeof(Sqe->Overlapped)); + Sqe->Completion = [](QUIC_CQE* Cqe) { + QuicAddr Addr; + Connection->GetRemoteAddr(Addr); + QUIC_ADDR_STR AddrStr; + QuicAddrToString(&Addr.SockAddr, &AddrStr); + printf("Connected to %s.\n", AddrStr.Address); + delete CONTAINING_RECORD(Cqe->lpOverlapped, QUIC_SQE, Overlapped); + }; + IOCP->Enqueue(&Sqe->Overlapped); +} - IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); - if (IOCP == nullptr) { - return 1; - } - - QUIC_STATUS Status; - QUIC_EXECUTION_CONTEXT* ExecContext = nullptr; - QUIC_EXECUTION_CONTEXT_CONFIG ExecConfig = { 0, &IOCP }; - if (QUIC_FAILED(Status = MsQuic->ExecutionCreate(QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 1, &ExecConfig, &ExecContext))) { - return 1; +QUIC_STATUS MsQuicConnectionCallback(_In_ struct MsQuicConnection* Connection, _In_opt_ void*, _Inout_ QUIC_CONNECTION_EVENT* Event) { + if (Event->Type == QUIC_CONNECTION_EVENT_CONNECTED) { + QueueConnectedJob(); + Connection->Shutdown(0); + } else if (Event->Type == QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE) { + QueueCleanupJob(); } +} + +void ConnectJob(QUIC_CQE* Cqe) { + bool Success = false; do { - bool AllDone = false; - MsQuicRegistration Registration("quicexec"); MsQuicSettings Settings; Settings.SetPeerUnidiStreamCount(3); // required for H3 MsQuicConfiguration Configuration(Registration, "h3", Settings, MsQuicCredentialConfig()); if (!Configuration.IsValid()) { break; } - 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 Sqe = new(std::nothrow) QUIC_SQE; - ZeroMemory(&Sqe->Overlapped, sizeof(Sqe->Overlapped)); - Sqe->Completion = [](QUIC_CQE* Cqe) { - printf("Connected.\n"); - delete CONTAINING_RECORD(Cqe->lpOverlapped, QUIC_SQE, Overlapped); - }; - PostQueuedCompletionStatus(IOCP, 0, 0, &Sqe->Overlapped); - Connection->Shutdown(0); - } else if (Event->Type == QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE) { - *((bool*)Context) = true; - } - } - }; - - MsQuicConnection Connection(Registration, CleanUpManual, ConnectionCallback::MsQuicConnectionCallback, &AllDone); - if (QUIC_FAILED( - Status = Connection.Start(Configuration, argv[1], 443))) { + Connection = new(std::nothrow) MsQuicConnection(Registration, CleanUpAutoDelete, MsQuicConnectionCallback); + if (QUIC_FAILED(Connection->Start(Configuration, Host, 443))) { + delete Connection; break; } - while (!AllDone) { - uint32_t WaitTime = MsQuic->ExecutionPoll(ExecContext); + Success = true; + } while (false); - 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]); - } - } - } + if (!Success) { + QueueCleanupJob(); + } - } while (false); + delete CONTAINING_RECORD(Cqe->lpOverlapped, QUIC_SQE, Overlapped); +} + +void QueueConnectJob() { + auto Sqe = new(std::nothrow) QUIC_SQE; + ZeroMemory(&Sqe->Overlapped, sizeof(Sqe->Overlapped)); + Sqe->Completion = ConnectJob; + IOCP->Enqueue(&Sqe->Overlapped); +} + +int +QUIC_MAIN_EXPORT +main( + _In_ int argc, + _In_reads_(argc) _Null_terminated_ char* argv[] + ) +{ + if (argc < 2) { return 1; } + Host = argv[1]; - MsQuic->ExecutionDelete(1, &ExecContext); - CloseHandle(IOCP); + WindowsIOCP _IOCP; if (!_IOCP.IsValid()) { return 1; } + IOCP = &_IOCP; + + MsQuicApi _MsQuic; if (!_MsQuic.IsValid()) { return 1; } + MsQuic = &_MsQuic; + + MsQuicExecution Execution(&_IOCP.IOCP); if (!Execution.IsValid()) { return 1; } + + MsQuicRegistration _Registration("quicexec"); if (!_Registration.IsValid()) { return 1; } + Registration = &_Registration; + + QueueConnectJob(); + + while (!AllDone) { + uint32_t WaitTime = Execution[0].Poll(); + + ULONG OverlappedCount = 0; + OVERLAPPED_ENTRY Overlapped[8]; + if (IOCP->Dequeue(Overlapped, ARRAYSIZE(Overlapped), &OverlappedCount, WaitTime)) { + for (ULONG i = 0; i < OverlappedCount; ++i) { + QUIC_SQE* Sqe = CONTAINING_RECORD(Overlapped[i].lpOverlapped, QUIC_SQE, Overlapped); + Sqe->Completion(&Overlapped[i]); + } + } + } return 0; } From c5b97f89348acd2fca876811f9d969f5f73ae2f8 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Thu, 17 Apr 2025 09:11:17 -0400 Subject: [PATCH 17/34] WIP --- src/core/library.c | 38 +++-- src/inc/quic_platform.h | 6 + src/platform/platform_worker.c | 252 ++++++++++++++++++++++----------- 3 files changed, 202 insertions(+), 94 deletions(-) diff --git a/src/core/library.c b/src/core/library.c index 4322c5729a..f5fb533887 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -686,11 +686,13 @@ QuicLibraryLazyInitialize( } #ifndef _KERNEL_MODE - MsQuicLib.WorkerPool = CxPlatWorkerPoolCreate(MsQuicLib.ExecutionConfig); - if (!MsQuicLib.WorkerPool) { - Status = QUIC_STATUS_OUT_OF_MEMORY; - MsQuicLibraryFreePartitions(); - goto Exit; + if (MsQuicLib.WorkerPool == NULL) { + MsQuicLib.WorkerPool = CxPlatWorkerPoolCreate(MsQuicLib.ExecutionConfig); + if (!MsQuicLib.WorkerPool) { + Status = QUIC_STATUS_OUT_OF_MEMORY; + MsQuicLibraryFreePartitions(); + goto Exit; + } } #endif @@ -1820,7 +1822,7 @@ MsQuicOpenVersion( Api->StreamProvideReceiveBuffers = MsQuicStreamProvideReceiveBuffers; Api->DatagramSend = MsQuicDatagramSend; - + #ifndef _KERNEL_MODE Api->ExecutionCreate = MsQuicExecutionCreate; Api->ExecutionDelete = MsQuicExecutionDelete; @@ -2429,7 +2431,25 @@ MsQuicExecutionCreate( _Out_writes_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts ) { - + if (MsQuicLib.LazyInitComplete) { + // + // Not allowed to change the execution config after we've already + // started running the library. + // + return QUIC_STATUS_INVALID_STATE; + } + + // + // Clean up any previous worker pool and create a new one. + // + CxPlatWorkerPoolDelete(MsQuicLib.WorkerPool); + MsQuicLib.WorkerPool = + CxPlatWorkerPoolCreateExternal(Count, Configs); + if (MsQuicLib.WorkerPool == NULL) { + return QUIC_STATUS_OUT_OF_MEMORY; + } + + return QUIC_STATUS_SUCCESS; } _IRQL_requires_max_(PASSIVE_LEVEL) @@ -2440,7 +2460,7 @@ MsQuicExecutionDelete( _In_reads_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts ) { - + } _IRQL_requires_max_(PASSIVE_LEVEL) @@ -2450,7 +2470,7 @@ MsQuicExecutionPoll( _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext ) { - + } #endif diff --git a/src/inc/quic_platform.h b/src/inc/quic_platform.h index ec53487996..ac90c004dc 100644 --- a/src/inc/quic_platform.h +++ b/src/inc/quic_platform.h @@ -470,6 +470,12 @@ CxPlatWorkerPoolCreate( _In_opt_ QUIC_EXECUTION_CONFIG* Config ); +CXPLAT_WORKER_POOL* +CxPlatWorkerPoolCreateExternal( + _In_ uint32_t Count, + _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs + ); + void CxPlatWorkerPoolDelete( _In_opt_ CXPLAT_WORKER_POOL* WorkerPool diff --git a/src/platform/platform_worker.c b/src/platform/platform_worker.c index 23fb6c4a70..ab0372e7c6 100644 --- a/src/platform/platform_worker.c +++ b/src/platform/platform_worker.c @@ -144,9 +144,107 @@ UpdatePollCompletion( CxPlatUpdateExecutionContexts(Worker); } -#pragma warning(push) -#pragma warning(disable:6385) -#pragma warning(disable:6386) // SAL is confused about the worker size +BOOLEAN +CxPlatWorkerPoolInitWorker( + _Inout_ CXPLAT_WORKER* Worker, + _In_ uint16_t IdealProcessor, + _In_opt_ CXPLAT_EVENTQ* EventQ, // Only for external workers + _In_opt_ CXPLAT_THREAD_CONFIG* ThreadConfig // Only for internal workers + ) +{ + CxPlatLockInitialize(&Worker->ECLock); + CxPlatListInitializeHead(&Worker->DynamicPoolList); + Worker->InitializedECLock = TRUE; + Worker->IdealProcessor = IdealProcessor; + + if (EventQ != NULL) { + Worker->EventQ = *EventQ; + } else { + if (!CxPlatEventQInitialize(&Worker->EventQ)) { + QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "CxPlatEventQInitialize"); + return FALSE; + } + Worker->InitializedEventQ = TRUE; + } + + if (!CxPlatSqeInitialize(&Worker->EventQ, ShutdownCompletion, &Worker->ShutdownSqe)) { + QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "CxPlatSqeInitialize(shutdown)"); + return FALSE; + } + Worker->InitializedShutdownSqe = TRUE; + + if (!CxPlatSqeInitialize(&Worker->EventQ, WakeCompletion, &Worker->WakeSqe)) { + QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "CxPlatSqeInitialize(wake)"); + return FALSE; + } + Worker->InitializedWakeSqe = TRUE; + + if (!CxPlatSqeInitialize(&Worker->EventQ, UpdatePollCompletion, &Worker->UpdatePollSqe)) { + QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "CxPlatSqeInitialize(updatepoll)"); + return FALSE; + } + Worker->InitializedUpdatePollSqe = TRUE; + + if (ThreadConfig != NULL) { + ThreadConfig->IdealProcessor = IdealProcessor; + ThreadConfig->Context = Worker; + if (QUIC_FAILED( + CxPlatThreadCreate(ThreadConfig, &Worker->Thread))) { + return FALSE; + } + Worker->InitializedThread = TRUE; + } + + return TRUE; +} + +void +CxPlatWorkerPoolDestroyWorker( + _In_ CXPLAT_WORKER* Worker + ) +{ + if (Worker->InitializedThread) { + Worker->StoppingThread = TRUE; + CxPlatEventQEnqueue(&Worker->EventQ, &Worker->ShutdownSqe); + CxPlatThreadWait(&Worker->Thread); + CxPlatThreadDelete(&Worker->Thread); +#if DEBUG + CXPLAT_DBG_ASSERT(Worker->ThreadStarted); + CXPLAT_DBG_ASSERT(Worker->ThreadFinished); +#endif + Worker->DestroyedThread = TRUE; + } else { + // TODO - Handle synchronized cleanup for external event queues? + } + if (Worker->InitializedUpdatePollSqe) { + CxPlatSqeCleanup(&Worker->EventQ, &Worker->UpdatePollSqe); + } + if (Worker->InitializedWakeSqe) { + CxPlatSqeCleanup(&Worker->EventQ, &Worker->WakeSqe); + } + if (Worker->InitializedShutdownSqe) { + CxPlatSqeCleanup(&Worker->EventQ, &Worker->ShutdownSqe); + } + if (Worker->InitializedEventQ) { + CxPlatEventQCleanup(&Worker->EventQ); + } + if (Worker->InitializedECLock) { + CxPlatLockUninitialize(&Worker->ECLock); + } +} + CXPLAT_WORKER_POOL* CxPlatWorkerPoolCreate( _In_opt_ QUIC_EXECUTION_CONFIG* Config @@ -216,51 +314,75 @@ CxPlatWorkerPoolCreate( // the worker. // for (uint32_t i = 0; i < WorkerPool->WorkerCount; ++i) { + const uint16_t IdealProcessor = ProcessorList ? ProcessorList[i] : (uint16_t)i; + CXPLAT_DBG_ASSERT(IdealProcessor < CxPlatProcCount()); + CXPLAT_WORKER* Worker = &WorkerPool->Workers[i]; - CxPlatLockInitialize(&Worker->ECLock); - CxPlatListInitializeHead(&Worker->DynamicPoolList); - Worker->InitializedECLock = TRUE; - Worker->IdealProcessor = ProcessorList ? ProcessorList[i] : (uint16_t)i; - CXPLAT_DBG_ASSERT(Worker->IdealProcessor < CxPlatProcCount()); - ThreadConfig.IdealProcessor = Worker->IdealProcessor; - ThreadConfig.Context = &WorkerPool->Workers[i]; - if (!CxPlatEventQInitialize(&Worker->EventQ)) { - QuicTraceEvent( - LibraryError, - "[ lib] ERROR, %s.", - "CxPlatEventQInitialize"); - goto Error; - } - Worker->InitializedEventQ = TRUE; - if (!CxPlatSqeInitialize(&Worker->EventQ, ShutdownCompletion, &Worker->ShutdownSqe)) { - QuicTraceEvent( - LibraryError, - "[ lib] ERROR, %s.", - "CxPlatSqeInitialize(shutdown)"); - goto Error; - } - Worker->InitializedShutdownSqe = TRUE; - if (!CxPlatSqeInitialize(&Worker->EventQ, WakeCompletion, &Worker->WakeSqe)) { - QuicTraceEvent( - LibraryError, - "[ lib] ERROR, %s.", - "CxPlatSqeInitialize(wake)"); - goto Error; - } - Worker->InitializedWakeSqe = TRUE; - if (!CxPlatSqeInitialize(&Worker->EventQ, UpdatePollCompletion, &Worker->UpdatePollSqe)) { - QuicTraceEvent( - LibraryError, - "[ lib] ERROR, %s.", - "CxPlatSqeInitialize(updatepoll)"); + if (!CxPlatWorkerPoolInitWorker( + Worker, IdealProcessor, NULL, &ThreadConfig)) { goto Error; } - Worker->InitializedUpdatePollSqe = TRUE; - if (QUIC_FAILED( - CxPlatThreadCreate(&ThreadConfig, &Worker->Thread))) { + } + + CxPlatRundownInitialize(&WorkerPool->Rundown); + + return WorkerPool; + +Error: + + // + // On failure, clean up all the workers that did get started. + // + for (uint32_t i = 0; i < WorkerPool->WorkerCount; ++i) { + CXPLAT_WORKER* Worker = &WorkerPool->Workers[i]; + CxPlatWorkerPoolDestroyWorker(Worker); + } + + CXPLAT_FREE(WorkerPool, QUIC_POOL_PLATFORM_WORKER); + + return NULL; +} + +CXPLAT_WORKER_POOL* +CxPlatWorkerPoolCreateExternal( + _In_ uint32_t Count, + _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs + ) +{ + CXPLAT_DBG_ASSERT(Count > 0 && Count <= UINT16_MAX); + + // + // Allocate enough space for the pool and worker structs. + // + const size_t WorkerPoolSize = + sizeof(CXPLAT_WORKER_POOL) + sizeof(CXPLAT_WORKER) * Count; + CXPLAT_WORKER_POOL* WorkerPool = + CXPLAT_ALLOC_PAGED(WorkerPoolSize, QUIC_POOL_PLATFORM_WORKER); + if (WorkerPool == NULL) { + QuicTraceEvent( + AllocFailure, + "Allocation of '%s' failed. (%llu bytes)", + "CXPLAT_WORKER_POOL", + WorkerPoolSize); + return NULL; + } + CxPlatZeroMemory(WorkerPool, WorkerPoolSize); + WorkerPool->WorkerCount = Count; + + // + // Set up each worker thread with the configuration initialized above. Also + // creates the event queue and all the SQEs used to shutdown, wake and poll + // the worker. + // + for (uint32_t i = 0; i < Count; ++i) { + const uint16_t IdealProcessor = (uint16_t)Configs[i].IdealProcessor; + CXPLAT_DBG_ASSERT(IdealProcessor < CxPlatProcCount()); + + CXPLAT_WORKER* Worker = &WorkerPool->Workers[i]; + if (!CxPlatWorkerPoolInitWorker( + Worker, IdealProcessor, &Configs[i].EventQ, NULL)) { goto Error; } - Worker->InitializedThread = TRUE; } CxPlatRundownInitialize(&WorkerPool->Rundown); @@ -274,39 +396,13 @@ CxPlatWorkerPoolCreate( // for (uint32_t i = 0; i < WorkerPool->WorkerCount; ++i) { CXPLAT_WORKER* Worker = &WorkerPool->Workers[i]; - if (Worker->InitializedThread) { - Worker->StoppingThread = TRUE; - CxPlatEventQEnqueue(&Worker->EventQ, &Worker->ShutdownSqe); - CxPlatThreadWait(&Worker->Thread); - CxPlatThreadDelete(&Worker->Thread); -#if DEBUG - CXPLAT_DBG_ASSERT(Worker->ThreadStarted); - CXPLAT_DBG_ASSERT(Worker->ThreadFinished); -#endif - Worker->DestroyedThread = TRUE; - } - if (Worker->InitializedUpdatePollSqe) { - CxPlatSqeCleanup(&Worker->EventQ, &Worker->UpdatePollSqe); - } - if (Worker->InitializedWakeSqe) { - CxPlatSqeCleanup(&Worker->EventQ, &Worker->WakeSqe); - } - if (Worker->InitializedShutdownSqe) { - CxPlatSqeCleanup(&Worker->EventQ, &Worker->ShutdownSqe); - } - if (Worker->InitializedEventQ) { - CxPlatEventQCleanup(&Worker->EventQ); - } - if (Worker->InitializedECLock) { - CxPlatLockUninitialize(&Worker->ECLock); - } + CxPlatWorkerPoolDestroyWorker(Worker); } CXPLAT_FREE(WorkerPool, QUIC_POOL_PLATFORM_WORKER); return NULL; } -#pragma warning(pop) void CxPlatWorkerPoolDelete( @@ -318,21 +414,7 @@ CxPlatWorkerPoolDelete( for (uint32_t i = 0; i < WorkerPool->WorkerCount; ++i) { CXPLAT_WORKER* Worker = &WorkerPool->Workers[i]; - Worker->StoppingThread = TRUE; - CxPlatEventQEnqueue(&Worker->EventQ, &Worker->ShutdownSqe); - CxPlatThreadWait(&Worker->Thread); - CxPlatThreadDelete(&Worker->Thread); -#if DEBUG - CXPLAT_DBG_ASSERT(Worker->ThreadStarted); - CXPLAT_DBG_ASSERT(Worker->ThreadFinished); -#endif - Worker->DestroyedThread = TRUE; - CxPlatSqeCleanup(&Worker->EventQ, &Worker->UpdatePollSqe); - CxPlatSqeCleanup(&Worker->EventQ, &Worker->WakeSqe); - CxPlatSqeCleanup(&Worker->EventQ, &Worker->ShutdownSqe); - CxPlatEventQCleanup(&Worker->EventQ); - CXPLAT_DBG_ASSERT(CxPlatListIsEmpty(&Worker->DynamicPoolList)); - CxPlatLockUninitialize(&Worker->ECLock); + CxPlatWorkerPoolDestroyWorker(Worker); } CxPlatRundownUninitialize(&WorkerPool->Rundown); From 9603086a986aa11ead194c3b350b10fdbbdad857 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Mon, 28 Apr 2025 09:17:21 -0400 Subject: [PATCH 18/34] wip --- docs/Execution.md | 4 +- src/core/api.h | 8 +-- src/core/library.c | 17 ++++--- src/cs/lib/msquic_generated.cs | 10 ++-- src/inc/msquic.h | 16 +++--- src/inc/msquic.hpp | 30 +++++------ src/inc/quic_platform.h | 11 +++- src/platform/platform_worker.c | 93 ++++++++++++++++++++++------------ 8 files changed, 111 insertions(+), 78 deletions(-) diff --git a/docs/Execution.md b/docs/Execution.md index b8b0fc6b39..018541c698 100644 --- a/docs/Execution.md +++ b/docs/Execution.md @@ -155,9 +155,9 @@ Once the app has the event queue, it may create the execution context with the ` ```c++ HANDLE IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); -QUIC_EXECUTION_CONTEXT_CONFIG ExecConfig = { 0, &IOCP }; +QUIC_EXECUTION_CONFIG_2 ExecConfig = { 0, &IOCP }; -QUIC_EXECUTION_CONTEXT* ExecContext = nullptr; +QUIC_EXECUTION* ExecContext = nullptr; QUIC_STATUS Status = MsQuic->ExecutionCreate(QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 1, &ExecConfig, &ExecContext); ``` diff --git a/src/core/api.h b/src/core/api.h index df9be3bdf6..a174402a1b 100644 --- a/src/core/api.h +++ b/src/core/api.h @@ -12,8 +12,8 @@ MsQuicExecutionCreate( _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs, - _Out_writes_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + _In_reads_(Count) QUIC_EXECUTION_CONFIG_2* Configs, + _Out_writes_(Count) QUIC_EXECUTION* Executions ); _IRQL_requires_max_(PASSIVE_LEVEL) @@ -21,14 +21,14 @@ void QUIC_API MsQuicExecutionDelete( _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + _In_reads_(Count) QUIC_EXECUTION* Executions ); _IRQL_requires_max_(PASSIVE_LEVEL) uint32_t QUIC_API MsQuicExecutionPoll( - _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext + _In_ QUIC_EXECUTION* Execution ); _IRQL_requires_max_(PASSIVE_LEVEL) diff --git a/src/core/library.c b/src/core/library.c index f5fb533887..49357a6c5b 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -2427,8 +2427,8 @@ MsQuicExecutionCreate( _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs, - _Out_writes_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + _In_reads_(Count) QUIC_EXECUTION_CONFIG_2* Configs, + _Out_writes_(Count) QUIC_EXECUTION* Executions ) { if (MsQuicLib.LazyInitComplete) { @@ -2444,7 +2444,7 @@ MsQuicExecutionCreate( // CxPlatWorkerPoolDelete(MsQuicLib.WorkerPool); MsQuicLib.WorkerPool = - CxPlatWorkerPoolCreateExternal(Count, Configs); + CxPlatWorkerPoolCreateExternal(Count, Configs, Executions); if (MsQuicLib.WorkerPool == NULL) { return QUIC_STATUS_OUT_OF_MEMORY; } @@ -2457,20 +2457,23 @@ void QUIC_API MsQuicExecutionDelete( _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + _In_reads_(Count) QUIC_EXECUTION* Executions ) { - + UNREFERENCED_PARAMETER(Count); + UNREFERENCED_PARAMETER(Executions); + CxPlatWorkerPoolDelete(MsQuicLib.WorkerPool); + MsQuicLib.WorkerPool = NULL; } _IRQL_requires_max_(PASSIVE_LEVEL) uint32_t QUIC_API MsQuicExecutionPoll( - _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext + _In_ QUIC_EXECUTION* Execution ) { - + return CxPlatWorkerPoolWorkerPoll(Execution); } #endif diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs index fba4d34026..7653e83e30 100644 --- a/src/cs/lib/msquic_generated.cs +++ b/src/cs/lib/msquic_generated.cs @@ -237,7 +237,7 @@ internal unsafe partial struct QUIC_EXECUTION_CONFIG internal fixed ushort ProcessorList[1]; } - internal unsafe partial struct QUIC_EXECUTION_CONTEXT_CONFIG + internal unsafe partial struct QUIC_EXECUTION_CONFIG_2 { [NativeTypeName("uint32_t")] internal uint IdealProcessor; @@ -246,7 +246,7 @@ internal unsafe partial struct QUIC_EXECUTION_CONTEXT_CONFIG internal void** EventQ; } - internal partial struct QUIC_EXECUTION_CONTEXT + internal partial struct QUIC_EXECUTION { } @@ -3411,13 +3411,13 @@ internal unsafe partial struct QUIC_API_TABLE internal delegate* unmanaged[Cdecl] ConnectionPoolCreate; [NativeTypeName("QUIC_EXECUTION_CREATE_FN")] - internal delegate* unmanaged[Cdecl] ExecutionCreate; + internal delegate* unmanaged[Cdecl] ExecutionCreate; [NativeTypeName("QUIC_EXECUTION_DELETE_FN")] - internal delegate* unmanaged[Cdecl] ExecutionDelete; + internal delegate* unmanaged[Cdecl] ExecutionDelete; [NativeTypeName("QUIC_EXECUTION_POLL_FN")] - internal delegate* unmanaged[Cdecl] ExecutionPoll; + internal delegate* unmanaged[Cdecl] ExecutionPoll; } internal static unsafe partial class MsQuic diff --git a/src/inc/msquic.h b/src/inc/msquic.h index 16a87e7755..47ba79d26e 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -306,12 +306,12 @@ typedef struct QUIC_EXECUTION_CONFIG { // completely control execution of all MsQuic work. // -typedef struct QUIC_EXECUTION_CONTEXT_CONFIG { +typedef struct QUIC_EXECUTION_CONFIG_2 { uint32_t IdealProcessor; QUIC_EVENTQ* EventQ; -} QUIC_EXECUTION_CONTEXT_CONFIG; +} QUIC_EXECUTION_CONFIG_2; -typedef struct QUIC_EXECUTION_CONTEXT QUIC_EXECUTION_CONTEXT; +typedef struct QUIC_EXECUTION QUIC_EXECUTION; // // This is called to create the execution contexts. @@ -323,8 +323,8 @@ QUIC_STATUS _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs, - _Out_writes_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + _In_reads_(Count) QUIC_EXECUTION_CONFIG_2* Configs, + _Out_writes_(Count) QUIC_EXECUTION** Executions ); // @@ -336,7 +336,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void (QUIC_API * QUIC_EXECUTION_SHUTDOWN_FN)( _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + _In_reads_(Count) QUIC_EXECUTION** Executions ); // @@ -347,7 +347,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void (QUIC_API * QUIC_EXECUTION_DELETE_FN)( _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONTEXT** ExecutionContexts + _In_reads_(Count) QUIC_EXECUTION** Executions ); // @@ -360,7 +360,7 @@ typedef _IRQL_requires_max_(PASSIVE_LEVEL) uint32_t (QUIC_API * QUIC_EXECUTION_POLL_FN)( - _In_ QUIC_EXECUTION_CONTEXT* ExecutionContext + _In_ QUIC_EXECUTION* Execution ); #endif // _KERNEL_MODE diff --git a/src/inc/msquic.hpp b/src/inc/msquic.hpp index 581f1b32c3..a18e9aa293 100644 --- a/src/inc/msquic.hpp +++ b/src/inc/msquic.hpp @@ -449,21 +449,15 @@ class MsQuicApi : public QUIC_API_TABLE { extern const MsQuicApi* MsQuic; -struct MsQuicExecutionContext : public QUIC_EXECUTION_CONTEXT { - uint32_t Poll() noexcept { - return MsQuic->ExecutionPoll(this); - } -}; - struct MsQuicExecution { - MsQuicExecutionContext* Contexts {nullptr}; + QUIC_EXECUTION* Executions {nullptr}; uint32_t Count {0}; MsQuicExecution(QUIC_EVENTQ* EventQ, QUIC_EXECUTION_CONFIG_FLAGS Flags = QUIC_EXECUTION_CONFIG_FLAG_NONE, uint32_t PollingIdleTimeoutUs = 0) noexcept : Count(1) { - QUIC_EXECUTION_CONTEXT_CONFIG Config = { 0, EventQ }; + QUIC_EXECUTION_CONFIG_2 Config = { 0, EventQ }; Initialize(Flags, PollingIdleTimeoutUs, &Config); } MsQuicExecution(QUIC_EVENTQ** EventQ, uint32_t Count, QUIC_EXECUTION_CONFIG_FLAGS Flags = QUIC_EXECUTION_CONFIG_FLAG_NONE, uint32_t PollingIdleTimeoutUs = 0) noexcept : Count(Count) { - auto Configs = new(std::nothrow) QUIC_EXECUTION_CONTEXT_CONFIG[Count]; + auto Configs = new(std::nothrow) QUIC_EXECUTION_CONFIG_2[Count]; if (Configs != nullptr) { for (uint32_t i = 0; i < Count; ++i) { Configs[i].IdealProcessor = i; @@ -476,27 +470,27 @@ struct MsQuicExecution { void Initialize( _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, - _In_reads_(this->Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs + _In_reads_(this->Count) QUIC_EXECUTION_CONFIG_2* Configs ) { - Contexts = new(std::nothrow) MsQuicExecutionContext[Count]; - if (Contexts != nullptr) { + Executions = new(std::nothrow) MsQuicExecutionContext[Count]; + if (Executions != nullptr) { auto Status = MsQuic->ExecutionCreate( Flags, PollingIdleTimeoutUs, Count, Configs, - &Contexts); + &Executions); if (QUIC_FAILED(Status)) { - delete [] Contexts; - Contexts = nullptr; + delete [] Executions; + Executions = nullptr; } } } - bool IsValid() const noexcept { return Contexts != nullptr; } - MsQuicExecutionContext& operator[](size_t i) const { - return *(Contexts + i); + bool IsValid() const noexcept { return Executions != nullptr; } + QUIC_EXECUTION& operator[](size_t i) const { + return *(Executions + i); } }; diff --git a/src/inc/quic_platform.h b/src/inc/quic_platform.h index ac90c004dc..220d32ab5c 100644 --- a/src/inc/quic_platform.h +++ b/src/inc/quic_platform.h @@ -444,8 +444,9 @@ CxPlatGetAllocFailDenominator( // loops. // +typedef struct QUIC_EXECUTION QUIC_EXECUTION; typedef struct QUIC_EXECUTION_CONFIG QUIC_EXECUTION_CONFIG; - +typedef struct QUIC_EXECUTION_CONFIG_2 QUIC_EXECUTION_CONFIG_2; typedef struct CXPLAT_EXECUTION_CONTEXT CXPLAT_EXECUTION_CONTEXT; typedef struct CXPLAT_EXECUTION_STATE { @@ -473,7 +474,8 @@ CxPlatWorkerPoolCreate( CXPLAT_WORKER_POOL* CxPlatWorkerPoolCreateExternal( _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs + _In_reads_(Count) QUIC_EXECUTION_CONFIG_2* Configs, + _Out_writes_(Count) QUIC_EXECUTION** Executions ); void @@ -509,6 +511,11 @@ CxPlatWorkerPoolAddExecutionContext( _In_ uint16_t Index // Into the execution config processor array ); +uint32_t +CxPlatWorkerPoolWorkerPoll( + _In_ QUIC_EXECUTION* Execution + ); + // // Supports more dynamic operations, but must be submitted to the platform worker // to manage. diff --git a/src/platform/platform_worker.c b/src/platform/platform_worker.c index ab0372e7c6..857546df21 100644 --- a/src/platform/platform_worker.c +++ b/src/platform/platform_worker.c @@ -18,7 +18,8 @@ typedef struct QUIC_CACHEALIGN CXPLAT_WORKER { // - // Thread used to drive the worker. + // Thread used to drive the worker. Only set when the worker is created and + // managed internally (default case). // CXPLAT_THREAD Thread; @@ -47,6 +48,11 @@ typedef struct QUIC_CACHEALIGN CXPLAT_WORKER { // CXPLAT_LOCK ECLock; + // + // The current execution state for the worker. + // + CXPLAT_EXECUTION_STATE State; + // // List of dynamic pools to manage. // @@ -156,6 +162,8 @@ CxPlatWorkerPoolInitWorker( CxPlatListInitializeHead(&Worker->DynamicPoolList); Worker->InitializedECLock = TRUE; Worker->IdealProcessor = IdealProcessor; + Worker->State.WaitTime = UINT32_MAX; + Worker->State.ThreadID = UINT32_MAX; if (EventQ != NULL) { Worker->EventQ = *EventQ; @@ -346,7 +354,8 @@ CxPlatWorkerPoolCreate( CXPLAT_WORKER_POOL* CxPlatWorkerPoolCreateExternal( _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONTEXT_CONFIG* Configs + _In_reads_(Count) QUIC_EXECUTION_CONFIG_2* Configs, + _Out_writes_(Count) QUIC_EXECUTION** Executions ) { CXPLAT_DBG_ASSERT(Count > 0 && Count <= UINT16_MAX); @@ -383,6 +392,7 @@ CxPlatWorkerPoolCreateExternal( Worker, IdealProcessor, &Configs[i].EventQ, NULL)) { goto Error; } + Executions[i] = (QUIC_EXECUTION*)Worker; } CxPlatRundownInitialize(&WorkerPool->Rundown); @@ -514,12 +524,11 @@ CxPlatUpdateExecutionContexts( void CxPlatRunExecutionContexts( - _In_ CXPLAT_WORKER* Worker, - _Inout_ CXPLAT_EXECUTION_STATE* State + _In_ CXPLAT_WORKER* Worker ) { if (Worker->ExecutionContexts == NULL) { - State->WaitTime = UINT32_MAX; + Worker->State.WaitTime = UINT32_MAX; return; } @@ -533,12 +542,12 @@ CxPlatRunExecutionContexts( CXPLAT_EXECUTION_CONTEXT* Context = CXPLAT_CONTAINING_RECORD(*EC, CXPLAT_EXECUTION_CONTEXT, Entry); BOOLEAN Ready = InterlockedFetchAndClearBoolean(&Context->Ready); - if (Ready || Context->NextTimeUs <= State->TimeNow) { + if (Ready || Context->NextTimeUs <= Worker->State.TimeNow) { #if DEBUG // Debug statistics ++Worker->EcRunCount; #endif CXPLAT_SLIST_ENTRY* Next = Context->Entry.Next; - if (!Context->Callback(Context->Context, State)) { + if (!Context->Callback(Context->Context, &Worker->State)) { *EC = Next; // Remove Context from the list. continue; } @@ -553,20 +562,37 @@ CxPlatRunExecutionContexts( } while (*EC != NULL); if (NextTime == 0) { - State->WaitTime = 0; + Worker->State.WaitTime = 0; } else if (NextTime != UINT64_MAX) { - uint64_t Diff = NextTime - State->TimeNow; + uint64_t Diff = NextTime - Worker->State.TimeNow; Diff = US_TO_MS(Diff); if (Diff == 0) { - State->WaitTime = 1; + Worker->State.WaitTime = 1; } else if (Diff < UINT32_MAX) { - State->WaitTime = (uint32_t)Diff; + Worker->State.WaitTime = (uint32_t)Diff; } else { - State->WaitTime = UINT32_MAX-1; + Worker->State.WaitTime = UINT32_MAX-1; } } else { - State->WaitTime = UINT32_MAX; + Worker->State.WaitTime = UINT32_MAX; + } +} + +uint32_t +CxPlatWorkerPoolWorkerPoll( + _In_ QUIC_EXECUTION* Execution + ) +{ + CXPLAT_WORKER* Worker = (CXPLAT_WORKER*)Execution; + Worker->State.ThreadID = CxPlatCurThreadID(); + + CxPlatRunExecutionContexts(Worker); + if (Worker->State.WaitTime && InterlockedFetchAndClearBoolean(&Worker->Running)) { + Worker->State.TimeNow = CxPlatTimeUs64(); + CxPlatRunExecutionContexts(Worker); // Run once more to handle race conditions } + + return Worker->State.WaitTime; } #define DYNAMIC_POOL_PROCESSING_PERIOD 1000000 // 1 second @@ -633,18 +659,22 @@ CxPlatProcessDynamicPoolAllocators( void CxPlatProcessEvents( - _In_ CXPLAT_WORKER* Worker, - _Inout_ CXPLAT_EXECUTION_STATE* State + _In_ CXPLAT_WORKER* Worker ) { CXPLAT_CQE Cqes[16]; - uint32_t CqeCount = CxPlatEventQDequeue(&Worker->EventQ, Cqes, ARRAYSIZE(Cqes), State->WaitTime); + uint32_t CqeCount = + CxPlatEventQDequeue( + &Worker->EventQ, + Cqes, + ARRAYSIZE(Cqes), + Worker->State.WaitTime); InterlockedFetchAndSetBoolean(&Worker->Running); if (CqeCount != 0) { #if DEBUG // Debug statistics Worker->CqeCount += CqeCount; #endif - State->NoWorkCount = 0; + Worker->State.NoWorkCount = 0; for (uint32_t i = 0; i < CqeCount; ++i) { CXPLAT_SQE* Sqe = CxPlatCqeGetSqe(&Cqes[i]); Sqe->Completion(&Cqes[i]); @@ -671,36 +701,35 @@ CXPLAT_THREAD_CALLBACK(CxPlatWorkerThread, Context) Worker->ThreadStarted = TRUE; #endif - CXPLAT_EXECUTION_STATE State = { 0, 0, 0, UINT32_MAX, 0, CxPlatCurThreadID() }; - + Worker->State.ThreadID = CxPlatCurThreadID(); Worker->Running = TRUE; while (!Worker->StoppedThread) { - ++State.NoWorkCount; + ++Worker->State.NoWorkCount; #if DEBUG // Debug statistics ++Worker->LoopCount; #endif - State.TimeNow = CxPlatTimeUs64(); + Worker->State.TimeNow = CxPlatTimeUs64(); - CxPlatRunExecutionContexts(Worker, &State); - if (State.WaitTime && InterlockedFetchAndClearBoolean(&Worker->Running)) { - State.TimeNow = CxPlatTimeUs64(); - CxPlatRunExecutionContexts(Worker, &State); // Run once more to handle race conditions + CxPlatRunExecutionContexts(Worker); + if (Worker->State.WaitTime && InterlockedFetchAndClearBoolean(&Worker->Running)) { + Worker->State.TimeNow = CxPlatTimeUs64(); + CxPlatRunExecutionContexts(Worker); // Run once more to handle race conditions } - CxPlatProcessEvents(Worker, &State); + CxPlatProcessEvents(Worker); - if (State.NoWorkCount == 0) { - State.LastWorkTime = State.TimeNow; - } else if (State.NoWorkCount > CXPLAT_WORKER_IDLE_WORK_THRESHOLD_COUNT) { + if (Worker->State.NoWorkCount == 0) { + Worker->State.LastWorkTime = Worker->State.TimeNow; + } else if (Worker->State.NoWorkCount > CXPLAT_WORKER_IDLE_WORK_THRESHOLD_COUNT) { CxPlatSchedulerYield(); - State.NoWorkCount = 0; + Worker->State.NoWorkCount = 0; } - if (State.TimeNow - State.LastPoolProcessTime > DYNAMIC_POOL_PROCESSING_PERIOD) { + if (Worker->State.TimeNow - Worker->State.LastPoolProcessTime > DYNAMIC_POOL_PROCESSING_PERIOD) { CxPlatProcessDynamicPoolAllocators(Worker); - State.LastPoolProcessTime = State.TimeNow; + Worker->State.LastPoolProcessTime = Worker->State.TimeNow; } } From a06aa81a00509fa62fb635a0a80efbb5ec0c9f13 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Mon, 28 Apr 2025 11:45:00 -0400 Subject: [PATCH 19/34] renames --- docs/Execution.md | 2 +- docs/Settings.md | 2 +- src/core/api.h | 6 +++--- src/core/library.c | 16 ++++++++-------- src/core/library.h | 2 +- src/core/unittest/SettingsTest.cpp | 10 +++++----- src/cs/lib/msquic_generated.cs | 10 +++++----- src/inc/msquic.h | 16 ++++++++-------- src/inc/msquic.hpp | 20 ++++++++++++-------- src/inc/quic_datapath.h | 4 ++-- src/inc/quic_platform.h | 7 ++++--- src/perf/lib/SecNetPerfMain.cpp | 6 +++--- src/platform/datapath_epoll.c | 4 ++-- src/platform/datapath_kqueue.c | 4 ++-- src/platform/datapath_raw.c | 4 ++-- src/platform/datapath_raw.h | 4 ++-- src/platform/datapath_raw_dpdk.c | 6 +++--- src/platform/datapath_raw_dummy.c | 4 ++-- src/platform/datapath_raw_xdp_linux.c | 6 +++--- src/platform/datapath_raw_xdp_win.c | 6 +++--- src/platform/datapath_winkernel.c | 4 ++-- src/platform/datapath_winuser.c | 4 ++-- src/platform/datapath_xplat.c | 4 ++-- src/platform/platform_internal.h | 10 +++++----- src/platform/platform_worker.c | 7 ++++--- src/platform/unittest/DataPathTest.cpp | 16 ++++++++-------- src/rs/ffi/linux_bindings.rs | 24 ++++++++++++------------ src/rs/ffi/win_bindings.rs | 24 ++++++++++++------------ src/test/MsQuicTests.h | 4 ++-- src/test/bin/quic_gtest.cpp | 2 +- src/test/bin/winkernel/control.cpp | 2 +- src/test/lib/ApiTest.cpp | 6 +++--- src/tools/attack/attack.cpp | 2 +- src/tools/spin/spinquic.cpp | 6 +++--- 34 files changed, 130 insertions(+), 124 deletions(-) diff --git a/docs/Execution.md b/docs/Execution.md index 018541c698..7866c57051 100644 --- a/docs/Execution.md +++ b/docs/Execution.md @@ -155,7 +155,7 @@ Once the app has the event queue, it may create the execution context with the ` ```c++ HANDLE IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); -QUIC_EXECUTION_CONFIG_2 ExecConfig = { 0, &IOCP }; +QUIC_EXECUTION_CONFIG ExecConfig = { 0, &IOCP }; QUIC_EXECUTION* ExecContext = nullptr; QUIC_STATUS Status = MsQuic->ExecutionCreate(QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 1, &ExecConfig, &ExecContext); diff --git a/docs/Settings.md b/docs/Settings.md index 485e505a61..e0df4f707b 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -113,7 +113,7 @@ These parameters are accessed by calling [GetParam](./api/GetParam.md) or [SetPa | `QUIC_PARAM_GLOBAL_GLOBAL_SETTINGS`
6 | QUIC_GLOBAL_SETTINGS | Both | Globally change global only settings. | | `QUIC_PARAM_GLOBAL_VERSION_SETTINGS`
7 | QUIC_VERSIONS_SETTINGS | Both | Globally change version settings for all subsequent connections. | | `QUIC_PARAM_GLOBAL_LIBRARY_GIT_HASH`
8 | char[64] | Get-only | Git hash used to build MsQuic (null terminated string) | -| `QUIC_PARAM_GLOBAL_EXECUTION_CONFIG`
9 | QUIC_EXECUTION_CONFIG | Both | Globally configure the execution model used for QUIC. Must be set before opening registration. | +| `QUIC_PARAM_GLOBAL_EXECUTION_CONFIG`
9 | QUIC_GLOBAL_EXECUTION_CONFIG | Both | Globally configure the execution model used for QUIC. Must be set before opening registration. | | `QUIC_PARAM_GLOBAL_TLS_PROVIDER`
10 | QUIC_TLS_PROVIDER | Get-Only | The TLS provider being used by MsQuic for the TLS handshake. | | `QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY`
11 | uint8_t[] | Set-Only | Globally change the stateless reset key for all subsequent connections. | | `QUIC_PARAM_GLOBAL_VERSION_NEGOTIATION_ENABLED`
(preview) | uint8_t (BOOLEAN) | Both | Globally enable the version negotiation extension for all client and server connections. | diff --git a/src/core/api.h b/src/core/api.h index a174402a1b..d0a8631210 100644 --- a/src/core/api.h +++ b/src/core/api.h @@ -12,8 +12,8 @@ MsQuicExecutionCreate( _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONFIG_2* Configs, - _Out_writes_(Count) QUIC_EXECUTION* Executions + _In_reads_(Count) QUIC_EXECUTION_CONFIG* Configs, + _Out_writes_(Count) QUIC_EXECUTION** Executions ); _IRQL_requires_max_(PASSIVE_LEVEL) @@ -21,7 +21,7 @@ void QUIC_API MsQuicExecutionDelete( _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION* Executions + _In_reads_(Count) QUIC_EXECUTION** Executions ); _IRQL_requires_max_(PASSIVE_LEVEL) diff --git a/src/core/library.c b/src/core/library.c index 49357a6c5b..fc0a5bc188 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -1015,13 +1015,13 @@ QuicLibrarySetGlobalParam( return QUIC_STATUS_SUCCESS; } - if (Buffer == NULL || BufferLength < QUIC_EXECUTION_CONFIG_MIN_SIZE) { + if (Buffer == NULL || BufferLength < QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE) { return QUIC_STATUS_INVALID_PARAMETER; } - QUIC_EXECUTION_CONFIG* Config = (QUIC_EXECUTION_CONFIG*)Buffer; + QUIC_GLOBAL_EXECUTION_CONFIG* Config = (QUIC_GLOBAL_EXECUTION_CONFIG*)Buffer; - if (BufferLength < QUIC_EXECUTION_CONFIG_MIN_SIZE + sizeof(uint16_t) * Config->ProcessorCount) { + if (BufferLength < QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE + sizeof(uint16_t) * Config->ProcessorCount) { return QUIC_STATUS_INVALID_PARAMETER; } @@ -1053,7 +1053,7 @@ QuicLibrarySetGlobalParam( break; } - QUIC_EXECUTION_CONFIG* NewConfig = + QUIC_GLOBAL_EXECUTION_CONFIG* NewConfig = CXPLAT_ALLOC_NONPAGED(BufferLength, QUIC_POOL_EXECUTION_CONFIG); if (NewConfig == NULL) { QuicTraceEvent( @@ -1342,7 +1342,7 @@ QuicLibraryGetGlobalParam( } const uint32_t ConfigLength = - QUIC_EXECUTION_CONFIG_MIN_SIZE + + QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE + sizeof(uint16_t) * MsQuicLib.ExecutionConfig->ProcessorCount; if (*BufferLength < ConfigLength) { @@ -2427,8 +2427,8 @@ MsQuicExecutionCreate( _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONFIG_2* Configs, - _Out_writes_(Count) QUIC_EXECUTION* Executions + _In_reads_(Count) QUIC_EXECUTION_CONFIG* Configs, + _Out_writes_(Count) QUIC_EXECUTION** Executions ) { if (MsQuicLib.LazyInitComplete) { @@ -2457,7 +2457,7 @@ void QUIC_API MsQuicExecutionDelete( _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION* Executions + _In_reads_(Count) QUIC_EXECUTION** Executions ) { UNREFERENCED_PARAMETER(Count); diff --git a/src/core/library.h b/src/core/library.h index 17509ee522..2d1c3a9611 100644 --- a/src/core/library.h +++ b/src/core/library.h @@ -170,7 +170,7 @@ typedef struct QUIC_LIBRARY { // // Configuration for execution of the library (optionally set by the app). // - QUIC_EXECUTION_CONFIG* ExecutionConfig; + QUIC_GLOBAL_EXECUTION_CONFIG* ExecutionConfig; // // Datapath instance for the library. diff --git a/src/core/unittest/SettingsTest.cpp b/src/core/unittest/SettingsTest.cpp index 7a1f9f9ebf..d181e57787 100644 --- a/src/core/unittest/SettingsTest.cpp +++ b/src/core/unittest/SettingsTest.cpp @@ -559,8 +559,8 @@ TEST(SettingsTest, GlobalLoadBalancingServerIDSet) #ifdef QUIC_API_ENABLE_PREVIEW_FEATURES TEST(SettingsTest, GlobalExecutionConfigSetAndGet) { - uint8_t RawConfig[QUIC_EXECUTION_CONFIG_MIN_SIZE + 2 * sizeof(uint16_t)] = {0}; - QUIC_EXECUTION_CONFIG* Config = (QUIC_EXECUTION_CONFIG*)RawConfig; + uint8_t RawConfig[QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE + 2 * sizeof(uint16_t)] = {0}; + QUIC_GLOBAL_EXECUTION_CONFIG* Config = (QUIC_GLOBAL_EXECUTION_CONFIG*)RawConfig; Config->ProcessorCount = 2; if (CxPlatProcCount() < 2) { Config->ProcessorCount = CxPlatProcCount(); @@ -586,7 +586,7 @@ TEST(SettingsTest, GlobalExecutionConfigSetAndGet) nullptr)); ASSERT_EQ((uint32_t)sizeof(RawConfig), BufferLength); uint16_t GetRawConfig[sizeof(RawConfig)] = {0}; - QUIC_EXECUTION_CONFIG* GetConfig = (QUIC_EXECUTION_CONFIG*)GetRawConfig; + QUIC_GLOBAL_EXECUTION_CONFIG* GetConfig = (QUIC_GLOBAL_EXECUTION_CONFIG*)GetRawConfig; ASSERT_EQ( QUIC_STATUS_SUCCESS, QuicLibraryGetGlobalParam( @@ -629,8 +629,8 @@ TEST(SettingsTest, GlobalExecutionConfigSetAndGet) TEST(SettingsTest, GlobalRawDataPathProcsSetAfterDataPathInit) { - uint8_t RawConfig[QUIC_EXECUTION_CONFIG_MIN_SIZE + 2 * sizeof(uint16_t)] = {0}; - QUIC_EXECUTION_CONFIG* Config = (QUIC_EXECUTION_CONFIG*)RawConfig; + uint8_t RawConfig[QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE + 2 * sizeof(uint16_t)] = {0}; + QUIC_GLOBAL_EXECUTION_CONFIG* Config = (QUIC_GLOBAL_EXECUTION_CONFIG*)RawConfig; Config->ProcessorCount = 2; Config->ProcessorList[0] = 0; Config->ProcessorList[1] = 1; diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs index 7653e83e30..9bb5404570 100644 --- a/src/cs/lib/msquic_generated.cs +++ b/src/cs/lib/msquic_generated.cs @@ -223,7 +223,7 @@ internal enum QUIC_EXECUTION_CONFIG_FLAGS AFFINITIZE = 0x0020, } - internal unsafe partial struct QUIC_EXECUTION_CONFIG + internal unsafe partial struct QUIC_GLOBAL_EXECUTION_CONFIG { internal QUIC_EXECUTION_CONFIG_FLAGS Flags; @@ -237,7 +237,7 @@ internal unsafe partial struct QUIC_EXECUTION_CONFIG internal fixed ushort ProcessorList[1]; } - internal unsafe partial struct QUIC_EXECUTION_CONFIG_2 + internal unsafe partial struct QUIC_EXECUTION_CONFIG { [NativeTypeName("uint32_t")] internal uint IdealProcessor; @@ -3411,7 +3411,7 @@ internal unsafe partial struct QUIC_API_TABLE internal delegate* unmanaged[Cdecl] ConnectionPoolCreate; [NativeTypeName("QUIC_EXECUTION_CREATE_FN")] - internal delegate* unmanaged[Cdecl] ExecutionCreate; + internal delegate* unmanaged[Cdecl] ExecutionCreate; [NativeTypeName("QUIC_EXECUTION_DELETE_FN")] internal delegate* unmanaged[Cdecl] ExecutionDelete; @@ -3441,8 +3441,8 @@ internal static unsafe partial class MsQuic [NativeTypeName("#define QUIC_STATELESS_RESET_KEY_LENGTH 32")] internal const uint QUIC_STATELESS_RESET_KEY_LENGTH = 32; - [NativeTypeName("#define QUIC_EXECUTION_CONFIG_MIN_SIZE (uint32_t)FIELD_OFFSET(QUIC_EXECUTION_CONFIG, ProcessorList)")] - internal static readonly uint QUIC_EXECUTION_CONFIG_MIN_SIZE = unchecked((uint)((int)(Marshal.OffsetOf("ProcessorList")))); + [NativeTypeName("#define QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE (uint32_t)FIELD_OFFSET(QUIC_GLOBAL_EXECUTION_CONFIG, ProcessorList)")] + internal static readonly uint QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE = unchecked((uint)((int)(Marshal.OffsetOf("ProcessorList")))); [NativeTypeName("#define QUIC_MAX_TICKET_KEY_COUNT 16")] internal const uint QUIC_MAX_TICKET_KEY_COUNT = 16; diff --git a/src/inc/msquic.h b/src/inc/msquic.h index 47ba79d26e..4337a3ec69 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -286,7 +286,7 @@ DEFINE_ENUM_FLAG_OPERATORS(QUIC_EXECUTION_CONFIG_FLAGS) // // A custom configuration for thread execution in QUIC. // -typedef struct QUIC_EXECUTION_CONFIG { +typedef struct QUIC_GLOBAL_EXECUTION_CONFIG { QUIC_EXECUTION_CONFIG_FLAGS Flags; uint32_t PollingIdleTimeoutUs; // Time before a polling thread, with no work to do, sleeps. @@ -294,10 +294,10 @@ typedef struct QUIC_EXECUTION_CONFIG { _Field_size_(ProcessorCount) uint16_t ProcessorList[1]; // List of processors to use for threads. -} QUIC_EXECUTION_CONFIG; +} QUIC_GLOBAL_EXECUTION_CONFIG; -#define QUIC_EXECUTION_CONFIG_MIN_SIZE \ - (uint32_t)FIELD_OFFSET(QUIC_EXECUTION_CONFIG, ProcessorList) +#define QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE \ + (uint32_t)FIELD_OFFSET(QUIC_GLOBAL_EXECUTION_CONFIG, ProcessorList) #ifndef _KERNEL_MODE @@ -306,10 +306,10 @@ typedef struct QUIC_EXECUTION_CONFIG { // completely control execution of all MsQuic work. // -typedef struct QUIC_EXECUTION_CONFIG_2 { +typedef struct QUIC_EXECUTION_CONFIG { uint32_t IdealProcessor; QUIC_EVENTQ* EventQ; -} QUIC_EXECUTION_CONFIG_2; +} QUIC_EXECUTION_CONFIG; typedef struct QUIC_EXECUTION QUIC_EXECUTION; @@ -323,7 +323,7 @@ QUIC_STATUS _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONFIG_2* Configs, + _In_reads_(Count) QUIC_EXECUTION_CONFIG* Configs, _Out_writes_(Count) QUIC_EXECUTION** Executions ); @@ -931,7 +931,7 @@ void #endif #define QUIC_PARAM_GLOBAL_LIBRARY_GIT_HASH 0x01000008 // char[64] #ifdef QUIC_API_ENABLE_PREVIEW_FEATURES -#define QUIC_PARAM_GLOBAL_EXECUTION_CONFIG 0x01000009 // QUIC_EXECUTION_CONFIG +#define QUIC_PARAM_GLOBAL_EXECUTION_CONFIG 0x01000009 // QUIC_GLOBAL_EXECUTION_CONFIG #endif #define QUIC_PARAM_GLOBAL_TLS_PROVIDER 0x0100000A // QUIC_TLS_PROVIDER #define QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY 0x0100000B // uint8_t[] - Array size is QUIC_STATELESS_RESET_KEY_LENGTH diff --git a/src/inc/msquic.hpp b/src/inc/msquic.hpp index a18e9aa293..9169674e01 100644 --- a/src/inc/msquic.hpp +++ b/src/inc/msquic.hpp @@ -449,15 +449,17 @@ class MsQuicApi : public QUIC_API_TABLE { extern const MsQuicApi* MsQuic; +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES + struct MsQuicExecution { - QUIC_EXECUTION* Executions {nullptr}; + QUIC_EXECUTION** Executions {nullptr}; uint32_t Count {0}; MsQuicExecution(QUIC_EVENTQ* EventQ, QUIC_EXECUTION_CONFIG_FLAGS Flags = QUIC_EXECUTION_CONFIG_FLAG_NONE, uint32_t PollingIdleTimeoutUs = 0) noexcept : Count(1) { - QUIC_EXECUTION_CONFIG_2 Config = { 0, EventQ }; + QUIC_EXECUTION_CONFIG Config = { 0, EventQ }; Initialize(Flags, PollingIdleTimeoutUs, &Config); } MsQuicExecution(QUIC_EVENTQ** EventQ, uint32_t Count, QUIC_EXECUTION_CONFIG_FLAGS Flags = QUIC_EXECUTION_CONFIG_FLAG_NONE, uint32_t PollingIdleTimeoutUs = 0) noexcept : Count(Count) { - auto Configs = new(std::nothrow) QUIC_EXECUTION_CONFIG_2[Count]; + auto Configs = new(std::nothrow) QUIC_EXECUTION_CONFIG[Count]; if (Configs != nullptr) { for (uint32_t i = 0; i < Count; ++i) { Configs[i].IdealProcessor = i; @@ -470,10 +472,10 @@ struct MsQuicExecution { void Initialize( _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, - _In_reads_(this->Count) QUIC_EXECUTION_CONFIG_2* Configs + _In_reads_(this->Count) QUIC_EXECUTION_CONFIG* Configs ) { - Executions = new(std::nothrow) MsQuicExecutionContext[Count]; + Executions = new(std::nothrow) QUIC_EXECUTION*[Count]; if (Executions != nullptr) { auto Status = MsQuic->ExecutionCreate( @@ -481,7 +483,7 @@ struct MsQuicExecution { PollingIdleTimeoutUs, Count, Configs, - &Executions); + Executions); if (QUIC_FAILED(Status)) { delete [] Executions; Executions = nullptr; @@ -489,11 +491,13 @@ struct MsQuicExecution { } } bool IsValid() const noexcept { return Executions != nullptr; } - QUIC_EXECUTION& operator[](size_t i) const { - return *(Executions + i); + QUIC_EXECUTION* operator[](size_t i) const { + return Executions[i]; } }; +#endif // QUIC_API_ENABLE_PREVIEW_FEATURES + struct MsQuicRegistration { bool CloseAllConnectionsOnDelete {false}; HQUIC Handle {nullptr}; diff --git a/src/inc/quic_datapath.h b/src/inc/quic_datapath.h index 1082f70dbc..ae3d7a0fa9 100644 --- a/src/inc/quic_datapath.h +++ b/src/inc/quic_datapath.h @@ -447,7 +447,7 @@ CxPlatDataPathInitialize( _In_opt_ const CXPLAT_UDP_DATAPATH_CALLBACKS* UdpCallbacks, _In_opt_ const CXPLAT_TCP_DATAPATH_CALLBACKS* TcpCallbacks, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ QUIC_EXECUTION_CONFIG* Config, + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config, _Out_ CXPLAT_DATAPATH** NewDatapath ); @@ -467,7 +467,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatDataPathUpdateConfig( _In_ CXPLAT_DATAPATH* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ); #define CXPLAT_DATAPATH_FEATURE_RECV_SIDE_SCALING 0x0001 diff --git a/src/inc/quic_platform.h b/src/inc/quic_platform.h index 220d32ab5c..f3000c161d 100644 --- a/src/inc/quic_platform.h +++ b/src/inc/quic_platform.h @@ -445,8 +445,8 @@ CxPlatGetAllocFailDenominator( // typedef struct QUIC_EXECUTION QUIC_EXECUTION; +typedef struct QUIC_GLOBAL_EXECUTION_CONFIG QUIC_GLOBAL_EXECUTION_CONFIG; typedef struct QUIC_EXECUTION_CONFIG QUIC_EXECUTION_CONFIG; -typedef struct QUIC_EXECUTION_CONFIG_2 QUIC_EXECUTION_CONFIG_2; typedef struct CXPLAT_EXECUTION_CONTEXT CXPLAT_EXECUTION_CONTEXT; typedef struct CXPLAT_EXECUTION_STATE { @@ -468,13 +468,14 @@ typedef struct CXPLAT_WORKER_POOL CXPLAT_WORKER_POOL; CXPLAT_WORKER_POOL* CxPlatWorkerPoolCreate( - _In_opt_ QUIC_EXECUTION_CONFIG* Config + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ); +_Success_(return != NULL) CXPLAT_WORKER_POOL* CxPlatWorkerPoolCreateExternal( _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONFIG_2* Configs, + _In_reads_(Count) QUIC_EXECUTION_CONFIG* Configs, _Out_writes_(Count) QUIC_EXECUTION** Executions ); diff --git a/src/perf/lib/SecNetPerfMain.cpp b/src/perf/lib/SecNetPerfMain.cpp index 2594d253fe..430fcc4635 100644 --- a/src/perf/lib/SecNetPerfMain.cpp +++ b/src/perf/lib/SecNetPerfMain.cpp @@ -177,8 +177,8 @@ QuicMainStart( return Status; } - uint8_t RawConfig[QUIC_EXECUTION_CONFIG_MIN_SIZE + 256 * sizeof(uint16_t)] = {0}; - QUIC_EXECUTION_CONFIG* Config = (QUIC_EXECUTION_CONFIG*)RawConfig; + uint8_t RawConfig[QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE + 256 * sizeof(uint16_t)] = {0}; + QUIC_GLOBAL_EXECUTION_CONFIG* Config = (QUIC_GLOBAL_EXECUTION_CONFIG*)RawConfig; Config->PollingIdleTimeoutUs = 0; // Default to no polling. bool SetConfig = false; const char* IoMode = GetValue(argc, argv, "io"); @@ -235,7 +235,7 @@ QuicMainStart( MsQuic->SetParam( nullptr, QUIC_PARAM_GLOBAL_EXECUTION_CONFIG, - (uint32_t)QUIC_EXECUTION_CONFIG_MIN_SIZE + Config->ProcessorCount * sizeof(uint16_t), + (uint32_t)QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE + Config->ProcessorCount * sizeof(uint16_t), Config))) { WriteOutput("Failed to set execution config %d\n", Status); return Status; diff --git a/src/platform/datapath_epoll.c b/src/platform/datapath_epoll.c index 65e46c5a40..6352562074 100644 --- a/src/platform/datapath_epoll.c +++ b/src/platform/datapath_epoll.c @@ -360,7 +360,7 @@ DataPathInitialize( _In_opt_ const CXPLAT_UDP_DATAPATH_CALLBACKS* UdpCallbacks, _In_opt_ const CXPLAT_TCP_DATAPATH_CALLBACKS* TcpCallbacks, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ QUIC_EXECUTION_CONFIG* Config, + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config, _Out_ CXPLAT_DATAPATH** NewDatapath ) { @@ -485,7 +485,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void DataPathUpdateConfig( _In_ CXPLAT_DATAPATH* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { UNREFERENCED_PARAMETER(Datapath); diff --git a/src/platform/datapath_kqueue.c b/src/platform/datapath_kqueue.c index d1d77783f1..cd57b19c93 100644 --- a/src/platform/datapath_kqueue.c +++ b/src/platform/datapath_kqueue.c @@ -434,7 +434,7 @@ CxPlatDataPathInitialize( _In_opt_ const CXPLAT_UDP_DATAPATH_CALLBACKS* UdpCallbacks, _In_opt_ const CXPLAT_TCP_DATAPATH_CALLBACKS* TcpCallbacks, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ QUIC_EXECUTION_CONFIG* Config, + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config, _Out_ CXPLAT_DATAPATH** NewDataPath ) { @@ -546,7 +546,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatDataPathUpdateConfig( _In_ CXPLAT_DATAPATH* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { UNREFERENCED_PARAMETER(Datapath); diff --git a/src/platform/datapath_raw.c b/src/platform/datapath_raw.c index 285e0bbf9d..5c0f97319a 100644 --- a/src/platform/datapath_raw.c +++ b/src/platform/datapath_raw.c @@ -22,7 +22,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS RawDataPathInitialize( _In_ uint32_t ClientRecvContextLength, - _In_opt_ QUIC_EXECUTION_CONFIG* Config, + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config, _In_opt_ const CXPLAT_DATAPATH* ParentDataPath, _In_ CXPLAT_WORKER_POOL* WorkerPool, _Out_ CXPLAT_DATAPATH_RAW** NewDataPath @@ -133,7 +133,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void RawDataPathUpdateConfig( _In_ CXPLAT_DATAPATH_RAW* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { CxPlatDpRawUpdateConfig(Datapath, Config);; diff --git a/src/platform/datapath_raw.h b/src/platform/datapath_raw.h index b1feee9326..a04f54f715 100644 --- a/src/platform/datapath_raw.h +++ b/src/platform/datapath_raw.h @@ -113,7 +113,7 @@ CxPlatDpRawInitialize( _Inout_ CXPLAT_DATAPATH_RAW* Datapath, _In_ uint32_t ClientRecvContextLength, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ const QUIC_EXECUTION_CONFIG* Config + _In_opt_ const QUIC_GLOBAL_EXECUTION_CONFIG* Config ); // @@ -141,7 +141,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatDpRawUpdateConfig( _In_ CXPLAT_DATAPATH_RAW* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ); // diff --git a/src/platform/datapath_raw_dpdk.c b/src/platform/datapath_raw_dpdk.c index 59706a55ed..f338ecce9e 100644 --- a/src/platform/datapath_raw_dpdk.c +++ b/src/platform/datapath_raw_dpdk.c @@ -126,7 +126,7 @@ CxPlatDpdkReadConfig( _IRQL_requires_max_(PASSIVE_LEVEL) size_t CxPlatDpRawGetDatapathSize( - _In_opt_ const QUIC_EXECUTION_CONFIG* Config + _In_opt_ const QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { UNREFERENCED_PARAMETER(Config); @@ -139,7 +139,7 @@ CxPlatDpRawInitialize( _Inout_ CXPLAT_DATAPATH* Datapath, _In_ uint32_t ClientRecvContextLength, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ const QUIC_EXECUTION_CONFIG* Config + _In_opt_ const QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { UNREFERENCED_PARAMETER(WorkerPool); @@ -214,7 +214,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatDpRawUpdateConfig( _In_ CXPLAT_DATAPATH* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { UNREFERENCED_PARAMETER(Datapath); diff --git a/src/platform/datapath_raw_dummy.c b/src/platform/datapath_raw_dummy.c index 5fc69b879d..2372f3e14c 100644 --- a/src/platform/datapath_raw_dummy.c +++ b/src/platform/datapath_raw_dummy.c @@ -54,7 +54,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS RawDataPathInitialize( _In_ uint32_t ClientRecvContextLength, - _In_opt_ QUIC_EXECUTION_CONFIG* Config, + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config, _In_opt_ const CXPLAT_DATAPATH* ParentDataPath, _In_ CXPLAT_WORKER_POOL* WorkerPool, _Out_ CXPLAT_DATAPATH_RAW** DataPath @@ -81,7 +81,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void RawDataPathUpdateConfig( _In_ CXPLAT_DATAPATH_RAW* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { UNREFERENCED_PARAMETER(Datapath); diff --git a/src/platform/datapath_raw_xdp_linux.c b/src/platform/datapath_raw_xdp_linux.c index afb7c61501..0b2efad9df 100644 --- a/src/platform/datapath_raw_xdp_linux.c +++ b/src/platform/datapath_raw_xdp_linux.c @@ -646,7 +646,7 @@ CxPlatDpRawInterfaceUpdateRules( _IRQL_requires_max_(PASSIVE_LEVEL) size_t CxPlatDpRawGetDatapathSize( - _In_opt_ const QUIC_EXECUTION_CONFIG* Config + _In_opt_ const QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { const uint32_t PartitionCount = @@ -676,7 +676,7 @@ CxPlatDpRawInitialize( _Inout_ CXPLAT_DATAPATH_RAW* Datapath, _In_ uint32_t ClientRecvContextLength, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ const QUIC_EXECUTION_CONFIG* Config + _In_opt_ const QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { XDP_DATAPATH* Xdp = (XDP_DATAPATH*)Datapath; @@ -914,7 +914,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatDpRawUpdateConfig( _In_ CXPLAT_DATAPATH_RAW* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { XDP_DATAPATH* Xdp = (XDP_DATAPATH*)Datapath; diff --git a/src/platform/datapath_raw_xdp_win.c b/src/platform/datapath_raw_xdp_win.c index b0efe3bd27..49c977211f 100644 --- a/src/platform/datapath_raw_xdp_win.c +++ b/src/platform/datapath_raw_xdp_win.c @@ -925,7 +925,7 @@ CxPlatDpRawInterfaceRemoveRules( _IRQL_requires_max_(PASSIVE_LEVEL) size_t CxPlatDpRawGetDatapathSize( - _In_opt_ const QUIC_EXECUTION_CONFIG* Config + _In_opt_ const QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { const uint32_t PartitionCount = @@ -939,7 +939,7 @@ CxPlatDpRawInitialize( _Inout_ CXPLAT_DATAPATH_RAW* Datapath, _In_ uint32_t ClientRecvContextLength, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ const QUIC_EXECUTION_CONFIG* Config + _In_opt_ const QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { XDP_DATAPATH* Xdp = (XDP_DATAPATH*)Datapath; @@ -1232,7 +1232,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatDpRawUpdateConfig( _In_ CXPLAT_DATAPATH_RAW* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { XDP_DATAPATH* Xdp = (XDP_DATAPATH*)Datapath; diff --git a/src/platform/datapath_winkernel.c b/src/platform/datapath_winkernel.c index 28418ec04d..69c8e514eb 100644 --- a/src/platform/datapath_winkernel.c +++ b/src/platform/datapath_winkernel.c @@ -683,7 +683,7 @@ DataPathInitialize( _In_opt_ const CXPLAT_UDP_DATAPATH_CALLBACKS* UdpCallbacks, _In_opt_ const CXPLAT_TCP_DATAPATH_CALLBACKS* TcpCallbacks, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ QUIC_EXECUTION_CONFIG* Config, + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config, _Out_ CXPLAT_DATAPATH* *NewDataPath ) { @@ -923,7 +923,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void DataPathUpdateConfig( _In_ CXPLAT_DATAPATH* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { UNREFERENCED_PARAMETER(Datapath); diff --git a/src/platform/datapath_winuser.c b/src/platform/datapath_winuser.c index b07d80b14c..c12858f759 100644 --- a/src/platform/datapath_winuser.c +++ b/src/platform/datapath_winuser.c @@ -753,7 +753,7 @@ DataPathInitialize( _In_opt_ const CXPLAT_UDP_DATAPATH_CALLBACKS* UdpCallbacks, _In_opt_ const CXPLAT_TCP_DATAPATH_CALLBACKS* TcpCallbacks, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ QUIC_EXECUTION_CONFIG* Config, // TODO:: If we move RIO to a per-socket option, we can remove this from all datapaths + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config, // TODO:: If we move RIO to a per-socket option, we can remove this from all datapaths _Out_ CXPLAT_DATAPATH** NewDatapath ) { @@ -1015,7 +1015,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void DataPathUpdateConfig( _In_ CXPLAT_DATAPATH* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { UNREFERENCED_PARAMETER(Datapath); diff --git a/src/platform/datapath_xplat.c b/src/platform/datapath_xplat.c index 99cb714828..c994e62d85 100644 --- a/src/platform/datapath_xplat.c +++ b/src/platform/datapath_xplat.c @@ -22,7 +22,7 @@ CxPlatDataPathInitialize( _In_opt_ const CXPLAT_UDP_DATAPATH_CALLBACKS* UdpCallbacks, _In_opt_ const CXPLAT_TCP_DATAPATH_CALLBACKS* TcpCallbacks, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ QUIC_EXECUTION_CONFIG* Config, + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config, _Out_ CXPLAT_DATAPATH** NewDataPath ) { @@ -86,7 +86,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatDataPathUpdateConfig( _In_ CXPLAT_DATAPATH* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { DataPathUpdateConfig(Datapath, Config); diff --git a/src/platform/platform_internal.h b/src/platform/platform_internal.h index 56d31ce06e..a507422d9b 100644 --- a/src/platform/platform_internal.h +++ b/src/platform/platform_internal.h @@ -778,7 +778,7 @@ CxPlatCryptUninitialize( _IRQL_requires_max_(PASSIVE_LEVEL) size_t CxPlatDpRawGetDatapathSize( - _In_opt_ const QUIC_EXECUTION_CONFIG* Config + _In_opt_ const QUIC_GLOBAL_EXECUTION_CONFIG* Config ); #if defined(CX_PLATFORM_LINUX) @@ -1068,7 +1068,7 @@ DataPathInitialize( _In_opt_ const CXPLAT_UDP_DATAPATH_CALLBACKS* UdpCallbacks, _In_opt_ const CXPLAT_TCP_DATAPATH_CALLBACKS* TcpCallbacks, _In_ CXPLAT_WORKER_POOL* WorkerPool, - _In_opt_ QUIC_EXECUTION_CONFIG* Config, + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config, _Out_ CXPLAT_DATAPATH** NewDatapath ); @@ -1082,7 +1082,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void DataPathUpdateConfig( _In_ CXPLAT_DATAPATH* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ); _IRQL_requires_max_(DISPATCH_LEVEL) @@ -1177,7 +1177,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS RawDataPathInitialize( _In_ uint32_t ClientRecvContextLength, - _In_opt_ QUIC_EXECUTION_CONFIG* Config, + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config, _In_opt_ const CXPLAT_DATAPATH* ParentDataPath, _In_ CXPLAT_WORKER_POOL* WorkerPool, _Out_ CXPLAT_DATAPATH_RAW** DataPath @@ -1193,7 +1193,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) void RawDataPathUpdateConfig( _In_ CXPLAT_DATAPATH_RAW* Datapath, - _In_ QUIC_EXECUTION_CONFIG* Config + _In_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ); _IRQL_requires_max_(DISPATCH_LEVEL) diff --git a/src/platform/platform_worker.c b/src/platform/platform_worker.c index 857546df21..c07b1dff6b 100644 --- a/src/platform/platform_worker.c +++ b/src/platform/platform_worker.c @@ -255,7 +255,7 @@ CxPlatWorkerPoolDestroyWorker( CXPLAT_WORKER_POOL* CxPlatWorkerPoolCreate( - _In_opt_ QUIC_EXECUTION_CONFIG* Config + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config ) { // @@ -351,10 +351,11 @@ CxPlatWorkerPoolCreate( return NULL; } +_Success_(return != NULL) CXPLAT_WORKER_POOL* CxPlatWorkerPoolCreateExternal( _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION_CONFIG_2* Configs, + _In_reads_(Count) QUIC_EXECUTION_CONFIG* Configs, _Out_writes_(Count) QUIC_EXECUTION** Executions ) { @@ -389,7 +390,7 @@ CxPlatWorkerPoolCreateExternal( CXPLAT_WORKER* Worker = &WorkerPool->Workers[i]; if (!CxPlatWorkerPoolInitWorker( - Worker, IdealProcessor, &Configs[i].EventQ, NULL)) { + Worker, IdealProcessor, Configs[i].EventQ, NULL)) { goto Error; } Executions[i] = (QUIC_EXECUTION*)Worker; diff --git a/src/platform/unittest/DataPathTest.cpp b/src/platform/unittest/DataPathTest.cpp index 5ae541b6d6..fb85b83410 100644 --- a/src/platform/unittest/DataPathTest.cpp +++ b/src/platform/unittest/DataPathTest.cpp @@ -450,7 +450,7 @@ QuicAddr DataPathTest::UnspecIPv4; QuicAddr DataPathTest::UnspecIPv6; struct CxPlatDataPath { - QUIC_EXECUTION_CONFIG DefaultExecutionConfig { QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 0, {0} }; + QUIC_GLOBAL_EXECUTION_CONFIG DefaultExecutionConfig { QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 0, {0} }; CXPLAT_WORKER_POOL* WorkerPool {nullptr}; CXPLAT_DATAPATH* Datapath {nullptr}; QUIC_STATUS InitStatus; @@ -458,7 +458,7 @@ struct CxPlatDataPath { _In_opt_ const CXPLAT_UDP_DATAPATH_CALLBACKS* UdpCallbacks, _In_opt_ const CXPLAT_TCP_DATAPATH_CALLBACKS* TcpCallbacks = nullptr, _In_ uint32_t ClientRecvContextLength = 0, - _In_opt_ QUIC_EXECUTION_CONFIG* Config = nullptr + _In_opt_ QUIC_GLOBAL_EXECUTION_CONFIG* Config = nullptr ) noexcept { if (UseDuoNic && Config == nullptr) { @@ -679,25 +679,25 @@ TEST_F(DataPathTest, Initialize) ASSERT_NE(nullptr, Datapath.Datapath); } { - QUIC_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NONE, UINT32_MAX, 0 }; + QUIC_GLOBAL_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NONE, UINT32_MAX, 0 }; CxPlatDataPath Datapath(&EmptyUdpCallbacks, nullptr, 0, &Config); VERIFY_QUIC_SUCCESS(Datapath.GetInitStatus()); ASSERT_NE(nullptr, Datapath.Datapath); } { - QUIC_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 0 }; + QUIC_GLOBAL_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 0 }; CxPlatDataPath Datapath(&EmptyUdpCallbacks, nullptr, 0, &Config); VERIFY_QUIC_SUCCESS(Datapath.GetInitStatus()); ASSERT_NE(nullptr, Datapath.Datapath); } { - QUIC_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NONE, UINT32_MAX, 1, {0} }; + QUIC_GLOBAL_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NONE, UINT32_MAX, 1, {0} }; CxPlatDataPath Datapath(&EmptyUdpCallbacks, nullptr, 0, &Config); VERIFY_QUIC_SUCCESS(Datapath.GetInitStatus()); ASSERT_NE(nullptr, Datapath.Datapath); } if (UseDuoNic) { - QUIC_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_XDP, 0, 1, {0} }; + QUIC_GLOBAL_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_XDP, 0, 1, {0} }; CxPlatDataPath Datapath(&EmptyUdpCallbacks, nullptr, 0, &Config); VERIFY_QUIC_SUCCESS(Datapath.GetInitStatus()); ASSERT_NE(nullptr, Datapath.Datapath); @@ -832,7 +832,7 @@ TEST_P(DataPathTest, UdpData) TEST_P(DataPathTest, UdpDataPolling) { - QUIC_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NONE, UINT32_MAX, 0 }; + QUIC_GLOBAL_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NONE, UINT32_MAX, 0 }; UdpRecvContext RecvContext; CxPlatDataPath Datapath(&UdpRecvCallbacks, nullptr, 0, &Config); RecvContext.TtlSupported = Datapath.IsSupported(CXPLAT_DATAPATH_FEATURE_TTL); @@ -1060,7 +1060,7 @@ TEST_P(DataPathTest, MultiBindListener) { TEST_P(DataPathTest, MultiBindListenerSingleProcessor) { UdpRecvContext RecvContext; - QUIC_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NO_IDEAL_PROC, UINT32_MAX, 1, 0 }; + QUIC_GLOBAL_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NO_IDEAL_PROC, UINT32_MAX, 1, 0 }; CxPlatDataPath Datapath(&UdpRecvCallbacks, nullptr, 0, &Config); RecvContext.TtlSupported = Datapath.IsSupported(CXPLAT_DATAPATH_FEATURE_TTL); RecvContext.DscpSupported = Datapath.IsSupported(CXPLAT_DATAPATH_FEATURE_SEND_DSCP); diff --git a/src/rs/ffi/linux_bindings.rs b/src/rs/ffi/linux_bindings.rs index 3a7af63ce4..613b9b19d8 100644 --- a/src/rs/ffi/linux_bindings.rs +++ b/src/rs/ffi/linux_bindings.rs @@ -427,7 +427,7 @@ pub const QUIC_EXECUTION_CONFIG_FLAGS_QUIC_EXECUTION_CONFIG_FLAG_AFFINITIZE: pub type QUIC_EXECUTION_CONFIG_FLAGS = ::std::os::raw::c_uint; #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct QUIC_EXECUTION_CONFIG { +pub struct QUIC_GLOBAL_EXECUTION_CONFIG { pub Flags: QUIC_EXECUTION_CONFIG_FLAGS, pub PollingIdleTimeoutUs: u32, pub ProcessorCount: u32, @@ -435,17 +435,17 @@ pub struct QUIC_EXECUTION_CONFIG { } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of QUIC_EXECUTION_CONFIG"][::std::mem::size_of::() - 16usize]; - ["Alignment of QUIC_EXECUTION_CONFIG"] - [::std::mem::align_of::() - 4usize]; - ["Offset of field: QUIC_EXECUTION_CONFIG::Flags"] - [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, Flags) - 0usize]; - ["Offset of field: QUIC_EXECUTION_CONFIG::PollingIdleTimeoutUs"] - [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, PollingIdleTimeoutUs) - 4usize]; - ["Offset of field: QUIC_EXECUTION_CONFIG::ProcessorCount"] - [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, ProcessorCount) - 8usize]; - ["Offset of field: QUIC_EXECUTION_CONFIG::ProcessorList"] - [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, ProcessorList) - 12usize]; + ["Size of QUIC_GLOBAL_EXECUTION_CONFIG"][::std::mem::size_of::() - 16usize]; + ["Alignment of QUIC_GLOBAL_EXECUTION_CONFIG"] + [::std::mem::align_of::() - 4usize]; + ["Offset of field: QUIC_GLOBAL_EXECUTION_CONFIG::Flags"] + [::std::mem::offset_of!(QUIC_GLOBAL_EXECUTION_CONFIG, Flags) - 0usize]; + ["Offset of field: QUIC_GLOBAL_EXECUTION_CONFIG::PollingIdleTimeoutUs"] + [::std::mem::offset_of!(QUIC_GLOBAL_EXECUTION_CONFIG, PollingIdleTimeoutUs) - 4usize]; + ["Offset of field: QUIC_GLOBAL_EXECUTION_CONFIG::ProcessorCount"] + [::std::mem::offset_of!(QUIC_GLOBAL_EXECUTION_CONFIG, ProcessorCount) - 8usize]; + ["Offset of field: QUIC_GLOBAL_EXECUTION_CONFIG::ProcessorList"] + [::std::mem::offset_of!(QUIC_GLOBAL_EXECUTION_CONFIG, ProcessorList) - 12usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] diff --git a/src/rs/ffi/win_bindings.rs b/src/rs/ffi/win_bindings.rs index 7963fe10b0..9fb4cf1065 100644 --- a/src/rs/ffi/win_bindings.rs +++ b/src/rs/ffi/win_bindings.rs @@ -426,7 +426,7 @@ pub const QUIC_EXECUTION_CONFIG_FLAGS_QUIC_EXECUTION_CONFIG_FLAG_AFFINITIZE: pub type QUIC_EXECUTION_CONFIG_FLAGS = ::std::os::raw::c_int; #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct QUIC_EXECUTION_CONFIG { +pub struct QUIC_GLOBAL_EXECUTION_CONFIG { pub Flags: QUIC_EXECUTION_CONFIG_FLAGS, pub PollingIdleTimeoutUs: u32, pub ProcessorCount: u32, @@ -434,17 +434,17 @@ pub struct QUIC_EXECUTION_CONFIG { } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of QUIC_EXECUTION_CONFIG"][::std::mem::size_of::() - 16usize]; - ["Alignment of QUIC_EXECUTION_CONFIG"] - [::std::mem::align_of::() - 4usize]; - ["Offset of field: QUIC_EXECUTION_CONFIG::Flags"] - [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, Flags) - 0usize]; - ["Offset of field: QUIC_EXECUTION_CONFIG::PollingIdleTimeoutUs"] - [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, PollingIdleTimeoutUs) - 4usize]; - ["Offset of field: QUIC_EXECUTION_CONFIG::ProcessorCount"] - [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, ProcessorCount) - 8usize]; - ["Offset of field: QUIC_EXECUTION_CONFIG::ProcessorList"] - [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, ProcessorList) - 12usize]; + ["Size of QUIC_GLOBAL_EXECUTION_CONFIG"][::std::mem::size_of::() - 16usize]; + ["Alignment of QUIC_GLOBAL_EXECUTION_CONFIG"] + [::std::mem::align_of::() - 4usize]; + ["Offset of field: QUIC_GLOBAL_EXECUTION_CONFIG::Flags"] + [::std::mem::offset_of!(QUIC_GLOBAL_EXECUTION_CONFIG, Flags) - 0usize]; + ["Offset of field: QUIC_GLOBAL_EXECUTION_CONFIG::PollingIdleTimeoutUs"] + [::std::mem::offset_of!(QUIC_GLOBAL_EXECUTION_CONFIG, PollingIdleTimeoutUs) - 4usize]; + ["Offset of field: QUIC_GLOBAL_EXECUTION_CONFIG::ProcessorCount"] + [::std::mem::offset_of!(QUIC_GLOBAL_EXECUTION_CONFIG, ProcessorCount) - 8usize]; + ["Offset of field: QUIC_GLOBAL_EXECUTION_CONFIG::ProcessorList"] + [::std::mem::offset_of!(QUIC_GLOBAL_EXECUTION_CONFIG, ProcessorList) - 12usize]; }; #[repr(C)] #[derive(Debug, Copy, Clone)] diff --git a/src/test/MsQuicTests.h b/src/test/MsQuicTests.h index 86525643cf..4349171d9b 100644 --- a/src/test/MsQuicTests.h +++ b/src/test/MsQuicTests.h @@ -748,7 +748,7 @@ static const GUID QUIC_TEST_DEVICE_INSTANCE = typedef struct { BOOLEAN UseDuoNic; - QUIC_EXECUTION_CONFIG Config; + QUIC_GLOBAL_EXECUTION_CONFIG Config; char CurrentDirectory[MAX_PATH]; } QUIC_TEST_CONFIGURATION_PARAMS; @@ -1385,7 +1385,7 @@ typedef struct { #define IOCTL_QUIC_RUN_TEST_KEY_UPDATE_DURING_HANDSHAKE \ QUIC_CTL_CODE(130, METHOD_BUFFERED, FILE_WRITE_DATA) // int - Family - + #define IOCTL_QUIC_RUN_RETRY_MEMORY_LIMIT_CONNECT \ QUIC_CTL_CODE(131, METHOD_BUFFERED, FILE_WRITE_DATA) //int - Family diff --git a/src/test/bin/quic_gtest.cpp b/src/test/bin/quic_gtest.cpp index 386a008e63..23bb9de8e9 100644 --- a/src/test/bin/quic_gtest.cpp +++ b/src/test/bin/quic_gtest.cpp @@ -67,7 +67,7 @@ class QuicTestEnvironment : public ::testing::Environment { TRUE, NULL )) != nullptr); - QUIC_EXECUTION_CONFIG Config = {QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 0, {0}}; + QUIC_GLOBAL_EXECUTION_CONFIG Config = {QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 0, {0}}; if (TestingKernelMode) { printf("Initializing for Kernel Mode tests\n"); const char* DriverName; diff --git a/src/test/bin/winkernel/control.cpp b/src/test/bin/winkernel/control.cpp index 9b0a9f031f..8cc906e4c3 100644 --- a/src/test/bin/winkernel/control.cpp +++ b/src/test/bin/winkernel/control.cpp @@ -701,7 +701,7 @@ QuicTestCtlEvtIoDeviceControl( STRSAFE_NULL_ON_FAILURE); #if defined(QUIC_API_ENABLE_PREVIEW_FEATURES) - QUIC_EXECUTION_CONFIG Config = Params->TestConfigurationParams.Config; + QUIC_GLOBAL_EXECUTION_CONFIG Config = Params->TestConfigurationParams.Config; if (Config.Flags != QUIC_EXECUTION_CONFIG_FLAG_NONE) { Status = MsQuic->SetParam( diff --git a/src/test/lib/ApiTest.cpp b/src/test/lib/ApiTest.cpp index 5aa17fec89..0026931b66 100644 --- a/src/test/lib/ApiTest.cpp +++ b/src/test/lib/ApiTest.cpp @@ -2596,13 +2596,13 @@ void QuicTestGlobalParam() nullptr)); } - uint8_t Data[QUIC_EXECUTION_CONFIG_MIN_SIZE + sizeof(uint16_t) * 4] = {}; + uint8_t Data[QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE + sizeof(uint16_t) * 4] = {}; uint32_t DataLength = sizeof(Data); - QUIC_EXECUTION_CONFIG* Config = (QUIC_EXECUTION_CONFIG*)Data; + QUIC_GLOBAL_EXECUTION_CONFIG* Config = (QUIC_GLOBAL_EXECUTION_CONFIG*)Data; Config->ProcessorCount = 4; if (CxPlatProcCount() < Config->ProcessorCount) { Config->ProcessorCount = CxPlatProcCount(); - DataLength = QUIC_EXECUTION_CONFIG_MIN_SIZE + sizeof(uint16_t) * Config->ProcessorCount; + DataLength = QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE + sizeof(uint16_t) * Config->ProcessorCount; } for (uint16_t i = 0; i < (uint16_t)Config->ProcessorCount; ++i) { Config->ProcessorList[i] = i; diff --git a/src/tools/attack/attack.cpp b/src/tools/attack/attack.cpp index 4d99613f07..26bed416f8 100644 --- a/src/tools/attack/attack.cpp +++ b/src/tools/attack/attack.cpp @@ -458,7 +458,7 @@ main( // flag QUIC_EXECUTION_CONFIG_FLAGS Flags = QUIC_EXECUTION_CONFIG_FLAG_XDP; - QUIC_EXECUTION_CONFIG DatapathFlags = { + QUIC_GLOBAL_EXECUTION_CONFIG DatapathFlags = { Flags, }; CxPlatSystemLoad(); diff --git a/src/tools/spin/spinquic.cpp b/src/tools/spin/spinquic.cpp index 71140c0f76..dccf3a1bae 100644 --- a/src/tools/spin/spinquic.cpp +++ b/src/tools/spin/spinquic.cpp @@ -28,7 +28,7 @@ } while (0) #define ASSERT_ON_NOT(x) CXPLAT_FRE_ASSERT(x) -QUIC_EXECUTION_CONFIG* ExecConfig = nullptr; +QUIC_GLOBAL_EXECUTION_CONFIG* ExecConfig = nullptr; uint32_t ExecConfigSize = 0; class FuzzingData { @@ -1557,8 +1557,8 @@ void start() { 1 : 1 + GetRandom(CxPlatProcCount() - 1); printf("Using %u partitions...\n", ProcCount); - ExecConfigSize = QUIC_EXECUTION_CONFIG_MIN_SIZE + sizeof(uint16_t)*ProcCount; - ExecConfig = (QUIC_EXECUTION_CONFIG*)malloc(ExecConfigSize); + ExecConfigSize = QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE + sizeof(uint16_t)*ProcCount; + ExecConfig = (QUIC_GLOBAL_EXECUTION_CONFIG*)malloc(ExecConfigSize); if (strncmp(SpinSettings.ServerName, "192.168.1.11", 12) == 0) { ExecConfig->Flags = QUIC_EXECUTION_CONFIG_FLAG_XDP; } else { From 8c7c05b9b1ecfb8c2eaece0ccca28cf871c75271 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Mon, 28 Apr 2025 11:58:12 -0400 Subject: [PATCH 20/34] Fix clog --- src/generated/linux/platform_worker.c.clog.h | 36 +++++++++--------- .../linux/platform_worker.c.clog.h.lttng.h | 38 +++++++++---------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/generated/linux/platform_worker.c.clog.h b/src/generated/linux/platform_worker.c.clog.h index 232cda11c5..4752bf0a0c 100644 --- a/src/generated/linux/platform_worker.c.clog.h +++ b/src/generated/linux/platform_worker.c.clog.h @@ -83,6 +83,24 @@ tracepoint(CLOG_PLATFORM_WORKER_C, PlatformWorkerProcessPools , arg2);\ +/*---------------------------------------------------------- +// Decoder Ring for LibraryError +// [ lib] ERROR, %s. +// QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "CxPlatEventQInitialize"); +// arg2 = arg2 = "CxPlatEventQInitialize" = arg2 +----------------------------------------------------------*/ +#ifndef _clog_3_ARGS_TRACE_LibraryError +#define _clog_3_ARGS_TRACE_LibraryError(uniqueId, encoded_arg_string, arg2)\ +tracepoint(CLOG_PLATFORM_WORKER_C, LibraryError , arg2);\ + +#endif + + + + /*---------------------------------------------------------- // Decoder Ring for AllocFailure // Allocation of '%s' failed. (%llu bytes) @@ -103,24 +121,6 @@ tracepoint(CLOG_PLATFORM_WORKER_C, AllocFailure , arg2, arg3);\ -/*---------------------------------------------------------- -// Decoder Ring for LibraryError -// [ lib] ERROR, %s. -// QuicTraceEvent( - LibraryError, - "[ lib] ERROR, %s.", - "CxPlatEventQInitialize"); -// arg2 = arg2 = "CxPlatEventQInitialize" = arg2 -----------------------------------------------------------*/ -#ifndef _clog_3_ARGS_TRACE_LibraryError -#define _clog_3_ARGS_TRACE_LibraryError(uniqueId, encoded_arg_string, arg2)\ -tracepoint(CLOG_PLATFORM_WORKER_C, LibraryError , arg2);\ - -#endif - - - - #ifdef __cplusplus } #endif diff --git a/src/generated/linux/platform_worker.c.clog.h.lttng.h b/src/generated/linux/platform_worker.c.clog.h.lttng.h index 5df85e5ab2..5d62d3ad15 100644 --- a/src/generated/linux/platform_worker.c.clog.h.lttng.h +++ b/src/generated/linux/platform_worker.c.clog.h.lttng.h @@ -58,6 +58,25 @@ TRACEPOINT_EVENT(CLOG_PLATFORM_WORKER_C, PlatformWorkerProcessPools, +/*---------------------------------------------------------- +// Decoder Ring for LibraryError +// [ lib] ERROR, %s. +// QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "CxPlatEventQInitialize"); +// arg2 = arg2 = "CxPlatEventQInitialize" = arg2 +----------------------------------------------------------*/ +TRACEPOINT_EVENT(CLOG_PLATFORM_WORKER_C, LibraryError, + TP_ARGS( + const char *, arg2), + TP_FIELDS( + ctf_string(arg2, arg2) + ) +) + + + /*---------------------------------------------------------- // Decoder Ring for AllocFailure // Allocation of '%s' failed. (%llu bytes) @@ -78,22 +97,3 @@ TRACEPOINT_EVENT(CLOG_PLATFORM_WORKER_C, AllocFailure, ctf_integer(uint64_t, arg3, arg3) ) ) - - - -/*---------------------------------------------------------- -// Decoder Ring for LibraryError -// [ lib] ERROR, %s. -// QuicTraceEvent( - LibraryError, - "[ lib] ERROR, %s.", - "CxPlatEventQInitialize"); -// arg2 = arg2 = "CxPlatEventQInitialize" = arg2 -----------------------------------------------------------*/ -TRACEPOINT_EVENT(CLOG_PLATFORM_WORKER_C, LibraryError, - TP_ARGS( - const char *, arg2), - TP_FIELDS( - ctf_string(arg2, arg2) - ) -) From 74b8cb918e11e22771e3e57d376b4ef4e3dc7606 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 08:02:48 -0400 Subject: [PATCH 21/34] Fix clean up code --- src/core/library.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/library.c b/src/core/library.c index fc0a5bc188..c963c86bf2 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -668,6 +668,7 @@ QuicLibraryLazyInitialize( }; QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + BOOLEAN CreatedWorkerPool = FALSE; if (AcquireLock) { CxPlatLockAcquire(&MsQuicLib.Lock); @@ -693,6 +694,7 @@ QuicLibraryLazyInitialize( MsQuicLibraryFreePartitions(); goto Exit; } + CreatedWorkerPool = TRUE; } #endif @@ -712,8 +714,10 @@ QuicLibraryLazyInitialize( } else { MsQuicLibraryFreePartitions(); #ifndef _KERNEL_MODE - CxPlatWorkerPoolDelete(MsQuicLib.WorkerPool); - MsQuicLib.WorkerPool = NULL; + if (CreatedWorkerPool) { + CxPlatWorkerPoolDelete(MsQuicLib.WorkerPool); + MsQuicLib.WorkerPool = NULL; + } #endif goto Exit; } From 9ccc97dec7be0c54a08bcff61905a56a801b8e15 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 08:18:21 -0400 Subject: [PATCH 22/34] fix builds --- docs/Execution.md | 2 +- src/core/api.h | 2 +- src/core/library.c | 62 ++++++++++++++++---- src/core/operation.h | 2 +- src/cs/lib/msquic_generated.cs | 2 +- src/generated/linux/library.c.clog.h | 54 +++++++++++++++++ src/generated/linux/library.c.clog.h.lttng.h | 58 ++++++++++++++++++ src/inc/msquic.h | 14 +---- src/inc/msquic.hpp | 6 +- src/inc/quic_trace.h | 3 + src/manifest/MsQuicEtw.man | 48 +++++++++++++++ 11 files changed, 220 insertions(+), 33 deletions(-) diff --git a/docs/Execution.md b/docs/Execution.md index 7866c57051..2730689faf 100644 --- a/docs/Execution.md +++ b/docs/Execution.md @@ -158,7 +158,7 @@ HANDLE IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); QUIC_EXECUTION_CONFIG ExecConfig = { 0, &IOCP }; QUIC_EXECUTION* ExecContext = nullptr; -QUIC_STATUS Status = MsQuic->ExecutionCreate(QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 1, &ExecConfig, &ExecContext); +QUIC_STATUS Status = MsQuic->ExecutionCreate(QUIC_GLOBAL_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. diff --git a/src/core/api.h b/src/core/api.h index d0a8631210..e6c8e52645 100644 --- a/src/core/api.h +++ b/src/core/api.h @@ -9,7 +9,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS QUIC_API MsQuicExecutionCreate( - _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type + _In_ QUIC_GLOBAL_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, _In_ uint32_t Count, _In_reads_(Count) QUIC_EXECUTION_CONFIG* Configs, diff --git a/src/core/library.c b/src/core/library.c index c963c86bf2..1f79238e57 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -2428,32 +2428,46 @@ _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS QUIC_API MsQuicExecutionCreate( - _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type + _In_ QUIC_GLOBAL_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, _In_ uint32_t Count, _In_reads_(Count) QUIC_EXECUTION_CONFIG* Configs, _Out_writes_(Count) QUIC_EXECUTION** Executions ) { + QuicTraceEvent( + ApiEnter, + "[ api] Enter %u (%p).", + QUIC_TRACE_API_EXECUTION_CREATE, + NULL); + + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + if (MsQuicLib.LazyInitComplete) { // // Not allowed to change the execution config after we've already // started running the library. // - return QUIC_STATUS_INVALID_STATE; - } + Status = QUIC_STATUS_INVALID_STATE; - // - // Clean up any previous worker pool and create a new one. - // - CxPlatWorkerPoolDelete(MsQuicLib.WorkerPool); - MsQuicLib.WorkerPool = - CxPlatWorkerPoolCreateExternal(Count, Configs, Executions); - if (MsQuicLib.WorkerPool == NULL) { - return QUIC_STATUS_OUT_OF_MEMORY; + } else { + // + // Clean up any previous worker pool and create a new one. + // + CxPlatWorkerPoolDelete(MsQuicLib.WorkerPool); + MsQuicLib.WorkerPool = + CxPlatWorkerPoolCreateExternal(Count, Configs, Executions); + if (MsQuicLib.WorkerPool == NULL) { + Status = QUIC_STATUS_OUT_OF_MEMORY; + } } - return QUIC_STATUS_SUCCESS; + QuicTraceEvent( + ApiExitStatus, + "[ api] Exit %u", + Status); + + return Status; } _IRQL_requires_max_(PASSIVE_LEVEL) @@ -2464,10 +2478,20 @@ MsQuicExecutionDelete( _In_reads_(Count) QUIC_EXECUTION** Executions ) { + QuicTraceEvent( + ApiEnter, + "[ api] Enter %u (%p).", + QUIC_TRACE_API_EXECUTION_DELETE, + NULL); + UNREFERENCED_PARAMETER(Count); UNREFERENCED_PARAMETER(Executions); CxPlatWorkerPoolDelete(MsQuicLib.WorkerPool); MsQuicLib.WorkerPool = NULL; + + QuicTraceEvent( + ApiExit, + "[ api] Exit"); } _IRQL_requires_max_(PASSIVE_LEVEL) @@ -2477,7 +2501,19 @@ MsQuicExecutionPoll( _In_ QUIC_EXECUTION* Execution ) { - return CxPlatWorkerPoolWorkerPoll(Execution); + QuicTraceEvent( + ApiEnter, + "[ api] Enter %u (%p).", + QUIC_TRACE_API_EXECUTION_POLL, + NULL); + + uint32_t Result = CxPlatWorkerPoolWorkerPoll(Execution); + + QuicTraceEvent( + ApiExit, + "[ api] Exit"); + + return Result; } #endif diff --git a/src/core/operation.h b/src/core/operation.h index e39be6b7d4..43a94033a5 100644 --- a/src/core/operation.h +++ b/src/core/operation.h @@ -56,7 +56,6 @@ typedef enum QUIC_API_TYPE { QUIC_API_TYPE_STRM_SEND, QUIC_API_TYPE_STRM_RECV_COMPLETE, QUIC_API_TYPE_STRM_RECV_SET_ENABLED, - QUIC_API_TYPE_STRM_PROVIDE_RECV_BUFFERS, QUIC_API_TYPE_SET_PARAM, QUIC_API_TYPE_GET_PARAM, @@ -64,6 +63,7 @@ typedef enum QUIC_API_TYPE { QUIC_API_TYPE_DATAGRAM_SEND, QUIC_API_TYPE_CONN_COMPLETE_RESUMPTION_TICKET_VALIDATION, QUIC_API_TYPE_CONN_COMPLETE_CERTIFICATE_VALIDATION, + QUIC_API_TYPE_STRM_PROVIDE_RECV_BUFFERS, } QUIC_API_TYPE; diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs index 2c750664c6..c782db998c 100644 --- a/src/cs/lib/msquic_generated.cs +++ b/src/cs/lib/msquic_generated.cs @@ -3430,7 +3430,7 @@ internal unsafe partial struct QUIC_API_TABLE internal delegate* unmanaged[Cdecl] ConnectionPoolCreate; [NativeTypeName("QUIC_EXECUTION_CREATE_FN")] - internal delegate* unmanaged[Cdecl] ExecutionCreate; + internal delegate* unmanaged[Cdecl] ExecutionCreate; [NativeTypeName("QUIC_EXECUTION_DELETE_FN")] internal delegate* unmanaged[Cdecl] ExecutionDelete; diff --git a/src/generated/linux/library.c.clog.h b/src/generated/linux/library.c.clog.h index cd74c3dcb8..bdee508a83 100644 --- a/src/generated/linux/library.c.clog.h +++ b/src/generated/linux/library.c.clog.h @@ -573,6 +573,60 @@ tracepoint(CLOG_LIBRARY_C, LibrarySendRetryStateUpdated , arg2);\ +/*---------------------------------------------------------- +// Decoder Ring for ApiEnter +// [ api] Enter %u (%p). +// QuicTraceEvent( + ApiEnter, + "[ api] Enter %u (%p).", + QUIC_TRACE_API_EXECUTION_CREATE, + NULL); +// arg2 = arg2 = QUIC_TRACE_API_EXECUTION_CREATE = arg2 +// arg3 = arg3 = NULL = arg3 +----------------------------------------------------------*/ +#ifndef _clog_4_ARGS_TRACE_ApiEnter +#define _clog_4_ARGS_TRACE_ApiEnter(uniqueId, encoded_arg_string, arg2, arg3)\ +tracepoint(CLOG_LIBRARY_C, ApiEnter , arg2, arg3);\ + +#endif + + + + +/*---------------------------------------------------------- +// Decoder Ring for ApiExitStatus +// [ api] Exit %u +// QuicTraceEvent( + ApiExitStatus, + "[ api] Exit %u", + Status); +// arg2 = arg2 = Status = arg2 +----------------------------------------------------------*/ +#ifndef _clog_3_ARGS_TRACE_ApiExitStatus +#define _clog_3_ARGS_TRACE_ApiExitStatus(uniqueId, encoded_arg_string, arg2)\ +tracepoint(CLOG_LIBRARY_C, ApiExitStatus , arg2);\ + +#endif + + + + +/*---------------------------------------------------------- +// Decoder Ring for ApiExit +// [ api] Exit +// QuicTraceEvent( + ApiExit, + "[ api] Exit"); +----------------------------------------------------------*/ +#ifndef _clog_2_ARGS_TRACE_ApiExit +#define _clog_2_ARGS_TRACE_ApiExit(uniqueId, encoded_arg_string)\ +tracepoint(CLOG_LIBRARY_C, ApiExit );\ + +#endif + + + + #ifdef __cplusplus } #endif diff --git a/src/generated/linux/library.c.clog.h.lttng.h b/src/generated/linux/library.c.clog.h.lttng.h index f73ec8abac..0d39a4a180 100644 --- a/src/generated/linux/library.c.clog.h.lttng.h +++ b/src/generated/linux/library.c.clog.h.lttng.h @@ -561,3 +561,61 @@ TRACEPOINT_EVENT(CLOG_LIBRARY_C, LibrarySendRetryStateUpdated, ctf_integer(unsigned char, arg2, arg2) ) ) + + + +/*---------------------------------------------------------- +// Decoder Ring for ApiEnter +// [ api] Enter %u (%p). +// QuicTraceEvent( + ApiEnter, + "[ api] Enter %u (%p).", + QUIC_TRACE_API_EXECUTION_CREATE, + NULL); +// arg2 = arg2 = QUIC_TRACE_API_EXECUTION_CREATE = arg2 +// arg3 = arg3 = NULL = arg3 +----------------------------------------------------------*/ +TRACEPOINT_EVENT(CLOG_LIBRARY_C, ApiEnter, + TP_ARGS( + unsigned int, arg2, + const void *, arg3), + TP_FIELDS( + ctf_integer(unsigned int, arg2, arg2) + ctf_integer_hex(uint64_t, arg3, (uint64_t)arg3) + ) +) + + + +/*---------------------------------------------------------- +// Decoder Ring for ApiExitStatus +// [ api] Exit %u +// QuicTraceEvent( + ApiExitStatus, + "[ api] Exit %u", + Status); +// arg2 = arg2 = Status = arg2 +----------------------------------------------------------*/ +TRACEPOINT_EVENT(CLOG_LIBRARY_C, ApiExitStatus, + TP_ARGS( + unsigned int, arg2), + TP_FIELDS( + ctf_integer(unsigned int, arg2, arg2) + ) +) + + + +/*---------------------------------------------------------- +// Decoder Ring for ApiExit +// [ api] Exit +// QuicTraceEvent( + ApiExit, + "[ api] Exit"); +----------------------------------------------------------*/ +TRACEPOINT_EVENT(CLOG_LIBRARY_C, ApiExit, + TP_ARGS( +), + TP_FIELDS( + ) +) diff --git a/src/inc/msquic.h b/src/inc/msquic.h index be4bf43ea6..36b15deba2 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -320,25 +320,13 @@ typedef _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS (QUIC_API * QUIC_EXECUTION_CREATE_FN)( - _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type + _In_ QUIC_GLOBAL_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, _In_ uint32_t Count, _In_reads_(Count) QUIC_EXECUTION_CONFIG* Configs, _Out_writes_(Count) QUIC_EXECUTION** Executions ); -// -// This is called to stop any further usage of the execution contexts. MsQuic -// will no longer queue any additional work to the contexts. -// -typedef -_IRQL_requires_max_(PASSIVE_LEVEL) -void -(QUIC_API * QUIC_EXECUTION_SHUTDOWN_FN)( - _In_ uint32_t Count, - _In_reads_(Count) QUIC_EXECUTION** Executions - ); - // // This is called to delete the execution contexts. // diff --git a/src/inc/msquic.hpp b/src/inc/msquic.hpp index 9169674e01..c0540d2a4a 100644 --- a/src/inc/msquic.hpp +++ b/src/inc/msquic.hpp @@ -454,11 +454,11 @@ extern const MsQuicApi* MsQuic; struct MsQuicExecution { QUIC_EXECUTION** Executions {nullptr}; uint32_t Count {0}; - MsQuicExecution(QUIC_EVENTQ* EventQ, QUIC_EXECUTION_CONFIG_FLAGS Flags = QUIC_EXECUTION_CONFIG_FLAG_NONE, uint32_t PollingIdleTimeoutUs = 0) noexcept : Count(1) { + MsQuicExecution(QUIC_EVENTQ* EventQ, QUIC_GLOBAL_EXECUTION_CONFIG_FLAGS Flags = QUIC_GLOBAL_EXECUTION_CONFIG_FLAG_NONE, uint32_t PollingIdleTimeoutUs = 0) noexcept : Count(1) { QUIC_EXECUTION_CONFIG Config = { 0, EventQ }; Initialize(Flags, PollingIdleTimeoutUs, &Config); } - MsQuicExecution(QUIC_EVENTQ** EventQ, uint32_t Count, QUIC_EXECUTION_CONFIG_FLAGS Flags = QUIC_EXECUTION_CONFIG_FLAG_NONE, uint32_t PollingIdleTimeoutUs = 0) noexcept : Count(Count) { + MsQuicExecution(QUIC_EVENTQ** EventQ, uint32_t Count, QUIC_GLOBAL_EXECUTION_CONFIG_FLAGS Flags = QUIC_GLOBAL_EXECUTION_CONFIG_FLAG_NONE, uint32_t PollingIdleTimeoutUs = 0) noexcept : Count(Count) { auto Configs = new(std::nothrow) QUIC_EXECUTION_CONFIG[Count]; if (Configs != nullptr) { for (uint32_t i = 0; i < Count; ++i) { @@ -470,7 +470,7 @@ struct MsQuicExecution { } } void Initialize( - _In_ QUIC_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type + _In_ QUIC_GLOBAL_EXECUTION_CONFIG_FLAGS Flags, // Used for datapath type _In_ uint32_t PollingIdleTimeoutUs, _In_reads_(this->Count) QUIC_EXECUTION_CONFIG* Configs ) diff --git a/src/inc/quic_trace.h b/src/inc/quic_trace.h index 1d22930d79..d157f962be 100644 --- a/src/inc/quic_trace.h +++ b/src/inc/quic_trace.h @@ -105,6 +105,9 @@ typedef enum QUIC_TRACE_API_TYPE { QUIC_TRACE_API_CONNECTION_COMPLETE_CERTIFICATE_VALIDATION, QUIC_TRACE_API_STREAM_PROVIDE_RECEIVE_BUFFERS, QUIC_TRACE_API_CONNECTION_POOL_CREATE, + QUIC_TRACE_API_EXECUTION_CREATE, + QUIC_TRACE_API_EXECUTION_DELETE, + QUIC_TRACE_API_EXECUTION_POLL, QUIC_TRACE_API_COUNT // Must be last } QUIC_TRACE_API_TYPE; diff --git a/src/manifest/MsQuicEtw.man b/src/manifest/MsQuicEtw.man index d221e5d1ff..46572a5420 100644 --- a/src/manifest/MsQuicEtw.man +++ b/src/manifest/MsQuicEtw.man @@ -242,6 +242,18 @@ message="$(string.Enum.QUIC_API_TYPE.DATAGRAM_SEND)" value="13" /> + + + + + + + + + + + + Date: Tue, 6 May 2025 08:37:05 -0400 Subject: [PATCH 23/34] fixes --- src/core/library.c | 17 ++++++++++++++++- src/tools/CMakeLists.txt | 1 + src/tools/execution/execution_windows.cpp | 10 ++++++---- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/core/library.c b/src/core/library.c index 1f79238e57..e3cd01751a 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -125,7 +125,15 @@ QuicLibraryInitializePartitions( CXPLAT_FRE_ASSERT(MsQuicLib.PartitionCount > 0); uint16_t* ProcessorList = NULL; - if (MsQuicLib.ExecutionConfig && +#ifndef _KERNEL_MODE + if (MsQuicLib.WorkerPool != NULL) { + MsQuicLib.CustomPartitions = TRUE; + MsQuicLib.PartitionCount = (uint16_t)CxPlatWorkerPoolGetCount(MsQuicLib.WorkerPool); + } else if ( +#else + if ( +#endif + MsQuicLib.ExecutionConfig && MsQuicLib.ExecutionConfig->ProcessorCount && MsQuicLib.ExecutionConfig->ProcessorCount != MsQuicLib.PartitionCount) { // @@ -183,7 +191,14 @@ QuicLibraryInitializePartitions( QuicPartitionInitialize( &MsQuicLib.Partitions[i], i, +#ifndef _KERNEL_MODE + ProcessorList ? ProcessorList[i] : + (MsQuicLib.CustomPartitions ? + CxPlatWorkerPoolGetIdealProcessor(MsQuicLib.WorkerPool, i) : + i), +#else ProcessorList ? ProcessorList[i] : i, +#endif CXPLAT_HASH_SHA256, ResetHashKey, sizeof(ResetHashKey)); diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 14582b3d7a..8535e99ff6 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -35,6 +35,7 @@ add_subdirectory(post) add_subdirectory(sample) add_subdirectory(spin) if(WIN32 AND (NOT QUIC_UWP_BUILD AND NOT QUIC_GAMECORE_BUILD)) + add_subdirectory(execution) add_subdirectory(etw) add_subdirectory(recvfuzz) endif() diff --git a/src/tools/execution/execution_windows.cpp b/src/tools/execution/execution_windows.cpp index 6d18b40887..3cf71b23d7 100644 --- a/src/tools/execution/execution_windows.cpp +++ b/src/tools/execution/execution_windows.cpp @@ -51,6 +51,7 @@ struct WindowsIOCP { char* Host = nullptr; WindowsIOCP* IOCP; +const MsQuicApi* MsQuic; MsQuicRegistration* Registration; MsQuicConnection* Connection; bool AllDone = false; @@ -79,13 +80,14 @@ void QueueConnectedJob() { IOCP->Enqueue(&Sqe->Overlapped); } -QUIC_STATUS MsQuicConnectionCallback(_In_ struct MsQuicConnection* Connection, _In_opt_ void*, _Inout_ QUIC_CONNECTION_EVENT* Event) { +QUIC_STATUS QUIC_API ConnectionCallback(_In_ struct MsQuicConnection* Conn, _In_opt_ void*, _Inout_ QUIC_CONNECTION_EVENT* Event) { if (Event->Type == QUIC_CONNECTION_EVENT_CONNECTED) { QueueConnectedJob(); - Connection->Shutdown(0); + Conn->Shutdown(0); } else if (Event->Type == QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE) { QueueCleanupJob(); } + return QUIC_STATUS_SUCCESS; } void ConnectJob(QUIC_CQE* Cqe) { @@ -97,7 +99,7 @@ void ConnectJob(QUIC_CQE* Cqe) { MsQuicConfiguration Configuration(Registration, "h3", Settings, MsQuicCredentialConfig()); if (!Configuration.IsValid()) { break; } - Connection = new(std::nothrow) MsQuicConnection(Registration, CleanUpAutoDelete, MsQuicConnectionCallback); + Connection = new(std::nothrow) MsQuicConnection(Registration, CleanUpAutoDelete, ConnectionCallback); if (QUIC_FAILED(Connection->Start(Configuration, Host, 443))) { delete Connection; break; @@ -144,7 +146,7 @@ main( QueueConnectJob(); while (!AllDone) { - uint32_t WaitTime = Execution[0].Poll(); + uint32_t WaitTime = MsQuic->ExecutionPoll(Execution[0]); ULONG OverlappedCount = 0; OVERLAPPED_ENTRY Overlapped[8]; From ad3289480dbed60a53eb501cc731b58670ca4a0b Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 09:18:09 -0400 Subject: [PATCH 24/34] Single thread test app works! --- src/core/api.c | 13 ++++++++----- src/core/library.c | 2 ++ src/core/library.h | 5 +++++ src/core/worker.c | 14 ++++++++++++-- src/platform/platform_worker.c | 3 +++ src/tools/execution/execution_windows.cpp | 11 +++++++++-- 6 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/core/api.c b/src/core/api.c index eb65ea1f34..54deb1836c 100644 --- a/src/core/api.c +++ b/src/core/api.c @@ -157,7 +157,7 @@ MsQuicConnectionClose( CXPLAT_TEL_ASSERT(!Connection->State.HandleClosed); - if (IsWorkerThread) { + if (MsQuicLib.CustomExecutions || IsWorkerThread) { // // Execute this blocking API call inline if called on the worker thread. // @@ -749,7 +749,7 @@ MsQuicStreamClose( CXPLAT_TEL_ASSERT(!Stream->Flags.HandleClosed); - if (IsWorkerThread) { + if (MsQuicLib.CustomExecutions || IsWorkerThread) { // // Execute this blocking API call inline if called on the worker thread. // @@ -959,7 +959,8 @@ MsQuicStreamShutdown( QUIC_CONN_VERIFY(Connection, !Connection->State.Freed); if (Flags & QUIC_STREAM_SHUTDOWN_FLAG_INLINE && - Connection->WorkerThreadID == CxPlatCurThreadID()) { + (MsQuicLib.CustomExecutions || + Connection->WorkerThreadID == CxPlatCurThreadID())) { CXPLAT_PASSIVE_CODE(); @@ -1613,7 +1614,8 @@ MsQuicSetParam( QUIC_CONN_VERIFY(Connection, !Connection->State.Freed); - if (Connection->WorkerThreadID == CxPlatCurThreadID()) { + if (MsQuicLib.CustomExecutions || + Connection->WorkerThreadID == CxPlatCurThreadID()) { // // Execute this blocking API call inline if called on the worker thread. // @@ -1738,7 +1740,8 @@ MsQuicGetParam( QUIC_CONN_VERIFY(Connection, !Connection->State.Freed); - if (Connection->WorkerThreadID == CxPlatCurThreadID()) { + if (MsQuicLib.CustomExecutions || + Connection->WorkerThreadID == CxPlatCurThreadID()) { // // Execute this blocking API call inline if called on the worker thread. // diff --git a/src/core/library.c b/src/core/library.c index a225daed68..cc183c3ca7 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -2494,6 +2494,8 @@ MsQuicExecutionCreate( if (MsQuicLib.WorkerPool == NULL) { Status = QUIC_STATUS_OUT_OF_MEMORY; } + + MsQuicLib.CustomExecutions = TRUE; } QuicTraceEvent( diff --git a/src/core/library.h b/src/core/library.h index 2d1c3a9611..47643e2e72 100644 --- a/src/core/library.h +++ b/src/core/library.h @@ -55,6 +55,11 @@ typedef struct QUIC_LIBRARY { // BOOLEAN LazyInitComplete : 1; + // + // Indicates the app has configured their own execution contexts. + // + BOOLEAN CustomExecutions : 1; + // // Indicates the app has configured non-default (per-processor) partitioning. // diff --git a/src/core/worker.c b/src/core/worker.c index f12d4e0d94..6cf03175eb 100644 --- a/src/core/worker.c +++ b/src/core/worker.c @@ -30,6 +30,12 @@ QuicWorkerLoop( _Inout_ CXPLAT_EXECUTION_STATE* State ); +_IRQL_requires_max_(PASSIVE_LEVEL) +void +QuicWorkerLoopCleanup( + _In_ QUIC_WORKER* Worker + ); + // // Thread callback for processing the work queued for the worker. // @@ -169,8 +175,12 @@ QuicWorkerUninitialize( // Worker->Enabled = FALSE; if (Worker->ExecutionContext.Context) { - QuicWorkerThreadWake(Worker); - CxPlatEventWaitForever(Worker->Done); + if (MsQuicLib.CustomExecutions) { + QuicWorkerLoopCleanup(Worker); + } else { + QuicWorkerThreadWake(Worker); + CxPlatEventWaitForever(Worker->Done); + } } CxPlatEventUninitialize(Worker->Done); diff --git a/src/platform/platform_worker.c b/src/platform/platform_worker.c index 9dbcf095d9..51ef7f0a23 100644 --- a/src/platform/platform_worker.c +++ b/src/platform/platform_worker.c @@ -597,6 +597,7 @@ CxPlatWorkerPoolWorkerPoll( { CXPLAT_WORKER* Worker = (CXPLAT_WORKER*)Execution; Worker->State.ThreadID = CxPlatCurThreadID(); + Worker->State.TimeNow = CxPlatTimeUs64(); CxPlatRunExecutionContexts(Worker); if (Worker->State.WaitTime && InterlockedFetchAndClearBoolean(&Worker->Running)) { @@ -604,6 +605,8 @@ CxPlatWorkerPoolWorkerPoll( CxPlatRunExecutionContexts(Worker); // Run once more to handle race conditions } + Worker->State.ThreadID = UINT32_MAX; + return Worker->State.WaitTime; } diff --git a/src/tools/execution/execution_windows.cpp b/src/tools/execution/execution_windows.cpp index 3cf71b23d7..f4813078b8 100644 --- a/src/tools/execution/execution_windows.cpp +++ b/src/tools/execution/execution_windows.cpp @@ -60,6 +60,7 @@ void QueueCleanupJob() { auto Sqe = new(std::nothrow) QUIC_SQE; ZeroMemory(&Sqe->Overlapped, sizeof(Sqe->Overlapped)); Sqe->Completion = [](QUIC_CQE* Cqe) { + printf("Cleaning up...\n"); AllDone = true; delete CONTAINING_RECORD(Cqe->lpOverlapped, QUIC_SQE, Overlapped); }; @@ -86,6 +87,8 @@ QUIC_STATUS QUIC_API ConnectionCallback(_In_ struct MsQuicConnection* Conn, _In_ Conn->Shutdown(0); } else if (Event->Type == QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE) { QueueCleanupJob(); + } else if (Event->Type == QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED) { + MsQuic->StreamClose(Event->PEER_STREAM_STARTED.Stream); } return QUIC_STATUS_SUCCESS; } @@ -93,13 +96,15 @@ QUIC_STATUS QUIC_API ConnectionCallback(_In_ struct MsQuicConnection* Conn, _In_ void ConnectJob(QUIC_CQE* Cqe) { bool Success = false; + printf("Connecting...\n"); + do { MsQuicSettings Settings; Settings.SetPeerUnidiStreamCount(3); // required for H3 - MsQuicConfiguration Configuration(Registration, "h3", Settings, MsQuicCredentialConfig()); + MsQuicConfiguration Configuration(*Registration, "h3", Settings, MsQuicCredentialConfig()); if (!Configuration.IsValid()) { break; } - Connection = new(std::nothrow) MsQuicConnection(Registration, CleanUpAutoDelete, ConnectionCallback); + Connection = new(std::nothrow) MsQuicConnection(*Registration, CleanUpAutoDelete, ConnectionCallback); if (QUIC_FAILED(Connection->Start(Configuration, Host, 443))) { delete Connection; break; @@ -158,5 +163,7 @@ main( } } + printf("Done.\n"); + return 0; } From 20397716f6cc1395a8c555528a03375845be0e6c Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 09:23:40 -0400 Subject: [PATCH 25/34] unreferenced parameters --- src/core/library.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/library.c b/src/core/library.c index cc183c3ca7..ac49e331ef 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -2477,6 +2477,9 @@ MsQuicExecutionCreate( QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + UNREFERENCED_PARAMETER(Flags); + UNREFERENCED_PARAMETER(PollingIdleTimeoutUs); + if (MsQuicLib.LazyInitComplete) { // // Not allowed to change the execution config after we've already From a44b9992123a5922158d4ebf04f4801f4a96063b Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 10:25:40 -0400 Subject: [PATCH 26/34] Fix kernel builds --- src/inc/msquic.hpp | 2 ++ src/tools/execution/execution_windows.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/inc/msquic.hpp b/src/inc/msquic.hpp index 7f4fa98d54..dec67c25a2 100644 --- a/src/inc/msquic.hpp +++ b/src/inc/msquic.hpp @@ -450,6 +450,7 @@ class MsQuicApi : public QUIC_API_TABLE { extern const MsQuicApi* MsQuic; #ifdef QUIC_API_ENABLE_PREVIEW_FEATURES +#ifdef CX_PLATFORM_TYPE struct MsQuicExecution { QUIC_EXECUTION** Executions {nullptr}; @@ -496,6 +497,7 @@ struct MsQuicExecution { } }; +#endif // CX_PLATFORM_TYPE #endif // QUIC_API_ENABLE_PREVIEW_FEATURES struct MsQuicRegistration { diff --git a/src/tools/execution/execution_windows.cpp b/src/tools/execution/execution_windows.cpp index f4813078b8..55bb3e9db8 100644 --- a/src/tools/execution/execution_windows.cpp +++ b/src/tools/execution/execution_windows.cpp @@ -11,6 +11,7 @@ #define QUIC_API_ENABLE_PREVIEW_FEATURES 1 +#include "quic_platform.h" #include "msquic.hpp" #include From da2636291cffac8098c0c5c8517663cfd67e6a35 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 10:48:32 -0400 Subject: [PATCH 27/34] another try for kernel mode --- src/inc/msquic.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inc/msquic.hpp b/src/inc/msquic.hpp index dec67c25a2..4bf1cef2c0 100644 --- a/src/inc/msquic.hpp +++ b/src/inc/msquic.hpp @@ -449,8 +449,8 @@ class MsQuicApi : public QUIC_API_TABLE { extern const MsQuicApi* MsQuic; +#ifndef _KERNEL_MODE #ifdef QUIC_API_ENABLE_PREVIEW_FEATURES -#ifdef CX_PLATFORM_TYPE struct MsQuicExecution { QUIC_EXECUTION** Executions {nullptr}; @@ -497,8 +497,8 @@ struct MsQuicExecution { } }; -#endif // CX_PLATFORM_TYPE #endif // QUIC_API_ENABLE_PREVIEW_FEATURES +#endif // _KERNEL_MODE struct MsQuicRegistration { bool CloseAllConnectionsOnDelete {false}; From e5731f4831e6aa79f09d33daa05b631f704afc0a Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 15:08:56 -0400 Subject: [PATCH 28/34] Trying to fix rust --- src/inc/msquic_winuser.h | 16 +++++++++++++++- src/rs/ffi/mod.rs | 6 ++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/inc/msquic_winuser.h b/src/inc/msquic_winuser.h index e1b40645a1..451cdb5b27 100644 --- a/src/inc/msquic_winuser.h +++ b/src/inc/msquic_winuser.h @@ -392,8 +392,22 @@ void ); typedef QUIC_EVENT_COMPLETION *QUIC_EVENT_COMPLETION_HANDLER; +typedef struct QUIC_OVERLAPPED { + ULONG_PTR Internal; + ULONG_PTR InternalHigh; + union { + struct { + DWORD Offset; + DWORD OffsetHigh; + } DUMMYSTRUCTNAME; + PVOID Pointer; + } DUMMYUNIONNAME; + + HANDLE hEvent; +} QUIC_OVERLAPPED; + typedef struct QUIC_SQE { - OVERLAPPED Overlapped; + QUIC_OVERLAPPED Overlapped; QUIC_EVENT_COMPLETION_HANDLER Completion; #if DEBUG BOOLEAN IsQueued; // Debug flag to catch double queueing. diff --git a/src/rs/ffi/mod.rs b/src/rs/ffi/mod.rs index 829432a078..43fa3770cf 100644 --- a/src/rs/ffi/mod.rs +++ b/src/rs/ffi/mod.rs @@ -8,6 +8,12 @@ pub type QUIC_ADDR = std::ffi::c_void; +#[cfg(target_os = "windows")] +pub type HANDLE = std::os::windows::raw::HANDLE; + +#[cfg(not(target_os = "windows"))] +pub type epoll_event = libc::epoll_event; + // TODO: macos currently is using the linux bindings. #[cfg(not(target_os = "windows"))] pub type sa_family_t = u16; From 38feb45715a2b96a7ea3245ed19617c90870cdb1 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 15:18:10 -0400 Subject: [PATCH 29/34] another try --- src/inc/msquic_winuser.h | 10 +++--- src/rs/ffi/linux_bindings.rs | 63 +++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/inc/msquic_winuser.h b/src/inc/msquic_winuser.h index 451cdb5b27..62f918d4e4 100644 --- a/src/inc/msquic_winuser.h +++ b/src/inc/msquic_winuser.h @@ -393,14 +393,14 @@ void typedef QUIC_EVENT_COMPLETION *QUIC_EVENT_COMPLETION_HANDLER; typedef struct QUIC_OVERLAPPED { - ULONG_PTR Internal; - ULONG_PTR InternalHigh; + unsigned long long Internal; + unsigned long long InternalHigh; union { struct { - DWORD Offset; - DWORD OffsetHigh; + unsigned long Offset; + unsigned long OffsetHigh; } DUMMYSTRUCTNAME; - PVOID Pointer; + void* Pointer; } DUMMYUNIONNAME; HANDLE hEvent; diff --git a/src/rs/ffi/linux_bindings.rs b/src/rs/ffi/linux_bindings.rs index 59364a9cdf..2523e0a05b 100644 --- a/src/rs/ffi/linux_bindings.rs +++ b/src/rs/ffi/linux_bindings.rs @@ -231,6 +231,24 @@ const _: () = { ["Offset of field: QUIC_ADDR_STR::Address"] [::std::mem::offset_of!(QUIC_ADDR_STR, Address) - 0usize]; }; +pub type QUIC_EVENTQ = ::std::os::raw::c_int; +pub type QUIC_CQE = epoll_event; +pub type QUIC_EVENT_COMPLETION = ::std::option::Option; +pub type QUIC_EVENT_COMPLETION_HANDLER = QUIC_EVENT_COMPLETION; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct QUIC_SQE { + pub fd: ::std::os::raw::c_int, + pub Completion: QUIC_EVENT_COMPLETION_HANDLER, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of QUIC_SQE"][::std::mem::size_of::() - 16usize]; + ["Alignment of QUIC_SQE"][::std::mem::align_of::() - 8usize]; + ["Offset of field: QUIC_SQE::fd"][::std::mem::offset_of!(QUIC_SQE, fd) - 0usize]; + ["Offset of field: QUIC_SQE::Completion"] + [::std::mem::offset_of!(QUIC_SQE, Completion) - 8usize]; +}; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct QUIC_HANDLE { @@ -446,6 +464,40 @@ const _: () = { }; #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct QUIC_EXECUTION_CONFIG { + pub IdealProcessor: u32, + pub EventQ: *mut QUIC_EVENTQ, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of QUIC_EXECUTION_CONFIG"][::std::mem::size_of::() - 16usize]; + ["Alignment of QUIC_EXECUTION_CONFIG"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: QUIC_EXECUTION_CONFIG::IdealProcessor"] + [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, IdealProcessor) - 0usize]; + ["Offset of field: QUIC_EXECUTION_CONFIG::EventQ"] + [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, EventQ) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct QUIC_EXECUTION { + _unused: [u8; 0], +} +pub type QUIC_EXECUTION_CREATE_FN = ::std::option::Option< + unsafe extern "C" fn( + Flags: QUIC_GLOBAL_EXECUTION_CONFIG_FLAGS, + PollingIdleTimeoutUs: u32, + Count: u32, + Configs: *mut QUIC_EXECUTION_CONFIG, + Executions: *mut *mut QUIC_EXECUTION, + ) -> ::std::os::raw::c_uint, +>; +pub type QUIC_EXECUTION_DELETE_FN = + ::std::option::Option; +pub type QUIC_EXECUTION_POLL_FN = + ::std::option::Option u32>; +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct QUIC_REGISTRATION_CONFIG { pub AppName: *const ::std::os::raw::c_char, pub ExecutionProfile: QUIC_EXECUTION_PROFILE, @@ -6545,10 +6597,13 @@ pub struct QUIC_API_TABLE { pub ConnectionOpenInPartition: QUIC_CONNECTION_OPEN_IN_PARTITION_FN, pub StreamProvideReceiveBuffers: QUIC_STREAM_PROVIDE_RECEIVE_BUFFERS_FN, pub ConnectionPoolCreate: QUIC_CONN_POOL_CREATE_FN, + pub ExecutionCreate: QUIC_EXECUTION_CREATE_FN, + pub ExecutionDelete: QUIC_EXECUTION_DELETE_FN, + pub ExecutionPoll: QUIC_EXECUTION_POLL_FN, } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of QUIC_API_TABLE"][::std::mem::size_of::() - 272usize]; + ["Size of QUIC_API_TABLE"][::std::mem::size_of::() - 296usize]; ["Alignment of QUIC_API_TABLE"][::std::mem::align_of::() - 8usize]; ["Offset of field: QUIC_API_TABLE::SetContext"] [::std::mem::offset_of!(QUIC_API_TABLE, SetContext) - 0usize]; @@ -6622,6 +6677,12 @@ const _: () = { [::std::mem::offset_of!(QUIC_API_TABLE, StreamProvideReceiveBuffers) - 256usize]; ["Offset of field: QUIC_API_TABLE::ConnectionPoolCreate"] [::std::mem::offset_of!(QUIC_API_TABLE, ConnectionPoolCreate) - 264usize]; + ["Offset of field: QUIC_API_TABLE::ExecutionCreate"] + [::std::mem::offset_of!(QUIC_API_TABLE, ExecutionCreate) - 272usize]; + ["Offset of field: QUIC_API_TABLE::ExecutionDelete"] + [::std::mem::offset_of!(QUIC_API_TABLE, ExecutionDelete) - 280usize]; + ["Offset of field: QUIC_API_TABLE::ExecutionPoll"] + [::std::mem::offset_of!(QUIC_API_TABLE, ExecutionPoll) - 288usize]; }; pub const QUIC_STATUS_SUCCESS: QUIC_STATUS = 0; pub const QUIC_STATUS_PENDING: QUIC_STATUS = 4294967294; From fd4a6233e8983a0d724557d395088c73c2ecf5d6 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 15:26:06 -0400 Subject: [PATCH 30/34] Another try --- src/inc/msquic_winuser.h | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/inc/msquic_winuser.h b/src/inc/msquic_winuser.h index 62f918d4e4..3e9052e1eb 100644 --- a/src/inc/msquic_winuser.h +++ b/src/inc/msquic_winuser.h @@ -380,18 +380,6 @@ QuicAddrToString( // Event Queue Abstraction // -typedef HANDLE QUIC_EVENTQ; - -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_OVERLAPPED { unsigned long long Internal; unsigned long long InternalHigh; @@ -406,6 +394,25 @@ typedef struct QUIC_OVERLAPPED { HANDLE hEvent; } QUIC_OVERLAPPED; +typedef struct QUIC_OVERLAPPED_ENTRY { + unsigned long long lpCompletionKey; + QUIC_OVERLAPPED* lpOverlapped; + unsigned long long Internal; + unsigned long dwNumberOfBytesTransferred; +} QUIC_OVERLAPPED_ENTRY; + +typedef HANDLE QUIC_EVENTQ; + +typedef QUIC_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 { QUIC_OVERLAPPED Overlapped; QUIC_EVENT_COMPLETION_HANDLER Completion; From 630aa78c0e63a9e8c9506ae71869bb34790e74b6 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 15:37:40 -0400 Subject: [PATCH 31/34] Another try --- src/rs/ffi/mod.rs | 37 +++++++++++++++++++++- src/rs/ffi/win_bindings.rs | 63 +++++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/rs/ffi/mod.rs b/src/rs/ffi/mod.rs index 43fa3770cf..9d451eb005 100644 --- a/src/rs/ffi/mod.rs +++ b/src/rs/ffi/mod.rs @@ -11,7 +11,42 @@ pub type QUIC_ADDR = std::ffi::c_void; #[cfg(target_os = "windows")] pub type HANDLE = std::os::windows::raw::HANDLE; -#[cfg(not(target_os = "windows"))] +#[cfg(target_os = "windows")] +#[repr(C)] +pub struct OVERLAPPED { + pub Internal: ::std::os::raw::c_ulonglong, + pub InternalHigh: ::std::os::raw::c_ulonglong, + pub __bindgen_anon_1: OVERLAPPED__bindgen_ty_1, + pub hEvent: std::os::windows::raw::HANDLE, +} + +#[cfg(target_os = "windows")] +#[repr(C)] +#[derive(Copy, Clone)] +pub union OVERLAPPED__bindgen_ty_1 { + pub __bindgen_anon_1: OVERLAPPED__bindgen_ty_1__bindgen_ty_1, + pub Pointer: *mut ::std::os::raw::c_void, +} + +#[cfg(target_os = "windows")] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct OVERLAPPED__bindgen_ty_1__bindgen_ty_1 { + pub Offset: ::std::os::raw::c_ulong, + pub OffsetHigh: ::std::os::raw::c_ulong, +} + +#[cfg(target_os = "windows")] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct OVERLAPPED_ENTRY { + pub lpCompletionKey: ::std::os::raw::c_ulonglong, + pub lpOverlapped: *mut OVERLAPPED, + pub Internal: ::std::os::raw::c_ulonglong, + pub dwNumberOfBytesTransferred: ::std::os::raw::c_ulong, +} + +#[cfg(not(target_os = "linux"))] pub type epoll_event = libc::epoll_event; // TODO: macos currently is using the linux bindings. diff --git a/src/rs/ffi/win_bindings.rs b/src/rs/ffi/win_bindings.rs index d3a77f2ccf..5b5a00e980 100644 --- a/src/rs/ffi/win_bindings.rs +++ b/src/rs/ffi/win_bindings.rs @@ -230,6 +230,24 @@ const _: () = { ["Offset of field: QUIC_ADDR_STR::Address"] [::std::mem::offset_of!(QUIC_ADDR_STR, Address) - 0usize]; }; +pub type QUIC_EVENTQ = HANDLE; +pub type QUIC_CQE = OVERLAPPED_ENTRY; +pub type QUIC_EVENT_COMPLETION = ::std::option::Option; +pub type QUIC_EVENT_COMPLETION_HANDLER = QUIC_EVENT_COMPLETION; +#[repr(C)] +pub struct QUIC_SQE { + pub Overlapped: OVERLAPPED, + pub Completion: QUIC_EVENT_COMPLETION_HANDLER, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of QUIC_SQE"][::std::mem::size_of::() - 40usize]; + ["Alignment of QUIC_SQE"][::std::mem::align_of::() - 8usize]; + ["Offset of field: QUIC_SQE::Overlapped"] + [::std::mem::offset_of!(QUIC_SQE, Overlapped) - 0usize]; + ["Offset of field: QUIC_SQE::Completion"] + [::std::mem::offset_of!(QUIC_SQE, Completion) - 32usize]; +}; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct QUIC_HANDLE { @@ -445,6 +463,40 @@ const _: () = { }; #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct QUIC_EXECUTION_CONFIG { + pub IdealProcessor: u32, + pub EventQ: *mut QUIC_EVENTQ, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of QUIC_EXECUTION_CONFIG"][::std::mem::size_of::() - 16usize]; + ["Alignment of QUIC_EXECUTION_CONFIG"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: QUIC_EXECUTION_CONFIG::IdealProcessor"] + [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, IdealProcessor) - 0usize]; + ["Offset of field: QUIC_EXECUTION_CONFIG::EventQ"] + [::std::mem::offset_of!(QUIC_EXECUTION_CONFIG, EventQ) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct QUIC_EXECUTION { + _unused: [u8; 0], +} +pub type QUIC_EXECUTION_CREATE_FN = ::std::option::Option< + unsafe extern "C" fn( + Flags: QUIC_GLOBAL_EXECUTION_CONFIG_FLAGS, + PollingIdleTimeoutUs: u32, + Count: u32, + Configs: *mut QUIC_EXECUTION_CONFIG, + Executions: *mut *mut QUIC_EXECUTION, + ) -> HRESULT, +>; +pub type QUIC_EXECUTION_DELETE_FN = + ::std::option::Option; +pub type QUIC_EXECUTION_POLL_FN = + ::std::option::Option u32>; +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct QUIC_REGISTRATION_CONFIG { pub AppName: *const ::std::os::raw::c_char, pub ExecutionProfile: QUIC_EXECUTION_PROFILE, @@ -6566,10 +6618,13 @@ pub struct QUIC_API_TABLE { pub ConnectionOpenInPartition: QUIC_CONNECTION_OPEN_IN_PARTITION_FN, pub StreamProvideReceiveBuffers: QUIC_STREAM_PROVIDE_RECEIVE_BUFFERS_FN, pub ConnectionPoolCreate: QUIC_CONN_POOL_CREATE_FN, + pub ExecutionCreate: QUIC_EXECUTION_CREATE_FN, + pub ExecutionDelete: QUIC_EXECUTION_DELETE_FN, + pub ExecutionPoll: QUIC_EXECUTION_POLL_FN, } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of QUIC_API_TABLE"][::std::mem::size_of::() - 272usize]; + ["Size of QUIC_API_TABLE"][::std::mem::size_of::() - 296usize]; ["Alignment of QUIC_API_TABLE"][::std::mem::align_of::() - 8usize]; ["Offset of field: QUIC_API_TABLE::SetContext"] [::std::mem::offset_of!(QUIC_API_TABLE, SetContext) - 0usize]; @@ -6643,6 +6698,12 @@ const _: () = { [::std::mem::offset_of!(QUIC_API_TABLE, StreamProvideReceiveBuffers) - 256usize]; ["Offset of field: QUIC_API_TABLE::ConnectionPoolCreate"] [::std::mem::offset_of!(QUIC_API_TABLE, ConnectionPoolCreate) - 264usize]; + ["Offset of field: QUIC_API_TABLE::ExecutionCreate"] + [::std::mem::offset_of!(QUIC_API_TABLE, ExecutionCreate) - 272usize]; + ["Offset of field: QUIC_API_TABLE::ExecutionDelete"] + [::std::mem::offset_of!(QUIC_API_TABLE, ExecutionDelete) - 280usize]; + ["Offset of field: QUIC_API_TABLE::ExecutionPoll"] + [::std::mem::offset_of!(QUIC_API_TABLE, ExecutionPoll) - 288usize]; }; pub const QUIC_STATUS_SUCCESS: QUIC_STATUS = 0; pub const QUIC_STATUS_PENDING: QUIC_STATUS = 459749; From f387952b4b8053510c61687d9cea811f4f2a19e1 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 15:38:21 -0400 Subject: [PATCH 32/34] forgot one --- src/inc/msquic_winuser.h | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/inc/msquic_winuser.h b/src/inc/msquic_winuser.h index 3e9052e1eb..e1b40645a1 100644 --- a/src/inc/msquic_winuser.h +++ b/src/inc/msquic_winuser.h @@ -380,30 +380,9 @@ QuicAddrToString( // Event Queue Abstraction // -typedef struct QUIC_OVERLAPPED { - unsigned long long Internal; - unsigned long long InternalHigh; - union { - struct { - unsigned long Offset; - unsigned long OffsetHigh; - } DUMMYSTRUCTNAME; - void* Pointer; - } DUMMYUNIONNAME; - - HANDLE hEvent; -} QUIC_OVERLAPPED; - -typedef struct QUIC_OVERLAPPED_ENTRY { - unsigned long long lpCompletionKey; - QUIC_OVERLAPPED* lpOverlapped; - unsigned long long Internal; - unsigned long dwNumberOfBytesTransferred; -} QUIC_OVERLAPPED_ENTRY; - typedef HANDLE QUIC_EVENTQ; -typedef QUIC_OVERLAPPED_ENTRY QUIC_CQE; +typedef OVERLAPPED_ENTRY QUIC_CQE; typedef _IRQL_requires_max_(PASSIVE_LEVEL) @@ -414,7 +393,7 @@ void typedef QUIC_EVENT_COMPLETION *QUIC_EVENT_COMPLETION_HANDLER; typedef struct QUIC_SQE { - QUIC_OVERLAPPED Overlapped; + OVERLAPPED Overlapped; QUIC_EVENT_COMPLETION_HANDLER Completion; #if DEBUG BOOLEAN IsQueued; // Debug flag to catch double queueing. From cfefdf8df39179798d2cc5cdfe697d9eb0f560be Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 15:42:20 -0400 Subject: [PATCH 33/34] oops --- src/rs/ffi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rs/ffi/mod.rs b/src/rs/ffi/mod.rs index 9d451eb005..5f13085991 100644 --- a/src/rs/ffi/mod.rs +++ b/src/rs/ffi/mod.rs @@ -46,7 +46,7 @@ pub struct OVERLAPPED_ENTRY { pub dwNumberOfBytesTransferred: ::std::os::raw::c_ulong, } -#[cfg(not(target_os = "linux"))] +#[cfg(target_os = "linux")] pub type epoll_event = libc::epoll_event; // TODO: macos currently is using the linux bindings. From 2924b64578c0cd4a6fe666c9a424349e3618d055 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 6 May 2025 16:14:10 -0400 Subject: [PATCH 34/34] hack for mac --- src/rs/ffi/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rs/ffi/mod.rs b/src/rs/ffi/mod.rs index 5f13085991..8f05c34ed4 100644 --- a/src/rs/ffi/mod.rs +++ b/src/rs/ffi/mod.rs @@ -49,6 +49,9 @@ pub struct OVERLAPPED_ENTRY { #[cfg(target_os = "linux")] pub type epoll_event = libc::epoll_event; +#[cfg(target_os = "macos")] +pub type epoll_event = u32; // HACK: TODO - Fix once we have macOS support + // TODO: macos currently is using the linux bindings. #[cfg(not(target_os = "windows"))] pub type sa_family_t = u16;