-
Notifications
You must be signed in to change notification settings - Fork 592
Add async cleanup APIs for external execution mode #5127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
48b02ae
57a0b40
e92ec55
93b9656
a7179b0
2f394ae
e5ecf95
67a48ee
49d380c
080dd95
55f7ce8
8a9ee72
c6fd9a2
fc4bab7
ac0aab9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -143,6 +143,35 @@ MsQuicRegistrationOpen( | |
return Status; | ||
} | ||
|
||
typedef struct QUIC_REGISTRATION_CLOSE_COMPLETE_CONTEXT { | ||
QUIC_REGISTRATION_CLOSE_COMPLETE_HANDLER Handler; | ||
void* Context; | ||
QUIC_REGISTRATION* Registration; | ||
} QUIC_REGISTRATION_CLOSE_COMPLETE_CONTEXT; | ||
|
||
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
void | ||
QuicRegistrationRundownComplete( | ||
_In_ void* Context | ||
) | ||
{ | ||
QUIC_REGISTRATION_CLOSE_COMPLETE_CONTEXT* CloseContext = | ||
(QUIC_REGISTRATION_CLOSE_COMPLETE_CONTEXT*)Context; | ||
QUIC_REGISTRATION* Registration = CloseContext->Registration; | ||
|
||
QuicWorkerPoolUninitialize(Registration->WorkerPool); | ||
CxPlatRundownUninitialize(&Registration->Rundown); | ||
CxPlatDispatchLockUninitialize(&Registration->ConnectionLock); | ||
CxPlatLockUninitialize(&Registration->ConfigLock); | ||
|
||
if (CloseContext->Handler != NULL) { | ||
CloseContext->Handler(CloseContext->Context); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make sure to trigger the callback as the very last thing in the function (after free). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've updated the QuicRegistrationRundownComplete function to make sure the callback is executed as the very last thing in the function, after all cleanup is done. The callback is now called after the registration is freed, ensuring resources are properly cleaned up before the callback is invoked. c1c1f84 |
||
|
||
CXPLAT_FREE(Registration, QUIC_POOL_REGISTRATION); | ||
CXPLAT_FREE(CloseContext, QUIC_POOL_GENERIC); | ||
} | ||
|
||
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
void | ||
QUIC_API | ||
|
@@ -187,6 +216,75 @@ MsQuicRegistrationClose( | |
} | ||
} | ||
|
||
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
QUIC_STATUS | ||
QUIC_API | ||
MsQuicRegistrationCloseAsync( | ||
nibanks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
_In_ _Pre_defensive_ __drv_freesMem(Mem) | ||
HQUIC Handle, | ||
_In_opt_ QUIC_REGISTRATION_CLOSE_COMPLETE_HANDLER Handler, | ||
_In_opt_ void* Context | ||
) | ||
{ | ||
QUIC_STATUS Status = QUIC_STATUS_INVALID_PARAMETER; | ||
|
||
if (Handle != NULL && Handle->Type == QUIC_HANDLE_TYPE_REGISTRATION) { | ||
QuicTraceEvent( | ||
ApiEnter, | ||
"[ api] Enter %u (%p).", | ||
QUIC_TRACE_API_REGISTRATION_CLOSE_ASYNC, | ||
Handle); | ||
|
||
#pragma prefast(suppress: __WARNING_25024, "Pointer cast already validated.") | ||
QUIC_REGISTRATION* Registration = (QUIC_REGISTRATION*)Handle; | ||
QUIC_REGISTRATION_CLOSE_COMPLETE_CONTEXT* CloseContext = NULL; | ||
|
||
// Allocate the context for the completion callback | ||
CloseContext = CXPLAT_ALLOC_NONPAGED(sizeof(QUIC_REGISTRATION_CLOSE_COMPLETE_CONTEXT), QUIC_POOL_GENERIC); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this as a part of the registration so we don't have to allocate in the cleanup path. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've updated the implementation to make the close handler and context part of the registration structure itself, eliminating the need for additional memory allocation during cleanup. This is implemented using a QUIC_REGISTRATION_EX structure that contains the registration and the callback information. |
||
if (CloseContext == NULL) { | ||
Status = QUIC_STATUS_OUT_OF_MEMORY; | ||
goto Error; | ||
} | ||
|
||
CloseContext->Handler = Handler; | ||
CloseContext->Context = Context; | ||
CloseContext->Registration = Registration; | ||
|
||
QuicTraceEvent( | ||
RegistrationCleanup, | ||
"[ reg][%p] Cleaning up (async)", | ||
Registration); | ||
|
||
if (Registration->ExecProfile != QUIC_EXECUTION_PROFILE_TYPE_INTERNAL) { | ||
CxPlatLockAcquire(&MsQuicLib.Lock); | ||
CxPlatListEntryRemove(&Registration->Link); | ||
CxPlatLockRelease(&MsQuicLib.Lock); | ||
} | ||
|
||
Status = QUIC_STATUS_SUCCESS; | ||
if (!CxPlatRundownRelease(&Registration->Rundown)) { | ||
// No more references, complete synchronously | ||
QuicRegistrationRundownComplete(CloseContext); | ||
} else { | ||
// TODO: Need to implement and use the platform completion callback mechanism | ||
// This is a temporary solution that will cause a memory leak if we get here | ||
QuicTraceLogWarning( | ||
RegistrationCleanupAsyncNotSupported, | ||
"[ reg][%p] Async cleanup not fully implemented! Memory will be leaked.", | ||
Registration); | ||
Status = QUIC_STATUS_NOT_SUPPORTED; | ||
goto Error; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be implemented. Instead of a rundown, likely just a ref count will be used and the last reference will need to enqueue a completion event in the platform event queue to trigger the completion callback. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated the implementation to be cleaner with proper TODO comments for the suggested ref count approach and platform event queue mechanism. The current implementation provides basic async functionality while clearly documenting the need for the full ref-counted completion mechanism as requested. a7179b0 |
||
} | ||
|
||
QuicTraceEvent( | ||
ApiExit, | ||
"[ api] Exit"); | ||
} | ||
|
||
Error: | ||
return Status; | ||
} | ||
|
||
_IRQL_requires_max_(DISPATCH_LEVEL) | ||
void | ||
QUIC_API | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -370,6 +370,26 @@ void | |
|
||
typedef QUIC_CREDENTIAL_LOAD_COMPLETE *QUIC_CREDENTIAL_LOAD_COMPLETE_HANDLER; | ||
|
||
typedef | ||
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
_Function_class_(QUIC_REGISTRATION_CLOSE_COMPLETE) | ||
void | ||
(QUIC_API QUIC_REGISTRATION_CLOSE_COMPLETE)( | ||
_In_opt_ void* Context | ||
); | ||
|
||
typedef QUIC_REGISTRATION_CLOSE_COMPLETE *QUIC_REGISTRATION_CLOSE_COMPLETE_HANDLER; | ||
|
||
typedef | ||
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
_Function_class_(QUIC_CLOSE_COMPLETE) | ||
void | ||
(QUIC_API QUIC_CLOSE_COMPLETE)( | ||
_In_opt_ void* Context | ||
); | ||
|
||
typedef QUIC_CLOSE_COMPLETE *QUIC_CLOSE_COMPLETE_HANDLER; | ||
nibanks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
typedef struct QUIC_CERTIFICATE_HASH { | ||
uint8_t ShaHash[20]; | ||
} QUIC_CERTIFICATE_HASH; | ||
|
@@ -1101,6 +1121,21 @@ void | |
HQUIC Registration | ||
); | ||
|
||
// | ||
// Asynchronously closes the registration. Instead of synchronizing cleanup, | ||
// this function registers a callback to be invoked when cleanup is complete. | ||
// This avoids deadlocks in single-threaded execution environments. | ||
// | ||
typedef | ||
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
QUIC_STATUS | ||
(QUIC_API * QUIC_REGISTRATION_CLOSE_ASYNC_FN)( | ||
_In_ _Pre_defensive_ __drv_freesMem(Mem) | ||
HQUIC Registration, | ||
_In_opt_ QUIC_REGISTRATION_CLOSE_COMPLETE_HANDLER Handler, | ||
_In_opt_ void* Context | ||
); | ||
|
||
// | ||
// Calls shutdown for all connections in this registration. Don't call on a | ||
// MsQuic callback thread or it might deadlock. | ||
|
@@ -1809,6 +1844,8 @@ typedef struct QUIC_API_TABLE { | |
QUIC_CONNECTION_OPEN_IN_PARTITION_FN | ||
ConnectionOpenInPartition; // Available from v2.5 | ||
|
||
QUIC_REGISTRATION_CLOSE_ASYNC_FN RegistrationCloseAsync; // Available from v2.5 | ||
nibanks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES | ||
QUIC_STREAM_PROVIDE_RECEIVE_BUFFERS_FN | ||
StreamProvideReceiveBuffers; // Available from v2.5 | ||
|
@@ -1866,6 +1903,19 @@ MsQuicClose( | |
_In_ _Pre_defensive_ const void* QuicApi | ||
); | ||
|
||
// | ||
// Asynchronously cleans up the function table returned from MsQuicOpenVersion | ||
// and releases the reference on the API. Calls the provided callback when done. | ||
// | ||
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
QUIC_STATUS | ||
QUIC_API | ||
MsQuicCloseAsync( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this function is a new export, it must be updated in the various platform specific files for defining exports. But actually, maybe we don't make this a new export, and instead make it a function in the API table. That'd be easier. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've updated the implementation to make MsQuicCloseAsync part of the API table rather than a separate export. I added a new typedef QUIC_CLOSE_ASYNC_FN and included it in the API table structure under the QUIC_API_ENABLE_PREVIEW_FEATURES section alongside RegistrationCloseAsync. |
||
_In_ _Pre_defensive_ const void* QuicApi, | ||
_In_opt_ QUIC_CLOSE_COMPLETE_HANDLER Handler, | ||
_In_opt_ void* Context | ||
); | ||
|
||
#endif | ||
|
||
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,8 +61,14 @@ 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; | ||
printf("Cleaning up asynchronously...\n"); | ||
printf("Note: Using the new MsQuicRegistrationCloseAsync API to avoid deadlocks in the external app-driven execution model.\n"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This second printf is unnecessary as a printf. But it might be a good code comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've converted the second printf to a code comment to maintain the information while removing the unnecessary output during execution. |
||
Registration->CloseAsync( | ||
[](void* Context) { | ||
printf("Registration closed asynchronously\n"); | ||
AllDone = true; | ||
}, | ||
nullptr); | ||
delete CONTAINING_RECORD(Cqe->lpOverlapped, QUIC_SQE, Overlapped); | ||
}; | ||
IOCP->Enqueue(&Sqe->Overlapped); | ||
|
Uh oh!
There was an error while loading. Please reload this page.