Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spec ref-counted producers #197

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 74 additions & 26 deletions spec.bs
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ interface Subscriber {
};
</xmp>

Each {{Subscriber}} has a <dfn for=Subscriber>internal observer</dfn>, which is an [=internal
observer=].
Each {{Subscriber}} has an [=ordered set=] of <dfn for=Subscriber>internal observers</dfn>,
initially empty.

Each {{Subscriber}} has a <dfn for=Subscriber>teardown callbacks</dfn>, which is a [=list=] of
{{VoidFunction}}s, initially empty.
Expand All @@ -188,6 +188,9 @@ Each {{Subscriber}} has a <dfn for=Subscriber>subscription controller</dfn>, whi

Each {{Subscriber}} has a <dfn for=Subscriber>active</dfn> boolean, initially true.

Each {{Subscriber}} has an <dfn for=Subscriber>owning Observable</dfn>, an {{Observable}}, which is
initialized during construction.

Note: This is a bookkeeping variable to ensure that a {{Subscriber}} never calls any of the
callbacks it owns after it has been [=close a subscription|closed=].

Expand All @@ -205,21 +208,22 @@ The <dfn attribute for=Subscriber><code>signal</code></dfn> getter steps are to
1. If [=this=]'s [=relevant global object=] is a {{Window}} object, and its [=associated
Document=] is not [=Document/fully active=], then return.

1. Run [=this=]'s [=Subscriber/internal observer=]'s [=internal observer/next steps=] given
|value|.
1. [=set/For each=] |observer| of [=this=]'s [=Subscriber/internal observers=]:

[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.
1. Run |observer|'s [=internal observer/next steps=] given |value|.

<div class=note>
<p>Note: No exception can be thrown here because in the case where the
[=Subscriber/internal observer=]'s [=internal observer/next steps=] is just a wrapper
around a script-provided callback, the <a href=#process-observer>process observer</a> steps
take care to wrap these callbacks in logic that, when invoking them, catches any
exceptions, and reports them to the global.</p>

<p>When the [=internal observer/next steps=] is a spec algorithm, those steps take care to
not throw any exceptions outside of itself, to appease this assert.</p>
</div>
[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.

<div class=note>
<p>Note: No exception can be thrown here because in the case where the
[=Subscriber/internal observer=]'s [=internal observer/next steps=] is just a wrapper
around a script-provided callback, the <a href=#process-observer>process observer</a>
steps take care to wrap these callbacks in logic that, when invoking them, catches any
exceptions, and reports them to the global.</p>

<p>When the [=internal observer/next steps=] is a spec algorithm, those steps take care
to not throw any exceptions outside of itself, to appease this assert.</p>
</div>
</div>

<div algorithm>
Expand All @@ -232,12 +236,13 @@ The <dfn attribute for=Subscriber><code>signal</code></dfn> getter steps are to

1. [=close a subscription|Close=] [=this=].

1. Run [=this=]'s [=Subscriber/internal observer=]'s [=internal observer/error steps=] given
|error|.
1. [=set/For each=] |observer| of [=this=]'s [=Subscriber/internal observers=]:

1. Run |observer|'s [=internal observer/error steps=] given |error|.

[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.
[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.

Note: See the documentation in {{Subscriber/next()}} for details on why this is true.
Note: See the documentation in {{Subscriber/next()}} for details on why this is true.
</div>

<div algorithm>
Expand All @@ -250,11 +255,13 @@ The <dfn attribute for=Subscriber><code>signal</code></dfn> getter steps are to

1. [=close a subscription|Close=] [=this=].

1. Run [=this=]'s [=Subscriber/internal observer=]'s [=internal observer/complete steps=].
1. [=set/For each=] |observer| of [=this=]'s [=Subscriber/internal observers=]:

[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.
1. Run |observer|'s [=internal observer/complete steps=].

Note: See the documentation in {{Subscriber/next()}} for details on why this is true.
[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.

Note: See the documentation in {{Subscriber/next()}} for details on why this is true.
</div>

<div algorithm>
Expand Down Expand Up @@ -304,6 +311,8 @@ observable.subscribe({}, {signal: outerController.signal});

1. Set |subscriber|'s [=Subscriber/active=] boolean to false.

1. Set [=this=]'s [=Subscriber/owning Observable=]'s [=Observable/active subscriber=] to null.

1. [=AbortSignal/Signal abort=] |subscriber|'s [=Subscriber/subscription controller=] with
|reason|, if it is given.

Expand Down Expand Up @@ -407,6 +416,9 @@ interface Observable {
Each {{Observable}} has a <dfn for=Observable>subscribe callback</dfn>, which is a
{{SubscribeCallback}} or a set of steps that take in a {{Subscriber}}.

Each {{Observable}} has a <dfn for=Observable>active subscriber</dfn>, which is a
{{Subscriber}}-or-null, initially null.

Note: The "union" of these types is to support both {{Observable}}s created by JavaScript (that are
always constructed with a {{SubscribeCallback}}), and natively-constructed {{Observable}} objects
(whose [=Observable/subscribe callback=] could be an arbitrary set of native steps, not a JavaScript
Expand Down Expand Up @@ -732,9 +744,39 @@ An <dfn>internal observer</dfn> is a [=struct=] with the following [=struct/item
error algorithm=], or an algorithm that [=invokes=] the provided
{{SubscriptionObserver/error}} [=callback function=].

1. Let |subscriber| be a [=new=] {{Subscriber}}.
1. If [=this=]'s [=Observable/active subscriber=] is not null and [=this=]'s [=Observable/active
subscriber=]'s [=Subscriber/active=] is true:

1. Let |subscriber| be [=this=]'s [=Observable/active subscriber=].

1. [=set/Append=] |internal observer| to |subscriber|'s [=Subscriber/internal observers=].

1. Set |subscriber|'s [=Subscriber/internal observer=] to |internal observer|.
1. If |options|'s {{SubscribeOptions/signal}} [=map/exists=], then:

1. If |options|'s {{SubscribeOptions/signal}} is [=AbortSignal/aborted=], then
[=set/remove=] |internal observer| from |subscriber|'s [=Subscriber/internal
observers=].

1. Otherwise, [=AbortSignal/add|add the following abort algorithm=] to |options|'s
{{SubscribeOptions/signal}}:

1. If |subscriber|'s [=Subscriber/active=] is false, then abort these steps.

1. [=set/Remove=] |internal observer| from |subscriber|'s [=Subscriber/internal
observers=].

1. If |subscriber|'s [=Subscriber/internal observers=] is [=set/empty=], then [=close a
subscription|close=] |subscriber| with |options|'s {{SubscribeOptions/signal}}'s
[=AbortSignal/abort reason=].

1. Return.

1. Let |subscriber| be a [=new=] {{Subscriber}}, initialized with [=Subscriber/owning
Observable=] as [=this=].

1. [=set/Append=] |internal observer| to |subscriber|'s [=Subscriber/internal observers=].

1. Set [=this=]'s [=Observable/active subscriber=] to |subscriber|.

1. If |options|'s {{SubscribeOptions/signal}} [=map/exists=], then:

Expand All @@ -745,8 +787,14 @@ An <dfn>internal observer</dfn> is a [=struct=] with the following [=struct/item
1. Otherwise, [=AbortSignal/add|add the following abort algorithm=] to |options|'s
{{SubscribeOptions/signal}}:

1. [=close a subscription|Close=] |subscriber| with |options|'s
{{SubscribeOptions/signal}} [=AbortSignal/abort reason=].
1. If |subscriber|'s [=Subscriber/active=] is false, then abort these steps.

1. [=set/Remove=] |internal observer| from |subscriber|'s [=Subscriber/internal
observers=].

1. If |subscriber|'s [=Subscriber/internal observers=] is [=set/empty=], then [=close a
subscription|close=] |subscriber| with |options|'s {{SubscribeOptions/signal}}'s
[=AbortSignal/abort reason=].

1. If [=this=]'s [=Observable/subscribe callback=] is a {{SubscribeCallback}}, [=invoke=] it
with |subscriber|.
Expand Down