Skip to content

Commit 0b0b4b2

Browse files
radekdoulikam11jkotas
authored
Move FlushProcessWriteBuffers to minipal (#118161)
* Move FlushProcessWriteBuffers to minipal Implements #117977 Also move the initialization of it and get of page size * Fix build * Fix linux build * Update definitions Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> * Feedback Do not care about cleanup * Feedback Do not move page size to minipal * Feedback Rename the files * Feedback Changes which were missed in earlier commits * Feedback Remove PalFlushProcessWriteBuffers wrapper * Feedback Remove parts around g_helperPage on mac * Feedback Rename to memory_barrier_process_wide * Fix mac build Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> * Fix build * Fix windows build * More specific include Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> * Fix windows build Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> * Feedback Remove windows wrapper too * Use more generic include again Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> * Apply suggestions from code review Refactor ifdefs Co-authored-by: Jan Kotas <jkotas@microsoft.com> * Fix build * Complete the suggestion from feedback * Feedback Call `minipal_initialize_memory_barrier_process_wide` from unix `GCToOSInterface::Initialize`. Let it return true if it was previously successfully initialized. * Fix windows build * Apply suggestions from code review Co-authored-by: Jan Kotas <jkotas@microsoft.com> * Feedback Refactor minipal_memory_barrier_process_wide * Fix build * Apply suggestions from code review --------- Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent fad7576 commit 0b0b4b2

File tree

23 files changed

+296
-411
lines changed

23 files changed

+296
-411
lines changed

src/coreclr/gc/env/gcenv.os.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,9 +451,6 @@ class GCToOSInterface
451451
// Misc
452452
//
453453

454-
// Flush write buffers of processors that are executing threads of the current process
455-
static void FlushProcessWriteBuffers();
456-
457454
// Break into a debugger
458455
static void DebugBreak();
459456

src/coreclr/gc/gc.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "handletable.inl"
2828
#include "gcenv.inl"
2929
#include "gceventstatus.h"
30+
#include <minipal/memorybarrierprocesswide.h>
3031

3132
#ifdef __INTELLISENSE__
3233
#if defined(FEATURE_SVR_GC)
@@ -9871,7 +9872,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start,
98719872
if (!write_barrier_updated)
98729873
{
98739874
seg_mapping_table = new_seg_mapping_table;
9874-
GCToOSInterface::FlushProcessWriteBuffers();
9875+
minipal_memory_barrier_process_wide();
98759876
g_gc_lowest_address = saved_g_lowest_address;
98769877
g_gc_highest_address = saved_g_highest_address;
98779878

src/coreclr/gc/softwarewritewatch.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "gcenv.h"
66
#include "env/gcenv.os.h"
77
#include "softwarewritewatch.h"
8+
#include <minipal/memorybarrierprocesswide.h>
89

910
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1011
#ifndef DACCESS_COMPILE
@@ -131,7 +132,7 @@ void SoftwareWriteWatch::GetDirty(
131132
{
132133
// When a page is marked as dirty, a memory barrier is not issued after the write most of the time. Issue a memory
133134
// barrier on all active threads of the process now to make recent changes to dirty state visible to this thread.
134-
GCToOSInterface::FlushProcessWriteBuffers();
135+
minipal_memory_barrier_process_wide();
135136
}
136137

137138
uint8_t *tableRegionStart;
@@ -233,7 +234,7 @@ void SoftwareWriteWatch::GetDirty(
233234
// that the GC will not miss marking through dirtied objects in the page. Issue a memory barrier on all active threads
234235
// of the process now.
235236
MemoryBarrier(); // flush writes from this thread first to guarantee ordering
236-
GCToOSInterface::FlushProcessWriteBuffers();
237+
minipal_memory_barrier_process_wide();
237238
}
238239
}
239240

src/coreclr/gc/unix/gcenv.unix.cpp

Lines changed: 3 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,14 @@
2323
#include "volatile.h"
2424
#include "gcconfig.h"
2525
#include "numasupport.h"
26+
#include <minipal/memorybarrierprocesswide.h>
2627
#include <minipal/thread.h>
2728
#include <minipal/time.h>
2829

2930
#if HAVE_SWAPCTL
3031
#include <sys/swap.h>
3132
#endif
3233

33-
#ifdef __linux__
34-
#include <linux/membarrier.h>
35-
#include <sys/syscall.h>
36-
#define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__)
37-
#elif HAVE_SYS_MEMBARRIER_H
38-
#include <sys/membarrier.h>
39-
#ifdef TARGET_BROWSER
40-
#define membarrier(cmd, flags, cpu_id) 0 // browser/wasm is currently single threaded
41-
#endif
42-
#endif
43-
4434
#include <sys/resource.h>
4535

4636
#undef min
@@ -85,21 +75,6 @@
8575

8676
#include <mach/task.h>
8777
#include <mach/vm_map.h>
88-
extern "C"
89-
{
90-
# include <mach/thread_state.h>
91-
}
92-
93-
#define CHECK_MACH(_msg, machret) do { \
94-
if (machret != KERN_SUCCESS) \
95-
{ \
96-
char _szError[1024]; \
97-
snprintf(_szError, ARRAY_SIZE(_szError), "%s: %u: %s", __FUNCTION__, __LINE__, _msg); \
98-
mach_error(_szError, machret); \
99-
abort(); \
100-
} \
101-
} while (false)
102-
10378
#endif // __APPLE__
10479

10580
#ifdef __HAIKU__
@@ -140,48 +115,6 @@ typedef cpuset_t cpu_set_t;
140115
// The cached total number of CPUs that can be used in the OS.
141116
static uint32_t g_totalCpuCount = 0;
142117

143-
bool CanFlushUsingMembarrier()
144-
{
145-
#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H
146-
147-
#ifdef TARGET_ANDROID
148-
// Avoid calling membarrier on older Android versions where membarrier
149-
// may be barred by seccomp causing the process to be killed.
150-
int apiLevel = android_get_device_api_level();
151-
if (apiLevel < __ANDROID_API_Q__)
152-
{
153-
return false;
154-
}
155-
#endif
156-
157-
// Starting with Linux kernel 4.14, process memory barriers can be generated
158-
// using MEMBARRIER_CMD_PRIVATE_EXPEDITED.
159-
160-
int mask = membarrier(MEMBARRIER_CMD_QUERY, 0, 0);
161-
162-
if (mask >= 0 &&
163-
mask & MEMBARRIER_CMD_PRIVATE_EXPEDITED &&
164-
// Register intent to use the private expedited command.
165-
membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0) == 0)
166-
{
167-
return true;
168-
}
169-
#endif
170-
171-
return false;
172-
}
173-
174-
//
175-
// Tracks if the OS supports FlushProcessWriteBuffers using membarrier
176-
//
177-
static int s_flushUsingMemBarrier = 0;
178-
179-
// Helper memory page used by the FlushProcessWriteBuffers
180-
static uint8_t* g_helperPage = 0;
181-
182-
// Mutex to make the FlushProcessWriteBuffersMutex thread safe
183-
static pthread_mutex_t g_flushProcessWriteBuffersMutex;
184-
185118
size_t GetRestrictedPhysicalMemoryLimit();
186119
bool GetPhysicalMemoryUsed(size_t* val);
187120

