Re: Simultaneous destructors

Benjamin Smedberg <[email protected]>
Newsgroups gmane.comp.mozilla.devel.dom
Message-ID <[email protected]>
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;
}

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.

> (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?
* 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.

(If they aren't re-used, aren't we likely to run out of IDs in normal
runtime situations?)

> (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.

--BDS
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.