174 lines
6.9 KiB
Diff
174 lines
6.9 KiB
Diff
From 50de22c7fa57c687a996bfdf6702871216881c0f Mon Sep 17 00:00:00 2001
|
|
Patch-Source: https://github.com/dotnet/runtime/pull/79856
|
|
From: Antoine Martin <dev@ayakael.net>
|
|
Date: Sat, 25 Feb 2023 15:19:00 -0500
|
|
Subject: [PATCH 1/1] [threads] Save errno when using posix semaphores for
|
|
thread transitions
|
|
|
|
We already save/restore GetLastError on win32. Do it on posix
|
|
platforms, too.
|
|
|
|
If one thread is in a pinvoke wrapper, while another thread triggers a
|
|
STW, the pinvoke wrapper will self-suspend the thread and wait for a
|
|
notification to resume. Depending on the platform we can use win32
|
|
primitives, Mach semaphores or POSIX semaphores. win32 and posix can
|
|
both change the value of last error (errno, respectively) while the
|
|
thread is suspended.
|
|
|
|
That means that code like this (generated by the
|
|
LibraryImportAttribute source generator) cannot reliably retrieve the
|
|
error from the last pinvoke:
|
|
|
|
```csharp
|
|
__retVal = __PInvoke(__path_native, mode); // there is a pinvoke wrapper here, that transitions from GC Safe to GC Unsafe mode
|
|
__lastError = System.Runtime.InteropServices.Marshal.GetLastSystemError();
|
|
```
|
|
|
|
The solution is to explicitly preserve the value of GetLastError/errno
|
|
when exiting from GC Safe.
|
|
|
|
Fixes https://github.com/dotnet/runtime/issues/77364
|
|
---
|
|
.../src/mono/mono/utils/mono-threads-coop.c | 12 +++---
|
|
.../src/mono/mono/utils/mono-threads.c | 4 +-
|
|
.../src/mono/mono/utils/mono-threads.h | 43 ++++++++++++++++---
|
|
3 files changed, 45 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/src/runtime/src/mono/mono/utils/mono-threads-coop.c b/src/runtime/src/mono/mono/utils/mono-threads-coop.c
|
|
index 4ed659d66..c562ada67 100644
|
|
--- a/src/runtime/src/mono/mono/utils/mono-threads-coop.c
|
|
+++ b/src/runtime/src/mono/mono/utils/mono-threads-coop.c
|
|
@@ -337,10 +337,10 @@ mono_threads_exit_gc_safe_region_internal (gpointer cookie, MonoStackData *stack
|
|
return;
|
|
|
|
#ifdef ENABLE_CHECKED_BUILD_GC
|
|
- W32_DEFINE_LAST_ERROR_RESTORE_POINT;
|
|
+ MONO_DEFINE_LAST_ERROR_RESTORE_POINT;
|
|
if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
|
|
coop_tls_pop (cookie);
|
|
- W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
|
|
+ MONO_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
|
|
#endif
|
|
|
|
mono_threads_exit_gc_safe_region_unbalanced_internal (cookie, stackdata);
|
|
@@ -365,7 +365,7 @@ mono_threads_exit_gc_safe_region_unbalanced_internal (gpointer cookie, MonoStack
|
|
/* Common to use enter/exit gc safe around OS API's affecting last error. */
|
|
/* This method can call OS API's that will reset last error on some platforms. */
|
|
/* To reduce errors, we need to restore last error before exit gc safe. */
|
|
- W32_DEFINE_LAST_ERROR_RESTORE_POINT;
|
|
+ MONO_DEFINE_LAST_ERROR_RESTORE_POINT;
|
|
|
|
info = (MonoThreadInfo *)cookie;
|
|
|
|
@@ -398,7 +398,7 @@ mono_threads_exit_gc_safe_region_unbalanced_internal (gpointer cookie, MonoStack
|
|
info->user_data = NULL;
|
|
}
|
|
|
|
- W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
|
|
+ MONO_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
|
|
}
|
|
|
|
void
|
|
@@ -652,14 +652,14 @@ mono_threads_suspend_policy_init (void)
|
|
// otherwise if one of the old environment variables is set, use that.
|
|
// otherwise use full preemptive suspend.
|
|
|
|
- W32_DEFINE_LAST_ERROR_RESTORE_POINT;
|
|
+ MONO_DEFINE_LAST_ERROR_RESTORE_POINT;
|
|
|
|
(policy = threads_suspend_policy_getenv ())
|
|
|| (policy = threads_suspend_policy_default ())
|
|
|| (policy = threads_suspend_policy_getenv_compat ())
|
|
|| (policy = MONO_THREADS_SUSPEND_FULL_PREEMPTIVE);
|
|
|
|
- W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
|
|
+ MONO_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
|
|
|
|
g_assert (policy);
|
|
mono_threads_suspend_policy_hidden_dont_modify = (char)policy;
|
|
diff --git a/src/runtime/src/mono/mono/utils/mono-threads.c b/src/runtime/src/mono/mono/utils/mono-threads.c
|
|
index 2ef2fe45b..41dbf3032 100644
|
|
--- a/src/runtime/src/mono/mono/utils/mono-threads.c
|
|
+++ b/src/runtime/src/mono/mono/utils/mono-threads.c
|
|
@@ -1894,7 +1894,7 @@ mono_thread_info_uninstall_interrupt (gboolean *interrupted)
|
|
/* Common to uninstall interrupt handler around OS API's affecting last error. */
|
|
/* This method could call OS API's on some platforms that will reset last error so make sure to restore */
|
|
/* last error before exit. */
|
|
- W32_DEFINE_LAST_ERROR_RESTORE_POINT;
|
|
+ MONO_DEFINE_LAST_ERROR_RESTORE_POINT;
|
|
|
|
g_assert (interrupted);
|
|
*interrupted = FALSE;
|
|
@@ -1917,7 +1917,7 @@ mono_thread_info_uninstall_interrupt (gboolean *interrupted)
|
|
THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
|
|
mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
|
|
|
|
- W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
|
|
+ MONO_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
|
|
}
|
|
|
|
static MonoThreadInfoInterruptToken*
|
|
diff --git a/src/runtime/src/mono/mono/utils/mono-threads.h b/src/runtime/src/mono/mono/utils/mono-threads.h
|
|
index 6a548b183..45353db4e 100644
|
|
--- a/src/runtime/src/mono/mono/utils/mono-threads.h
|
|
+++ b/src/runtime/src/mono/mono/utils/mono-threads.h
|
|
@@ -866,19 +866,50 @@ mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD
|
|
void
|
|
mono_win32_abort_blocking_io_call (THREAD_INFO_TYPE *info);
|
|
|
|
-#define W32_DEFINE_LAST_ERROR_RESTORE_POINT \
|
|
+#else
|
|
+
|
|
+
|
|
+#endif
|
|
+
|
|
+#ifdef USE_WINDOWS_BACKEND
|
|
+
|
|
+/* APC calls can change GetLastError while a thread is suspended. Save/restore it when doing thread
|
|
+ state transitions (for example in m2n wrappers) in order to protect the result of the last
|
|
+ pinvoke */
|
|
+
|
|
+#define MONO_DEFINE_LAST_ERROR_RESTORE_POINT \
|
|
const DWORD _last_error_restore_point = GetLastError ();
|
|
|
|
-#define W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT \
|
|
+#define MONO_RESTORE_LAST_ERROR_FROM_RESTORE_POINT \
|
|
/* Only restore if changed to prevent unnecessary writes. */ \
|
|
if (GetLastError () != _last_error_restore_point) \
|
|
mono_SetLastError (_last_error_restore_point);
|
|
|
|
+#elif defined(USE_WASM_BACKEND) || defined (USE_POSIX_BACKEND)
|
|
+
|
|
+#define MONO_DEFINE_LAST_ERROR_RESTORE_POINT \
|
|
+ int _last_errno_restore_point = errno;
|
|
+
|
|
+#define MONO_RESTORE_LAST_ERROR_FROM_RESTORE_POINT \
|
|
+ if (errno != _last_errno_restore_point) \
|
|
+ errno = _last_errno_restore_point;
|
|
+
|
|
+/* Posix semaphores set errno on failure and sporadic wakeup. GC state transitions are done in n2m
|
|
+ * and m2n wrappers and may change the value of errno from the last pinvoke. Use these macros to
|
|
+ * save/restore errno when doing thread state transitions. */
|
|
+
|
|
+#elif defined(USE_MACH_BACKEND)
|
|
+
|
|
+/* Mach semaphores don't set errno on failure. Change this to be the same as POSIX if some other primitives used
|
|
+ in thread state transitions pollute errno. */
|
|
+
|
|
+#define MONO_DEFINE_LAST_ERROR_RESTORE_POINT /* nothing */
|
|
+#define MONO_RESTORE_LAST_ERROR_FROM_RESTORE_POINT /* nothing */
|
|
+
|
|
#else
|
|
-
|
|
-#define W32_DEFINE_LAST_ERROR_RESTORE_POINT /* nothing */
|
|
-#define W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT /* nothing */
|
|
-
|
|
+#error "unknown threads backend, not sure how to save/restore last error"
|
|
#endif
|
|
|
|
+
|
|
+
|
|
#endif /* __MONO_THREADS_H__ */
|
|
--
|
|
2.38.4
|
|
|