-
Notifications
You must be signed in to change notification settings - Fork 5k
Distinguish OOM and failure to reprotect in thunks APIs #116010
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
Conversation
Resolves dotnet#113114. I wasn't sure how much cleanup to do here, the failure handling is a bit odd, I assume for historical reasons.
lock (this) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was scratching my head about this - we're in a constructor. Nobody else can have this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's obviously wrong, but also GetNewThunksBlock
doesn't seem to be thread-safe, so perhaps the intention was to protect multiple simultaneous calls to that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are not addressing this, should we open a follow-up issue? (or did I miss some mechanism that is making the static variable access thread safe?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of doing more within a PR than what's needed and this is orthogonal. Filed #116089.
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR enhances the thunk-allocation APIs to distinguish out‐of‐memory failures from other reprotect failures by propagating the exact exception back to managed code.
- Added
out Exception
parameters onGetNewThunksBlock
,ExpandHeap
, andAllocateThunk
so callers can throw the correct exception. - Updated the native
RhAllocateThunksMapping
signature and implementations to set anisOOM
flag. - Changed managed callers (
CreateThunksHeap
/AllocateThunk
) to remove null returns and instead throw the propagated exception.
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
ThunkPool.cs | Added out Exception to block- and thunk-allocation methods and removed null-return fallbacks |
RuntimeImports.cs | Updated the RhAllocateThunksMapping P/Invoke to take an int* isOOM parameter |
RuntimeAugments.cs | Switched CreateThunksHeap / AllocateThunk to the new exception-based APIs |
portable.cpp | Stubbed RhAllocateThunksMapping to always set isOOM = 0 |
ThunksMapping.cpp | Populated isOOM on allocation failure |
Comments suppressed due to low confidence (3)
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ThunkPool.cs:115
- The new
CreateThunksHeap
API can now throw different exception types; add unit tests to cover both the OOM and NotSupported failure scenarios.
public static unsafe ThunksHeap CreateThunksHeap(IntPtr commonStubAddress)
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ThunkPool.cs:172
- [nitpick] The out parameter
exception
is ambiguous; consider renaming it to something likeallocationException
for clarity.
public unsafe IntPtr AllocateThunk(out Exception exception)
src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs:714
- This change now throws NotSupportedException for reprotect failures instead of always OutOfMemoryException. Consider whether callers expect the original behavior or need to handle both exception types.
return ThunksHeap.CreateThunksHeap(commonStubAddress);
// The only option is for the caller to attempt and recycle unused thunks to be able to | ||
// find some free entries. | ||
if (isOOM == 0) | ||
exception = new NotSupportedException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a custom exception message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a suggestion for a message that would be actually useful (we might not have a full list of reasons why this could fail; we just found out about a new reason a couple months ago...). I mostly just wanted to distinguish OOM from "everything else".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Dynamic entrypoint allocation is not supported in the current environment."?
@@ -100,13 +100,18 @@ FCIMPL0(int, RhpGetThunkBlockSize) | |||
} | |||
FCIMPLEND | |||
|
|||
EXTERN_C void* QCALLTYPE RhAllocateThunksMapping() | |||
EXTERN_C void* QCALLTYPE RhAllocateThunksMapping(int* isOOM) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is unusual to return error code via out argument, and only allow that error code to be OOM.
Can we return proper error code, and return the pointer via [out]
argument instead?
The error code can be an HRESULT or an enum specific to this call. (I know HRESULTs are Windows-specific, but they are part of BCL Exception so they are not going away x-plat, so there is not much value in trying hard to avoid them.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea with reusing HRESULT! I didn't like the bool either, but I didn't want to invent a dedicated enum that we need to keep in sync between native and managed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AaronRobinsonMSFT deleted the HRESULT definitions in #115858 so this no longer compiles.
I would prefer going back to the bool if we don't want HRESULTs anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is fine add back what you need to get this to compile.
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ThunkPool.cs
Outdated
Show resolved
Hide resolved
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ThunkPool.cs
Outdated
Show resolved
Hide resolved
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ThunkPool.cs
Outdated
Show resolved
Hide resolved
if (result == HResults.E_OUTOFMEMORY) | ||
throw new OutOfMemoryException(); | ||
else if (result != HResults.S_OK) | ||
throw new PlatformNotSupportedException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot Could you please suggest a good error message for this exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
#ifndef __GCENV_BASE_INCLUDED__ | ||
#if !defined(_INC_WINDOWS) | ||
#ifdef _WIN32 | ||
// this must exactly match the typedef used by windows.h | ||
typedef long HRESULT; | ||
#else | ||
typedef int32_t HRESULT; | ||
#endif | ||
|
||
#define S_OK 0x0 | ||
#define E_FAIL 0x80004005 | ||
#define E_OUTOFMEMORY 0x8007000E | ||
|
||
#endif // !defined(_INC_WINDOWS) | ||
#endif // __GCENV_BASE_INCLUDED__ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#ifndef __GCENV_BASE_INCLUDED__ | |
#if !defined(_INC_WINDOWS) | |
#ifdef _WIN32 | |
// this must exactly match the typedef used by windows.h | |
typedef long HRESULT; | |
#else | |
typedef int32_t HRESULT; | |
#endif | |
#define S_OK 0x0 | |
#define E_FAIL 0x80004005 | |
#define E_OUTOFMEMORY 0x8007000E | |
#endif // !defined(_INC_WINDOWS) | |
#endif // __GCENV_BASE_INCLUDED__ | |
#ifdef TARGET_UNIX | |
typedef int32_t HRESULT; | |
#define S_OK 0x0 | |
#define E_FAIL 0x80004005 | |
#define E_OUTOFMEMORY 0x8007000E | |
#endif |
Could you please change this to above and move it to PalRedhawk.h right after HRESULT
definition`?
This reverts commit 0248f31.
/ba-g the failing test was supposedly fixed in #116062, I'm not sure about the timing but we'll see if it's really fixed in the coming days... |
Resolves #113114.
I wasn't sure how much cleanup to do here, the failure handling is a bit odd, I assume for historical reasons.
Cc @dotnet/ilc-contrib