diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -32,6 +32,8 @@ namespace scudo { +enum class Option { PrimaryReleaseInterval, SecondaryReleaseInterval }; + template class Allocator { public: @@ -143,9 +145,8 @@ static_cast(getFlags()->quarantine_max_chunk_size); Stats.initLinkerInitialized(); - const s32 ReleaseToOsIntervalMs = getFlags()->release_to_os_interval_ms; - Primary.initLinkerInitialized(ReleaseToOsIntervalMs); - Secondary.initLinkerInitialized(&Stats, ReleaseToOsIntervalMs); + Primary.initLinkerInitialized(getFlags()->primary_release_to_os_interval_ms); + Secondary.initLinkerInitialized(&Stats, getFlags()->secondary_release_to_os_interval_ms); Quarantine.init( static_cast(getFlags()->quarantine_size_kb << 10), @@ -613,8 +614,16 @@ return Options.MayReturnNull; } - // TODO(kostyak): implement this as a "backend" to mallopt. - bool setOption(UNUSED uptr Option, UNUSED uptr Value) { return false; } + bool setOption(Option O, sptr Value) { + if (O == Option::PrimaryReleaseInterval) { + Primary.setReleaseToOsIntervalMs(Value); + return true; + } else if (O == Option::SecondaryReleaseInterval) { + Secondary.setReleaseToOsIntervalMs(Value); + return true; + } + return false; + } // Return the usable size for a given chunk. Technically we lie, as we just // report the actual size of a chunk. This is done to counteract code actively diff --git a/compiler-rt/lib/scudo/standalone/flags.inc b/compiler-rt/lib/scudo/standalone/flags.inc --- a/compiler-rt/lib/scudo/standalone/flags.inc +++ b/compiler-rt/lib/scudo/standalone/flags.inc @@ -45,6 +45,12 @@ "returning NULL in otherwise non-fatal error scenarios, eg: OOM, " "invalid allocation alignments, etc.") -SCUDO_FLAG(int, release_to_os_interval_ms, SCUDO_ANDROID ? 1000 : 5000, +SCUDO_FLAG(int, primary_release_to_os_interval_ms, SCUDO_ANDROID ? 1000 : 5000, "Interval (in milliseconds) at which to attempt release of unused " - "memory to the OS. Negative values disable the feature.") + "memory to the OS in the primary. Negative values disable the " + "feature.") + +SCUDO_FLAG(int, secondary_release_to_os_interval_ms, SCUDO_ANDROID ? 0 : 5000, + "Interval (in milliseconds) at which to attempt release of unused " + "memory to the OS in the secondary. Negative values disable the " + "feature.") diff --git a/compiler-rt/lib/scudo/standalone/primary32.h b/compiler-rt/lib/scudo/standalone/primary32.h --- a/compiler-rt/lib/scudo/standalone/primary32.h +++ b/compiler-rt/lib/scudo/standalone/primary32.h @@ -76,7 +76,7 @@ Sci->CanRelease = (I != SizeClassMap::BatchClassId) && (getSizeByClassId(I) >= (PageSize / 32)); } - ReleaseToOsIntervalMs = ReleaseToOsInterval; + setReleaseToOsIntervalMs(ReleaseToOsInterval); } void init(s32 ReleaseToOsInterval) { memset(this, 0, sizeof(*this)); @@ -174,6 +174,10 @@ getStats(Str, I, 0); } + void setReleaseToOsIntervalMs(s32 Interval) { + atomic_store(&ReleaseToOsIntervalMs, Interval, memory_order_relaxed); + } + uptr releaseToOS() { uptr TotalReleasedBytes = 0; for (uptr I = 0; I < NumClasses; I++) { @@ -356,6 +360,10 @@ AvailableChunks, Rss >> 10); } + s32 getReleaseToOsIntervalMs() { + return atomic_load(&ReleaseToOsIntervalMs, memory_order_relaxed); + } + NOINLINE uptr releaseToOSMaybe(SizeClassInfo *Sci, uptr ClassId, bool Force = false) { const uptr BlockSize = getSizeByClassId(ClassId); @@ -374,7 +382,7 @@ } if (!Force) { - const s32 IntervalMs = ReleaseToOsIntervalMs; + const s32 IntervalMs = getReleaseToOsIntervalMs(); if (IntervalMs < 0) return 0; if (Sci->ReleaseInfo.LastReleaseAtNs + @@ -412,7 +420,7 @@ // through the whole NumRegions. uptr MinRegionIndex; uptr MaxRegionIndex; - s32 ReleaseToOsIntervalMs; + atomic_s32 ReleaseToOsIntervalMs; // Unless several threads request regions simultaneously from different size // classes, the stash rarely contains more than 1 entry. static constexpr uptr MaxStashedRegions = 4; diff --git a/compiler-rt/lib/scudo/standalone/primary64.h b/compiler-rt/lib/scudo/standalone/primary64.h --- a/compiler-rt/lib/scudo/standalone/primary64.h +++ b/compiler-rt/lib/scudo/standalone/primary64.h @@ -90,7 +90,7 @@ (getSizeByClassId(I) >= (PageSize / 32)); Region->RandState = getRandomU32(&Seed); } - ReleaseToOsIntervalMs = ReleaseToOsInterval; + setReleaseToOsIntervalMs(ReleaseToOsInterval); if (SupportsMemoryTagging) UseMemoryTagging = systemSupportsMemoryTagging(); @@ -186,6 +186,10 @@ getStats(Str, I, 0); } + void setReleaseToOsIntervalMs(s32 Interval) { + atomic_store(&ReleaseToOsIntervalMs, Interval, memory_order_relaxed); + } + uptr releaseToOS() { uptr TotalReleasedBytes = 0; for (uptr I = 0; I < NumClasses; I++) { @@ -241,7 +245,7 @@ uptr PrimaryBase; RegionInfo *RegionInfoArray; MapPlatformData Data; - s32 ReleaseToOsIntervalMs; + atomic_s32 ReleaseToOsIntervalMs; bool UseMemoryTagging; RegionInfo *getRegionInfo(uptr ClassId) const { @@ -375,6 +379,10 @@ getRegionBaseByClassId(ClassId)); } + s32 getReleaseToOsIntervalMs() { + return atomic_load(&ReleaseToOsIntervalMs, memory_order_relaxed); + } + NOINLINE uptr releaseToOSMaybe(RegionInfo *Region, uptr ClassId, bool Force = false) { const uptr BlockSize = getSizeByClassId(ClassId); @@ -394,7 +402,7 @@ } if (!Force) { - const s32 IntervalMs = ReleaseToOsIntervalMs; + const s32 IntervalMs = getReleaseToOsIntervalMs(); if (IntervalMs < 0) return 0; if (Region->ReleaseInfo.LastReleaseAtNs + diff --git a/compiler-rt/lib/scudo/standalone/secondary.h b/compiler-rt/lib/scudo/standalone/secondary.h --- a/compiler-rt/lib/scudo/standalone/secondary.h +++ b/compiler-rt/lib/scudo/standalone/secondary.h @@ -71,7 +71,7 @@ static_assert(!SCUDO_FUCHSIA || MaxEntriesCount == 0U, ""); void initLinkerInitialized(s32 ReleaseToOsInterval) { - ReleaseToOsIntervalMs = ReleaseToOsInterval; + setReleaseToOsIntervalMs(ReleaseToOsInterval); } void init(s32 ReleaseToOsInterval) { memset(this, 0, sizeof(*this)); @@ -105,11 +105,11 @@ } } } + s32 Interval; if (EmptyCache) empty(); - else if (ReleaseToOsIntervalMs >= 0) - releaseOlderThan(Time - - static_cast(ReleaseToOsIntervalMs) * 1000000); + else if ((Interval = getReleaseToOsIntervalMs()) >= 0) + releaseOlderThan(Time - static_cast(Interval) * 1000000); return EntryCached; } @@ -142,6 +142,10 @@ return MaxEntriesCount != 0U && Size <= MaxEntrySize; } + void setReleaseToOsIntervalMs(s32 Interval) { + atomic_store(&ReleaseToOsIntervalMs, Interval, memory_order_relaxed); + } + void releaseToOS() { releaseOlderThan(UINT64_MAX); } void disable() { Mutex.lock(); } @@ -189,6 +193,10 @@ } } + s32 getReleaseToOsIntervalMs() { + return atomic_load(&ReleaseToOsIntervalMs, memory_order_relaxed); + } + struct CachedBlock { uptr Block; uptr BlockEnd; @@ -203,7 +211,7 @@ u32 EntriesCount; uptr LargestSize; u32 IsFullEvents; - s32 ReleaseToOsIntervalMs; + atomic_s32 ReleaseToOsIntervalMs; }; template class MapAllocator { @@ -251,6 +259,10 @@ static uptr canCache(uptr Size) { return CacheT::canCache(Size); } + void setReleaseToOsIntervalMs(s32 Interval) { + Cache.setReleaseToOsIntervalMs(Interval); + } + void releaseToOS() { Cache.releaseToOS(); } private: diff --git a/compiler-rt/lib/scudo/standalone/wrappers_c.inc b/compiler-rt/lib/scudo/standalone/wrappers_c.inc --- a/compiler-rt/lib/scudo/standalone/wrappers_c.inc +++ b/compiler-rt/lib/scudo/standalone/wrappers_c.inc @@ -155,9 +155,26 @@ SCUDO_PREFIX(malloc_enable)); } -INTERFACE WEAK int SCUDO_PREFIX(mallopt)(int param, UNUSED int value) { +INTERFACE WEAK int SCUDO_PREFIX(mallopt)(int param, int value) { if (param == M_DECAY_TIME) { - // TODO(kostyak): set release_to_os_interval_ms accordingly. + int secondary_interval; + if (SCUDO_ANDROID) { + if (value == 0) { + // Never go below 1 second for the primary. + value = 1000; + secondary_interval = 0; + } else { + value = 1000; + secondary_interval = 1000; + } + } else { + secondary_interval = value; + } + + SCUDO_ALLOCATOR.setOption(scudo::Option::PrimaryReleaseInterval, + static_cast(value)); + SCUDO_ALLOCATOR.setOption(scudo::Option::SecondaryReleaseInterval, + static_cast(secondary_interval)); return 1; } else if (param == M_PURGE) { SCUDO_ALLOCATOR.releaseToOS();