From f7fe002cff7a27d44f9171aa283eebc186d8f629 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 29 Jul 2025 14:00:41 +0200 Subject: [PATCH 01/27] Move FlushProcessWriteBuffers to minipal Implements https://github.com/dotnet/runtime/issues/117977 Also move the initialization of it and get of page size --- src/coreclr/gc/env/gcenv.base.h | 13 -- src/coreclr/gc/env/gcenv.os.h | 3 - src/coreclr/gc/env/gcenv.unix.inl | 12 +- src/coreclr/gc/env/gcenv.windows.inl | 9 +- src/coreclr/gc/gc.cpp | 3 +- src/coreclr/gc/softwarewritewatch.cpp | 5 +- src/coreclr/gc/unix/gcenv.unix.cpp | 199 +--------------- src/coreclr/gc/windows/gcenv.windows.cpp | 6 - .../nativeaot/Runtime/unix/PalUnix.cpp | 3 +- src/coreclr/pal/inc/pal.h | 5 - src/coreclr/pal/inc/pal_mstypes.h | 2 - src/coreclr/pal/src/include/pal/process.h | 11 - src/coreclr/pal/src/init/pal.cpp | 3 +- src/coreclr/pal/src/thread/process.cpp | 152 ------------ src/coreclr/vm/callcounting.cpp | 3 +- src/coreclr/vm/comutilnative.cpp | 3 +- src/coreclr/vm/gcenv.ee.cpp | 5 +- src/coreclr/vm/profilinghelper.cpp | 6 +- src/coreclr/vm/threadsuspend.cpp | 9 +- src/native/minipal/memory.c | 219 ++++++++++++++++++ src/native/minipal/memory.h | 37 +++ 21 files changed, 287 insertions(+), 421 deletions(-) create mode 100644 src/native/minipal/memory.c create mode 100644 src/native/minipal/memory.h diff --git a/src/coreclr/gc/env/gcenv.base.h b/src/coreclr/gc/env/gcenv.base.h index f74a8c466a2ec2..71c549ccd5c889 100644 --- a/src/coreclr/gc/env/gcenv.base.h +++ b/src/coreclr/gc/env/gcenv.base.h @@ -19,19 +19,6 @@ #define _alloca alloca #endif //_MSC_VER -#ifndef _MSC_VER -#define __stdcall -#ifdef __GNUC__ -#define __forceinline __attribute__((always_inline)) inline -#else // __GNUC__ -#define __forceinline inline -#endif // __GNUC__ -// [LOCALGC TODO] is there a better place for this? -#define NOINLINE __attribute__((noinline)) -#else // !_MSC_VER -#define NOINLINE __declspec(noinline) -#endif // _MSC_VER - #ifdef _MSC_VER #define __UNREACHABLE() __assume(0) #else diff --git a/src/coreclr/gc/env/gcenv.os.h b/src/coreclr/gc/env/gcenv.os.h index 08e9b39e36eb1a..6a73ba6d378bc6 100644 --- a/src/coreclr/gc/env/gcenv.os.h +++ b/src/coreclr/gc/env/gcenv.os.h @@ -451,9 +451,6 @@ class GCToOSInterface // Misc // - // Flush write buffers of processors that are executing threads of the current process - static void FlushProcessWriteBuffers(); - // Break into a debugger static void DebugBreak(); diff --git a/src/coreclr/gc/env/gcenv.unix.inl b/src/coreclr/gc/env/gcenv.unix.inl index d6e5ca796a1499..1cb125b43a4d3e 100644 --- a/src/coreclr/gc/env/gcenv.unix.inl +++ b/src/coreclr/gc/env/gcenv.unix.inl @@ -5,16 +5,8 @@ #define __GCENV_UNIX_INL__ #include "gcenv.os.h" +#include -extern uint32_t g_pageSizeUnixInl; - -#define OS_PAGE_SIZE GCToOSInterface::GetPageSize() - -#ifndef DACCESS_COMPILE -__forceinline size_t GCToOSInterface::GetPageSize() -{ - return g_pageSizeUnixInl; -} -#endif // DACCESS_COMPILE +#define OS_PAGE_SIZE minipal_get_page_size() #endif // __GCENV_UNIX_INL__ diff --git a/src/coreclr/gc/env/gcenv.windows.inl b/src/coreclr/gc/env/gcenv.windows.inl index df34e1aaa7c7ae..802e3767ae418d 100644 --- a/src/coreclr/gc/env/gcenv.windows.inl +++ b/src/coreclr/gc/env/gcenv.windows.inl @@ -5,13 +5,8 @@ #define __GCENV_WINDOWS_INL__ #include "gcenv.os.h" +#include - -#define OS_PAGE_SIZE GCToOSInterface::GetPageSize() - -__forceinline size_t GCToOSInterface::GetPageSize() -{ - return 0x1000; -} +#define OS_PAGE_SIZE minipal_get_page_size() #endif // __GCENV_WINDOWS_INL__ diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index cacae581ae019a..edada57daae84d 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -27,6 +27,7 @@ #include "handletable.inl" #include "gcenv.inl" #include "gceventstatus.h" +#include #ifdef __INTELLISENSE__ #if defined(FEATURE_SVR_GC) @@ -9871,7 +9872,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, if (!write_barrier_updated) { seg_mapping_table = new_seg_mapping_table; - GCToOSInterface::FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); g_gc_lowest_address = saved_g_lowest_address; g_gc_highest_address = saved_g_highest_address; diff --git a/src/coreclr/gc/softwarewritewatch.cpp b/src/coreclr/gc/softwarewritewatch.cpp index c72e2c6fcb9db5..39da1f79281ba5 100644 --- a/src/coreclr/gc/softwarewritewatch.cpp +++ b/src/coreclr/gc/softwarewritewatch.cpp @@ -5,6 +5,7 @@ #include "gcenv.h" #include "env/gcenv.os.h" #include "softwarewritewatch.h" +#include #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP #ifndef DACCESS_COMPILE @@ -131,7 +132,7 @@ void SoftwareWriteWatch::GetDirty( { // When a page is marked as dirty, a memory barrier is not issued after the write most of the time. Issue a memory // barrier on all active threads of the process now to make recent changes to dirty state visible to this thread. - GCToOSInterface::FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); } uint8_t *tableRegionStart; @@ -233,7 +234,7 @@ void SoftwareWriteWatch::GetDirty( // that the GC will not miss marking through dirtied objects in the page. Issue a memory barrier on all active threads // of the process now. MemoryBarrier(); // flush writes from this thread first to guarantee ordering - GCToOSInterface::FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); } } diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 33855147497c2b..b9fb93ec4faa19 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -23,6 +23,7 @@ #include "volatile.h" #include "gcconfig.h" #include "numasupport.h" +#include #include #include @@ -30,17 +31,6 @@ #include #endif -#ifdef __linux__ -#include -#include -#define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__) -#elif HAVE_SYS_MEMBARRIER_H -#include -#ifdef TARGET_BROWSER -#define membarrier(cmd, flags, cpu_id) 0 // browser/wasm is currently single threaded -#endif -#endif - #include #undef min @@ -140,55 +130,11 @@ typedef cpuset_t cpu_set_t; // The cached total number of CPUs that can be used in the OS. static uint32_t g_totalCpuCount = 0; -bool CanFlushUsingMembarrier() -{ -#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H - -#ifdef TARGET_ANDROID - // Avoid calling membarrier on older Android versions where membarrier - // may be barred by seccomp causing the process to be killed. - int apiLevel = android_get_device_api_level(); - if (apiLevel < __ANDROID_API_Q__) - { - return false; - } -#endif - - // Starting with Linux kernel 4.14, process memory barriers can be generated - // using MEMBARRIER_CMD_PRIVATE_EXPEDITED. - - int mask = membarrier(MEMBARRIER_CMD_QUERY, 0, 0); - - if (mask >= 0 && - mask & MEMBARRIER_CMD_PRIVATE_EXPEDITED && - // Register intent to use the private expedited command. - membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0) == 0) - { - return true; - } -#endif - - return false; -} - -// -// Tracks if the OS supports FlushProcessWriteBuffers using membarrier -// -static int s_flushUsingMemBarrier = 0; - -// Helper memory page used by the FlushProcessWriteBuffers -static uint8_t* g_helperPage = 0; - -// Mutex to make the FlushProcessWriteBuffersMutex thread safe -static pthread_mutex_t g_flushProcessWriteBuffersMutex; - size_t GetRestrictedPhysicalMemoryLimit(); bool GetPhysicalMemoryUsed(size_t* val); static size_t g_RestrictedPhysicalMemoryLimit = 0; -uint32_t g_pageSizeUnixInl = 0; - AffinitySet g_processAffinitySet; extern "C" int g_highestNumaNode; @@ -206,10 +152,6 @@ static size_t g_kern_memorystatus_level_mib_length = 0; // true if it has succeeded, false if it has failed bool GCToOSInterface::Initialize() { - int pageSize = sysconf( _SC_PAGE_SIZE ); - - g_pageSizeUnixInl = uint32_t((pageSize > 0) ? pageSize : 0x1000); - // Calculate and cache the number of processors on this machine int cpuCount = sysconf(SYSCONF_GET_NUMPROCS); if (cpuCount == -1) @@ -219,51 +161,6 @@ bool GCToOSInterface::Initialize() g_totalCpuCount = cpuCount; - // - // support for FlusProcessWriteBuffers - // -#ifndef TARGET_WASM - assert(s_flushUsingMemBarrier == 0); - - if (CanFlushUsingMembarrier()) - { - s_flushUsingMemBarrier = TRUE; - } -#ifndef TARGET_APPLE - else - { - assert(g_helperPage == 0); - - g_helperPage = static_cast(mmap(0, OS_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); - - if (g_helperPage == MAP_FAILED) - { - return false; - } - - // Verify that the s_helperPage is really aligned to the g_SystemInfo.dwPageSize - assert((((size_t)g_helperPage) & (OS_PAGE_SIZE - 1)) == 0); - - // Locking the page ensures that it stays in memory during the two mprotect - // calls in the FlushProcessWriteBuffers below. If the page was unmapped between - // those calls, they would not have the expected effect of generating IPI. - int status = mlock(g_helperPage, OS_PAGE_SIZE); - - if (status != 0) - { - return false; - } - - status = pthread_mutex_init(&g_flushProcessWriteBuffersMutex, NULL); - if (status != 0) - { - munlock(g_helperPage, OS_PAGE_SIZE); - return false; - } - } -#endif // !TARGET_APPLE -#endif // !TARGET_WASM - InitializeCGroup(); #if HAVE_SCHED_GETAFFINITY @@ -331,7 +228,7 @@ bool GCToOSInterface::Initialize() return false; } - g_totalPhysicalMemSize = (uint64_t)pages * (uint64_t)g_pageSizeUnixInl; + g_totalPhysicalMemSize = (uint64_t)pages * (uint64_t)minipal_get_page_size(); #elif HAVE_SYSCTL int mib[2]; mib[0] = CTL_HW; @@ -354,12 +251,7 @@ bool GCToOSInterface::Initialize() // Shutdown the interface implementation void GCToOSInterface::Shutdown() { - int ret = munlock(g_helperPage, OS_PAGE_SIZE); - assert(ret == 0); - ret = pthread_mutex_destroy(&g_flushProcessWriteBuffersMutex); - assert(ret == 0); - - munmap(g_helperPage, OS_PAGE_SIZE); + minipal_shutdown_flush_process_write_buffers(); CleanupCGroup(); } @@ -410,91 +302,6 @@ bool GCToOSInterface::CanGetCurrentProcessorNumber() return HAVE_SCHED_GETCPU; } -// Flush write buffers of processors that are executing threads of the current process -void GCToOSInterface::FlushProcessWriteBuffers() -{ -#ifndef TARGET_WASM -#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H - if (s_flushUsingMemBarrier) - { - int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0); - assert(status == 0 && "Failed to flush using membarrier"); - } - else -#endif - if (g_helperPage != 0) - { - int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex); - assert(status == 0 && "Failed to lock the flushProcessWriteBuffersMutex lock"); - - // Changing a helper memory page protection from read / write to no access - // causes the OS to issue IPI to flush TLBs on all processors. This also - // results in flushing the processor buffers. - status = mprotect(g_helperPage, OS_PAGE_SIZE, PROT_READ | PROT_WRITE); - assert(status == 0 && "Failed to change helper page protection to read / write"); - - // Ensure that the page is dirty before we change the protection so that - // we prevent the OS from skipping the global TLB flush. - __sync_add_and_fetch((size_t*)g_helperPage, 1); - - status = mprotect(g_helperPage, OS_PAGE_SIZE, PROT_NONE); - assert(status == 0 && "Failed to change helper page protection to no access"); - - status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex); - assert(status == 0 && "Failed to unlock the flushProcessWriteBuffersMutex lock"); - } -#ifdef TARGET_APPLE - else - { - mach_msg_type_number_t cThreads; - thread_act_t *pThreads; - kern_return_t machret = task_threads(mach_task_self(), &pThreads, &cThreads); - CHECK_MACH("task_threads()", machret); - - uintptr_t sp; - uintptr_t registerValues[128]; - - // Iterate through each of the threads in the list. - for (mach_msg_type_number_t i = 0; i < cThreads; i++) - { - if (__builtin_available (macOS 10.14, iOS 12, tvOS 9, *)) - { - // Request the threads pointer values to force the thread to emit a memory barrier - size_t registers = 128; - machret = thread_get_register_pointer_values(pThreads[i], &sp, ®isters, registerValues); - } - else - { - // fallback implementation for older OS versions -#if defined(HOST_AMD64) - x86_thread_state64_t threadState; - mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; - machret = thread_get_state(pThreads[i], x86_THREAD_STATE64, (thread_state_t)&threadState, &count); -#elif defined(HOST_ARM64) - arm_thread_state64_t threadState; - mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT; - machret = thread_get_state(pThreads[i], ARM_THREAD_STATE64, (thread_state_t)&threadState, &count); -#else - #error Unexpected architecture -#endif - } - - if (machret == KERN_INSUFFICIENT_BUFFER_SIZE) - { - CHECK_MACH("thread_get_register_pointer_values()", machret); - } - - machret = mach_port_deallocate(mach_task_self(), pThreads[i]); - CHECK_MACH("mach_port_deallocate()", machret); - } - // Deallocate the thread list now we're done with it. - machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t)); - CHECK_MACH("vm_deallocate()", machret); - } -#endif // TARGET_APPLE -#endif // !TARGET_WASM -} - // Break into a debugger. Uses a compiler intrinsic if one is available, // otherwise raises a SIGTRAP. void GCToOSInterface::DebugBreak() diff --git a/src/coreclr/gc/windows/gcenv.windows.cpp b/src/coreclr/gc/windows/gcenv.windows.cpp index 3e8040be0bcbb1..8285e71d354aaf 100644 --- a/src/coreclr/gc/windows/gcenv.windows.cpp +++ b/src/coreclr/gc/windows/gcenv.windows.cpp @@ -640,12 +640,6 @@ bool GCToOSInterface::CanGetCurrentProcessorNumber() return true; } -// Flush write buffers of processors that are executing threads of the current process -void GCToOSInterface::FlushProcessWriteBuffers() -{ - ::FlushProcessWriteBuffers(); -} - // Break into a debugger void GCToOSInterface::DebugBreak() { diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index 76b18bb0ad8e2c..3ba482aef1629b 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #ifdef TARGET_LINUX @@ -1235,7 +1236,7 @@ int32_t PalGetModuleFileName(_Out_ const TCHAR** pModuleNameOut, HANDLE moduleBa void PalFlushProcessWriteBuffers() { - GCToOSInterface::FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); } static const int64_t SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL; diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 4c1b089882eccf..d9f338c40e7e5c 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -3577,11 +3577,6 @@ RtlCaptureContext( OUT PCONTEXT ContextRecord ); -PALIMPORT -VOID -PALAPI -FlushProcessWriteBuffers(); - typedef void (*PAL_ActivationFunction)(CONTEXT *context); typedef BOOL (*PAL_SafeActivationCheckFunction)(SIZE_T ip); diff --git a/src/coreclr/pal/inc/pal_mstypes.h b/src/coreclr/pal/inc/pal_mstypes.h index a0762726ba18e9..db0b021cfca59c 100644 --- a/src/coreclr/pal/inc/pal_mstypes.h +++ b/src/coreclr/pal/inc/pal_mstypes.h @@ -79,8 +79,6 @@ extern "C" { #define __inline inline #endif -#define __forceinline inline - #define PALIMPORT #ifndef DLLEXPORT diff --git a/src/coreclr/pal/src/include/pal/process.h b/src/coreclr/pal/src/include/pal/process.h index 1c48093af219da..45ef76577ad197 100644 --- a/src/coreclr/pal/src/include/pal/process.h +++ b/src/coreclr/pal/src/include/pal/process.h @@ -188,17 +188,6 @@ VOID PROCNotifyProcessShutdown(bool isExecutingOnAltStack = false); --*/ VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, bool serialize); -/*++ -Function: - InitializeFlushProcessWriteBuffers - -Abstract - This function initializes data structures needed for the FlushProcessWriteBuffers -Return - TRUE if it succeeded, FALSE otherwise ---*/ -BOOL InitializeFlushProcessWriteBuffers(); - #ifdef __cplusplus } #endif // __cplusplus diff --git a/src/coreclr/pal/src/init/pal.cpp b/src/coreclr/pal/src/init/pal.cpp index 64fffbef6e0f2e..da7d311a08ac78 100644 --- a/src/coreclr/pal/src/init/pal.cpp +++ b/src/coreclr/pal/src/init/pal.cpp @@ -36,6 +36,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PAL); // some headers have code with asserts, so do th #include "pal/stackstring.hpp" #include "pal/cgroup.h" #include +#include #if HAVE_MACH_EXCEPTIONS #include "../exception/machexception.h" @@ -580,7 +581,7 @@ Initialize( if (flags & PAL_INITIALIZE_FLUSH_PROCESS_WRITE_BUFFERS) { // Initialize before first thread is created for faster load on Linux - if (!InitializeFlushProcessWriteBuffers()) + if (!minipal_initialize_flush_process_write_buffers()) { ERROR("Unable to initialize flush process write buffers\n"); palError = ERROR_PALINIT_INITIALIZE_FLUSH_PROCESS_WRITE_BUFFERS; diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 9b779c0689c5c2..206a9629189b5b 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -134,21 +134,6 @@ CObjectType CorUnix::otProcess( CObjectType::NoOwner ); -// -// Tracks if the OS supports FlushProcessWriteBuffers using membarrier -// -static int s_flushUsingMemBarrier = 0; - -// -// Helper memory page used by the FlushProcessWriteBuffers -// -static int* s_helperPage = 0; - -// -// Mutex to make the FlushProcessWriteBuffersMutex thread safe -// -pthread_mutex_t flushProcessWriteBuffersMutex; - CAllowedObjectTypes aotProcess(otiProcess); // @@ -2810,70 +2795,6 @@ PROCAbort(int signal, siginfo_t* siginfo) abort(); } -/*++ -Function: - InitializeFlushProcessWriteBuffers - -Abstract - This function initializes data structures needed for the FlushProcessWriteBuffers -Return - TRUE if it succeeded, FALSE otherwise ---*/ -BOOL -InitializeFlushProcessWriteBuffers() -{ - _ASSERTE(s_helperPage == 0); - _ASSERTE(s_flushUsingMemBarrier == 0); - -#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H - // Starting with Linux kernel 4.14, process memory barriers can be generated - // using MEMBARRIER_CMD_PRIVATE_EXPEDITED. - int mask = membarrier(MEMBARRIER_CMD_QUERY, 0, 0); - if (mask >= 0 && - mask & MEMBARRIER_CMD_PRIVATE_EXPEDITED) - { - // Register intent to use the private expedited command. - if (membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0) == 0) - { - s_flushUsingMemBarrier = TRUE; - return TRUE; - } - } -#endif - -#if defined(TARGET_APPLE) || defined(TARGET_WASM) - return TRUE; -#else - s_helperPage = static_cast(mmap(0, GetVirtualPageSize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); - - if(s_helperPage == MAP_FAILED) - { - return FALSE; - } - - // Verify that the s_helperPage is really aligned to the GetVirtualPageSize() - _ASSERTE((((SIZE_T)s_helperPage) & (GetVirtualPageSize() - 1)) == 0); - - // Locking the page ensures that it stays in memory during the two mprotect - // calls in the FlushProcessWriteBuffers below. If the page was unmapped between - // those calls, they would not have the expected effect of generating IPI. - int status = mlock(s_helperPage, GetVirtualPageSize()); - - if (status != 0) - { - return FALSE; - } - - status = pthread_mutex_init(&flushProcessWriteBuffersMutex, NULL); - if (status != 0) - { - munlock(s_helperPage, GetVirtualPageSize()); - } - - return status == 0; -#endif // TARGET_APPLE || TARGET_WASM -} - #define FATAL_ASSERT(e, msg) \ do \ { \ @@ -2885,79 +2806,6 @@ InitializeFlushProcessWriteBuffers() } \ while(0) -/*++ -Function: - FlushProcessWriteBuffers - -See MSDN doc. ---*/ -VOID -PALAPI -FlushProcessWriteBuffers() -{ -#ifndef TARGET_WASM -#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H - if (s_flushUsingMemBarrier) - { - int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0); - FATAL_ASSERT(status == 0, "Failed to flush using membarrier"); - } - else -#endif - if (s_helperPage != 0) - { - int status = pthread_mutex_lock(&flushProcessWriteBuffersMutex); - FATAL_ASSERT(status == 0, "Failed to lock the flushProcessWriteBuffersMutex lock"); - - // Changing a helper memory page protection from read / write to no access - // causes the OS to issue IPI to flush TLBs on all processors. This also - // results in flushing the processor buffers. - status = mprotect(s_helperPage, GetVirtualPageSize(), PROT_READ | PROT_WRITE); - FATAL_ASSERT(status == 0, "Failed to change helper page protection to read / write"); - - // Ensure that the page is dirty before we change the protection so that - // we prevent the OS from skipping the global TLB flush. - InterlockedIncrement(s_helperPage); - - status = mprotect(s_helperPage, GetVirtualPageSize(), PROT_NONE); - FATAL_ASSERT(status == 0, "Failed to change helper page protection to no access"); - - status = pthread_mutex_unlock(&flushProcessWriteBuffersMutex); - FATAL_ASSERT(status == 0, "Failed to unlock the flushProcessWriteBuffersMutex lock"); - } -#ifdef TARGET_APPLE - else - { - mach_msg_type_number_t cThreads; - thread_act_t *pThreads; - kern_return_t machret = task_threads(mach_task_self(), &pThreads, &cThreads); - CHECK_MACH("task_threads()", machret); - - uintptr_t sp; - uintptr_t registerValues[128]; - - // Iterate through each of the threads in the list. - for (mach_msg_type_number_t i = 0; i < cThreads; i++) - { - // Request the threads pointer values to force the thread to emit a memory barrier - size_t registers = 128; - machret = thread_get_register_pointer_values(pThreads[i], &sp, ®isters, registerValues); - if (machret == KERN_INSUFFICIENT_BUFFER_SIZE) - { - CHECK_MACH("thread_get_register_pointer_values()", machret); - } - - machret = mach_port_deallocate(mach_task_self(), pThreads[i]); - CHECK_MACH("mach_port_deallocate()", machret); - } - // Deallocate the thread list now we're done with it. - machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t)); - CHECK_MACH("vm_deallocate()", machret); - } -#endif // TARGET_APPLE -#endif // !TARGET_WASM -} - /*++ Function: PROCGetProcessIDFromHandle diff --git a/src/coreclr/vm/callcounting.cpp b/src/coreclr/vm/callcounting.cpp index f654cc34b0aed0..b936711df87367 100644 --- a/src/coreclr/vm/callcounting.cpp +++ b/src/coreclr/vm/callcounting.cpp @@ -7,6 +7,7 @@ #include "callcounting.h" #include "threadsuspend.h" +#include #ifndef DACCESS_COMPILE extern "C" void STDCALL OnCallCountThresholdReachedStub(); @@ -1025,7 +1026,7 @@ void CallCountingManager::StopAndDeleteAllCallCountingStubs() // will not be used after resuming the runtime. The following ensures that other threads will not use an old cached // entry point value that will not be valid. Do this here in case of exception later. MemoryBarrier(); // flush writes from this thread first to guarantee ordering - FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); // At this point, allocated call counting stubs won't be used anymore. Call counting stubs and corresponding infos may // now be safely deleted. Note that call counting infos may not be deleted prior to this point because call counting diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index e8b08578d97d79..43c4715e90f41a 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -33,6 +33,7 @@ #include "typestring.h" #include "finalizerthread.h" #include "threadsuspend.h" +#include #ifdef FEATURE_COMINTEROP #include "comcallablewrapper.h" @@ -1561,7 +1562,7 @@ extern "C" void QCALLTYPE Interlocked_MemoryBarrierProcessWide() { QCALL_CONTRACT; - FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); } static BOOL HasOverriddenMethod(MethodTable* mt, MethodTable* classMT, WORD methodSlot) diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index aebced6bd4388b..1767bed0316674 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -25,6 +25,7 @@ #include "configuration.h" #include "genanalysis.h" #include "eventpipeadapter.h" +#include // Finalizes a weak reference directly. extern void FinalizeWeakReference(Object* obj); @@ -1006,7 +1007,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) { // If runtime is not suspended, force all threads to see the changed table before seeing updated heap boundaries. // See: http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/346765 - FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); } #endif @@ -1058,7 +1059,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) if (!is_runtime_suspended) { // If runtime is not suspended, force all threads to see the changed state before observing future allocations. - FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); } #endif diff --git a/src/coreclr/vm/profilinghelper.cpp b/src/coreclr/vm/profilinghelper.cpp index 99eb7eb0921ec0..539f8cafc07d5a 100644 --- a/src/coreclr/vm/profilinghelper.cpp +++ b/src/coreclr/vm/profilinghelper.cpp @@ -85,7 +85,7 @@ // these actions: // * (a) Set the profiler's status to a non-active state like kProfStatusDetaching or // kProfStatusNone -// * (b) Call FlushProcessWriteBuffers() +// * (b) Call minipal_flush_process_write_buffers() // * (c) Grab thread store lock, iterate through all threads, and verify each per-thread // evacuation counter is zero. // @@ -110,7 +110,7 @@ #include "proftoeeinterfaceimpl.inl" #include "profilinghelper.h" #include "profilinghelper.inl" - +#include #ifdef FEATURE_PROFAPI_ATTACH_DETACH #include "profdetach.h" @@ -208,7 +208,7 @@ void CurrentProfilerStatus::Set(ProfilerStatus newProfStatus) // can safely perform catchup at that time (see // code:#ProfCatchUpSynchronization). // - ::FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); } #endif // !defined(DACCESS_COMPILE) } diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index fd6bbdaede369a..c05496b91bd725 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -14,6 +14,7 @@ #include "finalizerthread.h" #include "dbginterface.h" +#include #include #ifdef FEATURE_EH_FUNCLETS @@ -3310,7 +3311,7 @@ void ThreadSuspend::SuspendAllThreads() // - we get a reliable reading of the threads' m_fPreemptiveGCDisabled state // - other threads see that g_TrapReturningThreads is set // See VSW 475315 and 488918 for details. - ::FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); int prevRemaining = INT32_MAX; bool observeOnly = true; @@ -3381,7 +3382,7 @@ void ThreadSuspend::SuspendAllThreads() // This is needed to synchronize threads that were running in preemptive mode thus were // left alone by suspension to flush their writes that they made before they switched to // preemptive mode. - ::FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); #endif //TARGET_ARM || TARGET_ARM64 STRESS_LOG0(LF_SYNC, LL_INFO1000, "Thread::SuspendAllThreads() - Success\n"); @@ -3606,7 +3607,7 @@ void ThreadSuspend::ResumeAllThreads(BOOL SuspendSucceeded) // This is needed to synchronize threads that were running in preemptive mode while // the runtime was suspended and that will return to cooperative mode after the runtime // is restarted. - ::FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); #endif //TARGET_ARM || TARGET_ARM64 // @@ -5388,7 +5389,7 @@ void ThreadSuspend::RestartEE(BOOL bFinishedGC, BOOL SuspendSucceeded) // This is needed to synchronize threads that were running in preemptive mode while // the runtime was suspended and that will return to cooperative mode after the runtime // is restarted. - ::FlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); #endif //TARGET_ARM || TARGET_ARM64 // diff --git a/src/native/minipal/memory.c b/src/native/minipal/memory.c new file mode 100644 index 00000000000000..088d30a024d63b --- /dev/null +++ b/src/native/minipal/memory.c @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include +#include +#include +#include +#include + +#ifndef TARGET_WINDOWS +#ifdef __linux__ +#include +#include +#define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__) +#elif HAVE_SYS_MEMBARRIER_H +#include +#ifdef TARGET_BROWSER +#define membarrier(cmd, flags, cpu_id) 0 // browser/wasm is currently single threaded +#endif +#endif + +static bool CanFlushUsingMembarrier() +{ +#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H + +#ifdef TARGET_ANDROID + // Avoid calling membarrier on older Android versions where membarrier + // may be barred by seccomp causing the process to be killed. + int apiLevel = android_get_device_api_level(); + if (apiLevel < __ANDROID_API_Q__) + { + return false; + } +#endif + + // Starting with Linux kernel 4.14, process memory barriers can be generated + // using MEMBARRIER_CMD_PRIVATE_EXPEDITED. + + int mask = membarrier(MEMBARRIER_CMD_QUERY, 0, 0); + + if (mask >= 0 && + mask & MEMBARRIER_CMD_PRIVATE_EXPEDITED && + // Register intent to use the private expedited command. + membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0) == 0) + { + return true; + } +#endif + + return false; +} + +// +// Tracks if the OS supports FlushProcessWriteBuffers using membarrier +// +static int s_flushUsingMemBarrier = 0; + +// Helper memory page used by the FlushProcessWriteBuffers +static uint8_t* g_helperPage = 0; + +// Mutex to make the FlushProcessWriteBuffersMutex thread safe +static pthread_mutex_t g_flushProcessWriteBuffersMutex; + +size_t g_pageSize = 0; + +bool minipal_initialize_flush_process_write_buffers() +{ + int pageSize = sysconf( _SC_PAGE_SIZE ); + + g_pageSize = (size_t)((pageSize > 0) ? pageSize : 0x1000); + +#ifndef TARGET_WASM + // + // support for FlusProcessWriteBuffers + // + assert(s_flushUsingMemBarrier == 0); + + if (CanFlushUsingMembarrier()) + { + s_flushUsingMemBarrier = TRUE; + } +#ifndef TARGET_APPLE + else + { + assert(g_helperPage == 0); + + g_helperPage = static_cast(mmap(0, minipal_get_page_size(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); + + if (g_helperPage == MAP_FAILED) + { + return false; + } + + // Verify that the s_helperPage is really aligned to the g_SystemInfo.dwPageSize + assert((((size_t)g_helperPage) & (minipal_get_page_size() - 1)) == 0); + + // Locking the page ensures that it stays in memory during the two mprotect + // calls in the FlushProcessWriteBuffers below. If the page was unmapped between + // those calls, they would not have the expected effect of generating IPI. + int status = mlock(g_helperPage, minipal_get_page_size()); + + if (status != 0) + { + return false; + } + + status = pthread_mutex_init(&g_flushProcessWriteBuffersMutex, NULL); + if (status != 0) + { + munlock(g_helperPage, minipal_get_page_size()); + return false; + } + } +#endif // !TARGET_APPLE +#endif // !TARGET_WASM + return true; +} + +void minipal_shutdown_flush_process_write_buffers() +{ + int ret = munlock(g_helperPage, minipal_get_page_size()); + assert(ret == 0); + ret = pthread_mutex_destroy(&g_flushProcessWriteBuffersMutex); + assert(ret == 0); + + munmap(g_helperPage, minipal_get_page_size()); +} + +// Flush write buffers of processors that are executing threads of the current process +void minipal_flush_process_write_buffers() +{ +#ifndef TARGET_WASM +#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H + if (s_flushUsingMemBarrier) + { + int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0); + assert(status == 0 && "Failed to flush using membarrier"); + } + else +#endif + if (g_helperPage != 0) + { + int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex); + assert(status == 0 && "Failed to lock the flushProcessWriteBuffersMutex lock"); + + // causes the OS to issue IPI to flush TLBs on all processors. This also + // results in flushing the processor buffers. + // Changing a helper memory page protection from read / write to no access + status = mprotect(g_helperPage, minipal_get_page_size(), PROT_READ | PROT_WRITE); + assert(status == 0 && "Failed to change helper page protection to read / write"); + + // Ensure that the page is dirty before we change the protection so that + // we prevent the OS from skipping the global TLB flush. + __sync_add_and_fetch((size_t*)g_helperPage, 1); + + status = mprotect(g_helperPage, minipal_get_page_size(), PROT_NONE); + assert(status == 0 && "Failed to change helper page protection to no access"); + + status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex); + assert(status == 0 && "Failed to unlock the flushProcessWriteBuffersMutex lock"); + } +#ifdef TARGET_APPLE + else + { + mach_msg_type_number_t cThreads; + thread_act_t *pThreads; + kern_return_t machret = task_threads(mach_task_self(), &pThreads, &cThreads); + CHECK_MACH("task_threads()", machret); + + uintptr_t sp; + uintptr_t registerValues[128]; + + // Iterate through each of the threads in the list. + for (mach_msg_type_number_t i = 0; i < cThreads; i++) + { + if (__builtin_available (macOS 10.14, iOS 12, tvOS 9, *)) + { + // Request the threads pointer values to force the thread to emit a memory barrier + size_t registers = 128; + machret = thread_get_register_pointer_values(pThreads[i], &sp, ®isters, registerValues); + } + else + { + // fallback implementation for older OS versions +#if defined(HOST_AMD64) + x86_thread_state64_t threadState; + mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; + machret = thread_get_state(pThreads[i], x86_THREAD_STATE64, (thread_state_t)&threadState, &count); +#elif defined(HOST_ARM64) + arm_thread_state64_t threadState; + mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT; + machret = thread_get_state(pThreads[i], ARM_THREAD_STATE64, (thread_state_t)&threadState, &count); +#else + #error Unexpected architecture +#endif + } + + if (machret == KERN_INSUFFICIENT_BUFFER_SIZE) + { + CHECK_MACH("thread_get_register_pointer_values()", machret); + } + + machret = mach_port_deallocate(mach_task_self(), pThreads[i]); + CHECK_MACH("mach_port_deallocate()", machret); + } + // Deallocate the thread list now we're done with it. + machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t)); + CHECK_MACH("vm_deallocate()", machret); + } +#endif // TARGET_APPLE +#endif // !TARGET_WASM +} +#else // !TARGET_WINDOWS +void minipal_flush_process_write_buffers() +{ + ::FlushProcessWriteBuffers(); +} +#endif // !TARGET_WINDOWS diff --git a/src/native/minipal/memory.h b/src/native/minipal/memory.h new file mode 100644 index 00000000000000..18ea0672bbc3b0 --- /dev/null +++ b/src/native/minipal/memory.h @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef HAVE_MINIPAL_MEMORY_H +#define HAVE_MINIPAL_MEMORY_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + bool minipal_initialize_flush_process_write_buffers(); + void minipal_shutdown_flush_process_write_buffers(); + void minipal_flush_process_write_buffers(); + +#ifndef TARGET_WINDOWS + extern size_t g_pageSize; +#endif + +#ifndef DACCESS_COMPILE + __forceinline size_t minipal_get_page_size() + { +#ifdef TARGET_WINDOWS + return 0x1000; +#else + return g_pageSize; +#endif + } +#endif // DACCESS_COMPILE + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // HAVE_MINIPAL_MEMORY_H From 33a4e83be2aa42efe1e46141e8d595c9d7c12439 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 29 Jul 2025 14:18:20 +0200 Subject: [PATCH 02/27] Fix build --- src/coreclr/gc/unix/gcenv.unix.cpp | 15 --------------- src/native/minipal/CMakeLists.txt | 1 + src/native/minipal/memory.c | 22 +++++++++++++++++++--- src/native/minipal/memory.h | 4 ++-- src/native/minipal/utils.h | 13 +++++++++++++ 5 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index b9fb93ec4faa19..3189301f5f6c05 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -75,21 +75,6 @@ #include #include -extern "C" -{ -# include -} - -#define CHECK_MACH(_msg, machret) do { \ - if (machret != KERN_SUCCESS) \ - { \ - char _szError[1024]; \ - snprintf(_szError, ARRAY_SIZE(_szError), "%s: %u: %s", __FUNCTION__, __LINE__, _msg); \ - mach_error(_szError, machret); \ - abort(); \ - } \ - } while (false) - #endif // __APPLE__ #ifdef __HAIKU__ diff --git a/src/native/minipal/CMakeLists.txt b/src/native/minipal/CMakeLists.txt index 78011d4779694a..f37db968ea50a9 100644 --- a/src/native/minipal/CMakeLists.txt +++ b/src/native/minipal/CMakeLists.txt @@ -2,6 +2,7 @@ include(configure.cmake) set(SOURCES cpufeatures.c + memory.c mutex.c guid.c random.c diff --git a/src/native/minipal/memory.c b/src/native/minipal/memory.c index 088d30a024d63b..7707b93aa69413 100644 --- a/src/native/minipal/memory.c +++ b/src/native/minipal/memory.c @@ -3,11 +3,27 @@ #include #include +#include #include #include #include #include +#ifdef __APPLE__ +#include + +#define CHECK_MACH(_msg, machret) do { \ + if (machret != KERN_SUCCESS) \ + { \ + char _szError[1024]; \ + snprintf(_szError, ARRAY_SIZE(_szError), "%s: %u: %s", __FUNCTION__, __LINE__, _msg); \ + mach_error(_szError, machret); \ + abort(); \ + } \ + } while (false) + +#endif // __APPLE__ + #ifndef TARGET_WINDOWS #ifdef __linux__ #include @@ -62,13 +78,13 @@ static uint8_t* g_helperPage = 0; // Mutex to make the FlushProcessWriteBuffersMutex thread safe static pthread_mutex_t g_flushProcessWriteBuffersMutex; -size_t g_pageSize = 0; +size_t g_pageSizeUnixInl = 0; bool minipal_initialize_flush_process_write_buffers() { int pageSize = sysconf( _SC_PAGE_SIZE ); - g_pageSize = (size_t)((pageSize > 0) ? pageSize : 0x1000); + g_pageSizeUnixInl = (size_t)((pageSize > 0) ? pageSize : 0x1000); #ifndef TARGET_WASM // @@ -78,7 +94,7 @@ bool minipal_initialize_flush_process_write_buffers() if (CanFlushUsingMembarrier()) { - s_flushUsingMemBarrier = TRUE; + s_flushUsingMemBarrier = true; } #ifndef TARGET_APPLE else diff --git a/src/native/minipal/memory.h b/src/native/minipal/memory.h index 18ea0672bbc3b0..4f489008488916 100644 --- a/src/native/minipal/memory.h +++ b/src/native/minipal/memory.h @@ -16,7 +16,7 @@ extern "C" void minipal_flush_process_write_buffers(); #ifndef TARGET_WINDOWS - extern size_t g_pageSize; + extern size_t g_pageSizeUnixInl; #endif #ifndef DACCESS_COMPILE @@ -25,7 +25,7 @@ extern "C" #ifdef TARGET_WINDOWS return 0x1000; #else - return g_pageSize; + return g_pageSizeUnixInl; #endif } #endif // DACCESS_COMPILE diff --git a/src/native/minipal/utils.h b/src/native/minipal/utils.h index 23a6a643601d1e..e65d77a894fc6c 100644 --- a/src/native/minipal/utils.h +++ b/src/native/minipal/utils.h @@ -4,6 +4,19 @@ #ifndef HAVE_MINIPAL_UTILS_H #define HAVE_MINIPAL_UTILS_H +#ifndef _MSC_VER +#define __stdcall +#ifdef __GNUC__ +#define __forceinline __attribute__((always_inline)) inline +#else // __GNUC__ +#define __forceinline inline +#endif // __GNUC__ +// [LOCALGC TODO] is there a better place for this? +#define NOINLINE __attribute__((noinline)) +#else // !_MSC_VER +#define NOINLINE __declspec(noinline) +#endif // _MSC_VER + #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) // Number of characters in a string literal. Excludes terminating NULL. From b2c5dba4fb7b83b042d903df08c9ae23eb1dc873 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 29 Jul 2025 17:27:30 +0200 Subject: [PATCH 03/27] Fix linux build --- src/native/minipal/memory.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/native/minipal/memory.c b/src/native/minipal/memory.c index 7707b93aa69413..be9627506bf686 100644 --- a/src/native/minipal/memory.c +++ b/src/native/minipal/memory.c @@ -1,13 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#ifndef TARGET_WINDOWS #include +#include #include +#include #include #include #include #include -#include #ifdef __APPLE__ #include @@ -24,7 +26,6 @@ #endif // __APPLE__ -#ifndef TARGET_WINDOWS #ifdef __linux__ #include #include @@ -101,7 +102,7 @@ bool minipal_initialize_flush_process_write_buffers() { assert(g_helperPage == 0); - g_helperPage = static_cast(mmap(0, minipal_get_page_size(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); + g_helperPage = (uint8_t*)(mmap(0, minipal_get_page_size(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); if (g_helperPage == MAP_FAILED) { From 70c2e2a443522becd7877fa6cc3489853e04c1ba Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 4 Aug 2025 17:36:33 +0200 Subject: [PATCH 04/27] Update definitions Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --- src/native/minipal/memory.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/native/minipal/memory.h b/src/native/minipal/memory.h index 4f489008488916..cbdad932dbaee6 100644 --- a/src/native/minipal/memory.h +++ b/src/native/minipal/memory.h @@ -11,9 +11,9 @@ extern "C" { #endif // __cplusplus - bool minipal_initialize_flush_process_write_buffers(); - void minipal_shutdown_flush_process_write_buffers(); - void minipal_flush_process_write_buffers(); + bool minipal_initialize_flush_process_write_buffers(void); + void minipal_shutdown_flush_process_write_buffers(void); + void minipal_flush_process_write_buffers(void); #ifndef TARGET_WINDOWS extern size_t g_pageSizeUnixInl; From af799e7cddfc9ec1b44103cf705e514c8819740f Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 4 Aug 2025 21:52:06 +0200 Subject: [PATCH 05/27] Feedback Do not care about cleanup --- src/coreclr/gc/unix/gcenv.unix.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 3189301f5f6c05..26f41acae9e568 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -236,8 +236,6 @@ bool GCToOSInterface::Initialize() // Shutdown the interface implementation void GCToOSInterface::Shutdown() { - minipal_shutdown_flush_process_write_buffers(); - CleanupCGroup(); } From a13c841f4f1bd840ffb806b4f90548a8fe6bb9e1 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 4 Aug 2025 21:53:05 +0200 Subject: [PATCH 06/27] Feedback Do not move page size to minipal --- src/coreclr/gc/env/gcenv.base.h | 13 +++++++++++++ src/coreclr/gc/env/gcenv.unix.inl | 12 ++++++++++-- src/coreclr/gc/env/gcenv.windows.inl | 9 +++++++-- src/coreclr/gc/unix/gcenv.unix.cpp | 8 +++++++- src/coreclr/pal/inc/pal_mstypes.h | 2 ++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/coreclr/gc/env/gcenv.base.h b/src/coreclr/gc/env/gcenv.base.h index 71c549ccd5c889..f74a8c466a2ec2 100644 --- a/src/coreclr/gc/env/gcenv.base.h +++ b/src/coreclr/gc/env/gcenv.base.h @@ -19,6 +19,19 @@ #define _alloca alloca #endif //_MSC_VER +#ifndef _MSC_VER +#define __stdcall +#ifdef __GNUC__ +#define __forceinline __attribute__((always_inline)) inline +#else // __GNUC__ +#define __forceinline inline +#endif // __GNUC__ +// [LOCALGC TODO] is there a better place for this? +#define NOINLINE __attribute__((noinline)) +#else // !_MSC_VER +#define NOINLINE __declspec(noinline) +#endif // _MSC_VER + #ifdef _MSC_VER #define __UNREACHABLE() __assume(0) #else diff --git a/src/coreclr/gc/env/gcenv.unix.inl b/src/coreclr/gc/env/gcenv.unix.inl index 1cb125b43a4d3e..d6e5ca796a1499 100644 --- a/src/coreclr/gc/env/gcenv.unix.inl +++ b/src/coreclr/gc/env/gcenv.unix.inl @@ -5,8 +5,16 @@ #define __GCENV_UNIX_INL__ #include "gcenv.os.h" -#include -#define OS_PAGE_SIZE minipal_get_page_size() +extern uint32_t g_pageSizeUnixInl; + +#define OS_PAGE_SIZE GCToOSInterface::GetPageSize() + +#ifndef DACCESS_COMPILE +__forceinline size_t GCToOSInterface::GetPageSize() +{ + return g_pageSizeUnixInl; +} +#endif // DACCESS_COMPILE #endif // __GCENV_UNIX_INL__ diff --git a/src/coreclr/gc/env/gcenv.windows.inl b/src/coreclr/gc/env/gcenv.windows.inl index 802e3767ae418d..df34e1aaa7c7ae 100644 --- a/src/coreclr/gc/env/gcenv.windows.inl +++ b/src/coreclr/gc/env/gcenv.windows.inl @@ -5,8 +5,13 @@ #define __GCENV_WINDOWS_INL__ #include "gcenv.os.h" -#include -#define OS_PAGE_SIZE minipal_get_page_size() + +#define OS_PAGE_SIZE GCToOSInterface::GetPageSize() + +__forceinline size_t GCToOSInterface::GetPageSize() +{ + return 0x1000; +} #endif // __GCENV_WINDOWS_INL__ diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 26f41acae9e568..83f0d7ef64b418 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -120,6 +120,8 @@ bool GetPhysicalMemoryUsed(size_t* val); static size_t g_RestrictedPhysicalMemoryLimit = 0; +uint32_t g_pageSizeUnixInl = 0; + AffinitySet g_processAffinitySet; extern "C" int g_highestNumaNode; @@ -137,6 +139,10 @@ static size_t g_kern_memorystatus_level_mib_length = 0; // true if it has succeeded, false if it has failed bool GCToOSInterface::Initialize() { + int pageSize = sysconf( _SC_PAGE_SIZE ); + + g_pageSizeUnixInl = uint32_t((pageSize > 0) ? pageSize : 0x1000); + // Calculate and cache the number of processors on this machine int cpuCount = sysconf(SYSCONF_GET_NUMPROCS); if (cpuCount == -1) @@ -213,7 +219,7 @@ bool GCToOSInterface::Initialize() return false; } - g_totalPhysicalMemSize = (uint64_t)pages * (uint64_t)minipal_get_page_size(); + g_totalPhysicalMemSize = (uint64_t)pages * (uint64_t)g_pageSizeUnixInl; #elif HAVE_SYSCTL int mib[2]; mib[0] = CTL_HW; diff --git a/src/coreclr/pal/inc/pal_mstypes.h b/src/coreclr/pal/inc/pal_mstypes.h index db0b021cfca59c..a0762726ba18e9 100644 --- a/src/coreclr/pal/inc/pal_mstypes.h +++ b/src/coreclr/pal/inc/pal_mstypes.h @@ -79,6 +79,8 @@ extern "C" { #define __inline inline #endif +#define __forceinline inline + #define PALIMPORT #ifndef DLLEXPORT From 23b9593c3e2f6150b3963b30409db910a473f330 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 4 Aug 2025 22:01:32 +0200 Subject: [PATCH 07/27] Feedback Rename the files --- src/coreclr/gc/gc.cpp | 2 +- src/coreclr/gc/softwarewritewatch.cpp | 2 +- src/coreclr/gc/unix/gcenv.unix.cpp | 2 +- src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp | 2 +- src/coreclr/pal/src/init/pal.cpp | 2 +- src/coreclr/vm/callcounting.cpp | 2 +- src/coreclr/vm/comutilnative.cpp | 2 +- src/coreclr/vm/gcenv.ee.cpp | 2 +- src/coreclr/vm/profilinghelper.cpp | 3 ++- src/coreclr/vm/threadsuspend.cpp | 2 +- src/native/minipal/{memory.c => flushprocesswritebuffers.c} | 0 src/native/minipal/{memory.h => flushprocesswritebuffers.h} | 0 12 files changed, 11 insertions(+), 10 deletions(-) rename src/native/minipal/{memory.c => flushprocesswritebuffers.c} (100%) rename src/native/minipal/{memory.h => flushprocesswritebuffers.h} (100%) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index edada57daae84d..890e9cd6cb9e04 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -27,7 +27,7 @@ #include "handletable.inl" #include "gcenv.inl" #include "gceventstatus.h" -#include +#include #ifdef __INTELLISENSE__ #if defined(FEATURE_SVR_GC) diff --git a/src/coreclr/gc/softwarewritewatch.cpp b/src/coreclr/gc/softwarewritewatch.cpp index 39da1f79281ba5..0c2d62012ca434 100644 --- a/src/coreclr/gc/softwarewritewatch.cpp +++ b/src/coreclr/gc/softwarewritewatch.cpp @@ -5,7 +5,7 @@ #include "gcenv.h" #include "env/gcenv.os.h" #include "softwarewritewatch.h" -#include +#include #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP #ifndef DACCESS_COMPILE diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 83f0d7ef64b418..cfc68cdb69cc6c 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -23,7 +23,7 @@ #include "volatile.h" #include "gcconfig.h" #include "numasupport.h" -#include +#include #include #include diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index 3ba482aef1629b..704d8592780168 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include #ifdef TARGET_LINUX diff --git a/src/coreclr/pal/src/init/pal.cpp b/src/coreclr/pal/src/init/pal.cpp index da7d311a08ac78..bcf8b96d4d03a5 100644 --- a/src/coreclr/pal/src/init/pal.cpp +++ b/src/coreclr/pal/src/init/pal.cpp @@ -36,7 +36,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PAL); // some headers have code with asserts, so do th #include "pal/stackstring.hpp" #include "pal/cgroup.h" #include -#include +#include #if HAVE_MACH_EXCEPTIONS #include "../exception/machexception.h" diff --git a/src/coreclr/vm/callcounting.cpp b/src/coreclr/vm/callcounting.cpp index b936711df87367..0f42e3be36adc6 100644 --- a/src/coreclr/vm/callcounting.cpp +++ b/src/coreclr/vm/callcounting.cpp @@ -7,7 +7,7 @@ #include "callcounting.h" #include "threadsuspend.h" -#include +#include #ifndef DACCESS_COMPILE extern "C" void STDCALL OnCallCountThresholdReachedStub(); diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 43c4715e90f41a..b8918c694dd3bc 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -33,7 +33,7 @@ #include "typestring.h" #include "finalizerthread.h" #include "threadsuspend.h" -#include +#include #ifdef FEATURE_COMINTEROP #include "comcallablewrapper.h" diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 1767bed0316674..f8d372fbc72ae9 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -25,7 +25,7 @@ #include "configuration.h" #include "genanalysis.h" #include "eventpipeadapter.h" -#include +#include // Finalizes a weak reference directly. extern void FinalizeWeakReference(Object* obj); diff --git a/src/coreclr/vm/profilinghelper.cpp b/src/coreclr/vm/profilinghelper.cpp index 539f8cafc07d5a..25dccff58df26c 100644 --- a/src/coreclr/vm/profilinghelper.cpp +++ b/src/coreclr/vm/profilinghelper.cpp @@ -110,7 +110,8 @@ #include "proftoeeinterfaceimpl.inl" #include "profilinghelper.h" #include "profilinghelper.inl" -#include +#include + #ifdef FEATURE_PROFAPI_ATTACH_DETACH #include "profdetach.h" diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index c05496b91bd725..d093a689fa0c39 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -14,7 +14,7 @@ #include "finalizerthread.h" #include "dbginterface.h" -#include +#include #include #ifdef FEATURE_EH_FUNCLETS diff --git a/src/native/minipal/memory.c b/src/native/minipal/flushprocesswritebuffers.c similarity index 100% rename from src/native/minipal/memory.c rename to src/native/minipal/flushprocesswritebuffers.c diff --git a/src/native/minipal/memory.h b/src/native/minipal/flushprocesswritebuffers.h similarity index 100% rename from src/native/minipal/memory.h rename to src/native/minipal/flushprocesswritebuffers.h From 6cc8ced894d0daf2a2967163f07976d07c412605 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 4 Aug 2025 22:10:13 +0200 Subject: [PATCH 08/27] Feedback Changes which were missed in earlier commits --- src/native/minipal/CMakeLists.txt | 2 +- src/native/minipal/flushprocesswritebuffers.c | 36 +++++++------------ src/native/minipal/flushprocesswritebuffers.h | 16 --------- src/native/minipal/utils.h | 13 ------- 4 files changed, 14 insertions(+), 53 deletions(-) diff --git a/src/native/minipal/CMakeLists.txt b/src/native/minipal/CMakeLists.txt index f37db968ea50a9..4c070eb247ac7c 100644 --- a/src/native/minipal/CMakeLists.txt +++ b/src/native/minipal/CMakeLists.txt @@ -2,7 +2,7 @@ include(configure.cmake) set(SOURCES cpufeatures.c - memory.c + flushprocesswritebuffers.c mutex.c guid.c random.c diff --git a/src/native/minipal/flushprocesswritebuffers.c b/src/native/minipal/flushprocesswritebuffers.c index be9627506bf686..7814926c969a9a 100644 --- a/src/native/minipal/flushprocesswritebuffers.c +++ b/src/native/minipal/flushprocesswritebuffers.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #ifdef __APPLE__ #include @@ -37,7 +37,7 @@ #endif #endif -static bool CanFlushUsingMembarrier() +static bool CanFlushUsingMembarrier(void) { #if defined(__linux__) || HAVE_SYS_MEMBARRIER_H @@ -79,13 +79,13 @@ static uint8_t* g_helperPage = 0; // Mutex to make the FlushProcessWriteBuffersMutex thread safe static pthread_mutex_t g_flushProcessWriteBuffersMutex; -size_t g_pageSizeUnixInl = 0; +static size_t s_pageSize = 0; -bool minipal_initialize_flush_process_write_buffers() +bool minipal_initialize_flush_process_write_buffers(void) { int pageSize = sysconf( _SC_PAGE_SIZE ); - g_pageSizeUnixInl = (size_t)((pageSize > 0) ? pageSize : 0x1000); + s_pageSize = (size_t)((pageSize > 0) ? pageSize : 0x1000); #ifndef TARGET_WASM // @@ -102,7 +102,7 @@ bool minipal_initialize_flush_process_write_buffers() { assert(g_helperPage == 0); - g_helperPage = (uint8_t*)(mmap(0, minipal_get_page_size(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); + g_helperPage = (uint8_t*)(mmap(0, s_pageSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); if (g_helperPage == MAP_FAILED) { @@ -110,12 +110,12 @@ bool minipal_initialize_flush_process_write_buffers() } // Verify that the s_helperPage is really aligned to the g_SystemInfo.dwPageSize - assert((((size_t)g_helperPage) & (minipal_get_page_size() - 1)) == 0); + assert((((size_t)g_helperPage) & (s_pageSize - 1)) == 0); // Locking the page ensures that it stays in memory during the two mprotect // calls in the FlushProcessWriteBuffers below. If the page was unmapped between // those calls, they would not have the expected effect of generating IPI. - int status = mlock(g_helperPage, minipal_get_page_size()); + int status = mlock(g_helperPage, s_pageSize); if (status != 0) { @@ -125,7 +125,7 @@ bool minipal_initialize_flush_process_write_buffers() status = pthread_mutex_init(&g_flushProcessWriteBuffersMutex, NULL); if (status != 0) { - munlock(g_helperPage, minipal_get_page_size()); + munlock(g_helperPage, s_pageSize); return false; } } @@ -134,18 +134,8 @@ bool minipal_initialize_flush_process_write_buffers() return true; } -void minipal_shutdown_flush_process_write_buffers() -{ - int ret = munlock(g_helperPage, minipal_get_page_size()); - assert(ret == 0); - ret = pthread_mutex_destroy(&g_flushProcessWriteBuffersMutex); - assert(ret == 0); - - munmap(g_helperPage, minipal_get_page_size()); -} - // Flush write buffers of processors that are executing threads of the current process -void minipal_flush_process_write_buffers() +void minipal_flush_process_write_buffers(void) { #ifndef TARGET_WASM #if defined(__linux__) || HAVE_SYS_MEMBARRIER_H @@ -164,14 +154,14 @@ void minipal_flush_process_write_buffers() // causes the OS to issue IPI to flush TLBs on all processors. This also // results in flushing the processor buffers. // Changing a helper memory page protection from read / write to no access - status = mprotect(g_helperPage, minipal_get_page_size(), PROT_READ | PROT_WRITE); + status = mprotect(g_helperPage, s_pageSize, PROT_READ | PROT_WRITE); assert(status == 0 && "Failed to change helper page protection to read / write"); // Ensure that the page is dirty before we change the protection so that // we prevent the OS from skipping the global TLB flush. __sync_add_and_fetch((size_t*)g_helperPage, 1); - status = mprotect(g_helperPage, minipal_get_page_size(), PROT_NONE); + status = mprotect(g_helperPage, s_pageSize, PROT_NONE); assert(status == 0 && "Failed to change helper page protection to no access"); status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex); @@ -229,7 +219,7 @@ void minipal_flush_process_write_buffers() #endif // !TARGET_WASM } #else // !TARGET_WINDOWS -void minipal_flush_process_write_buffers() +void minipal_flush_process_write_buffers(void) { ::FlushProcessWriteBuffers(); } diff --git a/src/native/minipal/flushprocesswritebuffers.h b/src/native/minipal/flushprocesswritebuffers.h index cbdad932dbaee6..dbce8844f65de4 100644 --- a/src/native/minipal/flushprocesswritebuffers.h +++ b/src/native/minipal/flushprocesswritebuffers.h @@ -12,24 +12,8 @@ extern "C" #endif // __cplusplus bool minipal_initialize_flush_process_write_buffers(void); - void minipal_shutdown_flush_process_write_buffers(void); void minipal_flush_process_write_buffers(void); -#ifndef TARGET_WINDOWS - extern size_t g_pageSizeUnixInl; -#endif - -#ifndef DACCESS_COMPILE - __forceinline size_t minipal_get_page_size() - { -#ifdef TARGET_WINDOWS - return 0x1000; -#else - return g_pageSizeUnixInl; -#endif - } -#endif // DACCESS_COMPILE - #ifdef __cplusplus } #endif // __cplusplus diff --git a/src/native/minipal/utils.h b/src/native/minipal/utils.h index e65d77a894fc6c..23a6a643601d1e 100644 --- a/src/native/minipal/utils.h +++ b/src/native/minipal/utils.h @@ -4,19 +4,6 @@ #ifndef HAVE_MINIPAL_UTILS_H #define HAVE_MINIPAL_UTILS_H -#ifndef _MSC_VER -#define __stdcall -#ifdef __GNUC__ -#define __forceinline __attribute__((always_inline)) inline -#else // __GNUC__ -#define __forceinline inline -#endif // __GNUC__ -// [LOCALGC TODO] is there a better place for this? -#define NOINLINE __attribute__((noinline)) -#else // !_MSC_VER -#define NOINLINE __declspec(noinline) -#endif // _MSC_VER - #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) // Number of characters in a string literal. Excludes terminating NULL. From 2fb42a39f6fbd7a87ade8003c9692e252fcc03ff Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 5 Aug 2025 12:49:54 +0200 Subject: [PATCH 09/27] Feedback Remove PalFlushProcessWriteBuffers wrapper --- src/coreclr/nativeaot/Runtime/MiscHelpers.cpp | 2 +- src/coreclr/nativeaot/Runtime/Pal.h | 2 +- src/coreclr/nativeaot/Runtime/gcenv.ee.cpp | 6 +++--- src/coreclr/nativeaot/Runtime/threadstore.cpp | 6 +++--- src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp | 5 ----- src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp | 2 +- 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp index 138c3917198ec4..0c4d97ef3ee65d 100644 --- a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp @@ -73,7 +73,7 @@ EXTERN_C void QCALLTYPE RhFlushProcessWriteBuffers() ASSERT_MSG(!ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode(), "You must p/invoke to RhFlushProcessWriteBuffers"); - PalFlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); } // Get the list of currently loaded NativeAOT modules (as OS HMODULE handles). The caller provides a reference diff --git a/src/coreclr/nativeaot/Runtime/Pal.h b/src/coreclr/nativeaot/Runtime/Pal.h index 6a4c57ce7307a1..75198761dc4234 100644 --- a/src/coreclr/nativeaot/Runtime/Pal.h +++ b/src/coreclr/nativeaot/Runtime/Pal.h @@ -23,6 +23,7 @@ #include #endif +#include #include #include "CommonTypes.h" @@ -290,7 +291,6 @@ int32_t _stricmp(const char *string1, const char *string2); uint16_t PalCaptureStackBackTrace(uint32_t arg1, uint32_t arg2, void* arg3, uint32_t* arg4); UInt32_BOOL PalCloseHandle(HANDLE arg1); -void PalFlushProcessWriteBuffers(); uint32_t PalGetCurrentProcessId(); #ifdef UNICODE diff --git a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp index e2c31db91e328b..1bdc6a589fabc5 100644 --- a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp +++ b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp @@ -63,7 +63,7 @@ void GCToEEInterface::RestartEE(bool /*bFinishedGC*/) // This is needed to synchronize threads that were running in preemptive mode while // the runtime was suspended and that will return to cooperative mode after the runtime // is restarted. - PalFlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); #endif // !defined(TARGET_X86) && !defined(TARGET_AMD64) SyncClean::CleanUp(); @@ -404,7 +404,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) { // If runtime is not suspended, force all threads to see the changed table before seeing updated heap boundaries. // See: http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/346765 - PalFlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); } #endif @@ -415,7 +415,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) if (!is_runtime_suspended) { // If runtime is not suspended, force all threads to see the changed state before observing future allocations. - PalFlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); } #endif return; diff --git a/src/coreclr/nativeaot/Runtime/threadstore.cpp b/src/coreclr/nativeaot/Runtime/threadstore.cpp index daf745ba0b65ef..660631aaaa9310 100644 --- a/src/coreclr/nativeaot/Runtime/threadstore.cpp +++ b/src/coreclr/nativeaot/Runtime/threadstore.cpp @@ -237,7 +237,7 @@ void ThreadStore::SuspendAllThreads(bool waitForGCEvent) // Our lock-free algorithm depends on flushing write buffers of all processors running RH code. The // reason for this is that we essentially implement Dekker's algorithm, which requires write ordering. - PalFlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); int prevRemaining = INT32_MAX; bool observeOnly = true; @@ -310,7 +310,7 @@ void ThreadStore::SuspendAllThreads(bool waitForGCEvent) // This is needed to synchronize threads that were running in preemptive mode thus were // left alone by suspension to flush their writes that they made before they switched to // preemptive mode. - PalFlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); #endif //TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 } @@ -329,7 +329,7 @@ void ThreadStore::ResumeAllThreads(bool waitForGCEvent) // This is needed to synchronize threads that were running in preemptive mode while // the runtime was suspended and that will return to cooperative mode after the runtime // is restarted. - PalFlushProcessWriteBuffers(); + minipal_flush_process_write_buffers(); #endif //TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 RhpTrapThreads &= ~(uint32_t)TrapThreadsFlags::TrapThreads; diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index 704d8592780168..9b41f233a3301f 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -1234,11 +1234,6 @@ int32_t PalGetModuleFileName(_Out_ const TCHAR** pModuleNameOut, HANDLE moduleBa #endif // defined(HOST_WASM) } -void PalFlushProcessWriteBuffers() -{ - minipal_flush_process_write_buffers(); -} - static const int64_t SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL; static const int64_t SECS_TO_100NS = 10000000; /* 10^7 */ diff --git a/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp index 8df2e75d1aaf81..dd670a36afb541 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp @@ -1039,7 +1039,7 @@ UInt32_BOOL PalCloseHandle(HANDLE arg1) return ::CloseHandle(arg1); } -void PalFlushProcessWriteBuffers() +void minipal_flush_process_write_buffers() { ::FlushProcessWriteBuffers(); } From 59e69589cc50bf4a772189161a43afe9d79a81c6 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 5 Aug 2025 13:06:19 +0200 Subject: [PATCH 10/27] Feedback Remove parts around g_helperPage on mac --- src/native/minipal/flushprocesswritebuffers.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/native/minipal/flushprocesswritebuffers.c b/src/native/minipal/flushprocesswritebuffers.c index 7814926c969a9a..036d235423bb05 100644 --- a/src/native/minipal/flushprocesswritebuffers.c +++ b/src/native/minipal/flushprocesswritebuffers.c @@ -73,6 +73,7 @@ static bool CanFlushUsingMembarrier(void) // static int s_flushUsingMemBarrier = 0; +#ifndef TARGET_APPLE // Helper memory page used by the FlushProcessWriteBuffers static uint8_t* g_helperPage = 0; @@ -80,13 +81,10 @@ static uint8_t* g_helperPage = 0; static pthread_mutex_t g_flushProcessWriteBuffersMutex; static size_t s_pageSize = 0; +#endif // !TARGET_APPLE bool minipal_initialize_flush_process_write_buffers(void) { - int pageSize = sysconf( _SC_PAGE_SIZE ); - - s_pageSize = (size_t)((pageSize > 0) ? pageSize : 0x1000); - #ifndef TARGET_WASM // // support for FlusProcessWriteBuffers @@ -102,6 +100,9 @@ bool minipal_initialize_flush_process_write_buffers(void) { assert(g_helperPage == 0); + int pageSize = sysconf( _SC_PAGE_SIZE ); + + s_pageSize = (size_t)((pageSize > 0) ? pageSize : 0x1000); g_helperPage = (uint8_t*)(mmap(0, s_pageSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); if (g_helperPage == MAP_FAILED) @@ -146,6 +147,7 @@ void minipal_flush_process_write_buffers(void) } else #endif +#ifndef TARGET_APPLE if (g_helperPage != 0) { int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex); @@ -167,8 +169,8 @@ void minipal_flush_process_write_buffers(void) status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex); assert(status == 0 && "Failed to unlock the flushProcessWriteBuffersMutex lock"); } -#ifdef TARGET_APPLE else +#else { mach_msg_type_number_t cThreads; thread_act_t *pThreads; From 5edc898a82d2df33b6b638806333828c49aa7c92 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 5 Aug 2025 13:21:33 +0200 Subject: [PATCH 11/27] Feedback Rename to memory_barrier_process_wide --- src/coreclr/gc/gc.cpp | 4 ++-- src/coreclr/gc/softwarewritewatch.cpp | 6 +++--- src/coreclr/gc/unix/gcenv.unix.cpp | 2 +- src/coreclr/nativeaot/Runtime/MiscHelpers.cpp | 2 +- src/coreclr/nativeaot/Runtime/Pal.h | 2 +- src/coreclr/nativeaot/Runtime/gcenv.ee.cpp | 6 +++--- src/coreclr/nativeaot/Runtime/threadstore.cpp | 6 +++--- src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp | 2 +- src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp | 2 +- src/coreclr/pal/src/init/pal.cpp | 4 ++-- src/coreclr/vm/callcounting.cpp | 4 ++-- src/coreclr/vm/comutilnative.cpp | 4 ++-- src/coreclr/vm/gcenv.ee.cpp | 6 +++--- src/coreclr/vm/profilinghelper.cpp | 6 +++--- src/coreclr/vm/threadsuspend.cpp | 10 +++++----- src/native/minipal/CMakeLists.txt | 2 +- ...rocesswritebuffers.c => memorybarrierprocesswide.c} | 8 ++++---- ...rocesswritebuffers.h => memorybarrierprocesswide.h} | 4 ++-- 18 files changed, 40 insertions(+), 40 deletions(-) rename src/native/minipal/{flushprocesswritebuffers.c => memorybarrierprocesswide.c} (97%) rename src/native/minipal/{flushprocesswritebuffers.h => memorybarrierprocesswide.h} (75%) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 890e9cd6cb9e04..025fe97bec77f5 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -27,7 +27,7 @@ #include "handletable.inl" #include "gcenv.inl" #include "gceventstatus.h" -#include +#include #ifdef __INTELLISENSE__ #if defined(FEATURE_SVR_GC) @@ -9872,7 +9872,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, if (!write_barrier_updated) { seg_mapping_table = new_seg_mapping_table; - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); g_gc_lowest_address = saved_g_lowest_address; g_gc_highest_address = saved_g_highest_address; diff --git a/src/coreclr/gc/softwarewritewatch.cpp b/src/coreclr/gc/softwarewritewatch.cpp index 0c2d62012ca434..c42c63dcd0d47a 100644 --- a/src/coreclr/gc/softwarewritewatch.cpp +++ b/src/coreclr/gc/softwarewritewatch.cpp @@ -5,7 +5,7 @@ #include "gcenv.h" #include "env/gcenv.os.h" #include "softwarewritewatch.h" -#include +#include #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP #ifndef DACCESS_COMPILE @@ -132,7 +132,7 @@ void SoftwareWriteWatch::GetDirty( { // When a page is marked as dirty, a memory barrier is not issued after the write most of the time. Issue a memory // barrier on all active threads of the process now to make recent changes to dirty state visible to this thread. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); } uint8_t *tableRegionStart; @@ -234,7 +234,7 @@ void SoftwareWriteWatch::GetDirty( // that the GC will not miss marking through dirtied objects in the page. Issue a memory barrier on all active threads // of the process now. MemoryBarrier(); // flush writes from this thread first to guarantee ordering - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); } } diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index cfc68cdb69cc6c..819d9ceef92419 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -23,7 +23,7 @@ #include "volatile.h" #include "gcconfig.h" #include "numasupport.h" -#include +#include #include #include diff --git a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp index 0c4d97ef3ee65d..b8d3b4f797839d 100644 --- a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp @@ -73,7 +73,7 @@ EXTERN_C void QCALLTYPE RhFlushProcessWriteBuffers() ASSERT_MSG(!ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode(), "You must p/invoke to RhFlushProcessWriteBuffers"); - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); } // Get the list of currently loaded NativeAOT modules (as OS HMODULE handles). The caller provides a reference diff --git a/src/coreclr/nativeaot/Runtime/Pal.h b/src/coreclr/nativeaot/Runtime/Pal.h index 75198761dc4234..0a9f9c2ea45599 100644 --- a/src/coreclr/nativeaot/Runtime/Pal.h +++ b/src/coreclr/nativeaot/Runtime/Pal.h @@ -23,7 +23,7 @@ #include #endif -#include +#include #include #include "CommonTypes.h" diff --git a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp index 1bdc6a589fabc5..58d7ffc0bfc963 100644 --- a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp +++ b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp @@ -63,7 +63,7 @@ void GCToEEInterface::RestartEE(bool /*bFinishedGC*/) // This is needed to synchronize threads that were running in preemptive mode while // the runtime was suspended and that will return to cooperative mode after the runtime // is restarted. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); #endif // !defined(TARGET_X86) && !defined(TARGET_AMD64) SyncClean::CleanUp(); @@ -404,7 +404,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) { // If runtime is not suspended, force all threads to see the changed table before seeing updated heap boundaries. // See: http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/346765 - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); } #endif @@ -415,7 +415,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) if (!is_runtime_suspended) { // If runtime is not suspended, force all threads to see the changed state before observing future allocations. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); } #endif return; diff --git a/src/coreclr/nativeaot/Runtime/threadstore.cpp b/src/coreclr/nativeaot/Runtime/threadstore.cpp index 660631aaaa9310..fd0c221f0725b4 100644 --- a/src/coreclr/nativeaot/Runtime/threadstore.cpp +++ b/src/coreclr/nativeaot/Runtime/threadstore.cpp @@ -237,7 +237,7 @@ void ThreadStore::SuspendAllThreads(bool waitForGCEvent) // Our lock-free algorithm depends on flushing write buffers of all processors running RH code. The // reason for this is that we essentially implement Dekker's algorithm, which requires write ordering. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); int prevRemaining = INT32_MAX; bool observeOnly = true; @@ -310,7 +310,7 @@ void ThreadStore::SuspendAllThreads(bool waitForGCEvent) // This is needed to synchronize threads that were running in preemptive mode thus were // left alone by suspension to flush their writes that they made before they switched to // preemptive mode. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); #endif //TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 } @@ -329,7 +329,7 @@ void ThreadStore::ResumeAllThreads(bool waitForGCEvent) // This is needed to synchronize threads that were running in preemptive mode while // the runtime was suspended and that will return to cooperative mode after the runtime // is restarted. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); #endif //TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 RhpTrapThreads &= ~(uint32_t)TrapThreadsFlags::TrapThreads; diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index 9b41f233a3301f..be67447030e85a 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include #ifdef TARGET_LINUX diff --git a/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp index dd670a36afb541..c57787a2a8d455 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp @@ -1039,7 +1039,7 @@ UInt32_BOOL PalCloseHandle(HANDLE arg1) return ::CloseHandle(arg1); } -void minipal_flush_process_write_buffers() +void minipal_memory_barrier_process_wide() { ::FlushProcessWriteBuffers(); } diff --git a/src/coreclr/pal/src/init/pal.cpp b/src/coreclr/pal/src/init/pal.cpp index bcf8b96d4d03a5..4d02460f16d51a 100644 --- a/src/coreclr/pal/src/init/pal.cpp +++ b/src/coreclr/pal/src/init/pal.cpp @@ -36,7 +36,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PAL); // some headers have code with asserts, so do th #include "pal/stackstring.hpp" #include "pal/cgroup.h" #include -#include +#include #if HAVE_MACH_EXCEPTIONS #include "../exception/machexception.h" @@ -581,7 +581,7 @@ Initialize( if (flags & PAL_INITIALIZE_FLUSH_PROCESS_WRITE_BUFFERS) { // Initialize before first thread is created for faster load on Linux - if (!minipal_initialize_flush_process_write_buffers()) + if (!minipal_initialize_memory_barrier_process_wide()) { ERROR("Unable to initialize flush process write buffers\n"); palError = ERROR_PALINIT_INITIALIZE_FLUSH_PROCESS_WRITE_BUFFERS; diff --git a/src/coreclr/vm/callcounting.cpp b/src/coreclr/vm/callcounting.cpp index 0f42e3be36adc6..dea3750dea0aef 100644 --- a/src/coreclr/vm/callcounting.cpp +++ b/src/coreclr/vm/callcounting.cpp @@ -7,7 +7,7 @@ #include "callcounting.h" #include "threadsuspend.h" -#include +#include #ifndef DACCESS_COMPILE extern "C" void STDCALL OnCallCountThresholdReachedStub(); @@ -1026,7 +1026,7 @@ void CallCountingManager::StopAndDeleteAllCallCountingStubs() // will not be used after resuming the runtime. The following ensures that other threads will not use an old cached // entry point value that will not be valid. Do this here in case of exception later. MemoryBarrier(); // flush writes from this thread first to guarantee ordering - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); // At this point, allocated call counting stubs won't be used anymore. Call counting stubs and corresponding infos may // now be safely deleted. Note that call counting infos may not be deleted prior to this point because call counting diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index b8918c694dd3bc..95a615ca36207d 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -33,7 +33,7 @@ #include "typestring.h" #include "finalizerthread.h" #include "threadsuspend.h" -#include +#include #ifdef FEATURE_COMINTEROP #include "comcallablewrapper.h" @@ -1562,7 +1562,7 @@ extern "C" void QCALLTYPE Interlocked_MemoryBarrierProcessWide() { QCALL_CONTRACT; - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); } static BOOL HasOverriddenMethod(MethodTable* mt, MethodTable* classMT, WORD methodSlot) diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index f8d372fbc72ae9..c16f6a1b31c9a2 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -25,7 +25,7 @@ #include "configuration.h" #include "genanalysis.h" #include "eventpipeadapter.h" -#include +#include // Finalizes a weak reference directly. extern void FinalizeWeakReference(Object* obj); @@ -1007,7 +1007,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) { // If runtime is not suspended, force all threads to see the changed table before seeing updated heap boundaries. // See: http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/346765 - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); } #endif @@ -1059,7 +1059,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) if (!is_runtime_suspended) { // If runtime is not suspended, force all threads to see the changed state before observing future allocations. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); } #endif diff --git a/src/coreclr/vm/profilinghelper.cpp b/src/coreclr/vm/profilinghelper.cpp index 25dccff58df26c..ccf23ed65ef1be 100644 --- a/src/coreclr/vm/profilinghelper.cpp +++ b/src/coreclr/vm/profilinghelper.cpp @@ -85,7 +85,7 @@ // these actions: // * (a) Set the profiler's status to a non-active state like kProfStatusDetaching or // kProfStatusNone -// * (b) Call minipal_flush_process_write_buffers() +// * (b) Call minipal_memory_barrier_process_wide() // * (c) Grab thread store lock, iterate through all threads, and verify each per-thread // evacuation counter is zero. // @@ -110,7 +110,7 @@ #include "proftoeeinterfaceimpl.inl" #include "profilinghelper.h" #include "profilinghelper.inl" -#include +#include #ifdef FEATURE_PROFAPI_ATTACH_DETACH @@ -209,7 +209,7 @@ void CurrentProfilerStatus::Set(ProfilerStatus newProfStatus) // can safely perform catchup at that time (see // code:#ProfCatchUpSynchronization). // - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); } #endif // !defined(DACCESS_COMPILE) } diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index d093a689fa0c39..1411e56ff39e7f 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -14,7 +14,7 @@ #include "finalizerthread.h" #include "dbginterface.h" -#include +#include #include #ifdef FEATURE_EH_FUNCLETS @@ -3311,7 +3311,7 @@ void ThreadSuspend::SuspendAllThreads() // - we get a reliable reading of the threads' m_fPreemptiveGCDisabled state // - other threads see that g_TrapReturningThreads is set // See VSW 475315 and 488918 for details. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); int prevRemaining = INT32_MAX; bool observeOnly = true; @@ -3382,7 +3382,7 @@ void ThreadSuspend::SuspendAllThreads() // This is needed to synchronize threads that were running in preemptive mode thus were // left alone by suspension to flush their writes that they made before they switched to // preemptive mode. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); #endif //TARGET_ARM || TARGET_ARM64 STRESS_LOG0(LF_SYNC, LL_INFO1000, "Thread::SuspendAllThreads() - Success\n"); @@ -3607,7 +3607,7 @@ void ThreadSuspend::ResumeAllThreads(BOOL SuspendSucceeded) // This is needed to synchronize threads that were running in preemptive mode while // the runtime was suspended and that will return to cooperative mode after the runtime // is restarted. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); #endif //TARGET_ARM || TARGET_ARM64 // @@ -5389,7 +5389,7 @@ void ThreadSuspend::RestartEE(BOOL bFinishedGC, BOOL SuspendSucceeded) // This is needed to synchronize threads that were running in preemptive mode while // the runtime was suspended and that will return to cooperative mode after the runtime // is restarted. - minipal_flush_process_write_buffers(); + minipal_memory_barrier_process_wide(); #endif //TARGET_ARM || TARGET_ARM64 // diff --git a/src/native/minipal/CMakeLists.txt b/src/native/minipal/CMakeLists.txt index 4c070eb247ac7c..f32281a56bb365 100644 --- a/src/native/minipal/CMakeLists.txt +++ b/src/native/minipal/CMakeLists.txt @@ -2,7 +2,7 @@ include(configure.cmake) set(SOURCES cpufeatures.c - flushprocesswritebuffers.c + memorybarrierprocesswide.c mutex.c guid.c random.c diff --git a/src/native/minipal/flushprocesswritebuffers.c b/src/native/minipal/memorybarrierprocesswide.c similarity index 97% rename from src/native/minipal/flushprocesswritebuffers.c rename to src/native/minipal/memorybarrierprocesswide.c index 036d235423bb05..6719039c6f359d 100644 --- a/src/native/minipal/flushprocesswritebuffers.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #ifdef __APPLE__ #include @@ -83,7 +83,7 @@ static pthread_mutex_t g_flushProcessWriteBuffersMutex; static size_t s_pageSize = 0; #endif // !TARGET_APPLE -bool minipal_initialize_flush_process_write_buffers(void) +bool minipal_initialize_memory_barrier_process_wide(void) { #ifndef TARGET_WASM // @@ -136,7 +136,7 @@ bool minipal_initialize_flush_process_write_buffers(void) } // Flush write buffers of processors that are executing threads of the current process -void minipal_flush_process_write_buffers(void) +void minipal_memory_barrier_process_wide(void) { #ifndef TARGET_WASM #if defined(__linux__) || HAVE_SYS_MEMBARRIER_H @@ -221,7 +221,7 @@ void minipal_flush_process_write_buffers(void) #endif // !TARGET_WASM } #else // !TARGET_WINDOWS -void minipal_flush_process_write_buffers(void) +void minipal_memory_barrier_process_wide(void) { ::FlushProcessWriteBuffers(); } diff --git a/src/native/minipal/flushprocesswritebuffers.h b/src/native/minipal/memorybarrierprocesswide.h similarity index 75% rename from src/native/minipal/flushprocesswritebuffers.h rename to src/native/minipal/memorybarrierprocesswide.h index dbce8844f65de4..99a0d03922e6db 100644 --- a/src/native/minipal/flushprocesswritebuffers.h +++ b/src/native/minipal/memorybarrierprocesswide.h @@ -11,8 +11,8 @@ extern "C" { #endif // __cplusplus - bool minipal_initialize_flush_process_write_buffers(void); - void minipal_flush_process_write_buffers(void); + bool minipal_initialize_memory_barrier_process_wide(void); + void minipal_memory_barrier_process_wide(void); #ifdef __cplusplus } From 4a423552492391293e6469f4a261fd347106a78c Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 5 Aug 2025 12:45:14 +0200 Subject: [PATCH 12/27] Fix mac build Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --- src/native/minipal/memorybarrierprocesswide.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index 6719039c6f359d..2f6382f1d87663 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -12,6 +12,7 @@ #include #ifdef __APPLE__ +#include #include #define CHECK_MACH(_msg, machret) do { \ From 036174622d526761037a998ef8b91f87548fb2cb Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 5 Aug 2025 14:10:31 +0200 Subject: [PATCH 13/27] Fix build --- src/native/minipal/memorybarrierprocesswide.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index 2f6382f1d87663..4997e9f2613bc1 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -170,7 +170,6 @@ void minipal_memory_barrier_process_wide(void) status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex); assert(status == 0 && "Failed to unlock the flushProcessWriteBuffersMutex lock"); } - else #else { mach_msg_type_number_t cThreads; From 9396dc8efe31c18ce12b7d054a3d9e1a74b3e939 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 5 Aug 2025 14:53:36 +0200 Subject: [PATCH 14/27] Fix windows build --- src/native/minipal/memorybarrierprocesswide.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index 4997e9f2613bc1..80336a6457c473 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -221,6 +221,9 @@ void minipal_memory_barrier_process_wide(void) #endif // !TARGET_WASM } #else // !TARGET_WINDOWS + +#include "windows.h" + void minipal_memory_barrier_process_wide(void) { ::FlushProcessWriteBuffers(); From de63052651203a5803f49b9266df5033edcbb088 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 5 Aug 2025 15:06:45 +0200 Subject: [PATCH 15/27] More specific include Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --- src/native/minipal/memorybarrierprocesswide.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index 80336a6457c473..cbdf43a3b63cf4 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -222,7 +222,7 @@ void minipal_memory_barrier_process_wide(void) } #else // !TARGET_WINDOWS -#include "windows.h" +#include void minipal_memory_barrier_process_wide(void) { From 7066c110b579c3c232a005ee8ebbfeba132c04e9 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 5 Aug 2025 15:44:38 +0200 Subject: [PATCH 16/27] Fix windows build Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --- src/native/minipal/memorybarrierprocesswide.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index cbdf43a3b63cf4..f36bfd037d945f 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#ifndef TARGET_WINDOWS +#ifndef HOST_WINDOWS #include #include #include @@ -152,6 +152,7 @@ void minipal_memory_barrier_process_wide(void) if (g_helperPage != 0) { int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex); + (void)status; // unused in release config assert(status == 0 && "Failed to lock the flushProcessWriteBuffersMutex lock"); // causes the OS to issue IPI to flush TLBs on all processors. This also @@ -220,7 +221,7 @@ void minipal_memory_barrier_process_wide(void) #endif // TARGET_APPLE #endif // !TARGET_WASM } -#else // !TARGET_WINDOWS +#else // !HOST_WINDOWS #include @@ -228,4 +229,4 @@ void minipal_memory_barrier_process_wide(void) { ::FlushProcessWriteBuffers(); } -#endif // !TARGET_WINDOWS +#endif // !HOST_WINDOWS From f1c4f4fe64fdf6431272f54f148690b0d0bc8c95 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 5 Aug 2025 16:14:29 +0200 Subject: [PATCH 17/27] Feedback Remove windows wrapper too --- src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp index c57787a2a8d455..839e0943877a65 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp @@ -1039,11 +1039,6 @@ UInt32_BOOL PalCloseHandle(HANDLE arg1) return ::CloseHandle(arg1); } -void minipal_memory_barrier_process_wide() -{ - ::FlushProcessWriteBuffers(); -} - uint32_t PalGetCurrentProcessId() { return static_cast(::GetCurrentProcessId()); From 2949aac23d7b72828c1cbe1687e37631235fb46c Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 6 Aug 2025 13:32:06 +0200 Subject: [PATCH 18/27] Use more generic include again Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --- src/native/minipal/memorybarrierprocesswide.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index f36bfd037d945f..0216d8639e125d 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -223,7 +223,7 @@ void minipal_memory_barrier_process_wide(void) } #else // !HOST_WINDOWS -#include +#include void minipal_memory_barrier_process_wide(void) { From 77e13945b381cdb6a13bebf8ed07f91094f49809 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 6 Aug 2025 13:35:06 +0200 Subject: [PATCH 19/27] Apply suggestions from code review Refactor ifdefs Co-authored-by: Jan Kotas --- src/native/minipal/memorybarrierprocesswide.c | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index 0216d8639e125d..20e9ebfb8762d3 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -31,16 +31,15 @@ #include #include #define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__) +#undef HAVE_SYS_MEMBARRIER_H +#define HAVE_SYS_MEMBARRIER_H 1 #elif HAVE_SYS_MEMBARRIER_H #include -#ifdef TARGET_BROWSER -#define membarrier(cmd, flags, cpu_id) 0 // browser/wasm is currently single threaded -#endif #endif +#if HAVE_SYS_MEMBARRIER_H static bool CanFlushUsingMembarrier(void) { -#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H #ifdef TARGET_ANDROID // Avoid calling membarrier on older Android versions where membarrier @@ -69,12 +68,14 @@ static bool CanFlushUsingMembarrier(void) return false; } +#if HAVE_SYS_MEMBARRIER_H // -// Tracks if the OS supports FlushProcessWriteBuffers using membarrier +// Tracks if the OS supports membarrier syscall // static int s_flushUsingMemBarrier = 0; +#endif // HAVE_SYS_MEMBARRIER_H -#ifndef TARGET_APPLE +#if !defined(HOST_APPLE) && !defined(HOST_WASM) // Helper memory page used by the FlushProcessWriteBuffers static uint8_t* g_helperPage = 0; @@ -86,7 +87,23 @@ static size_t s_pageSize = 0; bool minipal_initialize_memory_barrier_process_wide(void) { -#ifndef TARGET_WASM +#ifdef HOST_WASM + // browser/wasm is currently single threaded +#elif defined(HOST_APPLE) +... + +#else +#if HAVE_SYS_MEMBARRIER_H + if (CanFlushUsingMembarrier()) + { + s_flushUsingMemBarrier = true; + } + else +#else + { + // Fallback implementation + } +#endif // // support for FlusProcessWriteBuffers // From cf613bc18eee195bb44c6a00b6893f2303ad4575 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 6 Aug 2025 13:37:50 +0200 Subject: [PATCH 20/27] Fix build --- src/native/minipal/memorybarrierprocesswide.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index 20e9ebfb8762d3..c1092d65d43ee7 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -40,7 +40,6 @@ #if HAVE_SYS_MEMBARRIER_H static bool CanFlushUsingMembarrier(void) { - #ifdef TARGET_ANDROID // Avoid calling membarrier on older Android versions where membarrier // may be barred by seccomp causing the process to be killed. @@ -63,12 +62,10 @@ static bool CanFlushUsingMembarrier(void) { return true; } -#endif return false; } -#if HAVE_SYS_MEMBARRIER_H // // Tracks if the OS supports membarrier syscall // From f5a57b4af640290d5cd81630ae0f72adbfeeb93d Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 6 Aug 2025 13:48:02 +0200 Subject: [PATCH 21/27] Complete the suggestion from feedback --- src/native/minipal/memorybarrierprocesswide.c | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index c1092d65d43ee7..d461a5d4cbab2e 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -87,8 +87,7 @@ bool minipal_initialize_memory_barrier_process_wide(void) #ifdef HOST_WASM // browser/wasm is currently single threaded #elif defined(HOST_APPLE) -... - + // Apple platforms do not support membarrier, so we use a different mechanism #else #if HAVE_SYS_MEMBARRIER_H if (CanFlushUsingMembarrier()) @@ -99,20 +98,6 @@ bool minipal_initialize_memory_barrier_process_wide(void) #else { // Fallback implementation - } -#endif - // - // support for FlusProcessWriteBuffers - // - assert(s_flushUsingMemBarrier == 0); - - if (CanFlushUsingMembarrier()) - { - s_flushUsingMemBarrier = true; - } -#ifndef TARGET_APPLE - else - { assert(g_helperPage == 0); int pageSize = sysconf( _SC_PAGE_SIZE ); @@ -145,8 +130,9 @@ bool minipal_initialize_memory_barrier_process_wide(void) return false; } } -#endif // !TARGET_APPLE -#endif // !TARGET_WASM +#endif // HAVE_SYS_MEMBARRIER_H +#endif // !HOST_WASM && !HOST_APPLE + return true; } From 5491bd8e1fce08ff00816d3b3f1dea8198b6ac4a Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 6 Aug 2025 15:02:54 +0200 Subject: [PATCH 22/27] Feedback Call `minipal_initialize_memory_barrier_process_wide` from unix `GCToOSInterface::Initialize`. Let it return true if it was previously successfully initialized. --- src/coreclr/gc/unix/gcenv.unix.cpp | 5 +++++ src/native/minipal/memorybarrierprocesswide.c | 12 +++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 819d9ceef92419..682c6adac7807c 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -152,6 +152,11 @@ bool GCToOSInterface::Initialize() g_totalCpuCount = cpuCount; + if (!minipal_initialize_memory_barrier_process_wide()) + { + return false; + } + InitializeCGroup(); #if HAVE_SCHED_GETAFFINITY diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index d461a5d4cbab2e..2a4f6ed5944fa9 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -82,8 +82,15 @@ static pthread_mutex_t g_flushProcessWriteBuffersMutex; static size_t s_pageSize = 0; #endif // !TARGET_APPLE +static bool s_initializedMemoryBarrierSuccessfullyInitialized = false; + bool minipal_initialize_memory_barrier_process_wide(void) { + if (s_initializedMemoryBarrierSuccessfullyInitialized) + { + return true; + } + #ifdef HOST_WASM // browser/wasm is currently single threaded #elif defined(HOST_APPLE) @@ -95,7 +102,7 @@ bool minipal_initialize_memory_barrier_process_wide(void) s_flushUsingMemBarrier = true; } else -#else +#endif // HAVE_SYS_MEMBARRIER_H { // Fallback implementation assert(g_helperPage == 0); @@ -130,10 +137,9 @@ bool minipal_initialize_memory_barrier_process_wide(void) return false; } } -#endif // HAVE_SYS_MEMBARRIER_H #endif // !HOST_WASM && !HOST_APPLE - return true; + return s_initializedMemoryBarrierSuccessfullyInitialized = true; } // Flush write buffers of processors that are executing threads of the current process From c8f8a6ea8675ec39a891b1ada7f23a3f3410371c Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 6 Aug 2025 15:33:36 +0200 Subject: [PATCH 23/27] Fix windows build --- src/native/minipal/memorybarrierprocesswide.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index 2a4f6ed5944fa9..ac767756a7b61f 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -233,6 +233,6 @@ void minipal_memory_barrier_process_wide(void) void minipal_memory_barrier_process_wide(void) { - ::FlushProcessWriteBuffers(); + FlushProcessWriteBuffers(); } #endif // !HOST_WINDOWS From 491b28b35788fe9e18d5ba4b277c5bc9a104f52f Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 12 Aug 2025 15:45:34 +0200 Subject: [PATCH 24/27] Apply suggestions from code review Co-authored-by: Jan Kotas --- src/native/minipal/memorybarrierprocesswide.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index ac767756a7b61f..beb6fd5afefdd9 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -69,18 +69,18 @@ static bool CanFlushUsingMembarrier(void) // // Tracks if the OS supports membarrier syscall // -static int s_flushUsingMemBarrier = 0; +static bool s_flushUsingMemBarrier = false; #endif // HAVE_SYS_MEMBARRIER_H #if !defined(HOST_APPLE) && !defined(HOST_WASM) -// Helper memory page used by the FlushProcessWriteBuffers +// Helper memory page used by the fallback path static uint8_t* g_helperPage = 0; -// Mutex to make the FlushProcessWriteBuffersMutex thread safe +// Mutex to make the fallback path thread safe static pthread_mutex_t g_flushProcessWriteBuffersMutex; static size_t s_pageSize = 0; -#endif // !TARGET_APPLE +#endif // !TARGET_APPLE && !HOST_WASM static bool s_initializedMemoryBarrierSuccessfullyInitialized = false; @@ -117,7 +117,7 @@ bool minipal_initialize_memory_barrier_process_wide(void) return false; } - // Verify that the s_helperPage is really aligned to the g_SystemInfo.dwPageSize + // Verify that the s_helperPage is really aligned to the s_pageSize assert((((size_t)g_helperPage) & (s_pageSize - 1)) == 0); // Locking the page ensures that it stays in memory during the two mprotect @@ -139,12 +139,15 @@ bool minipal_initialize_memory_barrier_process_wide(void) } #endif // !HOST_WASM && !HOST_APPLE - return s_initializedMemoryBarrierSuccessfullyInitialized = true; + s_initializedMemoryBarrierSuccessfullyInitialized = true; + return true; } // Flush write buffers of processors that are executing threads of the current process void minipal_memory_barrier_process_wide(void) { + assert(s_initializedMemoryBarrierSuccessfullyInitialized); + #ifndef TARGET_WASM #if defined(__linux__) || HAVE_SYS_MEMBARRIER_H if (s_flushUsingMemBarrier) From 42e19d3384de8b04df58a554507eb26cca189e4d Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 12 Aug 2025 16:09:35 +0200 Subject: [PATCH 25/27] Feedback Refactor minipal_memory_barrier_process_wide --- src/native/minipal/memorybarrierprocesswide.c | 105 +++++++++--------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index beb6fd5afefdd9..ce365c8fa4eb7d 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -148,16 +148,62 @@ void minipal_memory_barrier_process_wide(void) { assert(s_initializedMemoryBarrierSuccessfullyInitialized); -#ifndef TARGET_WASM -#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H +#ifdef HOST_WASM + // browser/wasm is currently single threaded +#elif defined(HOST_APPLE) + mach_msg_type_number_t cThreads; + thread_act_t *pThreads; + kern_return_t machret = task_threads(mach_task_self(), &pThreads, &cThreads); + CHECK_MACH("task_threads()", machret); + + uintptr_t sp; + uintptr_t registerValues[128]; + + // Iterate through each of the threads in the list. + for (mach_msg_type_number_t i = 0; i < cThreads; i++) + { + if (__builtin_available (macOS 10.14, iOS 12, tvOS 9, *)) + { + // Request the threads pointer values to force the thread to emit a memory barrier + size_t registers = 128; + machret = thread_get_register_pointer_values(pThreads[i], &sp, ®isters, registerValues); + } + else + { + // fallback implementation for older OS versions +#if defined(HOST_AMD64) + x86_thread_state64_t threadState; + mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; + machret = thread_get_state(pThreads[i], x86_THREAD_STATE64, (thread_state_t)&threadState, &count); +#elif defined(HOST_ARM64) + arm_thread_state64_t threadState; + mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT; + machret = thread_get_state(pThreads[i], ARM_THREAD_STATE64, (thread_state_t)&threadState, &count); +#else + #error Unexpected architecture +#endif + } + + if (machret == KERN_INSUFFICIENT_BUFFER_SIZE) + { + CHECK_MACH("thread_get_register_pointer_values()", machret); + } + + machret = mach_port_deallocate(mach_task_self(), pThreads[i]); + CHECK_MACH("mach_port_deallocate()", machret); + } + // Deallocate the thread list now we're done with it. + machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t)); + CHECK_MACH("vm_deallocate()", machret); +#else +#if HAVE_SYS_MEMBARRIER_H if (s_flushUsingMemBarrier) { int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0); assert(status == 0 && "Failed to flush using membarrier"); } else -#endif -#ifndef TARGET_APPLE +#else if (g_helperPage != 0) { int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex); @@ -180,55 +226,8 @@ void minipal_memory_barrier_process_wide(void) status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex); assert(status == 0 && "Failed to unlock the flushProcessWriteBuffersMutex lock"); } -#else - { - mach_msg_type_number_t cThreads; - thread_act_t *pThreads; - kern_return_t machret = task_threads(mach_task_self(), &pThreads, &cThreads); - CHECK_MACH("task_threads()", machret); - - uintptr_t sp; - uintptr_t registerValues[128]; - - // Iterate through each of the threads in the list. - for (mach_msg_type_number_t i = 0; i < cThreads; i++) - { - if (__builtin_available (macOS 10.14, iOS 12, tvOS 9, *)) - { - // Request the threads pointer values to force the thread to emit a memory barrier - size_t registers = 128; - machret = thread_get_register_pointer_values(pThreads[i], &sp, ®isters, registerValues); - } - else - { - // fallback implementation for older OS versions -#if defined(HOST_AMD64) - x86_thread_state64_t threadState; - mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; - machret = thread_get_state(pThreads[i], x86_THREAD_STATE64, (thread_state_t)&threadState, &count); -#elif defined(HOST_ARM64) - arm_thread_state64_t threadState; - mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT; - machret = thread_get_state(pThreads[i], ARM_THREAD_STATE64, (thread_state_t)&threadState, &count); -#else - #error Unexpected architecture -#endif - } - - if (machret == KERN_INSUFFICIENT_BUFFER_SIZE) - { - CHECK_MACH("thread_get_register_pointer_values()", machret); - } - - machret = mach_port_deallocate(mach_task_self(), pThreads[i]); - CHECK_MACH("mach_port_deallocate()", machret); - } - // Deallocate the thread list now we're done with it. - machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t)); - CHECK_MACH("vm_deallocate()", machret); - } -#endif // TARGET_APPLE -#endif // !TARGET_WASM +#endif // !HAVE_SYS_MEMBARRIER_H +#endif // !HOST_APPLE && !HOST_WASM } #else // !HOST_WINDOWS From 59769154ca4deb719f211c4a8fcd0f8e9a82160c Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 12 Aug 2025 16:46:10 +0200 Subject: [PATCH 26/27] Fix build --- src/native/minipal/memorybarrierprocesswide.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index ce365c8fa4eb7d..0d87f019c63a68 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -195,7 +195,7 @@ void minipal_memory_barrier_process_wide(void) // Deallocate the thread list now we're done with it. machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t)); CHECK_MACH("vm_deallocate()", machret); -#else +#else // !HOST_APPLE && !HOST_WASM #if HAVE_SYS_MEMBARRIER_H if (s_flushUsingMemBarrier) { @@ -203,7 +203,7 @@ void minipal_memory_barrier_process_wide(void) assert(status == 0 && "Failed to flush using membarrier"); } else -#else +#endif // !HAVE_SYS_MEMBARRIER_H if (g_helperPage != 0) { int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex); @@ -226,7 +226,6 @@ void minipal_memory_barrier_process_wide(void) status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex); assert(status == 0 && "Failed to unlock the flushProcessWriteBuffersMutex lock"); } -#endif // !HAVE_SYS_MEMBARRIER_H #endif // !HOST_APPLE && !HOST_WASM } #else // !HOST_WINDOWS From 5cfda4f0e730b861cef68b3aa6e857d5cb9e21dc Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 12 Aug 2025 07:51:46 -0700 Subject: [PATCH 27/27] Apply suggestions from code review --- src/native/minipal/memorybarrierprocesswide.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index 0d87f019c63a68..b2fcad9ef789c0 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -74,7 +74,7 @@ static bool s_flushUsingMemBarrier = false; #if !defined(HOST_APPLE) && !defined(HOST_WASM) // Helper memory page used by the fallback path -static uint8_t* g_helperPage = 0; +static uint8_t* g_helperPage = NULL; // Mutex to make the fallback path thread safe static pthread_mutex_t g_flushProcessWriteBuffersMutex; @@ -105,7 +105,7 @@ bool minipal_initialize_memory_barrier_process_wide(void) #endif // HAVE_SYS_MEMBARRIER_H { // Fallback implementation - assert(g_helperPage == 0); + assert(g_helperPage == NULL); int pageSize = sysconf( _SC_PAGE_SIZE ); @@ -204,8 +204,9 @@ void minipal_memory_barrier_process_wide(void) } else #endif // !HAVE_SYS_MEMBARRIER_H - if (g_helperPage != 0) { + assert(g_helperPage != NULL); + int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex); (void)status; // unused in release config assert(status == 0 && "Failed to lock the flushProcessWriteBuffersMutex lock");