1
0
mirror of https://github.com/roytam1/UXP.git synced 2026-05-26 13:58:49 +00:00

Revert "Issue #3092 - Initial idle GC implementation"

This reverts commit fba218cff4e46233a65e7a6f1b52f3c154523981.
This commit is contained in:
Basilisk-Dev
2026-05-24 14:54:58 -04:00
committed by roytam1
parent 3ae3595f6a
commit 860d4457ae
8 changed files with 0 additions and 479 deletions
-11
View File
@@ -13,7 +13,6 @@
#include "jsgc.h"
#include "gc/Heap.h"
#include "gc/IdleGC.h"
#include "gc/Nursery.h"
#include "gc/Statistics.h"
#include "gc/StoreBuffer.h"
@@ -651,13 +650,6 @@ class GCRuntime
void onOutOfMallocMemory();
void onOutOfMallocMemory(const AutoLockGC& lock);
/* Idle-time GC notifications. */
void notifyJSExecutionStart();
void notifyJSExecutionEnd();
IdleGCManager& idleGCMgr() {
return idleGC;
}
size_t maxMallocBytesAllocated() { return maxMallocBytes; }
uint64_t nextCellUniqueId() {
@@ -1032,9 +1024,6 @@ class GCRuntime
GCSchedulingTunables tunables;
GCSchedulingState schedulingState;
/* Idle-time garbage collection manager. */
IdleGCManager idleGC;
MemProfiler mMemProfiler;
private:
-262
View File
@@ -1,262 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* IDLE-TIME GARBAGE COLLECTION SYSTEM
*
* Overview
* --------
* This system implements idle-time-only garbage collection, deferring GC work
* to periods when the JavaScript engine is not actively processing code.
* This improves perceived responsiveness by avoiding GC pauses during critical
* execution windows.
*
* Architecture
* -----------
*
* The system consists of several key components:
*
* 1. IdleGCManager (gc/IdleGC.h, gc/IdleGC.cpp)
* - Tracks when the JS engine is executing vs. idle
* - Maintains timestamp of last execution activity
* - Checks if sufficient idle time has passed
* - Configurable idle threshold (default: 100ms)
* - Can determine which GC reasons should bypass idle checks
*
* 2. GCRuntime Integration (gc/GCRuntime.h)
* - Contains an IdleGCManager instance
* - Provides public methods: notifyJSExecutionStart/End()
* - Modified checkIfGCAllowedInCurrentState() to check idle status
*
* 3. Activity Tracking (vm/Runtime.cpp)
* - triggerActivityCallback() now notifies IdleGCManager
* - Called when JS enters/exits request (execution boundary)
* - Updated in jsapi.cpp's StartRequest/StopRequest functions
*
* 4. Public API (jsapi.h, jsapi.cpp)
* - JS_SetIdleGCEnabled() / JS_IsIdleGCEnabled()
* - JS_SetIdleGCThreshold() / JS_GetIdleGCThreshold()
* - JS_GetIdleTimeSinceLastExecution()
* - Allows embedders to configure idle GC behavior
*
* Behavior
* --------
*
* Normal GC Trigger (Idle GC Enabled):
*
* 1. JS code executes → notifyJSExecutionStart() called
* 2. JS code finishes → notifyJSExecutionEnd() called, timestamp recorded
* 3. GC needed → checkIfGCAllowedInCurrentState() checks idle status
* 4a. If idle >= threshold → GC proceeds normally
* 4b. If still executing/idle < threshold → GC is deferred
* 5. Once idle threshold is met, next GC request proceeds
*
* Critical GC Triggers (Always Proceed):
*
* The following GC reasons bypass idle checking:
* - OUT_OF_MEMORY: Memory pressure conditions
* - ALLOC_TRIGGER: Allocation threshold exceeded
* - MALLOC_PRESSURE: Malloc pressure from OS
* - EAGER_ALLOC_TRIGGER: Eager allocation trigger
* - API: Explicit JS API calls
* - DETERMINISTIC: Deterministic tests
* - EVICT_NURSERY: Nursery eviction
* - SHUTDOWN_CC, DESTROY_RUNTIME, LAST_DITCH: Shutdown GCs
* - DESTROY_ZONE, COMPARTMENT_REVOKED: Zone/compartment destruction
*
* Configuration
* -------------
*
* Idle GC Enabled (default: true):
* - Enables idle-time-only GC mode
* - Can be toggled dynamically via JS_SetIdleGCEnabled()
*
* Idle Threshold (default: 100ms):
* - Minimum idle time before GC is permitted
* - Configurable via JS_SetIdleGCThreshold(ms)
* - Typical values: 50-200ms depending on application
*
* Integration Examples
* --------------------
*
* Browser Integration:
*
* // When browser loads configuration
* JS_SetIdleGCEnabled(cx, true);
* JS_SetIdleGCThreshold(cx, 100); // 100ms idle threshold
*
* // Monitor idle GC effectiveness (optional)
* uint64_t idleTime = JS_GetIdleTimeSinceLastExecution(cx);
* if (idleTime > JS_GetIdleGCThreshold(cx)) {
* // System is idle, GC would be allowed if triggered
* }
*
* Disabling for Specific Scenarios:
*
* // During initialization when nothing is "idle" yet
* JS_SetIdleGCEnabled(cx, false);
* // ... do initial setup ...
* JS_SetIdleGCEnabled(cx, true); // Re-enable for normal operation
*
* Performance Considerations
* --------------------------
*
* Benefits:
* - Reduced jank during active JS execution
* - GC pauses moved to idle periods where users won't notice
* - Especially effective for interactive applications
* - Improves Time-to-Interactive and First Input Delay metrics
*
* Tradeoffs:
* - May accumulate more garbage before collection
* - Requires predictable idle periods (not suitable for all workloads)
* - Critical memory pressure GCs still proceed immediately
*
* Tuning:
* - Lower threshold (50ms) = more frequent GC, less memory overhead
* - Higher threshold (200ms) = less GC overhead, more memory usage
* - Optimal value depends on application's execution pattern
*
* Testing
* -------
*
* Unit Tests:
* // Test idle detection
* JS_SetIdleGCThreshold(cx, 100);
* // Simulate JS execution
* cx->runtime()->gc.notifyJSExecutionStart();
* // ... wait 50ms ...
* MOZ_ASSERT(!cx->runtime()->gc.idleGCMgr().isIdleEnough());
* cx->runtime()->gc.notifyJSExecutionEnd();
* // ... wait 150ms ...
* MOZ_ASSERT(cx->runtime()->gc.idleGCMgr().isIdleEnough());
*
* Integration Tests:
* - Verify GC is deferred during active execution
* - Verify GC proceeds after idle period
* - Verify critical GC reasons bypass idle check
* - Measure latency improvements
*
* Implementation Notes
* --------------------
*
* Thread Safety:
* - IdleGCManager uses mozilla::Atomic for thread-safe state
* - TimeStamp operations are atomic
* - No additional locking needed beyond existing GC locks
*
* Compatibility:
* - Works with both incremental and non-incremental GC
* - Compatible with generational GC
* - Works with zone GC and full GC
* - Respects existing GC suppression mechanisms
*
* Future Enhancements
* -------------------
*
* Potential improvements:
* - Adaptive idle threshold based on historical GC times
* - Per-zone idle configuration
* - Integration with browser rendering idle callback API
* - Metrics/telemetry for idle GC effectiveness
* - Machine learning-based prediction of idle periods
* - Cooperative GC scheduling with other subsystems
*
*/
#include "gc/IdleGC.h"
#include "jsapi.h"
namespace js {
namespace gc {
IdleGCManager::IdleGCManager()
: lastExecutionTime_(mozilla::TimeStamp::Now()),
idleGCEnabled_(true),
idleThresholdMs_(100), // 100ms default idle threshold
isExecuting_(false)
{
}
void
IdleGCManager::notifyJSExecutionStart()
{
isExecuting_ = true;
}
void
IdleGCManager::notifyJSExecutionEnd()
{
isExecuting_ = false;
lastExecutionTime_ = mozilla::TimeStamp::Now();
}
bool
IdleGCManager::isIdleEnough() const
{
if (!idleGCEnabled_) {
return true; // If disabled, always consider idle
}
if (isExecuting_) {
return false; // Still executing, not idle
}
uint64_t idleTime = idleTimeSinceLastExecution();
return idleTime >= idleThresholdMs_;
}
uint64_t
IdleGCManager::idleTimeSinceLastExecution() const
{
mozilla::TimeStamp now = mozilla::TimeStamp::Now();
mozilla::TimeDuration idle = now - lastExecutionTime_;
return idle.ToMilliseconds();
}
bool
IdleGCManager::shouldBypassIdleCheck(JS::gcreason::Reason reason)
{
// These reasons indicate urgent GC needs that should bypass idle checking
switch (reason) {
// Allocation and memory pressure conditions.
case JS::gcreason::ALLOC_TRIGGER:
case JS::gcreason::EAGER_ALLOC_TRIGGER:
case JS::gcreason::TOO_MUCH_MALLOC:
case JS::gcreason::MEM_PRESSURE:
case JS::gcreason::LAST_DITCH:
// Nursery/store-buffer pressure.
case JS::gcreason::OUT_OF_NURSERY:
case JS::gcreason::EVICT_NURSERY:
case JS::gcreason::FULL_STORE_BUFFER:
case JS::gcreason::SHARED_MEMORY_LIMIT:
// Explicit API calls
case JS::gcreason::API:
case JS::gcreason::ABORT_GC:
// Shutdown and finalization
case JS::gcreason::SHUTDOWN_CC:
case JS::gcreason::DESTROY_RUNTIME:
case JS::gcreason::NSJSCONTEXT_DESTROY:
case JS::gcreason::XPCONNECT_SHUTDOWN:
return true;
default:
return false;
}
}
void
IdleGCManager::reset()
{
lastExecutionTime_ = mozilla::TimeStamp::Now();
isExecuting_ = false;
}
} // namespace gc
} // namespace js
-118
View File
@@ -1,118 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef gc_IdleGC_h
#define gc_IdleGC_h
#include <stdint.h>
#include "mozilla/Atomics.h"
#include "mozilla/TimeStamp.h"
#include "js/GCAPI.h"
namespace js {
namespace gc {
/*
* Idle-Time Garbage Collection System
* ====================================
*
* This system defers garbage collection to occur only during periods when
* the browser is not actively processing JavaScript. This helps maintain
* responsiveness by avoiding GC pauses during critical execution windows.
*
* When JavaScript execution is active, GC triggers are deferred. Once the
* JS engine has been idle for a configurable threshold period, pending GC
* work is performed immediately or incrementally as appropriate.
*
* Key characteristics:
* - Tracks JavaScript activity via hooks in the execution engine
* - Configurable idle time threshold (default: 100ms)
* - Can be disabled per-zone or globally
* - Works with both incremental and non-incremental GC modes
* - Respects critical GC reasons that override idle checking
*/
class IdleGCManager
{
public:
// Initialize the idle GC manager
IdleGCManager();
/*
* Called when JavaScript execution begins. Marks the engine as active.
*/
void notifyJSExecutionStart();
/*
* Called when JavaScript execution ends. Records the end time for
* idle detection purposes.
*/
void notifyJSExecutionEnd();
/*
* Check if the system has been idle for long enough to permit GC.
* Returns true if sufficient idle time has passed since last JS execution.
*/
bool isIdleEnough() const;
/*
* Get the amount of idle time since the last JS execution.
* Returns time in milliseconds.
*/
uint64_t idleTimeSinceLastExecution() const;
/*
* Set the idle threshold - minimum idle time before GC is permitted.
* Time is in milliseconds. Default is 100ms.
*/
void setIdleThresholdMs(uint64_t thresholdMs) {
idleThresholdMs_ = thresholdMs;
}
uint64_t idleThresholdMs() const {
return idleThresholdMs_;
}
/*
* Enable or disable idle-time-only GC mode.
*/
void setIdleGCEnabled(bool enabled) {
idleGCEnabled_ = enabled;
}
bool isIdleGCEnabled() const {
return idleGCEnabled_;
}
/*
* Check if a GC reason should bypass idle checking.
* Critical reasons (OOM-like pressure, nursery pressure, explicit requests)
* always proceed.
*/
static bool shouldBypassIdleCheck(JS::gcreason::Reason reason);
/*
* Reset idle tracking state (used during GC or at shutdown).
*/
void reset();
private:
// Timestamp of the last JavaScript execution activity
mozilla::TimeStamp lastExecutionTime_;
// Whether idle-time-only GC mode is enabled
mozilla::Atomic<bool, mozilla::ReleaseAcquire> idleGCEnabled_;
// Minimum idle time (in milliseconds) before GC is permitted
mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> idleThresholdMs_;
// Whether the JS engine is currently executing
mozilla::Atomic<bool, mozilla::ReleaseAcquire> isExecuting_;
};
} // namespace gc
} // namespace js
#endif // gc_IdleGC_h
-30
View File
@@ -1487,36 +1487,6 @@ JS_SetGCParametersBasedOnAvailableMemory(JSContext* cx, uint32_t availMem)
JS_SetGCParameter(cx, config[i].key, config[i].value);
}
JS_PUBLIC_API(void)
JS_SetIdleGCEnabled(JSContext* cx, bool enabled)
{
cx->gc.idleGCMgr().setIdleGCEnabled(enabled);
}
JS_PUBLIC_API(bool)
JS_IsIdleGCEnabled(JSContext* cx)
{
return cx->gc.idleGCMgr().isIdleGCEnabled();
}
JS_PUBLIC_API(void)
JS_SetIdleGCThreshold(JSContext* cx, uint64_t milliseconds)
{
cx->gc.idleGCMgr().setIdleThresholdMs(milliseconds);
}
JS_PUBLIC_API(uint64_t)
JS_GetIdleGCThreshold(JSContext* cx)
{
return cx->gc.idleGCMgr().idleThresholdMs();
}
JS_PUBLIC_API(uint64_t)
JS_GetIdleTimeSinceLastExecution(JSContext* cx)
{
return cx->gc.idleGCMgr().idleTimeSinceLastExecution();
}
JS_PUBLIC_API(JSString*)
JS_NewExternalString(JSContext* cx, const char16_t* chars, size_t length,
-34
View File
@@ -1724,40 +1724,6 @@ JS_GetGCParameter(JSContext* cx, JSGCParamKey key);
extern JS_PUBLIC_API(void)
JS_SetGCParametersBasedOnAvailableMemory(JSContext* cx, uint32_t availMem);
/*
* Idle-time garbage collection control.
* These functions allow control over when GC occurs - specifically, whether
* GC should only run when the browser/application is not doing active JS work.
*/
/**
* Enable or disable idle-time-only GC mode.
* When enabled, GC is deferred until the JS engine has been idle for the
* configured threshold period (default: 100ms).
*/
extern JS_PUBLIC_API(void)
JS_SetIdleGCEnabled(JSContext* cx, bool enabled);
extern JS_PUBLIC_API(bool)
JS_IsIdleGCEnabled(JSContext* cx);
/**
* Set the minimum idle time (in milliseconds) before GC is permitted.
* Use this to configure how long the JS engine must be inactive before
* pending GC work can proceed.
*/
extern JS_PUBLIC_API(void)
JS_SetIdleGCThreshold(JSContext* cx, uint64_t milliseconds);
extern JS_PUBLIC_API(uint64_t)
JS_GetIdleGCThreshold(JSContext* cx);
/**
* Get the current idle time since the last JS execution.
*/
extern JS_PUBLIC_API(uint64_t)
JS_GetIdleTimeSinceLastExecution(JSContext* cx);
/**
* Create a new JSString whose chars member refers to external memory, i.e.,
* memory requiring application-specific finalization.
-17
View File
@@ -215,7 +215,6 @@
#include "gc/FindSCCs.h"
#include "gc/GCInternals.h"
#include "gc/GCTrace.h"
#include "gc/IdleGC.h"
#include "gc/Marking.h"
#include "gc/Memory.h"
#include "gc/Policy.h"
@@ -6068,10 +6067,6 @@ GCRuntime::checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason)
if (rt->isBeingDestroyed() && !IsShutdownGC(reason))
return false;
// Check if the system is idle enough for GC, unless this is a critical GC
if (!IdleGCManager::shouldBypassIdleCheck(reason) && !idleGC.isIdleEnough())
return false;
return true;
}
@@ -6245,18 +6240,6 @@ GCRuntime::notifyDidPaint()
interFrameGC = false;
}
void
GCRuntime::notifyJSExecutionStart()
{
idleGC.notifyJSExecutionStart();
}
void
GCRuntime::notifyJSExecutionEnd()
{
idleGC.notifyJSExecutionEnd();
}
static bool
ZonesSelected(JSRuntime* rt)
{
-1
View File
@@ -173,7 +173,6 @@ main_deunified_sources = [
'gc/Allocator.cpp',
'gc/Barrier.cpp',
'gc/GCTrace.cpp',
'gc/IdleGC.cpp',
'gc/Iteration.cpp',
'gc/Marking.cpp',
'gc/Memory.cpp',
-6
View File
@@ -801,12 +801,6 @@ JSRuntime::traceSharedIntlData(JSTracer* trc)
void
JSRuntime::triggerActivityCallback(bool active)
{
if (active) {
gc.notifyJSExecutionStart();
} else {
gc.notifyJSExecutionEnd();
}
if (!activityCallback)
return;