diff --git a/Source/bmalloc/bmalloc/BSyscall.h b/Source/bmalloc/bmalloc/BSyscall.h index e5ecfb643b42..9b26d518f940 100644 --- a/Source/bmalloc/bmalloc/BSyscall.h +++ b/Source/bmalloc/bmalloc/BSyscall.h @@ -26,7 +26,18 @@ #pragma once #include +#include +/* Retry syscalls on EAGAIN with 1ms backoff to avoid busy-spinning. + madvise(MADV_DONTDUMP) can return EAGAIN under kernel mmap_write_lock + contention, causing 100% CPU usage with concurrent GC threads. + Cap retries at 100 (100ms total) as a safety net — madvise failures + here are advisory, not fatal. See also: virtual_alloc_with_retry() + in libpas/pas_page_malloc.c for the Windows equivalent. */ #define SYSCALL(x) do { \ - while ((x) == -1 && errno == EAGAIN) { } \ + int _syscall_tries = 0; \ + while ((x) == -1 && errno == EAGAIN) { \ + if (++_syscall_tries > 100) break; \ + usleep(1000); \ + } \ } while (0); diff --git a/Source/bmalloc/bmalloc/VMAllocate.h b/Source/bmalloc/bmalloc/VMAllocate.h index 06894519b5d1..07441912205f 100644 --- a/Source/bmalloc/bmalloc/VMAllocate.h +++ b/Source/bmalloc/bmalloc/VMAllocate.h @@ -302,9 +302,9 @@ inline void vmDeallocatePhysicalPages(void* p, size_t vmSize) SYSCALL(madvise(p, vmSize, MADV_FREE)); #else SYSCALL(madvise(p, vmSize, MADV_DONTNEED)); -#if BOS(LINUX) - SYSCALL(madvise(p, vmSize, MADV_DONTDUMP)); -#endif +/* MADV_DONTDUMP removed: it only reduces core dump size but requires + kernel mmap_write_lock, causing severe contention with concurrent + GC threads. MADV_DONTNEED (above) only needs mmap_read_lock. */ #endif } @@ -319,9 +319,7 @@ inline void vmAllocatePhysicalPages(void* p, size_t vmSize) // Instead the kernel will commit pages as they are touched. #else SYSCALL(madvise(p, vmSize, MADV_NORMAL)); -#if BOS(LINUX) - SYSCALL(madvise(p, vmSize, MADV_DODUMP)); -#endif +/* MADV_DODUMP removed: symmetric with MADV_DONTDUMP removal above. */ #endif } #else diff --git a/Source/bmalloc/libpas/src/libpas/pas_page_malloc.c b/Source/bmalloc/libpas/src/libpas/pas_page_malloc.c index 3f21987adef5..23fab5ff3de6 100644 --- a/Source/bmalloc/libpas/src/libpas/pas_page_malloc.c +++ b/Source/bmalloc/libpas/src/libpas/pas_page_malloc.c @@ -350,8 +350,9 @@ static void commit_impl(void* ptr, size_t size, bool do_mprotect, pas_mmap_capab #endif } -#if PAS_OS(LINUX) - PAS_SYSCALL(madvise(ptr, size, MADV_DODUMP)); +/* MADV_DODUMP removed: symmetric with MADV_DONTDUMP removal in decommit_impl. + MADV_DONTDUMP only reduces core dump size but requires kernel mmap_write_lock, + causing severe contention with concurrent GC threads. */ #elif PAS_OS(WINDOWS) /* Sometimes the returned memInfo.RegionSize < size, and VirtualAlloc can't span regions We loop to make sure we get the full requested range. */ @@ -415,7 +416,9 @@ static void decommit_impl(void* ptr, size_t size, PAS_SYSCALL(madvise(ptr, size, MADV_FREE)); #elif PAS_OS(LINUX) PAS_SYSCALL(madvise(ptr, size, MADV_DONTNEED)); - PAS_SYSCALL(madvise(ptr, size, MADV_DONTDUMP)); + /* MADV_DONTDUMP removed: it only reduces core dump size but requires + kernel mmap_write_lock, causing severe contention with concurrent + GC threads. MADV_DONTNEED (above) only needs mmap_read_lock. */ #elif PAS_OS(WINDOWS) // DiscardVirtualMemory returns memory to the OS faster, but fails sometimes on Windows 10 // Fall back to VirtualAlloc in those cases diff --git a/Source/bmalloc/libpas/src/libpas/pas_utils.h b/Source/bmalloc/libpas/src/libpas/pas_utils.h index 7036db695b81..6f6eae0b6d75 100644 --- a/Source/bmalloc/libpas/src/libpas/pas_utils.h +++ b/Source/bmalloc/libpas/src/libpas/pas_utils.h @@ -46,6 +46,10 @@ #include #include +#if !PAS_OS(WINDOWS) +#include +#endif + #if PAS_OS(WINDOWS) #include #endif @@ -1263,8 +1267,17 @@ static inline bool pas_is_divisible_by(unsigned value, uint64_t magic_constant) enum cpp_initialization_t { cpp_initialization }; #endif +/* Retry syscalls on EAGAIN with 1ms backoff to avoid busy-spinning. + madvise(MADV_DONTDUMP) can return EAGAIN under kernel mmap_write_lock + contention, causing 100% CPU usage with concurrent GC threads. + Cap retries at 100 (100ms total) as a safety net — madvise failures + here are advisory, not fatal. */ #define PAS_SYSCALL(x) do { \ - while ((x) == -1 && errno == EAGAIN) { } \ + int _pas_syscall_tries = 0; \ + while ((x) == -1 && errno == EAGAIN) { \ + if (++_pas_syscall_tries > 100) break; \ + usleep(1000); \ + } \ } while (0) PAS_END_EXTERN_C;