@@ -219,50 +152,10 @@ bool GCToOSInterface::Initialize()
219152

220153
g_totalCpuCount = cpuCount;
221154

222-
//
223-
// support for FlusProcessWriteBuffers
224-
//
225-
#ifndef TARGET_WASM
226-
assert(s_flushUsingMemBarrier == 0);
227-
228-
if (CanFlushUsingMembarrier())
229-
{
230-
s_flushUsingMemBarrier = TRUE;
231-
}
232-
#ifndef TARGET_APPLE
233-
else
155+
if (!minipal_initialize_memory_barrier_process_wide())
234156
{
235-
assert(g_helperPage == 0);
236-
237-
g_helperPage = static_cast<uint8_t*>(mmap(0, OS_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
238-
239-
if (g_helperPage == MAP_FAILED)
240-
{
241-
return false;
242-
}
243-
244-
// Verify that the s_helperPage is really aligned to the g_SystemInfo.dwPageSize
245-
assert((((size_t)g_helperPage) & (OS_PAGE_SIZE - 1)) == 0);
246-
247-
// Locking the page ensures that it stays in memory during the two mprotect
248-
// calls in the FlushProcessWriteBuffers below. If the page was unmapped between
249-
// those calls, they would not have the expected effect of generating IPI.
250-
int status = mlock(g_helperPage, OS_PAGE_SIZE);
251-
252-
if (status != 0)
253-
{
254-
return false;
255-
}
256-
257-
status = pthread_mutex_init(&g_flushProcessWriteBuffersMutex, NULL);
258-
if (status != 0)
259-
{
260-
munlock(g_helperPage, OS_PAGE_SIZE);
261-
return false;
262-
}
157+
return false;
263158
}
264-
#endif // !TARGET_APPLE
265-
#endif // !TARGET_WASM
266159

267160
InitializeCGroup();
268161

@@ -354,13 +247,6 @@ bool GCToOSInterface::Initialize()
354247
// Shutdown the interface implementation
355248
void GCToOSInterface::Shutdown()
356249
{
357-
int ret = munlock(g_helperPage, OS_PAGE_SIZE);
358-
assert(ret == 0);
359-
ret = pthread_mutex_destroy(&g_flushProcessWriteBuffersMutex);
360-
assert(ret == 0);
361-
362-
munmap(g_helperPage, OS_PAGE_SIZE);
363-
364250
CleanupCGroup();
365251
}
366252

@@ -410,91 +296,6 @@ bool GCToOSInterface::CanGetCurrentProcessorNumber()
410296
return HAVE_SCHED_GETCPU;
411297
}
412298

413-
// Flush write buffers of processors that are executing threads of the current process
414-
void GCToOSInterface::FlushProcessWriteBuffers()
415-
{
416-
#ifndef TARGET_WASM
417-
#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H
418-
if (s_flushUsingMemBarrier)
419-
{
420-
int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0);
421-
assert(status == 0 && "Failed to flush using membarrier");
422-
}
423-
else
424-
#endif
425-
if (g_helperPage != 0)
426-
{
427-
int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex);
428-
assert(status == 0 && "Failed to lock the flushProcessWriteBuffersMutex lock");
429-
430-
// Changing a helper memory page protection from read / write to no access
431-
// causes the OS to issue IPI to flush TLBs on all processors. This also
432-
// results in flushing the processor buffers.
433-
status = mprotect(g_helperPage, OS_PAGE_SIZE, PROT_READ | PROT_WRITE);
434-
assert(status == 0 && "Failed to change helper page protection to read / write");
435-
436-
// Ensure that the page is dirty before we change the protection so that
437-
// we prevent the OS from skipping the global TLB flush.
438-
__sync_add_and_fetch((size_t*)g_helperPage, 1);
439-
440-
status = mprotect(g_helperPage, OS_PAGE_SIZE, PROT_NONE);
441-
assert(status == 0 && "Failed to change helper page protection to no access");
442-
443-
status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex);
444-
assert(status == 0 && "Failed to unlock the flushProcessWriteBuffersMutex lock");
445-
}
446-
#ifdef TARGET_APPLE
447-
else
448-
{
449-
mach_msg_type_number_t cThreads;
450-
thread_act_t *pThreads;
451-
kern_return_t machret = task_threads(mach_task_self(), &pThreads, &cThreads);
452-
CHECK_MACH("task_threads()", machret);
453-
454-
uintptr_t sp;
455-
uintptr_t registerValues[128];
456-
457-
// Iterate through each of the threads in the list.
458-
for (mach_msg_type_number_t i = 0; i < cThreads; i++)
459-
{
460-
if (__builtin_available (macOS 10.14, iOS 12, tvOS 9, *))
461-
{
462-
// Request the threads pointer values to force the thread to emit a memory barrier
463-
size_t registers = 128;
464-
machret = thread_get_register_pointer_values(pThreads[i], &sp, &registers, registerValues);
465-
}
466-
else
467-
{
468-
// fallback implementation for older OS versions
469-
#if defined(HOST_AMD64)
470-
x86_thread_state64_t threadState;
471-
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
472-
machret = thread_get_state(pThreads[i], x86_THREAD_STATE64, (thread_state_t)&threadState, &count);
473-
#elif defined(HOST_ARM64)
474-
arm_thread_state64_t threadState;
475-
mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT;
476-
machret = thread_get_state(pThreads[i], ARM_THREAD_STATE64, (thread_state_t)&threadState, &count);
477-
#else
478-
#error Unexpected architecture
479-
#endif
480-
}
481-
482-
if (machret == KERN_INSUFFICIENT_BUFFER_SIZE)
483-
{
484-
CHECK_MACH("thread_get_register_pointer_values()", machret);
485-
}
486-
487-
machret = mach_port_deallocate(mach_task_self(), pThreads[i]);
488-
CHECK_MACH("mach_port_deallocate()", machret);
489-
}
490-
// Deallocate the thread list now we're done with it.
491-
machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t));
492-
CHECK_MACH("vm_deallocate()", machret);
493-
}
494-
#endif // TARGET_APPLE
495-
#endif // !TARGET_WASM
496-
}
497-
498299
// Break into a debugger. Uses a compiler intrinsic if one is available,
499300
// otherwise raises a SIGTRAP.
500301
void GCToOSInterface::DebugBreak()

