diff --git a/third_party/blink/renderer/core/dom/abort_controller.cc b/third_party/blink/renderer/core/dom/abort_controller.cc index dad1894e98794..8bed990d3895f 100644 --- a/third_party/blink/renderer/core/dom/abort_controller.cc +++ b/third_party/blink/renderer/core/dom/abort_controller.cc @@ -8,7 +8,6 @@ #include "third_party/blink/renderer/core/dom/abort_signal.h" #include "third_party/blink/renderer/platform/bindings/exception_code.h" #include "third_party/blink/renderer/platform/heap/visitor.h" -#include "third_party/blink/renderer/platform/runtime_enabled_features.h" namespace blink { @@ -22,12 +21,6 @@ AbortController::AbortController(AbortSignal* signal) : signal_(signal) {} AbortController::~AbortController() = default; -void AbortController::Dispose() { - if (RuntimeEnabledFeatures::AbortSignalAnyEnabled()) { - signal_->DetachFromController(); - } -} - void AbortController::abort(ScriptState* script_state) { v8::Local dom_exception = V8ThrowDOMException::CreateOrEmpty( script_state->GetIsolate(), DOMExceptionCode::kAbortError, diff --git a/third_party/blink/renderer/core/dom/abort_controller.h b/third_party/blink/renderer/core/dom/abort_controller.h index 4ac396d7b8a95..649fda0f6784e 100644 --- a/third_party/blink/renderer/core/dom/abort_controller.h +++ b/third_party/blink/renderer/core/dom/abort_controller.h @@ -8,7 +8,6 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/heap/member.h" -#include "third_party/blink/renderer/platform/heap/prefinalizer.h" namespace blink { @@ -21,7 +20,6 @@ class ScriptValue; // https://docs.google.com/document/d/1OuoCG2uiijbAwbCw9jaS7tHEO0LBO_4gMNio1ox0qlY/edit class CORE_EXPORT AbortController : public ScriptWrappable { DEFINE_WRAPPERTYPEINFO(); - USING_PRE_FINALIZER(AbortController, Dispose); public: static AbortController* Create(ExecutionContext*); @@ -38,8 +36,6 @@ class CORE_EXPORT AbortController : public ScriptWrappable { void abort(ScriptState*); void abort(ScriptState*, ScriptValue reason); - void Dispose(); - void Trace(Visitor*) const override; private: diff --git a/third_party/blink/renderer/core/dom/abort_signal.cc b/third_party/blink/renderer/core/dom/abort_signal.cc index 5649840994e7b..2c079655e95ed 100644 --- a/third_party/blink/renderer/core/dom/abort_signal.cc +++ b/third_party/blink/renderer/core/dom/abort_signal.cc @@ -98,8 +98,6 @@ class RemovableAbortAlgorithmCollection final void Clear() override { abort_algorithms_.clear(); } - bool Empty() const override { return abort_algorithms_.empty(); } - void Run() override { for (AbortSignal::AlgorithmHandle* handle : abort_algorithms_) { handle->GetAlgorithm()->Run(); @@ -138,8 +136,6 @@ class UnremovableAbortAlgorithmCollection final void Clear() override { abort_algorithms_.clear(); } - bool Empty() const override { return abort_algorithms_.empty(); } - void Run() override { for (AbortSignal::Algorithm* algorithm : abort_algorithms_) { algorithm->Run(); @@ -205,13 +201,6 @@ void AbortSignal::InitializeCommon(ExecutionContext* execution_context, abort_algorithms_ = MakeGarbageCollected(); } - - if (RuntimeEnabledFeatures::AbortSignalAnyEnabled() && - signal_type_ == AbortSignal::SignalType::kComposite) { - // Composite signals need to be kept alive when they have relevant event - // listeners or pending algorithms. - RegisterActiveScriptWrappable(); - } } AbortSignal::~AbortSignal() = default; @@ -232,9 +221,6 @@ AbortSignal* AbortSignal::abort(ScriptState* script_state, ScriptValue reason) { AbortSignal* signal = MakeGarbageCollected( ExecutionContext::From(script_state), SignalType::kAborted); signal->abort_reason_ = reason; - if (RuntimeEnabledFeatures::AbortSignalAnyEnabled()) { - signal->composition_manager_->Settle(); - } return signal; } @@ -305,29 +291,24 @@ ExecutionContext* AbortSignal::GetExecutionContext() const { } AbortSignal::AlgorithmHandle* AbortSignal::AddAlgorithm(Algorithm* algorithm) { - if (aborted() || (RuntimeEnabledFeatures::AbortSignalAnyEnabled() && - composition_manager_->IsSettled())) { + if (aborted()) return nullptr; - } + auto* handle = MakeGarbageCollected(algorithm); abort_algorithms_->AddAlgorithm(handle); return handle; } void AbortSignal::RemoveAlgorithm(AlgorithmHandle* handle) { - if (aborted() || (RuntimeEnabledFeatures::AbortSignalAnyEnabled() && - composition_manager_->IsSettled())) { + if (aborted()) return; - } abort_algorithms_->RemoveAlgorithm(handle); } AbortSignal::AlgorithmHandle* AbortSignal::AddAlgorithm( base::OnceClosure algorithm) { - if (aborted() || (RuntimeEnabledFeatures::AbortSignalAnyEnabled() && - composition_manager_->IsSettled())) { + if (aborted()) return nullptr; - } auto* callback_algorithm = MakeGarbageCollected(std::move(algorithm)); auto* handle = MakeGarbageCollected(callback_algorithm); @@ -358,10 +339,7 @@ void AbortSignal::SignalAbort(ScriptState* script_state, ScriptValue reason) { abort_reason_ = reason; } abort_algorithms_->Run(); - if (!RuntimeEnabledFeatures::AbortSignalAnyEnabled()) { - // This is cleared when the signal is settled when the feature is enabled. - abort_algorithms_->Clear(); - } + abort_algorithms_->Clear(); dependent_signal_algorithms_.clear(); DispatchEvent(*Event::Create(event_type_names::kAbort)); @@ -377,7 +355,6 @@ void AbortSignal::SignalAbort(ScriptState* script_state, ScriptValue reason) { signal->SignalAbort(script_state, abort_reason_); } } - composition_manager_->Settle(); } } @@ -412,34 +389,6 @@ AbortSignalCompositionManager* AbortSignal::GetCompositionManager( return nullptr; } -void AbortSignal::DetachFromController() { - DCHECK(RuntimeEnabledFeatures::AbortSignalAnyEnabled()); - if (aborted()) { - return; - } - composition_manager_->Settle(); -} - -void AbortSignal::OnSignalSettled(AbortSignalCompositionType type) { - DCHECK(RuntimeEnabledFeatures::AbortSignalAnyEnabled()); - DCHECK_EQ(type, AbortSignalCompositionType::kAbort); - abort_algorithms_->Clear(); -} - -bool AbortSignal::HasPendingActivity() const { - if (signal_type_ != SignalType::kComposite) { - return false; - } - DCHECK(RuntimeEnabledFeatures::AbortSignalAnyEnabled()); - // Settled signals cannot signal abort, so they can be GCed. - if (composition_manager_->IsSettled()) { - return false; - } - // Otherwise the signal needs to be kept alive if aborting can be observed. - return HasEventListeners(event_type_names::kAbort) || - !abort_algorithms_->Empty(); -} - AbortSignal::AlgorithmHandle::AlgorithmHandle(AbortSignal::Algorithm* algorithm) : algorithm_(algorithm) {} diff --git a/third_party/blink/renderer/core/dom/abort_signal.h b/third_party/blink/renderer/core/dom/abort_signal.h index ac87fa519825b..57910cfc3942c 100644 --- a/third_party/blink/renderer/core/dom/abort_signal.h +++ b/third_party/blink/renderer/core/dom/abort_signal.h @@ -6,7 +6,6 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_ABORT_SIGNAL_H_ #include "base/functional/callback_forward.h" -#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h" #include "third_party/blink/renderer/bindings/core/v8/script_value.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/abort_signal_composition_type.h" @@ -22,8 +21,7 @@ class ExecutionContext; class ScriptState; // Implementation of https://dom.spec.whatwg.org/#interface-AbortSignal -class CORE_EXPORT AbortSignal : public EventTargetWithInlineData, - public LazyActiveScriptWrappable { +class CORE_EXPORT AbortSignal : public EventTargetWithInlineData { DEFINE_WRAPPERTYPEINFO(); public: @@ -88,7 +86,6 @@ class CORE_EXPORT AbortSignal : public EventTargetWithInlineData, virtual void RemoveAlgorithm(AlgorithmHandle*) = 0; virtual void Clear() = 0; virtual void Run() = 0; - virtual bool Empty() const = 0; virtual void Trace(Visitor*) const {} }; @@ -118,7 +115,6 @@ class CORE_EXPORT AbortSignal : public EventTargetWithInlineData, const AtomicString& InterfaceName() const override; ExecutionContext* GetExecutionContext() const override; - bool HasPendingActivity() const override; // Internal API @@ -167,13 +163,6 @@ class CORE_EXPORT AbortSignal : public EventTargetWithInlineData, virtual AbortSignalCompositionManager* GetCompositionManager( AbortSignalCompositionType); - // Called by the composition manager when the signal is settled. - virtual void OnSignalSettled(AbortSignalCompositionType); - - // Callback from `AbortController` during prefinalization, when the controller - // can no longer emit events. - virtual void DetachFromController(); - private: // Common constructor initialization separated out to make mutually exclusive // constructors more readable. diff --git a/third_party/blink/renderer/core/dom/abort_signal.idl b/third_party/blink/renderer/core/dom/abort_signal.idl index 37d267b662fd0..fa2ebe8a311fb 100644 --- a/third_party/blink/renderer/core/dom/abort_signal.idl +++ b/third_party/blink/renderer/core/dom/abort_signal.idl @@ -5,8 +5,7 @@ // https://dom.spec.whatwg.org/#interface-AbortSignal [ - Exposed=(Window,Worker), - ActiveScriptWrappable + Exposed=(Window,Worker) ] interface AbortSignal : EventTarget { [ CallWith=ScriptState, diff --git a/third_party/blink/renderer/core/dom/abort_signal_composition_manager.cc b/third_party/blink/renderer/core/dom/abort_signal_composition_manager.cc index 41aed69889733..f07615bcc2aa7 100644 --- a/third_party/blink/renderer/core/dom/abort_signal_composition_manager.cc +++ b/third_party/blink/renderer/core/dom/abort_signal_composition_manager.cc @@ -26,13 +26,6 @@ void AbortSignalCompositionManager::Trace(Visitor* visitor) const { visitor->Trace(signal_); } -void AbortSignalCompositionManager::Settle() { - DCHECK(!is_settled_); - is_settled_ = true; - - signal_->OnSignalSettled(composition_type_); -} - DependentSignalCompositionManager::DependentSignalCompositionManager( AbortSignal& managed_signal, AbortSignalCompositionType type, @@ -52,10 +45,6 @@ DependentSignalCompositionManager::DependentSignalCompositionManager( AddSourceSignal(*source.Get()); } } - - if (source_signals_.empty()) { - Settle(); - } } DependentSignalCompositionManager::~DependentSignalCompositionManager() = @@ -67,15 +56,6 @@ void DependentSignalCompositionManager::Trace(Visitor* visitor) const { } void DependentSignalCompositionManager::AddSourceSignal(AbortSignal& source) { - auto* source_manager = To( - source.GetCompositionManager(GetCompositionType())); - DCHECK(source_manager); - // `source` won't emit `composition_type_` any longer, so there's no need to - // follow. This can happen if `source` is associated with a GCed controller. - if (source_manager->IsSettled()) { - return; - } - DCHECK(!source.IsCompositeSignal()); // Internal signals can add dependent signals after construction via // AbortSignal::Follow, which would violate our assumptions for @@ -90,27 +70,11 @@ void DependentSignalCompositionManager::AddSourceSignal(AbortSignal& source) { return; } source_signals_.insert(&source); - source_manager->AddDependentSignal(*this); -} -void DependentSignalCompositionManager::Settle() { - AbortSignalCompositionManager::Settle(); - source_signals_.clear(); -} - -void DependentSignalCompositionManager::OnSourceSettled( - SourceSignalCompositionManager& source_manager) { - DCHECK(GetSignal().IsCompositeSignal()); - DCHECK(!IsSettled()); - - // Note: `source_signals_` might not contain the source, and it might already - // be empty if this source was removed during prefinalization. That's okay -- - // we only need to detect that the collection is empty on this path (if the - // signal is being kept alive by the registry). - source_signals_.erase(&source_manager.GetSignal()); - if (source_signals_.empty()) { - Settle(); - } + auto* source_manager = To( + source.GetCompositionManager(GetCompositionType())); + DCHECK(source_manager); + source_manager->AddDependentSignal(*this); } SourceSignalCompositionManager::SourceSignalCompositionManager( @@ -127,8 +91,6 @@ void SourceSignalCompositionManager::Trace(Visitor* visitor) const { void SourceSignalCompositionManager::AddDependentSignal( DependentSignalCompositionManager& dependent_manager) { - DCHECK(!IsSettled()); - DCHECK(!dependent_manager.IsSettled()); DCHECK(dependent_manager.GetSignal().IsCompositeSignal()); // New dependents should not be added to aborted signals. DCHECK(GetCompositionType() != AbortSignalCompositionType::kAbort || @@ -137,22 +99,4 @@ void SourceSignalCompositionManager::AddDependentSignal( dependent_signals_.insert(&dependent_manager.GetSignal()); } -void SourceSignalCompositionManager::Settle() { - AbortSignalCompositionManager::Settle(); - - for (auto& signal : dependent_signals_) { - auto* manager = To( - signal->GetCompositionManager(GetCompositionType())); - DCHECK(manager); - // The signal might have been settled if its `source_signals_` were cleared - // during prefinalization and another source already notified it, or if the - // signal was aborted. - if (manager->IsSettled()) { - continue; - } - manager->OnSourceSettled(*this); - } - dependent_signals_.clear(); -} - } // namespace blink diff --git a/third_party/blink/renderer/core/dom/abort_signal_composition_manager.h b/third_party/blink/renderer/core/dom/abort_signal_composition_manager.h index e9e2e04379a70..5aaef2af0d7a8 100644 --- a/third_party/blink/renderer/core/dom/abort_signal_composition_manager.h +++ b/third_party/blink/renderer/core/dom/abort_signal_composition_manager.h @@ -32,17 +32,11 @@ class AbortSignal; // source or dependent, with composite signals being dependents and // non-composite signals being sources. // -// A signal is "settled" for a given event type (abort or prioritychange) when -// it can no longer emit that event, e.g. after abort or if a signal's -// controller is GCed. When all of a composite signal's sources are settled, it -// can be settled as well. -// // Source signals are stored weakly and can be either associated with a -// controller or timeout. Sources are removed when they're settled. +// controller or timeout. Sources are removed if the signal aborts. // -// Dependent signals are stored weakly, with `AbortSignalRegistry` used to store -// strong references when needed. This, along with detecting settled signals, -// ensures we only hold strong references to signals when necessary. +// Dependent signals are stored strongly since otherwise they could be GCed +// while they have observable effects. class CORE_EXPORT AbortSignalCompositionManager : public GarbageCollected { public: @@ -55,12 +49,6 @@ class CORE_EXPORT AbortSignalCompositionManager virtual void Trace(Visitor*) const; - // Settle `signal_`. This can be called by the signal or composition manager. - virtual void Settle(); - - // Returns true if `signal_` is settled for `composition_type_`. - bool IsSettled() const { return is_settled_; } - // Used for casting. virtual bool IsSourceSignalManager() const { return false; } virtual bool IsDependentSignalManager() const { return false; } @@ -75,7 +63,6 @@ class CORE_EXPORT AbortSignalCompositionManager private: Member signal_; AbortSignalCompositionType composition_type_; - bool is_settled_ = false; }; class DependentSignalCompositionManager; @@ -93,18 +80,17 @@ class CORE_EXPORT SourceSignalCompositionManager const SourceSignalCompositionManager&) = delete; void Trace(Visitor*) const override; - void Settle() override; bool IsSourceSignalManager() const override { return true; } void AddDependentSignal(DependentSignalCompositionManager&); - const HeapLinkedHashSet>& GetDependentSignals() { + const HeapLinkedHashSet>& GetDependentSignals() { return dependent_signals_; } private: - HeapLinkedHashSet> dependent_signals_; + HeapLinkedHashSet> dependent_signals_; }; // Manages composition for an `AbortSignal` that is dependent on zero or more @@ -123,7 +109,6 @@ class CORE_EXPORT DependentSignalCompositionManager const DependentSignalCompositionManager&) = delete; void Trace(Visitor*) const override; - void Settle() override; bool IsDependentSignalManager() const override { return true; } @@ -131,9 +116,6 @@ class CORE_EXPORT DependentSignalCompositionManager return source_signals_; } - // Callback invoked on this signal when `source` is settled. - void OnSourceSettled(SourceSignalCompositionManager& source); - private: void AddSourceSignal(AbortSignal&); diff --git a/third_party/blink/web_tests/wpt_internal/dom/abort/README.md b/third_party/blink/web_tests/wpt_internal/dom/abort/README.md index d8b9fb70e0cc5..998092f11f7f8 100644 --- a/third_party/blink/web_tests/wpt_internal/dom/abort/README.md +++ b/third_party/blink/web_tests/wpt_internal/dom/abort/README.md @@ -2,6 +2,3 @@ This directory contains a number of tests that check AbortSignal memory management using `gc()` to force a synchronous major GC. - -Most tests (see ./resources/) are parameterized with a signal and controller -interface so that the can be used by TaskSignal tests also. diff --git a/third_party/blink/web_tests/wpt_internal/dom/abort/abort-signal-memory-tests.any.js b/third_party/blink/web_tests/wpt_internal/dom/abort/abort-signal-memory-tests.any.js new file mode 100644 index 0000000000000..9d98e4d8f3ab1 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/dom/abort/abort-signal-memory-tests.any.js @@ -0,0 +1,18 @@ +test(t => { + let count = 0; + const controller = new AbortController(); + const signal = controller.signal; + addEventListener('test', () => { ++count; }, {signal}); + + // GC should not affect the event dispatch or listener removal below. + gc(); + + dispatchEvent(new Event('test')); + dispatchEvent(new Event('test')); + + assert_equals(count, 2); + + controller.abort(); + dispatchEvent(new Event('test')); + assert_equals(count, 2); +}, 'AbortSignalRegistry tracks algorithm handles for event listeners'); diff --git a/third_party/blink/web_tests/wpt_internal/dom/abort/abort-signal-memory-tests.https.any.js b/third_party/blink/web_tests/wpt_internal/dom/abort/abort-signal-memory-tests.https.any.js deleted file mode 100644 index 9beb627c388f5..0000000000000 --- a/third_party/blink/web_tests/wpt_internal/dom/abort/abort-signal-memory-tests.https.any.js +++ /dev/null @@ -1,65 +0,0 @@ -// META: script=./resources/abort-signal-any-memory-tests.js - -abortSignalAnyMemoryTests(AbortSignal, AbortController); - -test(t => { - let count = 0; - const controller = new AbortController(); - const signal = controller.signal; - addEventListener('test', () => { ++count; }, {signal}); - - // GC should not affect the event dispatch or listener removal below. - gc(); - - dispatchEvent(new Event('test')); - dispatchEvent(new Event('test')); - - assert_equals(count, 2); - - controller.abort(); - dispatchEvent(new Event('test')); - assert_equals(count, 2); -}, 'AbortSignalRegistry tracks algorithm handles for event listeners'); - -test(t => { - let count = 0; - const controller = new AbortController(); - - (function() { - let signal = AbortSignal.any([controller.signal]); - addEventListener('test2', () => { ++count; }, {signal}); - signal = null; - })(); - - dispatchEvent(new Event('test2')); - dispatchEvent(new Event('test2')); - - assert_equals(count, 2); - - // GC should not affect the listener removal below. The composite signal - // above is not held onto by JS, so this test will fail if nothing is - // holding a reference to it. - gc(); - - controller.abort(); - dispatchEvent(new Event('test2')); - assert_equals(count, 2); -}, 'AbortSignalRegistry tracks algorithm handles for event listeners (composite signal)'); - -promise_test(t => { - const controller = new AbortController(); - let promise; - - (function() { - let signal = AbortSignal.any([controller.signal]); - promise = navigator.locks.request("abort-signal-any-test-lock", {signal}, lock => {}); - signal = null; - })(); - - // Make sure the composite signal isn't GCed even though the lock request - // doesn't hold onto it. - gc(); - - controller.abort(); - return promise_rejects_dom(t, 'AbortError', promise); -}, 'Lock request keeps composite signal alive'); diff --git a/third_party/blink/web_tests/wpt_internal/dom/abort/resources/abort-signal-any-memory-tests.js b/third_party/blink/web_tests/wpt_internal/dom/abort/resources/abort-signal-any-memory-tests.js deleted file mode 100644 index 2c4f7b85ee93a..0000000000000 --- a/third_party/blink/web_tests/wpt_internal/dom/abort/resources/abort-signal-any-memory-tests.js +++ /dev/null @@ -1,241 +0,0 @@ -// Global state that should be prevented from being garbage collected. -let gRegistry; -let gController; -let gSignals = []; - -function abortSignalAnyMemoryTests(signalInterface, controllerInterface) { - const suffix = `(using ${signalInterface.name} and ${controllerInterface.name})`; - - // Use promise tests so tests are not interleaved (to prevent global state - // from getting clobbered). - promise_test(t => { - return new Promise((resolve) => { - let tokens = []; - gRegistry = new FinalizationRegistry(t.step_func(function(token) { - tokens.push(token); - if (tokens.length == 2) { - assert_in_array(1, tokens); - assert_in_array(2, tokens); - resolve(); - } - })); - - (function() { - let controller1 = new controllerInterface(); - let controller2 = new controllerInterface(); - - gSignals.push(controller1.signal); - gSignals.push(controller2.signal); - - signal = signalInterface.any(gSignals); - gSignals.push(signal); - - gRegistry.register(controller1, 1); - gRegistry.register(controller2, 2); - controller1 = null; - controller2 = null; - })(); - - gc(); - }); - }, `Controllers can be GCed when their signals are being followed ${suffix}`); - - promise_test(t => { - return new Promise((resolve) => { - let tokens = []; - gRegistry = new FinalizationRegistry(t.step_func(function(token) { - tokens.push(token); - if (tokens.length == 3) { - assert_in_array(1, tokens); - assert_in_array(2, tokens); - assert_in_array(3, tokens); - resolve(); - } - })); - - (function() { - let controller1 = new controllerInterface(); - let controller2 = new controllerInterface(); - let signal = signalInterface.any([controller1.signal, controller2.signal]); - - gRegistry.register(controller1, 1); - gRegistry.register(controller2, 2); - gRegistry.register(signal, 3); - - controller1 = null; - controller2 = null; - signal = null; - })(); - - gc(); - }); - }, `Signals can be GCed when all abort sources have been GCed ${suffix}`); - - promise_test(t => { - return new Promise((resolve) => { - let tokens = []; - gController = new controllerInterface(); - gRegistry = new FinalizationRegistry(t.step_func(function(token) { - tokens.push(token); - if (tokens.length == 2) { - assert_false(gController.signal.aborted); - assert_in_array(1, tokens); - assert_in_array(2, tokens); - resolve(); - } - })); - - (function() { - let signal1 = signalInterface.any([gController.signal]); - let signal2 = signalInterface.any([signal1]); - - gRegistry.register(signal1, 1); - gRegistry.register(signal2, 2); - - signal1 = null; - signal2 = null; - })(); - - gc(); - }); - }, `Signals can be GCed when they have no references or event listeners ${suffix}`); - - promise_test(t => { - return new Promise((resolve) => { - let tokens = []; - gController = new controllerInterface(); - gRegistry = new FinalizationRegistry(t.step_func(function(token) { - tokens.push(token); - if (tokens.length == 2) { - assert_false(gController.signal.aborted); - assert_in_array(1, tokens); - assert_in_array(2, tokens); - resolve(); - } - })); - - (function() { - let signal1 = signalInterface.any([gController.signal]); - signal1.addEventListener('event', () => {}); - - let signal2 = signalInterface.any([signal1]); - signal2.addEventListener('event', () => {}); - - gRegistry.register(signal1, 1); - gRegistry.register(signal2, 2); - - signal1 = null; - signal2 = null; - })(); - - gc(); - }); - }, `Signals can be GCed when they have no references or relevant event listeners ${suffix}`); - - promise_test(t => { - return new Promise((resolve) => { - let tokens = []; - - gRegistry = new FinalizationRegistry(t.step_func(function(token) { - tokens.push(token); - if (tokens.length == 2) { - assert_in_array(1, tokens); - assert_in_array(2, tokens); - resolve(); - } - })); - - gController = new controllerInterface(); - - (function() { - let signal1 = signalInterface.any([gController.signal]); - let signal2 = signalInterface.any([signal1]); - - gRegistry.register(signal1, 1); - gRegistry.register(signal2, 2); - - const abortCallback1 = () => {}; - const abortCallback2 = () => {}; - - signal1.addEventListener('abort', abortCallback1); - signal1.addEventListener('abort', abortCallback2); - - signal2.addEventListener('abort', abortCallback1); - signal2.addEventListener('abort', abortCallback2); - - signal1.removeEventListener('abort', abortCallback1); - signal1.removeEventListener('abort', abortCallback2); - - signal2.removeEventListener('abort', abortCallback1); - signal2.removeEventListener('abort', abortCallback2); - - signal1 = null; - signal2 = null; - })(); - - gc(); - }); - }, `Signals can be GCed when all abort event listeners have been removed ${suffix}`); - - promise_test(t => { - return new Promise((resolve) => { - let tokenCount = 0; - let fired = false; - - gController = new controllerInterface(); - - (function() { - let signal = signalInterface.any([gController.signal]); - signal.onabort = t.step_func((e) => { - assert_true(e.target.aborted); - resolve(); - }); - - signal = null; - })(); - - gc(); - gController.abort(); - }); - }, `Signals are not GCed before being aborted by a controller when they have abort event listeners ${suffix}`); - - promise_test(t => { - return new Promise((resolve) => { - let tokenCount = 0; - let fired = false; - - gController = new controllerInterface(); - gRegistry = new FinalizationRegistry(t.step_func(function(token) { - ++tokenCount; - if (tokenCount == 1) { - assert_equals(token, 1, 'signal1 should be GCed first'); - assert_false(fired, 'The abort listener should not run before signal1 is GCed'); - gController.abort(); - gc(); - } - - if (tokenCount == 2) { - assert_equals(token, 2, 'signal2 should be GCed second'); - assert_true(fired, 'The abort listener should run before signal2 is GCed'); - t.done(); - } - })); - - (function() { - let signal1 = signalInterface.any([gController.signal]); - gRegistry.register(signal1, 1); - - let signal2 = signalInterface.any([signal1, signalInterface.timeout(500)]); - signal2.onabort = t.step_func(() => { - fired = true; - }); - gRegistry.register(signal2, 2); - - signal1 = null; - signal2 = null; - })(); - - gc(); - }); - }, `Signals are not GCed before being aborted by timeout when they have abort event listeners ${suffix}`); -}