Re: Simultaneous destructors
Chris Jones <[email protected]>
| Newsgroups | gmane.comp.mozilla.devel.dom |
|---|---|
| Message-ID | <[email protected]> |
Benjamin Smedberg wrote:
> On 9/17/09 4:25 PM, Chris Jones wrote:
>
>> (1)
>> Make destructor messages part of the protocol of the actors they
>> destruct, rather than part of the manager's protocol. This would imply
>> that dtors can't race with any other message (including other dtors),
>> since dtors transition into a special _DEAD_ state. (I.e., the dtor and
>> another message can never commute.) For example
>>
>> protocol PluginInstance {
>> manages BrowserStream;
>> parent: BrowserStream();
>> // no more ~BrowserStream()
>> }
>>
>> protocol BrowserStream {
>> child: M1();
>> parent: M2();
>>
>> child: delete();
>> // could also be ~BrowserStream()
>>
>> state S1: send M1 goto S2;
>> state S2; recv M2 goto DYING;
>> state DYING: send delete;
>> }
>>
>> This proposal adds a new keyword "delete" which is understood to mean
>> "the destructor message for this protocol's actors." (We could also
>> just reuse ~Protocol, it's just a syntax thing that I don't have strong
>> feelings about.)
>>
>> I think this would work quite well ... except for "stateless" protocols
>> like some in NPAPI. For them it's impossible to declare a non-racy
>> destructor. They could be special-cased in that for them, IPDL would
>> not treat sending a message to a nonexistent actor as a "fatal" error,
>> only to the extent that doing so would cause a spurious fatal error.
>> (Fancier implementations are possible.)
>
> I don't understand how the extra state helps, at least if there are any
> async messages involved:
>
> protocol Channel {
> child: dataavailable();
> child: datadone()
> parent: cancel();
> parent: delete();
>
> state NORMAL:
> send dataavailable goto NORMAL;
> send datadone goto DYING;
> recv cancel goto DYING;
>
> state DYING:
> send delete;
> }
>
This protocol would fail type-checking because it has a race condition
in the NORMAL state, datadone vs. cancel. The protocol is stateful, and
datadone/cancel don't adhere to the Diamond Rule.
> Let's say the parent is sending dataavailable(). The child sends cancel()
> and then immediately sends delete(). Unless the parent somehow acknowledges
> that there has been a state transition to DYING, you could still end up
> sending a message to a dead actor. This is almost exactly how I see necko
> channels being modeled.
>
If this will be common, I agree that may need to special-case dtors
more, i.e. special case them beyond stateless protocols. One way would
be to allow racy dtor messages, and generate special code for protocols
that have racy dtors (IPDL already has that information for any state).
>> (2)
>> Keep dtors as part of the manager protocol, but only allow dtors to be
>> uni-directional. I.e., |both: ~Subprotocol()| would be explicitly
>> disallowed. Then, on the side that *sends* a particular dtor, receiving
>> a message addressed to a nonexistent actor would no longer be fatal.
>
> * Are protocol IDs re-used after destruction?
Currently no, but that probably should change.
> * If so, we need to make sure that we don't reassign the protocol ID until
> we're sure the other side won't be sending a racy message to it.
>
Yup.
>> (3)
>> Do either or none of { (1), (2) }, and treat destructor messages as
>> special in that the IPDL actors aren't "actually" destroyed until the
>> other side ACKs the dtor message. That is, actors can continue to
>> receive messages (though not send them) until the other side replies to
>> the dtor message. It's not really clear what should happen when a
>> "half-deleted" actor receives a message. Options:
>> (a) IPDL automatically returns EDESTROYED or something like that
>> (b) IPDL invokes a special handler that decides what to do
>> (c) the actor is marked as DEAD, that state is visible to C++, and
>> it's up to the implementation to take care of itself.
>>
>>
>> Strong opinions? I lean towards (1) plus (3a), that is, use (3a) for
>> stateless protocols.
>
> Right now I don't see how #1 helps, so I'm leaning towards 3a or 3c. 3c has
> the advantage that on the child side IPDL methods still cannot fail, which
> avoids some error-checking code.
>
I still lean towards (1) plus (3a), possibly adding to (1) the
special-case of racy dtors in stateful protocols. It seems to me that
(3c) still requires error checking, or at least |actor->IsLive()| checks
which I don't think would save much code compared to error checks.
Cheers,
Chris