src/coreclr/gc/windows/gcenv.windows.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -640,12 +640,6 @@ bool GCToOSInterface::CanGetCurrentProcessorNumber()
640640
return true;
641641
}
642642

643-
// Flush write buffers of processors that are executing threads of the current process
644-
void GCToOSInterface::FlushProcessWriteBuffers()
645-
{
646-
::FlushProcessWriteBuffers();
647-
}
648-
649643
// Break into a debugger
650644
void GCToOSInterface::DebugBreak()
651645
{

src/coreclr/nativeaot/Runtime/MiscHelpers.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ EXTERN_C void QCALLTYPE RhFlushProcessWriteBuffers()
7373
ASSERT_MSG(!ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode(),
7474
"You must p/invoke to RhFlushProcessWriteBuffers");
7575

76-
PalFlushProcessWriteBuffers();
76+
minipal_memory_barrier_process_wide();
7777
}
7878

7979
// Get the list of currently loaded NativeAOT modules (as OS HMODULE handles). The caller provides a reference

src/coreclr/nativeaot/Runtime/Pal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <pthread.h>
2424
#endif
2525

26+
#include <minipal/memorybarrierprocesswide.h>
2627
#include <minipal/guid.h>
2728

2829
#include "CommonTypes.h"
@@ -290,7 +291,6 @@ int32_t _stricmp(const char *string1, const char *string2);
290291

291292
uint16_t PalCaptureStackBackTrace(uint32_t arg1, uint32_t arg2, void* arg3, uint32_t* arg4);
292293
UInt32_BOOL PalCloseHandle(HANDLE arg1);
293-
void PalFlushProcessWriteBuffers();
294294
uint32_t PalGetCurrentProcessId();
295295

296296
#ifdef UNICODE

src/coreclr/nativeaot/Runtime/gcenv.ee.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ void GCToEEInterface::RestartEE(bool /*bFinishedGC*/)
6363
// This is needed to synchronize threads that were running in preemptive mode while
6464
// the runtime was suspended and that will return to cooperative mode after the runtime
6565
// is restarted.
66-
PalFlushProcessWriteBuffers();
66+
minipal_memory_barrier_process_wide();
6767
#endif // !defined(TARGET_X86) && !defined(TARGET_AMD64)
6868

6969
SyncClean::CleanUp();
@@ -404,7 +404,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
404404
{
405405
// If runtime is not suspended, force all threads to see the changed table before seeing updated heap boundaries.
406406
// See: http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/346765
407-
PalFlushProcessWriteBuffers();
407+
minipal_memory_barrier_process_wide();
408408
}
409409
#endif
410410

@@ -415,7 +415,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
415415
if (!is_runtime_suspended)
416416
{
417417
// If runtime is not suspended, force all threads to see the changed state before observing future allocations.
418-
PalFlushProcessWriteBuffers();
418+
minipal_memory_barrier_process_wide();
419419
}
420420
#endif
421421
return;

0 commit comments

Comments
 (0)