mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 15:15:23 +00:00
Merge mozilla-central up to this weekend's Electrolysis changes
This commit is contained in:
commit
fa2b155644
@ -293,18 +293,26 @@ class SEND:
|
||||
pretty = 'send'
|
||||
@classmethod
|
||||
def __hash__(cls): return hash(cls.pretty)
|
||||
@classmethod
|
||||
def direction(cls): return OUT
|
||||
class RECV:
|
||||
pretty = 'recv'
|
||||
@classmethod
|
||||
def __hash__(cls): return hash(cls.pretty)
|
||||
@classmethod
|
||||
def direction(cls): return IN
|
||||
class CALL:
|
||||
pretty = 'call'
|
||||
@classmethod
|
||||
def __hash__(cls): return hash(cls.pretty)
|
||||
@classmethod
|
||||
def direction(cls): return OUT
|
||||
class ANSWER:
|
||||
pretty = 'answer'
|
||||
@classmethod
|
||||
def __hash__(cls): return hash(cls.pretty)
|
||||
@classmethod
|
||||
def direction(cls): return IN
|
||||
|
||||
class State(Node):
|
||||
def __init__(self, loc, name):
|
||||
@ -314,6 +322,8 @@ class State(Node):
|
||||
return isinstance(o, State) and o.name == self.name
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
def __ne__(self, o):
|
||||
return not (self == o)
|
||||
def __repr__(self): return '<State %r>'% (self.name)
|
||||
def __str__(self): return '<State %s>'% (self.name)
|
||||
|
||||
|
@ -260,7 +260,7 @@ def p_NamespacedProtocolDefn(p):
|
||||
p[0] = protocol
|
||||
|
||||
def p_ProtocolDefn(p):
|
||||
"""ProtocolDefn : SendSemanticsQual PROTOCOL ID '{' ManagerStmtOpt ManagesStmts MessageDecls TransitionStmts '}' ';'"""
|
||||
"""ProtocolDefn : OptionalSendSemanticsQual PROTOCOL ID '{' ManagerStmtOpt ManagesStmts MessageDecls TransitionStmts '}' ';'"""
|
||||
protocol = Protocol(locFromTok(p, 2))
|
||||
protocol.name = p[3]
|
||||
protocol.sendSemantics = p[1]
|
||||
@ -322,8 +322,7 @@ def p_MessageDirectionLabel(p):
|
||||
assert 0
|
||||
|
||||
def p_MessageDecl(p):
|
||||
"""MessageDecl : SendSemanticsQual MessageBody
|
||||
| MessageBody"""
|
||||
"""MessageDecl : OptionalSendSemanticsQual MessageBody"""
|
||||
if Parser.current.direction is None:
|
||||
p_error(p[1])
|
||||
|
||||
@ -416,6 +415,12 @@ def p_State(p):
|
||||
|
||||
##--------------------
|
||||
## Minor stuff
|
||||
def p_OptionalSendSemanticsQual(p):
|
||||
"""OptionalSendSemanticsQual : SendSemanticsQual
|
||||
| """
|
||||
if 2 == len(p): p[0] = p[1]
|
||||
else: p[0] = ASYNC
|
||||
|
||||
def p_SendSemanticsQual(p):
|
||||
"""SendSemanticsQual : ASYNC
|
||||
| RPC
|
||||
|
@ -791,10 +791,110 @@ class CheckTypes(TcheckVisitor):
|
||||
class CheckStateMachine(TcheckVisitor):
|
||||
def __init__(self, symtab, errors):
|
||||
TcheckVisitor.__init__(self, symtab, errors)
|
||||
self.p = None
|
||||
|
||||
def visitProtocol(self, p):
|
||||
self.p = p
|
||||
self.checkReachability(p)
|
||||
for ts in p.transitionStmts:
|
||||
ts.accept(self)
|
||||
|
||||
def visitTransitionStmt(self, ts):
|
||||
# We want to disallow "race conditions" in protocols. These
|
||||
# can occur when a protocol state machine has triggers of
|
||||
# opposite direction from the same state. That means that,
|
||||
# e.g., the parent could send the child a message at the exact
|
||||
# instance the child sends the parent a message. One of those
|
||||
# messages would (probably) violate the state machine and
|
||||
# cause the child to be terminated. It's obviously very nice
|
||||
# if we can forbid this at the level of IPDL state machines,
|
||||
# rather than resorting to static or dynamic checking of C++
|
||||
# implementation code.
|
||||
#
|
||||
# An easy way to avoid this problem in IPDL is to only allow
|
||||
# "unidirectional" protocol states; that is, from each state,
|
||||
# only send or only recv triggers are allowed. This approach
|
||||
# is taken by the Singularity project's "contract-based
|
||||
# message channels." However, this is a bit of a notational
|
||||
# burden.
|
||||
#
|
||||
# IPDL's solution is to allow allow the IPDL programmer to
|
||||
# define "commutative transitions," that is, pairs of
|
||||
# transitions (A, B) that can happen in either order: first A
|
||||
# then B, or first B then A. So instead of checking state
|
||||
# unidirectionality, we instead do the following two checks.
|
||||
#
|
||||
# *Rule 1*: from a state S, all sync triggers must be of the same
|
||||
# "direction," i.e. only |send| or only |recv|
|
||||
#
|
||||
# (Pairs of sync messages can't commute, because otherwise
|
||||
# deadlock can occur from simultaneously in-flight sync
|
||||
# requests.)
|
||||
#
|
||||
# *Rule 2*: the "Diamond Rule".
|
||||
# from a state S,
|
||||
# for any pair of triggers t1 and t2,
|
||||
# where t1 and t2 have opposite direction,
|
||||
# and t1 transitions to state T1 and t2 to T2,
|
||||
# then the following must be true:
|
||||
# T2 allows the trigger t1, transitioning to state U
|
||||
# T1 allows the trigger t2, transitioning to state U"""
|
||||
#
|
||||
# This is a more formal way of expressing "it doesn't matter
|
||||
# in which order the triggers t1 and t2 occur / are processed."
|
||||
syncdirection = None
|
||||
syncok = True
|
||||
for trans in ts.transitions:
|
||||
if not trans.msg.type.isSync(): continue
|
||||
if syncdirection is None:
|
||||
syncdirection = trans.trigger.direction()
|
||||
elif syncdirection is not trans.trigger.direction():
|
||||
self.error(
|
||||
trans.loc,
|
||||
"sync trigger at state `%s' in protocol `%s' has different direction from earlier sync trigger at same state",
|
||||
ts.state.name, self.p.name)
|
||||
syncok = False
|
||||
# don't check the Diamond Rule if Rule 1 doesn't hold
|
||||
if not syncok:
|
||||
return
|
||||
|
||||
def triggerTarget(S, t):
|
||||
'''Return the state transitioned to from state |S|
|
||||
upon trigger |t|, or None if |t| is not a trigger in |S|.'''
|
||||
for trans in self.p.states[S].transitions:
|
||||
if t.trigger is trans.trigger and t.msg is trans.msg:
|
||||
return trans.toState
|
||||
return None
|
||||
|
||||
ntrans = len(ts.transitions)
|
||||
for i, t1 in enumerate(ts.transitions):
|
||||
for j in xrange(i+1, ntrans):
|
||||
t2 = ts.transitions[j]
|
||||
# if the triggers have the same direction, they can't race,
|
||||
# since only one endpoint can initiate either (and delivery
|
||||
# is in-order)
|
||||
if t1.trigger.direction() == t2.trigger.direction():
|
||||
continue
|
||||
|
||||
T1 = t1.toState
|
||||
T2 = t2.toState
|
||||
|
||||
U1 = triggerTarget(T1, t2)
|
||||
U2 = triggerTarget(T2, t1)
|
||||
|
||||
if U1 is None or U1 != U2:
|
||||
self.error(
|
||||
t2.loc,
|
||||
"trigger `%s' potentially races (does not commute) with `%s' at state `%s' in protocol `%s'",
|
||||
t1.msg.progname, t2.msg.progname,
|
||||
ts.state.name, self.p.name)
|
||||
# don't report more than one Diamond Rule
|
||||
# violation per state. there may be O(n^2) total,
|
||||
# way too many for a human to parse
|
||||
#
|
||||
# XXX/cjones: could set a limit on #printed and stop after
|
||||
# that limit ...
|
||||
return
|
||||
|
||||
def checkReachability(self, p):
|
||||
visited = set() # set(State)
|
||||
|
@ -1,10 +1,26 @@
|
||||
namespace mozilla {
|
||||
namespace test {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// "Hello world" example
|
||||
protocol Test
|
||||
{
|
||||
child:
|
||||
Hello();
|
||||
|
||||
parent:
|
||||
World();
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Example solution to exercise
|
||||
/*
|
||||
sync protocol Test
|
||||
{
|
||||
both:
|
||||
sync Ping() returns (int status);
|
||||
Ping();
|
||||
Pong(int status);
|
||||
|
||||
parent:
|
||||
GetValue(String key);
|
||||
@ -16,14 +32,14 @@ child:
|
||||
TellValues(StringArray keys, StringArray vals);
|
||||
|
||||
state START:
|
||||
recv Ping goto START;
|
||||
send Ping goto START;
|
||||
recv Pong goto START;
|
||||
|
||||
recv SetValue goto HAVE_VALUES;
|
||||
|
||||
state HAVE_VALUES:
|
||||
recv Ping goto HAVE_VALUES;
|
||||
send Ping goto HAVE_VALUES;
|
||||
recv Pong goto HAVE_VALUES;
|
||||
|
||||
recv SetValue goto HAVE_VALUES;
|
||||
|
||||
@ -31,10 +47,20 @@ state HAVE_VALUES:
|
||||
recv GetValues goto TELLING_VALUES;
|
||||
|
||||
state TELLING_VALUE:
|
||||
send Ping goto TELLING_VALUE;
|
||||
recv Pong goto TELLING_VALUE;
|
||||
|
||||
send TellValue goto HAVE_VALUES;
|
||||
|
||||
state TELLING_VALUES:
|
||||
send Ping goto TELLING_VALUES;
|
||||
recv Pong goto TELLING_VALUES;
|
||||
|
||||
send TellValues goto HAVE_VALUES;
|
||||
};
|
||||
//
|
||||
*/
|
||||
|
||||
} // namespace test
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -3,12 +3,38 @@
|
||||
using mozilla::test::TestChild;
|
||||
|
||||
// C++ file contents
|
||||
nsresult TestChild::RecvPing(int* status)
|
||||
TestChild::TestChild()
|
||||
{
|
||||
*status = 42;
|
||||
}
|
||||
|
||||
TestChild::~TestChild()
|
||||
{
|
||||
}
|
||||
|
||||
#if 1
|
||||
//-----------------------------------------------------------------------------
|
||||
// "Hello world" example
|
||||
nsresult TestChild::RecvHello()
|
||||
{
|
||||
puts("[TestChild] Hello, ");
|
||||
SendWorld();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
#elif 0
|
||||
//-----------------------------------------------------------------------------
|
||||
// Example solution to exercise
|
||||
nsresult TestChild::RecvPing()
|
||||
{
|
||||
return SendPong(42);
|
||||
}
|
||||
|
||||
nsresult TestChild::RecvPong(const int& status)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult TestChild::RecvTellValue(
|
||||
const String& key,
|
||||
const String& val)
|
||||
@ -22,11 +48,4 @@ nsresult TestChild::RecvTellValues(
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
TestChild::TestChild()
|
||||
{
|
||||
}
|
||||
|
||||
TestChild::~TestChild()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@ -8,13 +8,25 @@ class TestChild :
|
||||
public TestProtocolChild
|
||||
{
|
||||
protected:
|
||||
virtual nsresult RecvPing(int* status);
|
||||
|
||||
#if 1
|
||||
//-----------------------------------------------------------------------------
|
||||
// "Hello world" example
|
||||
virtual nsresult RecvHello();
|
||||
|
||||
|
||||
#elif 0
|
||||
//-----------------------------------------------------------------------------
|
||||
// Example solution to exercise
|
||||
virtual nsresult RecvPing();
|
||||
virtual nsresult RecvPong(const int& status);
|
||||
virtual nsresult RecvTellValue(
|
||||
const String& key,
|
||||
const String& val);
|
||||
virtual nsresult RecvTellValues(
|
||||
const StringArray& keys,
|
||||
const StringArray& vals);
|
||||
#endif
|
||||
|
||||
public:
|
||||
TestChild();
|
||||
|
@ -2,21 +2,53 @@
|
||||
|
||||
using mozilla::test::TestParent;
|
||||
|
||||
// C++ file contents
|
||||
TestParent::TestParent()
|
||||
{
|
||||
}
|
||||
|
||||
TestParent::~TestParent()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TestParent::DoStuff()
|
||||
{
|
||||
int ping;
|
||||
SendPing(&ping);
|
||||
|
||||
printf("[TestParent] child replied to ping with status code %d\n", ping);
|
||||
#if 1
|
||||
puts("[TestParent] in DoStuff()");
|
||||
SendHello();
|
||||
#elif 0
|
||||
puts("[TestParent] pinging child ...");
|
||||
SendPing();
|
||||
#endif
|
||||
}
|
||||
|
||||
// C++ file contents
|
||||
nsresult TestParent::RecvPing(int* status)
|
||||
|
||||
#if 1
|
||||
//-----------------------------------------------------------------------------
|
||||
// "Hello world" exampl
|
||||
nsresult TestParent::RecvWorld()
|
||||
{
|
||||
puts("[TestParent] world!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
#elif 0
|
||||
//-----------------------------------------------------------------------------
|
||||
// Example solution to exercise
|
||||
nsresult TestParent::RecvPing()
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult TestParent::RecvPong(const int& status)
|
||||
{
|
||||
printf("[TestParent] child replied to ping with status code %d\n", status);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult TestParent::RecvGetValue(const String& key)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
@ -35,10 +67,4 @@ nsresult TestParent::RecvSetValue(
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
TestParent::TestParent()
|
||||
{
|
||||
}
|
||||
|
||||
TestParent::~TestParent()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@ -7,13 +7,25 @@ namespace test {
|
||||
class TestParent :
|
||||
public TestProtocolParent
|
||||
{
|
||||
virtual nsresult RecvPing(int* status);
|
||||
protected:
|
||||
#if 1
|
||||
//-----------------------------------------------------------------------------
|
||||
// "Hello world" example
|
||||
virtual nsresult RecvWorld();
|
||||
|
||||
|
||||
#elif 0
|
||||
//-----------------------------------------------------------------------------
|
||||
// Example solution to exercise
|
||||
virtual nsresult RecvPing();
|
||||
virtual nsresult RecvPong(const int& status);
|
||||
virtual nsresult RecvGetValue(const String& key);
|
||||
virtual nsresult RecvGetValues(const StringArray& keys);
|
||||
virtual nsresult RecvSetValue(
|
||||
const String& key,
|
||||
const String& val,
|
||||
bool* ok);
|
||||
#endif
|
||||
|
||||
public:
|
||||
TestParent();
|
||||
|
@ -527,10 +527,10 @@ XRE_RunTestShell(int aArgc, char* aArgv[])
|
||||
static void
|
||||
IPCTestHarnessMain(TestProcessParent* subprocess)
|
||||
{
|
||||
TestParent parent;
|
||||
TestParent* parent = new TestParent(); // leaks
|
||||
|
||||
parent.Open(subprocess->GetChannel());
|
||||
parent.DoStuff();
|
||||
parent->Open(subprocess->GetChannel());
|
||||
parent->DoStuff();
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
Reference in New Issue
Block a user