Re: [webrtc-pc] The threading model of webrtc-pc: When are effects of in-parallel stuff surfaced? (#2502)

> I want to address those good ideas, but first I'd like to separate interpretation from remedy, because I don't accept those premises as interpretations of the current spec.

That's OK. I suppose multiple interpretations are valid when we are discussing something that the authors of either spec probably never thought of. Let's see if we can find agreement on what we _want_ the spec(s) to say :)

> **My interpretation is in #2476:** there's one set of transceivers in webrtc-pc that JSEP reads and modifies from a different thread.

That's an understandable interpretation, but one that that implies races in both webrtc-pc and JSEP. I fear this interpretation will lead to a lot of monkey patching.

### **In response to what is possible (whether or not we think its a good idea)**

> > **2. The API of JSEP (its set of transceivers and which signaling state it is in) is a single-threaded toolkit**
> 
> I'm not sure it matters how we characterize JSEP here, as we cannot "queue" or chain addTrack, because it's a synchronous method.

Prepare for cans of worms, but... :) perhaps we can? Grab your favourite tinfoil hat and follow me!

Feel free to disagree with this interpretation or approach, but try to help me understand if this actually works... Chrome is doing something similar today except that it blocks the JS thread on the JSEP thread any time it tries to do something "synchronous" (which we obviously shouldn't do). That might avoid some of the complexity of the proposed interpretation which relies on "dummy objects" (we don't have them today):

You see, addTrack() cannot actually fail, and neither can any of the _synchronous_ control surfaces exposed by the [RTP Media API](https://w3c.github.io/webrtc-pc/#rtp-media-api). So you could return a dummy transceiver object and start the process of performing AddTrack() on the JSEP thread in-parallel, unblocking the JS thread which has obtained either an existing transceiver or a dummy transceiver for the to-be-created transceiver.

You want to do `transceiver.direction = 'inactive'` on the returned object, even though the JSEP corresponding object might not exist yet? That's fine, we just queue a task to update the corresponding JSEP transceiver's direction to `'inactive'`. In the meantime, the `[[Direction]]` internal slot will return `'inactive'` and there is no way for you to observe what the JSEP transceiver's direction is synchronously.

If you set the direction followed by createOffer(), you will see `inactive` reflected in the SDP because by the time CreateOffer() happens in the JSEP thread, JSEP will already have performed the direction setting. I imagine all communication JS -> JSEP happens with PostTask, and all communication JSEP -> JS happens with PostTask as well.

JS modifies things like "direction". JSEP modifies things like "currentDirection". It's OK to be out-of-sync for a few milliseconds, because we're not depending on the same states at the same time.

I'm not aware of any scenario where the fact that JS and JSEP transceivers are out-of-sync causes a problem except for when there is a synchronous method that relies on the result of asynchronous operations. I.e. SRD+addTrack. Is there any other example than addTrack where we have a synchronous operation depending on asynchronous events?

There are other synchronous methods, like `pc.close()` or `transceiver.stop()`. But these are `void`, and the effect on any JS-exposed attributes as a result of these methods are predictable and could be replaced by a "dummy object" if we want to avoid blocking the JS thread on the JSEP thread. Today, Chrome blocks the JS thread on the JSEP thread, but I imagine setting internal slots and dummies could allow us to stop blocking it.

### **In response to use case C)**

> What I agree with is JSEP's intent here seems to clearly be C in [my previous comment](https://github.com/w3c/webrtc-pc/issues/2502#issuecomment-606668087).

But JSEP also does not seem to be multi-thread aware, so in JSEP, any example of `SRD(); AddTrack();` would be the JS-equivalent of `await SRD(); addTrack();`. And for that, the problem we are discussing does not arise. I believe the problems we are discussing are original to webrtc-pc, because it is webrtc-pc that introduced multiple threads. Thus, it is webrtc-pc that has to ensure that we do not get any contradictions.

> ```
> pc.setRemoteDescription(offer); // note: no await
> pc.addTrack(track, stream); // Supposed to always get associated
> ```

In that example, if we want addTrack() to be associated with a transceiver that was created by the in-parallel operation SRD(), why are we not awaiting for SRD() before we do addTrack()?

Is this really a use case we need to support or is this an example of racy application logic? In which case, does it matter that it doesn't get associated?

The example of a user clicking a button after SRD but before the promise is resolved is even more random. The user clicking a button is inherently racy with other application code.

-- 
GitHub Notification of comment by henbos
Please view or discuss this issue at https://github.com/w3c/webrtc-pc/issues/2502#issuecomment-606726811 using your GitHub account

Received on Tuesday, 31 March 2020 16:13:30 UTC