Merge mozilla-central up to this weekend's Electrolysis changes

This commit is contained in:
Benjamin Smedberg 2009-07-20 09:41:05 -04:00
commit fa2b155644
9 changed files with 244 additions and 34 deletions

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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();

View File

@ -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