[Date Prev][Date Next][Thread Prev][Thread Next][Author Index][Date Index][Thread Index]

SAFEty and the Cross Referencing of BLASTs



Date: Mon, 3 Dec 90 03:18:13 PST
   From: xanadu!michael (Michael McClary)

   > From vlad!mark Sat Dec  1 20:47:34 1990

   ...

   Nevertheless, now that we're using various CONSTRUCT macros, I'd prefer
   to use them everywhere, perhaps with an additional flavor equivalent to a
   bare 'new', rather than hopping back and forth between the normal language
   construct and our extensions.

Sounds good.

   > ... Hmm... I'm
   > suddenly confused about this one (especially the interactions between
   > SAFE and NO_FAULT).  Michael and Dean: any thoughts?  ...

   You'll need to ask Dean about locking.  Shepherds are simple:  At the
   PATRIARCH level only NOFAULT methods can be SAFE, so SAFE is a stronger
   qualification which must include NOFAULT.  (SAFE is also stronger because
   NOFAULT methods can be overridden by faulting methods.)

This makes sense to me.  SAFE in Shepherds implies NOFAULT.  Not vice
versa.

   Yes, you could have SAFE methods defined below the PATRIARCH.  But
   (unless you've found another NOFAULT method) your class should then
   be a COPY rather than a SHEPHERD_ANCESTOR, so no stubs can descend
   from it.  (Also, it should be NOT_A_TYPE, as with everything else
   below a PATRIARCH and isn't another PATRIARCH.)

Exactly the kind of situation I was thinking of.  A method of a
Shepherd implementation class could be SAFE given that the faulting
stub class wasn't a subclass of this class (but rather a subclass of
some common superclass PATRIARCH).

   ...

   When documenting a class in a library, you can never be sure that
   you have found all the subclasses, because more can be defined at
   a later time.  Therefore you can never remove the BLASTS_UNDER()
   entries.

The question is "can you be sure, given that the library that this
output was generated from hasn't changed (and the clients are playing
by the rules)?".  In certain cases (which I didn't go into earlier)
you can.  The most straight-forward is if the class exists only in the
private section of the module and you've scanned the whole module (and
the module lists no "module friends").  More complicated situations
will arise as we try to express more visibility constraints.  For
example, let us call a directory full of modules a mega-module.  Then
we currently have a visibility constraint (enforced *only* in our
makefiles at the moment) that mega-module visibility is as follows:

xpp		seen by everyone
xlatexpp	seen by everyone
febe		seen by febe, server, tapestry?
server		seen only by server
fm		seen by fm, tapestry?
tapestry	seen only by tapestry?
calc		seen only by calc

(It is interesting that I found it easier when generating the above
list to think in terms of who can see a given mega-module than who a
given mega-module can see.  Btw, if anyone spots any errors in the
above list, please say so.)

It would also be useful to list (and enforce) which modules are
exported by a mega-module.  Clients of a mega-module can only see the
public classes of its public modules.  Given this kind of information,
then X4Ref could know that, e.g., all subclasses of any class defined
in server exist only in server.

   (Once LEAF is enforced, of course, BLASTS_UNDER() would not be
   generated when a LEAF member function is called through a pointer
   to its actual class.  On the other hand, that is a "should never
   happen" in our abstract/implementation class style.)

Good observation--LEAF suppresses BLASTS_UNDER.  The other hand,
though, is only a small finger.  Our style tends away from LEAF
methods on exported classes, but this isn't a prohibition.  Declaring
such a method LEAF (once we can enforce it) is just choosing to make a
particular flexibility/time tradeoff in favor of time.  This will
often be appropriate.  (And will become more common as we move into
performance engineering phase.)  We can't get the benefits of inline
without this.

   Retaining the BLASTS_UNDER() term in the documentation gives a (nearly?)
   (complete?) map of who calls what.  This may not be desirable, either
   because it is a lot of information, or because it gives more information
   out with the documentation than we really want to give.  Once we reach
   the shippable documentation, most of the classes are unlikely to be
   subclassed further.  While we are developing, generating a new list
   is better than paying human attention to BLASTS_UNDER().

I wasn't really suggesting that this output from X4Ref become the
documentation in anything like this automatic a manner.  The reason to
include BLASTS_UNDER is so that X4Ref can be used compositionally--
given the X4Ref output for a set of module-groups (and some visibility
info, see below), one can generate the X4Ref output for the containing
module-group.

   In a more formal development environment, there would be a specification
   that would define the actual contract for the modules, and the
   documentation would either BE the specification, or would be written from
   it.  Does the appearance of a non-open-ended list of BLASTS in such a
   document imply a "contract" not to add any more?  (How would you feel
   if your new unix release added a bunch of previously undocumented ERRNO
   values for returns from the standard system calls?)

   I think our documentation should make it very clear that the BLAST
   list is the output from a tool, and that future releases may change
   the set that return from any given member function call.  Also, later
   releases should have, as part of their documentation, a list of member
   functions whose unhandled BLAST set has changed.  Otherwise, adding a
   new BLAST becomes heavyweight for our developers, potentially blowing
   away their already-working code.

I think this is a quite important issue, and I take it that my
comments below are in general agreement with your's above.  

I think the default interpretation of a BLAST list should be: "This is
a list of all the BLASTs you may encounter when invoking this [member]
function (and presumably some documentation as to when you may receive
each) in the current release of the software.  Unless a particular
statement is made otherwise, this list may change (renamings,
regroupings, additions, subtractions).  However, each release will
document what the changes are and how to go about modifying old code
to cope.  Good luck.  We do expect this to eventually settle down."

This seems like an absurdly weak specification.  What makes it
tolerable is that typically an extremely small amount of code actually
will care what the BLAST names and groupings are, and only a moderate
amount of code will care under what additional conditions a BLAST may
happen.  I know this contradicts the systems programming experience
that the bulk of code in a robust reactive system is error handling
(both a server and an interactive system are reactive systems.  A
compiler is not).  The reason I believe this is that virtually
anything may BLAST due to memory exhaustion, and virtually all callers
will prepare for this not with a BLAST_SHIELD but (at most) with a
Bomb.  Such a caller merely passes the BLAST backwards to one of the
few places in the program where there is a BLAST_SHIELD that can deal
with this BLASTs of this type.

I suspect that most UNIX utilities that are prepared for the occurence
of an error when doing a system call deal with the error fairly
generically.  If UNIX vendors added a new ERRNO, extended the table of
strings that perror uses, and re-linked all applications (so that
they'd use the new strings), the vast majority of applications would
still work as well as they used to.  This is not an excuse, just a
consideration to keep in mind when making tradeoffs wrt how early to
turn a "mutually evolving adequate arrangement" into a "contract".

An exception to all this is SAFE.  SAFE is a part of a constract, and
changing a [member] function from SAFE to non SAFE is a severe
non-upwards compatable change.  I like Bjarne's proposal for a
"throw(..)" declaration.  It let's you specify BLAST lists as being
contractual when you are ready to do so, and lets you take the above
approach until then.  I propose that we introduce an X++ macro for
this which currently expands to nothing except for XLint and X4Ref
purposes.  When C++ has exception handling (and we've re-implemented
the Bomb package in terms of it) then we can have our macro expand
into a throw declaration.  X4Ref can bottom out when it sees this
macro just as it can for SAFE.

Unfortunately, introduction of this macro shows that I suggested the
wrong name in my mail message.  This new macro should be called
"MAY_BLAST", whereas the term in the X4Ref output should be
"CAN_BLAST".  There is a wonderful scene about this in the movie
"Avalon". 

   Should the bomb package have a way to define BLASTs that are intended
   as a non-catchable global error?  Is the ALL_BUT mechanism a bad one?

I think this should be dealt with as an instance of a more general
issue: taxonomies of errors.  I believe it would also be good to move
this in the direction of Bjarne's proposal--use a class hierarchy of
Problem objects to encode the taxonomy.  Then, one can frequently
extend BLAST distinctions in the subclass direction without violating
earlier contracts.  Until then, we desperately need ALL_BUT for (at
least) one use in particular: enabling the comm code to forward any
BLAST back across the wire.