W3C home > Mailing lists > Public > public-webrtc@w3.org > June 2013

Re: Operations in invalid states: Exceptions don't make sense.

From: Adam Bergkvist <adam.bergkvist@ericsson.com>
Date: Thu, 20 Jun 2013 10:48:26 +0200
Message-ID: <51C2C1DA.8070207@ericsson.com>
To: Jan-Ivar Bruaroey <jib@mozilla.com>
CC: "public-webrtc@w3.org" <public-webrtc@w3.org>
On 2013-06-19 18:19, Jan-Ivar Bruaroey wrote:
> On 6/19/13 2:47 AM, Adam Bergkvist wrote:
>> On 2013-06-18 23:02, Jan-Ivar Bruaroey wrote:
>>> I can think of no cases, thankfully. My understanding is that the queue
>>> is there to make the API deterministic, rather than encourage enqueuing.
>>> E.g.:
>>
>> Thank you for clarifying this. But is this new approach any easier
>> than classical states?
>
> We may disagree on the classifications "new" and "classical" here. We
> have a state machine affected by operations that may fail. The classical
> approach, to me, is to transition state not until operational steps
> succeed, to preserve a sane state transition diagram. [1] - This means
> that, since our operations are asynchronous, state must transition at
> the completion of our async calls, not at the beginning.
>
> Introducing additional state(s) at the beginning (lets call it "the time
> of intention"), as you're suggesting, I would call new.

I fully agree with your description of the classical approach with 
states; the transition to the "target state" happens when the operation 
succeeds. But "transition states", used when an async operation needs to 
complete before the "target state" can be set, isn't a new thing. See 
WebSocket's CONNECTING and CLOSING states and EventSource's CONNECTING 
state.

>>>      // Bad code. state=have_local_offer
>>>      pc.setRemoteDescription(answer1, success, mayfail);
>>>      pc.setRemoteDescription(answer2, success, mayfail);
>>>
>>> Without a queue, the second call could succeed or fail based on whether
>>> the first call has completed.  With a queue in place it becomes
>>> deterministic: If call 1 succeeds, then call 2 always fails
>>> (state=stable). If call 1 fails, then call 2 has the same starting point
>>> (state=have_local_offer), always.
>>>
>>> Why care? Non-deterministic behavior would be bad for
>>> documentation-reasons alone (having to mention useless edge-cases like
>>> this).
>>>
>>
>> With a processing state, the second call would always fail. That's
>> deterministic and observable from JS (compared to a queue in the
>> background).
>>
>>>> Wouldn't it be simpler to have a model where the PeerConnection enters
>>>> a "processing" state when, for example, setLocalDescription() is
>>>> called, and the state is updated in the task that fires the methods
>>>> success or error callback? The "single operation at a time" rule would
>>>> then be enforced by the PeerConnection signaling state.
>>>
>>> Yes, we could have done that. It would let you throw on bad state, but
>>> at the cost of doubling the number of states in the state machine. I'm
>>> not sure it is simpler.
>>>
>>
>> A single processing state would be enough to start with, and it could
>> be expanded if developers asked for more.
>
> You want to do this for every operation, right? Then I think you mean a
> flag, i.e. a doubling of states effectively. Otherwise, please draw me
> the new state transition diagram.
>
> If you change state at the "time of intention", then you have to handle
> backing out to the previous state if/when the operation fails. With only
> a single "processing" state there's not enough info in the state model
> itself to know the previous state, so that's not a state. And
> signalingstatechange returning "processing" isn't very informative.
>
> I believe you effectively have this in your suggestion:
>
> enum RTCSignalingState {
>      "processing-stable",
>      "stable",
>      "processing-have-local-offer",
>      "have-local-offer",
>      "processing-have-remote-offer",
>      "have-remote-offer",
>      "processing-have-local-pranswer",
>      "have-local-pranswer",
>      "processing-have-remote-pranswer",
>      "have-remote-pranswer",
>      "processing-closed",
>      "closed"
> };
>
> That doesn't seem simpler to me.
>

The full state machine would look something like you describe. I would 
remove the "have" from the transition states though 
("processing-have-local-offer" -> "processing-local-offer"). I don't 
think it gets more complicated; it gets more complete. The transition 
states sits nicely between our current states in the diagram.

I think this discloses what really happens instead of the PeerConnection 
being in "stable" state and having some operations on a queue that 
possibly might change the state and the target state is dependent on 
which operations that are on the queue and the outcome of these.

/Adam

> [1] Our diagram
> http://dev.w3.org/2011/webrtc/editor/webrtc.html#rtcpeerstate-enum
Received on Thursday, 20 June 2013 08:48:54 UTC

This archive was generated by hypermail 2.3.1 : Monday, 23 October 2017 15:19:33 UTC