-
Notifications
You must be signed in to change notification settings - Fork 168
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
[audioworklet] Revise AWN/AWP instantiation algorithm: structured serialization and abstract operation #1193
Comments
@annevk We don't support |
Yeah, I think you want to invoke StructuredSerialize and StructuredDeserialize, referenced and with their proper names, but you also need to define the processing model of some of this in more detail. E.g., step 4 of "The instantiation of AudioWorkletNode and AudioWorkletProcessor" currently says structured cloning is involved, but I can't find where that actually happens. (That algorithm also weirdly seems to cross threads without any kind of message passing or acknowledment that it's doing so. Is there an issue on that?) |
I think ideally this would be an opportunity to make the actual usage more rigorous in "The instantiation of AudioWorkletNode and AudioWorkletProcessor". In particular, you'd have steps like:
I'm not sure exactly how to phrase the prose references that currently say "structured clone". Maybe something like replacing
by
|
Oh, I see I left my tab open for too long and @annevk said what I was going to say, basically. |
One more question:
@annevk How do you describe cross-thread operation in terms of EcmaScript? The algorithm is largely based on the PaintWorklet, except that it has several cross-thread tasks. Any example for this pattern? |
Well, each thread has an event loop. So what you end up doing is queuing tasks back and forth (message passing through tasks). |
We have several examples of cross-thread messaging in the Web Audio API. When talking to the rendering thread, use a control message. We don't have a traditional event loop, but we have our own mechanism that is compatible with our processing based on rendering quantum. When talking to the main thread, queue a task on the main thread as usual, quoting the current spec:
|
The way worklets are defined they have an event loop, so you'll have to reconcile that somehow I suppose. |
Yes, this is not hard to do. |
I was hoping to find some examples that describe cross-thread operation in terms of ES, but I guess a well-written prose is good enough for this case. |
@hoch in ECMAScript you'd have to use a worker and |
@hoch for the record, you can't describe anything in terms of ECMAScript in the specification. You'll always need to use some abstract concepts. There will be way too many gotchas otherwise. |
@annevk I used abstract methods like Get, Construct and others in the algorithm because I thought this is the norm. Isn't this the ECMAScript? |
Nvm. The document was huge, so I missed the description: "These operations are not a part of the ECMAScript language; they are defined here to solely to aid the specification of the semantics of the ECMAScript language. Other, more specialized abstract operations are defined throughout this specification." So I should refer them the abstract operation of ECMAScript. Thanks @annevk! |
Some of that is needed for worklets I believe, but Map() looks wrong. I'm not familiar enough with the setup to recommend something unfortunately. |
The summary for AudioWG teleconference:
|
@annevk I looked up the web worker's spec to see how the cross-thread operation is done during the instantiation. I could not find a clear example of such thing. Could you point me to any precedence of cross-thread tasks in the spec of worker variants? |
It's a little indirect because it goes through a couple algorithms, but if you start in https://html.spec.whatwg.org/#dom-worker and follow the "run a worker" algorithm you'll see the first step of that starts doing stuff in another thread. |
Thanks @domenic. However, the problem is that how the cross-thread interaction should be described like this:
I couldn't find any appearance of this pattern in Web API. (but my knowledge on Web API is pretty limited) If blocking/unblocking a thread is not allowed, that means AudioWorkletNode can get away with the corresponding AudioWorkletProcessor being absent and WG needs a discussion for it. The worker spec was certainly helpful, but it was mostly about 'doing something in a separate thread' rather than the interaction between two threads which is required for the instantiation of AudioWorkletNode and AudioWorkletProcessor. |
@hoch you're not allowed to block the main thread. You can block other threads with caution. HTML uses "pause" for this (although it uses that for historical "main thread" usage which we really don't want to expand); I agree we need more threading infrastructure to define all these things better, ordinary prose is your best bet for now. I'm not sure why construction needs to be atomic as all instructions to the object will come from subsequent tasks, no? So unless you somehow communicate using shared memory there's no real need for that. |
@annevk I see. I knew it'd be controversial but was worth to try.
td; dr: Non-blocking instantiation makes the timing of the processed audio stream from AWP ambiguous. Long version: AWN(AudioWorkletNode) lives on the main scope/thread as a JS reference for the node and AWP(AudioWorkletProcessor) lives on the worklet scope/rendering thread to process audio stream. If user creates an AWN and the main thread is not blocked, the creation of AWP will be async + parallel. That means we don't know when the AWP is going to be ready to process the audio stream. If everything works out, the closest timing of AWP to be up and ready would be the next render quantum. However, when AWP initialization takes a bit of time then we cannot guarantee when we will hear the sound. We already have the similar issue in PannerNode and I think this blocking node construction is the right thing to do for AudioNode. Because the timing matters. I'll try the "loose prose" approach first. |
So it's only synchronized upon creation and possibly parallel afterwards? I wonder if @padenot discussed this with the Servo folks at Mozilla. |
Yes, that's correct. At least that's how it works in Chrome. The main thread initializes AudioNode and the inner object that processes audio (equivalent of AudioWorkletProcessor), and once they are ready the inner object gets pulled by the rendering thread. |
It's been like that forever in the Web Audio API. You can't have guarantees about Indeed, you can create your There is no need to block any thread or to be atomic in the object creation. @hoch, does that answer your concern? Maybe I'm missing something? |
It means the timing of an AWP being ready will be "unknown". We saw the similar problem with PannerNode in Chrome and the issue is observable; the audio processing module takes too long to be initialized so PannerNode outputs silent. Also, what if the construction of AWP somehow fails? Somehow an AWN must render itself inactive because of the lack of a valid AWP on the other side. You can do whatever (e.g. connect/disconnect) but it won't do anything audible. Is that what we want?
Yes, this is easy enough to do, but the timing of synchronization is still undefined and this makes AudioWorkletNode different from the other nodes except for the PannerNode. (Chrome does the construction in the main thread, atomically.)
I think my wording was a bit extreme with "blocking the main thread", however I think the object creation should be atomic. Also the blocking cost, or the cost of making the process atomic, is supposedly minuscule (10~100µs), so I think I just have to find a better wording for the spec. |
We can make AWN output silence when it's in a weird state:
Then asynchronously notify the user with:
@padenot WDYT? |
For the notification, |
Hmm. I thought you wanted to make the instantiation process non-atomic, asynchronous. Then how can we throw an exception synchronously from the async process?
Agreed. I like it to be silent for the same reason.
Exactly. Having |
Oops sorry I misread AWP for AWN, you're right that it would put the node in error. |
- : introduce asynchronous construction process.
- Introduce structured serialization. - Remove redundant/incorrect abstract operation. - Introduce asynchronous construction process.
- Introduce structured serialization. - Remove redundant/incorrect abstract operation. - Introduce async node construction.
Here's the work-in-progress PR, so we can discuss in F2F: Preview |
From F2F: We'll adopt @hoch's state enumeration proposal. We need language explaining that the We also need to revise the description of how the active source flag is propagated from the rendering thread to the main thread to explicitly state that a message is queued. There are two copies of this flag: one on the AWP (to gate whether process() gets called again) and another on the AWN (to drive the internal reference that affects GC). |
- Introduce structured serialization. - Remove redundant/incorrect abstract operation. - Introduce async node construction.
The instantiation of AWN/AWP requires some thread-specific information for each object. For this purpose, we need some synchronization steps when |
- Introduce structured serialization. - Remove redundant/incorrect abstract operation. - Introduce async node construction.
Fixed by #1265 |
In whatwg/html@97d644c @domenic changed the way cloning works to be serializer-based instead. The Audio API needs to be updated to account for that.
The text was updated successfully, but these errors were encountered: