Re: Simultaneous destructors
Chris Jones <[email protected]>
| Newsgroups | gmane.comp.mozilla.devel.dom |
|---|---|
| Message-ID | <[email protected]> |
Jason Duell wrote:
> OK, Chris, you've convinced me that the extra 'moar' messages (and
> round-trip
> latency) is only needed when multiple OnDataAvailable events need to
> happen.
> And it seems likely as you suggest that these latencies are not likely
> to be
> noticeable given that parsing, etc. will be underway.
>
Yes, that's one side of the coin. The other side is when network is the
bottleneck; in that case the critical path (--network --> [chrome]
--IPC--> [content]) is the same with or without double buffering.
>>> Explicitly acknowledging messages has additional costs beyond just the IPC
>>> latency and ping-pong times. Most importantly is that we currently have
>>> programming models where we don't have to wait to fire multiple
>>> onDataAvailable events. Since we can't completely change necko, the channel
>>> parent class will have to accept "immediate" calls to
>>> onDataAvailable/onStopRequest and create a queue for them. This is a fair
>>> bit of complexity in the channel classes that we could avoid.
>> I'm not sure about this added complexity. Can't it be avoided by having
>> Necko "queue" OnDataAvailable events by appending the data to an
>> out-buffer?
>
> Um, that /is/ the additional complexity :) Yes, it sounds like
> nothing much in
> theory, but it's certainly going to be more work, and might be subtle
> (we
> currently report to listeners when OnDataAvailable is fired: would we
> consolidate the notifications, too? Firebug might get less accurate
> info about
> network activity this way, for instance).
>
I can't really comment on this because know very little about Necko
code. bsmedberg?
>>> It seems to me that we'd be better off putting more work into IPDL to allow
>>> parent and child to do out-of-sync state transitions. We can prove that the
>>> possible set of transitions is still theoretically sound, and at the same
>>> time reduce the burden on protocol implementers.
>> We could certainly do this, IPDL has all the information it needs. The
>> problem is that this could allow actor state to diverge permanently,
>> which I think would make protocols harder to understand.
>
> I understand that this is a concern in theory, but am wondering how
> likely it is
> in practice. What's the "permanent" divergence here, and why is it so
> bad if we
> still detect/handle all transitions correctly? If we're talking about
> a
> trade-off between the protocol being a bit more subtle (but still
> provably
> correct), versus a "simpler" protocol (only in the sense that
> divergence isn't
> possible: but I still need to add application-orthogonal messages
> like "moar"
> to make it correct, i.e. it's not unambugiously "simpler" IMHO) that
> makes me do more
> work do accomodate IPDL, I'd much rather choose the first.
>
I can offer a hand-wavy argument about why I think that state divergence
makes protocols harder to reason about.
protocol Diverges {
state START:
send M1 goto S1;
recv M2 goto S2;
state S1:
send M1 goto S1;
recv M2 goto S1;
state S2:
send M1 goto S2;
recv M2 goto S2;
};
is a protocol that allows state to permanently diverge. When reasoning
about this protocol, I need to keep the "branch" of the divergence in
mind, and consider two paths (states synced, states not). Now, I could
take this same pattern, recursively graft it into S1 and S2, and now I
have two more branch points. So there are 6 stable states in the
protocol, and my C++ code has to keep track of two levels of possibly
different paths taken through parent/child. Then recursively graft ...
IOW, divergence can make reasoning about protocols a path-sensitive
operation, and my brain doesn't like those. Maybe this is a little too
abstract, but that's where I'm coming from.
>> If we end needing to add this kind of feature (it's still not clear to me that
>> we need it), then IMHO, jduell's |discard| proposal is the best solution to
>> the problem --- we keep the simplicity of "entangled" state, and the
>> programmer has to explicitly acknowledge that he understands the race
>> conditions and wants IPDL to drop the messages that would cause bad
>> transitions.
>
> A nit: my "discard" proposal was just to explicitly drop messages
> when it's
> known that they aren't needed (i.e. an OnDataAvailable arriving after
> the child
> has already Cancel'ed the the channel). I'm not sure what you mean by
> "causing
> bad state transitions", as the example given doesn't transition state
> (we start
> CLOSED and stay CLOSED).
>
I think we're saying the same thing. I understood |discard| to be a
mechanism that allows you to tell the compiler exactly what you wrote
above, that a message is no longer needed.
More concretely, here's what I had in mind. If you run the IPDL
compiler on this protocol,
protocol Channel {
child:
dataavailable(); datadone();
parent:
cancel(); delete();
state NORMAL:
send dataavailable goto NORMAL;
send datadone goto DONE;
recv cancel goto CANCELED;
state DONE:
recv cancel goto DEAD;
state CANCELED:
send datadone goto DEAD;
state DEAD:
recv delete goto DEAD; // ignore |goto DEAD| for now
};
it will complain thusly
/home/cjones/ipdl/Channel.ipdl:10: error: in protocol `Channel' state
`NORMAL', trigger `dataavailable' potentially races (does not commute)
with `cancel'
Specification is not well typed.
With |discard|, you could tell IPDL that "Hey, if I the child receives
dataavailable() in CANCEL, I want to pretend like it never happened.
Drop dataavailable() on the floor." You would communicate this thusly
...
state CANCELED:
discard send datavailable;
send datadone goto DEAD;
...
and the IPDL type checker would shut up about that particular race
condition, and your C++ code would never see dataavailable() in the
CANCELED state. Does that make more sense?
Cheers,
Chris