-
Notifications
You must be signed in to change notification settings - Fork 167
AudioNode Lifetime section seems to attempt to make garbage collection observable #1471
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
Comments
I agree, this does seem to be a problem leading to the observability of GC. |
Thinking out loud here... possibly not very soundly... it seems we can go in at least two different directions:
In many ways Option 2 feels like the more rational way to go here, but it would obviously be a big change. The hugest piece of this change seems to be that nodes could become disconnected from the graph due to their own deterministic changes in state. Since JS references to the node could still exist (because they would no longer prevent such disconnection), the node would have to make its own disconnected state visible to applications. Such nodes would either be no longer usable (and many of our nodes do become unusable once they are finished with their job), or there would have to be a codified way to re-use them. |
I hadn't considered Option 1. It would work and is simple, but having explicit For option 2, I assume that an inactive node that became active again would be Regardless, I expect this would be simpler than option 2:
This would be equivalent to not having any AudioNode lifetime section at all. |
The WG discussed this today. We came up with the following proposal along the lines of Option 2:
As Karl and I pointed out, the big consequence of this change is that a node can now be discarded from the graph due to inactivity even though it has JS references. If the holder of the JS reference could reactivate the node somehow (e.g. by connecting new inputs to it), this would present a problem. There are no such source node types, though, except On reflection this approach seems to me to have a big flaw: Consider an app that puts a If that logic is correct, we're left with these choices:
|
I'll mention one other infeasible approach which is to require app developers to explicitly mark nodes that ought to magically go away when disconnected. Incompatible. |
I think, independent of what the lifetime section says, GC is actually observable because we added Consider a default I don't know how to fix this while also preserving the very, very useful feature of the worklet having dynamic channel count support. |
Here is an outline of a possible fix to the situation. I do not think we can have perfect dynamic channel upmixing and GC-blindness at the same time, so this approach makes the channel upmixing a little less dynamic in order to eliminate the GC problem.
This approach limits the cases where dynamic channel mixing is affected. In particular, dynamic channel count behavior will continue working, except in a small number of cases where developers rely on GC alone to eliminate nodes from the graph and these nodes have varying output channel counts. |
It is Interesting that we had the similar thought, but consider this case: N1 (6ch) -> N3 Here N3's
Correct me if I misunderstood your proposal. |
Let me explain what I think would happen here, I think maybe you misunderstood me (and I'm sure I could have been clearer!): At the time the graph is constructed, N3's input channel floors will be set to Thus, when N1 is removed by GC, then N3's input channel floors will be set to Note that if N1 had been explicitly removed by calling |
[previous hasty comment deleted] We also need a way for explicitly deactivated source nodes to communicate their state, by setting the deactivated flag on downstream nodes that then become eligible for release. This handles the case where subgraphs of sources chained through filter-type nodes like |
Uh oh. Now I am confused.
This sounds okay. How about during the rendering? What's the N3's input channel count?
So it'll be 6 when N1 is GCed. Then it'll be 2 when N2 GCed? Then can't we get a signal when the number goes from 6 to 2? It won't match the exact timing of GC, but still this is a piece of information that developers can act upon.
Well, this defeats the whole purpose of dynamic up/down mixing. The feels like sacrifice the most useful bit of WebAudio only to hide GC. In my opinion, the source nodes are the least of our problems. The GainNode (N2 in the example) and the ChannelMerger (N1 in the example) going into an AudioWorkletNode (N3 in the example) would be our litmus test for GC observability. |
I believe source nodes can be spec'ed so that GCing them isn't observable but this requires that The real problem is with nodes whose number of output channels is fixed, like a |
Before any GC occurs, input channel count follows the specified mixing rules.
No, this is just a mechanism to maintain the same behavior as before the GC.
Would this really be a sacrifice? It is specified that the input to be upmixed to 6 channels when N1 has stopped but GC has not occurred. |
On Mon, Jan 29, 2018 at 7:34 PM, Karl Tomlinson ***@***.***> wrote:
At the time the graph is constructed, N3's input channel floors will be
set to [0], not [6].
This sounds okay. How about during the rendering? What's the N3's input
channel count?
Before any GC occurs, input channel count follows the specified mixing
rules.
For channelCountMode = "max", computedNumberOfChannels will be 6. After GC
occurs, the maximum is recorded so that computedNumberOfChannels remains
at 6.
A floor value for an input only gets set when a GCed node is disconnected
from that input.
So it'll be 6 when N1 is GCed. Then it'll be 2 when N2 GCed?
No, this is just a mechanism to maintain the same behavior as before the
GC.
input channel floors need only record the maximum of the nodes that have
been
GCed. If N1 is GCed, followed by N2, then input channel floors will remain
at [6].
If N2 is GCed first, then input channel floors will first become [2], and
then
become [6] when N1 is GCed.
Correspondingly the input of N2 will continue being upmixed to 6.
Well, this defeats the whole purpose of dynamic up/down mixing. The feels
like sacrifice the most useful bit of WebAudio only to hide GC.
Would this really be a sacrifice?
In many ways, yes, it is a sacrifice. You can no longer duplicate many of
the builtin nodes using a worklet, because the number of output channels
will not match the native nodes. I've always felt that being able to
duplicate native nodes indicates that the worklet has the right
abstraction. As currently spec'ed, you can implement every node (I think)
with a worklet, except for a panner node, which needs magic access to the
AudioLIstener AudioParams to compute the appropriate direction vectors. No
real way to duplicate that unless we somehow pass that to process(). But
that's not something I would want to happen all the time because most nodes
wouldn't need it.
… It is specified that the input to be upmixed to 6 channels when N1 has
stopped but GC has not occurred.
If that is OK, then it is OK for the input to be upmixed when N1 has
finished and GC has occurred.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#1471 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAofPBM9PUOKh7bCeUmPL6db0NwAQd-Kks5tPo28gaJpZM4RbpU0>
.
--
Ray
|
Thanks @karlt for supplying the explanation, which is in line with my understanding. @rtoy I believe that an AWN would continue to have an ability to duplicate the builtin nodes, since the proposed GC/lifetime/channel-floor rules are the same for AWNs as for any other node type. There is no difference in either its input or its output channel count behavior from other nodes: the definition given in https://webaudio.github.io/web-audio-api/#dfn-computednumberofchannels would be modified to take account of the AWN's input channel floor. And when an AWN is released due to GC alone (i.e. while its active source flag is false), its output channel count at that moment would be propagated to the input channel floor of whatever nodes the AWN was connected to. If I've missed something, perhaps you could spell out exactly the case where you believe the AWN is going to act differently. That said, I do think there are some problems with this approach still, and it needs a more formal definition. I'm thinking about it and I expect to post an updated definition today or tomorrow in time for discussion on Thursday's call. |
The following changes will help us formulate new lifetime rules in a way that is more rigorous and which avoids magical connections between GC and lifetime, by introducing the general concept of an audio processor. It also clarifies the equivalence of AudioWorkletNode to other nodes, by defining
It's likely that we'd need some global changes to refer to "processor" instead of "node" in a bunch of places but that's just linguistic. |
On Tue, Jan 30, 2018 at 6:01 AM, Joe Berkovitz ***@***.***> wrote:
Thanks @karlt <https://github.com/karlt> for supplying the explanation,
which is in line with my understanding.
@rtoy <https://github.com/rtoy> I believe that an AWN would continue to
have an ability to duplicate the builtin nodes, since the proposed
GC/lifetime/channel-floor rules are the same for AWNs as for any other node
type. There is no difference in either its input or its output channel
count behavior from other nodes: the definition given in
https://webaudio.github.io/web-audio-api/#dfn-computednumberofchannels
would be modified to take account of the AWN's input channel floor. And
when an AWN is released due to GC alone (i.e. while its active source flag
is false), its output channel count at that moment would be propagated to
the input channel floor of whatever nodes the AWN was connected to. If I've
missed something, perhaps you could spell out exactly the case where you
believe the AWN is going to act differently.
Here's my thinking. I want a worklet to reproduce a BiquadFilter. A
biquad filters each channel separately. I connect a 32-channel source and
a mono oscillator to the biquad. The biquad sees 32 channels on the input
and creates 32 separate filter states, one per channel. The 32-channel
source goes away, leaving the mono oscillator. The biquad now only has to
filter just the mono channel, resulting in 32 times less computation.
IIUC, a worklet with the floor stuff will always have 32 channels. Even if
the input is now mono. And it produces 32 channels on the output, forcing
all downstream native nodes to process 32 channels, 31 of which are silence.
Of course, if you don't want the dynamic channel count changes, the worklet
can set channelCount and channelCountMode appropriately. But then it's not
the same as the biquad filter which would dynamically change the number of
output channels.
…--
Ray
|
The worklet would only be subject to "the floor stuff" if its 32-channel source is disconnected as a result of garbage collection. If the 32-channel source goes away because it stops producing output (e.g. it's an ABSN that finishes playing, or is a filter on an upstream source that stops), or because it's explicitly disconnected, then there will be no "floor stuff" and the worklet will revert to seeing a mono input. I think you're seeing this proposal as outright dispensing with dynamic channel counts, but the goal here is to make the "pinning" of channel counts apply to what will likely be edge cases, and ones that developers can easily remedy at that. |
As currently specified, if the 32-channel input is an ABSN that has finished, As you indicate, there is an opportunity for optimization if there is only a I'm not sure how important/common the temporary 32-channel input situation is, Beware though that BiquadFilterNode (and perhaps other nodes) would also need As you point out, specifying a change in the fixed output channel count of |
A benefit of this processor concept is that this definition of rule 1 would be much more clear than "normal GC rules", thanks.
But note that there is a race re whether GC of the AudioNode occurs before or If ABSN were changed, then its output channel count change would propagate |
I thought of the race condition also, and it led me to an unexpected and rather pessimistic conclusion about this whole scheme -- or, rather family of schemes -- where we attempt to distinguish GC-related node death from other causes. All such schemes appear doomed to make facts about the timing of GC observable in the following manner:
|
In light of the observability of GC timing, it's time to take a more detailed look at possible node lifetime schemes that truly do ignore GC. Here is a start. Let's begin with a very common use case that frames the central problem of what happens when GC has no influence on node lifetime. Our example application has:
Without GC influence on lifetime, it seems clear to me that we would need a new node attribute, endowing each of these GainNodes with different behavior, under application control. The master gain node needs to survive indefinitely, in spite of having no references. The scheduled source node needs to disappear as soon as its upstream Oscillator goes away. Obviously this would break some applications, no matter which default value we adopt for such an attribtue. |
On Wed, Jan 31, 2018 at 5:40 AM, Joe Berkovitz ***@***.***> wrote:
I thought of the race condition also, and it led me to an unexpected and
rather pessimistic conclusion about this whole scheme -- or, rather family
of schemes -- where we attempt to distinguish GC-related node death from
other causes.
All such schemes appear doomed to make *facts about the timing of GC*
observable in the following manner:
- create a source node with N channels (an ABSN will do)
- arrange for it to stop at time T
- connect its output to an AudioWorkletNode
- release all JS references to it
- wait until time T
- observe the input channel count of the AWN. If no channels are
found, GC of the source node occurred prior to time T. If it's N, then GC
occurred at or after time T.
I assume at some point before time T you actually called start. Let's
also assume that the channel count change from the ABSN doesn't actually
happen until the ABSN actually produces the multi-channel output (at the
scheduled start time).
Let's also change the AWN so that process() is always called with at least
a single channel (of silence) when there are no connections to the input.
Let's also say that when the ABSN stops, the output changes from the
multi-channel value to just mono.
Then, GC is not observable. When the ABSN stops, the output is mono,
*independent* of whether the node was GCed or not. If GC does happen, the
ABSN can disconnect itself from the AWN. Thus, the AWN has no input
connections. However, the AWN still gets a single channel (of silence).
You cannot tell if the ABSN was collected or not and if it were collected,
you can't tell when.
I think we should change the spec so that AWN's always get at least one
array of silence whether there are connections or not. We should the ABSN
so that the channel count change happens only when the source starts
playing, and when it stops, the ABSN should output mono again.
Am I missing something?
… —
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1471 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAofPICPXgQFTmbOp8Bm9dZEqfuPGvBsks5tQG1FgaJpZM4RbpU0>
.
--
Ray
|
If there's no worklet in any subgraph, I don't think this matters at all.
GC is not observable. Since you've dropped references to everything, you
can't tell if anything changed because the never communicate back to you.
The master gain can also go away when you drop the reference. The
destination node continues to produce exactly the same number of channels
of output as before, except it's all silence.
…On Wed, Jan 31, 2018 at 6:07 AM, Joe Berkovitz ***@***.***> wrote:
In light of the observability of GC timing, it's time to take a more
detailed look at possible node lifetime schemes that truly do ignore GC.
Here is a start.
Let's begin with a very common use case that frames the central problem of
what happens when GC has no influence on node lifetime. Our example
application has:
-
A "master gain" GainNode that is permanently connected to the
AudioDestinationNode, and is used to set the level of all sound output from
an application. The master gain node has no inputs when created; later,
when the application wishes to create sound, scheduled sources will be
attached. The application maintains a reference to this master gain node,
so that it can connect those sources to it. In today's world, that
reference prevents the master gain node from being removed from the graph.
-
A set of "scheduled source" subgraphs that are dynamically created to
create the application's sound output. Each subgraph is an OscillatorNode
with some definite stop and start times, connected to a GainNode giving
control of the level of this individual source. Each scheduled source's
GainNode is in turn connected to the "master gain" node. These scheduled
sources have no JS references and thus are fire-and-forget in today's
world: when the OscillatorNode stops, it disconnects from the source's
GainNode. Since there are no references to the GainNode, it disconnects
from the master node, and both scheduled source nodes disappear.
Without GC influence on lifetime, it seems clear to me that we would need
a new node attribute, endowing each of these GainNodes with different
behavior, under application control. The master gain node needs to survive
indefinitely, in spite of having no references. The scheduled source node
needs to disappear as soon as its upstream Oscillator goes away.
Obviously this would break some applications, no matter which default
value we adopt for such an attribtue.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1471 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAofPMU8RVNoLjkjtafeJjC_8B9w5rKWks5tQHOmgaJpZM4RbpU0>
.
--
Ray
|
On Tue, Jan 30, 2018 at 9:21 PM, Karl Tomlinson ***@***.***> wrote:
A biquad filters each channel separately. I connect a 32-channel source
and a mono oscillator to the biquad. The biquad sees 32 channels on the
input and creates 32 separate filter states, one per channel. The
32-channel source goes away, leaving the mono oscillator. The biquad now
only has to filter just the mono channel, resulting in 32 times less
computation.
As currently specified, if the 32-channel input is an ABSN that has
finished,
the BiquadFilterNode with channelCountMode "max" will continue to filter 32
channels, at least until the source "goes away". Whether the source can
just
go away due to GC is currently unclear from the current text (but that is
what
this issue is addressing).
As you indicate, there is an opportunity for optimization if there is only
a
single non-silent channel of input. A BiquadFilterNode implementation may
know that the 32 channels derive from a single channel and so skip some
processing (and downstream built-in nodes could even do similarly), but an
AudioWorkletProcessor wouldn't have this information, and so wouldn't be
able
to optimize so effectively.
Yes. But it would be really good if the AWN could process only one
channel because the number of input channels changed to 1, however that
happened.
I'm not sure how important/common the temporary 32-channel input situation
is,
but the probably more common case of ABSN could be addressed by changing
its
output to be a single channel when not playing, as you and I suggested in
#462 (comment)
<#462 (comment)>
and #462 (comment)
<#462 (comment)>
.
Beware though that BiquadFilterNode (and perhaps other nodes) would also
need
to changed because currently "The number of channels of the output always
equals the number of channels of the input." BiquadFilterNode has a tail
time. If the output channel count were to change from 32 to 1 at any time
before the tail time expires, then a step change (glitch) would be
observed on
any downstream node.
That would be an implementation bug. The channel count change should
happen after the tail is done. But this is really imprecise anyway, since
biquads have infinite tails that we truncate at some random point. But it
is all very easily observable by having the output go to a gain node with a
huge gain....
As you point out, specifying a change in the fixed output channel count of
other nodes such as PannerNode, would be more complicated. I'm inclined to
think that optimizing for processing of 1 channel instead of 2 from
PannerNode, or more in the case of ChannelMergerNode or other explicit
large
channel situations is not so important. I'm not clear whether or not
joeberkovitz's proposal is aiming to support optimization in these cases.
I have no statistics to prove this, but I think mono and stereo are by far
the most common. But I also don't want to prevent people from using 17
channels effectively if the so choose. I also don't want to force
everything to be mono. It would simplify so many things internally, but it
would be pretty awful for a developer. Besides, it's way too late to
remove mulit-channel support.
… —
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1471 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAofPJBRCeiyqHhb1JiKvNMN0y1Pawarks5tP_hNgaJpZM4RbpU0>
.
--
Ray
|
This is AudioWorklet-specific issue. We should not touch the other part of the spec to solve this. Also if this turns out to be a large-scale architecture problem, we should consider address this in V2. For that path, we maybe have to shut it down the dynamic channel count feature in AudioWorklet. The channel count change being visible is a useful feature. But the feature is also the violation of GC observability. I don't think we can reconcile this without a messy hack. If we have to do this with messy hack, I rather have the fixed channel count in AudioWorklet to avoid the confusion around the spec and the implementation. |
I think for an ABSN, it would output a single channel of silence only after the stop time had been reached. If the ABSN is looping a multi-channel silent buffer forever, it would never change to a single channel of silence. For a panner node and convolver node, if the input is known to be silent, and the tail time has passed, these nodes would produce an output of one channel of silence. As you say, cycles complicate this a lot. Consider absn -> convolver -> delay -> back to convolver. Even though the convolver is an FIR filter, the feedback loop makes the overall response be an IIR filter, so the output of the convolver is never really zero. But if the convolver detected that the input is "close enough" to zero that it can do the its tail processing, then the output of the convolver could go to a single channel of silence, which causes the delay node (eventually) to output one channel of silence, and the convolver then has one channel of true silence. The panner node could do the same thing, and get similar behavior to the convolver, so I think we're ok there. I think this would take care of the cycle case with these nodes. I think the only problematic node is the ChannelMerger which is supposed to have a fixed number of output channels. It would be kind of weird, but if we said that if all the inputs are silent, then the merger produces a single channel of silence for the output, we can make everything work and not expose GC. While strange, no native node could really tell that the channel merger channel count changed because any upmixing/downmixing would just duplicate silent channels and not affect the audio (but possibly affect the number of channels). The worklet would be able to tell, of course, and this is what we want. And the channel count changes are due to silence, not GC. Perhaps there are some corner cases that I've missed. |
Teleconf: write up something for review based on the idea in #1471 (comment) |
I'm not clear here on whether the proposal is that convolver and panner nodes ABSN -> Gain > Delay -> back to Gain, needs similar treatment as for when Consider if filter nodes were to always check for close enough to silence, but If filter nodes are to only check for silence while in a cycle, then this is I think these are the options from which to choose:
I expect any of these could be a solution. For 2 and 3, in other situations, filter nodes need only check whether all |
…denot This is necessary for efficient processing of silence, and is consistent with behavior of other nodes. A null block current has a single channel of silence, which isn't aligned with the current spec, but is consistent with the direction of WebAudio/web-audio-api#1471 (comment) Depends on D9210 Differential Revision: https://phabricator.services.mozilla.com/D9211 --HG-- extra : moz-landing-system : lando
TPAC resolution (2018-10-16): the group agrees on solution 3 above. |
This is actually the current normative behaviour. |
I think it's helpful to summarize where we are before writing up a PR.
Please feel free to add more if I am missing something. |
I'm not clear exactly what is meant by "disabled", but that seems like the gist of it. Also
A disabled or known-silent node may be collected later, but this need not be spec'd. It still cannot be disconnected just because an implementation wants to garbage collect the node. IOW any effects of connection counts must not be affected by GC. The solution proposed here means channel count effects from inactive nodes are not observable, but connection counts are a slightly different issue. ScriptProcessorNode behavior is what depends on this. AudioWorklet behavior would not be affected by inactive connections when #1453 is addressed. |
I think that's the following terms are to be clarified (so I can grasp what needs to be written):
In Chrome, the "disabled" status means that a node completed its rendering activity and won't be pulled any more. If a path in the graph is to be dismantled after a source node stops, nodes in such path will be disabled before they are disconnected. Thus, it does not mean either disconnection or garbage collection. We can mention the disconnection, but need not to say anything about GC. I see your reference on "inactive connection" is quite close to this concept. No? Once we settle on this idea, then defining known silent input should be easy: When an AudioNode is processed, it checks its upstream nodes to see whether they are disabled or not. If all upstream nodes are disabled, then the node will have known silent input in turn. |
If only aiming to define "disabled" for source nodes that have played and stopped, then it would be possible to do so without saying anything about GC, but I doubt that is useful here. IIUC the path "to be dismantled" includes filter nodes. These could only be disabled after they have no external references. It would be necessary to talk about GC to describe which filter nodes could be disabled. Source nodes that have not played would be in a similar situation. As "disabled" would only be useful for source nodes that have finished playing, we'd still need a mechanism for other nodes. The mechanism for other nodes would also be sufficient for source nodes that have finished, and so there is little value in adding the "disabled" concept.
It differs in that an inactive node can become active again. The concept of known silent is key here. |
Okay, it seems like you think known silent is the only term that needs to be newly introduced in the spec. Would you mind writing a prose or something so we can get this going? |
The note about a "variety of approaches" at https://webaudio.github.io/web-audio-api/#AudioWorkletProcessor-methods uses the term "active" for this, but "actively processing" would be more specific and avoid confusion with other active states. The general prose for AudioNode could be
There are few necessary special cases.
(This could be "input or output" to use the same sentence as onaudioprocess events because the actively processing state won't be observable if its output is not connected.) For AudioWorkletNode, I suggest including [[callable process]] in the active processing conditions:
"If process() is not called during some rendering quantum due to the lack of any applicable active processing conditions, the result is as if the processor emitted silence for this period." may be removed. Replace other active processing conditions references with references to actively processing. |
In line with #1471 (comment), the AudioNode lifetime section would also be removed. |
@hoch, do you agree with the above? I can take are of writing the prose here. |
Update the length of the parameter sequence after #1471.
…denot This is necessary for efficient processing of silence, and is consistent with behavior of other nodes. A null block current has a single channel of silence, which isn't aligned with the current spec, but is consistent with the direction of WebAudio/web-audio-api#1471 (comment) Depends on D9210 Differential Revision: https://phabricator.services.mozilla.com/D9211 UltraBlame original commit: 17f777b161890b63b207c412549e4afc7129fa32
…denot This is necessary for efficient processing of silence, and is consistent with behavior of other nodes. A null block current has a single channel of silence, which isn't aligned with the current spec, but is consistent with the direction of WebAudio/web-audio-api#1471 (comment) Depends on D9210 Differential Revision: https://phabricator.services.mozilla.com/D9211 UltraBlame original commit: 17f777b161890b63b207c412549e4afc7129fa32
…denot This is necessary for efficient processing of silence, and is consistent with behavior of other nodes. A null block current has a single channel of silence, which isn't aligned with the current spec, but is consistent with the direction of WebAudio/web-audio-api#1471 (comment) Depends on D9210 Differential Revision: https://phabricator.services.mozilla.com/D9211 UltraBlame original commit: 17f777b161890b63b207c412549e4afc7129fa32
…denot This is necessary for efficient processing of silence, and is consistent with behavior of other nodes. A null block current has a single channel of silence, which isn't aligned with the current spec, but is consistent with the direction of WebAudio/web-audio-api#1471 (comment) Depends on D9210 Differential Revision: https://phabricator.services.mozilla.com/D9211
Attempting to describe observable behavior on garbage collection wouldn't make
sense because garbage collection is the removal of objects that are no longer
needed. If their removal would cause changes in behaviour, then that implies
that the objects are still needed.
https://w3ctag.github.io/design-principles/#js-gc states the requirement in
reverse: "There must not be a way for author code to deduce when/if garbage
collection of JavaScript objects has run."
https://webaudio.github.io/web-audio-api/#lifetime-AudioNode
describes conditions that would keep an AudioNode alive and
the deletion of AudioNodes when none of these conditions are present.
The section appears to be describing garbage collection. The first condition
is "A normal reference obeying normal garbage collection rules". I'm not
clear exactly what this means. Garbage collection attempts to detect objects
that are no longer needed. Typically, implementations ensure that objects
that are still needed have "normal references" reachable from roots.
All other "types of references" listed seem to be explicit conditions where the
AudioNode is still needed.
One situation that is not explicitly described is when an AudioNode has
connections on its outputs. In this situation non-unit output channel counts
can affect downstream input mixing, and the behavior of some kinds of
downstream nodes (e.g. AudioWorkletNode) depends on whether or not these
connections exist. An implementation however is able to consider an otherwise
unneeded upstream AudioNode as unneeded if it maintains the (now unchangeable)
observable effects on downstream nodes when garbage collection the upstream node.
The section causing the most confusion is in
The releasing of "connection references" to other nodes is kind of consistent
with the model that permits otherwise unneeded upstream nodes to be deleted,
but "disconnect itself from any other AudioNodes which it is connected to"
could be interpreted to mean a disconnection similar to an explicit
disconnect(). Such a disconnection can produce observable behavior changes on
downstream nodes.
#1453 (comment) at least is interpreting this to mean that the effects of the connections are
removed together with the connection references when the AudioNode is deleted.
If this section is trying to define an object lifetime model that is different
from that of JS objects then that is problematic. AudioNodes are JS objects,
and efficient and effective garbage collection algorithms cannot immediately
detect when an object becomes otherwise unneeded. There is a indeterminate
delay before the object is deleted.
Requiring behavior changes when the object is otherwise unneeded leads to
unpredictable behavior, as the behavior may either continue as if the object
is otherwise needed for a long period of time or not at all. Such variations
may occur either between implementations or within implementations.
The text was updated successfully, but these errors were encountered: