Re: Simultaneous destructors
Benjamin Smedberg <[email protected]>
| Newsgroups | gmane.comp.mozilla.devel.dom |
|---|---|
| Message-ID | <[email protected]> |
On 10/7/09 4:13 PM, Chris Jones wrote:
> I was remiss in not showing how the races could be avoided in this
> example. The way to avoid them is a two-phase delete like what I
> described in an earlier post. For this protocol, it would look like
>
>
> protocol Channel {
> child:
> dataavailable();
> datadone(); NOTREACHED(); //<--ignore this
> parent:
> moar();
> cancel();
> delete();
>
> state NORMAL:
> send dataavailable goto SENTDATA;
> send datadone goto DONE;
> recv cancel goto CANCELED;
>
> state SENTDATA:
> recv moar goto NORMAL;
> recv cancel goto DEAD;
>
> state DONE:
> recv cancel goto DEAD;
>
> state CANCELED:
> send dataavailable goto DEAD;
> send datadone goto DEAD;
>
> state DEAD:
> recv delete goto __deleted__;
>
> // IPDL would generate this "state" automatically, so it can be ignored
> state __deleted__: send NOTREACHED goto __deleted__;
> };
> If the child sends cancel(), then a delete sequence is initiated,
> finished by the child sending the actual |delete()| message back to the
> parent. The tricky part here is that if the child sends an "abrupt
> cancel", a cancel from the NORMAL state, then the child must wait for
> the parent to ack the cancel with datavailable or datadone before
> sending the actual |delete()| message.
Well, I don't think you can use dataavailable/datadone for the ack messages
(since there may already be those messages in the async pipeline). So you'd
need an explicit "acknowledgecancel" message, right? But otherwise, this
seems to be theoretically sound.
It seems like you could apply the same idea to the mostly-stateless NPAPI
protocols, even... you'd just have a single main state for normal operation
and a secondary CANCELED state, and perhaps some logic to deal with
interleaved RPC messages.
> The substantial modification from the original protocol is the addition
> of the SENTDATA state and the "moar()" message. This captures the
> decision a child makes between wanting more data vs. cancelling. It
> prevents two dataavailable() messages from being sent in a row. This
> might seem strange, but we'll need this feature when the child and
> parent are transferring network data through two shmem buffers. The
> |moar()| message is where the child would send back to the parent a
> handle to the "free" shmem buffer (i.e., the one it's not currently
> using). (The parent would then fill that buffer and transfer it back to
> the child using |datavailable()|).
Oh, I misread the protocol. Although it might be good/necessary to use a
single shmem buffer like this, it's not strictly necessary to prevent
racing, right? Or to put it another way, is the following protocol is also
race-free and allows multiple dataavailable() calls in a row?
protocol Channel {
child:
dataavailable();
datadone();
oncanceled()
parent:
cancel();
delete();
state NORMAL:
send dataavailable goto NORMAL;
send datadone goto DONE;
recv cancel goto CANCELED;
state DONE:
recv cancel goto DONE;
recv delete goto __deleted__;
state CANCELED:
send dataavailable goto CANCELED;
send datadone goto CANCELED;
send oncanceled goto DONE;
};
> I agree that two-phase deletion is complicated, but (i) it's the price
> to be paid for race-free deletion; (ii) two-phase deletion is a kind of
> "IPDL pattern", like "empty subprotocols", that can be documented and
> copied by other protocols.
Ok, let's try it. Hopefully we can develop some mixins/C++ helpers which
make two-phase cancellation less confusing for developers: e.g. something
which would automatically throw away dataavailable in the CANCELED state.
--BDS