www.digitalmars.com         C & C++   DMDScript  

D - Design by contract - detecting reentrancy

reply John Nagle <nagle animats.com> writes:
   There's been some interest in what's usually called "design by
contract", with "entry", "exit", and "class invariant" assertions.
The class invariants are supposed to be true whenever control
is not within the object.  If you take that seriously, you have to
be explicit about when control leaves the object.  That's rarely done.

   The difficult problem in the single-thread case is deciding
when control has left the via a call, rather than a return.  If
a call out of an object results in entering the object reentrantly,
the assumption that the object is in a valid (invariant=true) state
at entry is violated.  This often leads to errors.  (It's a 
common problem inside GUI libraries.)

   Does D deal with this problem at all?  I haven't seen
any indication that it does.

   A starting point would be to provide a way to indicate that
you're leaving an object.  I'd once suggested using "public"
for this in C++, so that you'd write a member function like

   void foo() 
   {	x = 1;
	public { /* control has left the object */ }
	y = 2;
   }

Within a "public" block, you can't access private members
of the object.  But you can call public methods of the object.
The "public" block is thus "outside" the object.
The class invariant has to be true at the start of a public
block.

    If you call a function outside an object from within
a member function, without using a "public" block, then
somehow it must be determined that object-reentry 
via recursion is imposible.  This is easy to check at run
time, but hard to detect at compile time.  It's worth
thinking about conservative compile-time detection of recursion. 

    Comments?

					John Nagle
					Animats
Nov 10 2001
parent reply "Walter" <walter digitalmars.com> writes:
The class invariant is called at the entry and exit of each public member
function. I do not see how this can be evaded in the scenario you described.

"John Nagle" <nagle animats.com> wrote in message
news:3BEDDB78.4517D814 animats.com...
    There's been some interest in what's usually called "design by
 contract", with "entry", "exit", and "class invariant" assertions.
 The class invariants are supposed to be true whenever control
 is not within the object.  If you take that seriously, you have to
 be explicit about when control leaves the object.  That's rarely done.

    The difficult problem in the single-thread case is deciding
 when control has left the via a call, rather than a return.  If
 a call out of an object results in entering the object reentrantly,
 the assumption that the object is in a valid (invariant=true) state
 at entry is violated.  This often leads to errors.  (It's a
 common problem inside GUI libraries.)

    Does D deal with this problem at all?  I haven't seen
 any indication that it does.

    A starting point would be to provide a way to indicate that
 you're leaving an object.  I'd once suggested using "public"
 for this in C++, so that you'd write a member function like

    void foo()
    { x = 1;
 public { /* control has left the object */ }
 y = 2;
    }

 Within a "public" block, you can't access private members
 of the object.  But you can call public methods of the object.
 The "public" block is thus "outside" the object.
 The class invariant has to be true at the start of a public
 block.

     If you call a function outside an object from within
 a member function, without using a "public" block, then
 somehow it must be determined that object-reentry
 via recursion is imposible.  This is easy to check at run
 time, but hard to detect at compile time.  It's worth
 thinking about conservative compile-time detection of recursion.

     Comments?

 John Nagle
 Animats

Nov 10 2001
parent reply John Nagle <nagle animats.com> writes:
   If you really know when control is outside the object, you
don't have to check the invariant at entry, just at exit.
Even better, you only have to check the parts of the invariant that
mention variables changed in the function.  This allows big invariants
with less overhead.

   The other case where control "leaves" the object is when
a thread unlocks a "synchronized" object in the Java sense.
That's "leaving the object", since some other thread can get in.
If the language knows about synchronization, it needs to know
about that.

					John Nagle
					Animats

Walter wrote:
 
 The class invariant is called at the entry and exit of each public member
 function. I do not see how this can be evaded in the scenario you described.
 
 "John Nagle" <nagle animats.com> wrote in message
 news:3BEDDB78.4517D814 animats.com...
    There's been some interest in what's usually called "design by
 contract", with "entry", "exit", and "class invariant" assertions.
 The class invariants are supposed to be true whenever control
 is not within the object.  If you take that seriously, you have to
 be explicit about when control leaves the object.  That's rarely done.

    The difficult problem in the single-thread case is deciding
 when control has left the via a call, rather than a return.  If
 a call out of an object results in entering the object reentrantly,
 the assumption that the object is in a valid (invariant=true) state
 at entry is violated.  This often leads to errors.  (It's a
 common problem inside GUI libraries.)

    Does D deal with this problem at all?  I haven't seen
 any indication that it does.

    A starting point would be to provide a way to indicate that
 you're leaving an object.  I'd once suggested using "public"
 for this in C++, so that you'd write a member function like

    void foo()
    { x = 1;
 public { /* control has left the object */ }
 y = 2;
    }

 Within a "public" block, you can't access private members
 of the object.  But you can call public methods of the object.
 The "public" block is thus "outside" the object.
 The class invariant has to be true at the start of a public
 block.

     If you call a function outside an object from within
 a member function, without using a "public" block, then
 somehow it must be determined that object-reentry
 via recursion is imposible.  This is easy to check at run
 time, but hard to detect at compile time.  It's worth
 thinking about conservative compile-time detection of recursion.

     Comments?

 John Nagle
 Animats


Nov 11 2001
next sibling parent "Walter" <walter digitalmars.com> writes:
But another thread getting into the object must necessarilly pass through an
invariant first.

"John Nagle" <nagle animats.com> wrote in message
news:3BEED746.2D5B90F5 animats.com...
    If you really know when control is outside the object, you
 don't have to check the invariant at entry, just at exit.
 Even better, you only have to check the parts of the invariant that
 mention variables changed in the function.  This allows big invariants
 with less overhead.

    The other case where control "leaves" the object is when
 a thread unlocks a "synchronized" object in the Java sense.
 That's "leaving the object", since some other thread can get in.
 If the language knows about synchronization, it needs to know
 about that.

 John Nagle
 Animats

 Walter wrote:
 The class invariant is called at the entry and exit of each public


 function. I do not see how this can be evaded in the scenario you


 "John Nagle" <nagle animats.com> wrote in message
 news:3BEDDB78.4517D814 animats.com...
    There's been some interest in what's usually called "design by
 contract", with "entry", "exit", and "class invariant" assertions.
 The class invariants are supposed to be true whenever control
 is not within the object.  If you take that seriously, you have to
 be explicit about when control leaves the object.  That's rarely done.

    The difficult problem in the single-thread case is deciding
 when control has left the via a call, rather than a return.  If
 a call out of an object results in entering the object reentrantly,
 the assumption that the object is in a valid (invariant=true) state
 at entry is violated.  This often leads to errors.  (It's a
 common problem inside GUI libraries.)

    Does D deal with this problem at all?  I haven't seen
 any indication that it does.

    A starting point would be to provide a way to indicate that
 you're leaving an object.  I'd once suggested using "public"
 for this in C++, so that you'd write a member function like

    void foo()
    { x = 1;
 public { /* control has left the object */ }
 y = 2;
    }

 Within a "public" block, you can't access private members
 of the object.  But you can call public methods of the object.
 The "public" block is thus "outside" the object.
 The class invariant has to be true at the start of a public
 block.

     If you call a function outside an object from within
 a member function, without using a "public" block, then
 somehow it must be determined that object-reentry
 via recursion is imposible.  This is easy to check at run
 time, but hard to detect at compile time.  It's worth
 thinking about conservative compile-time detection of recursion.

     Comments?

 John Nagle
 Animats



Nov 12 2001
prev sibling parent reply Russell Borogove <kaleja estarcion.com> writes:
John Nagle wrote:
 
    If you really know when control is outside the object, you
 don't have to check the invariant at entry, just at exit.
 Even better, you only have to check the parts of the invariant that
 mention variables changed in the function.  This allows big invariants
 with less overhead.

It seems to me that checking the invariant on entry, while redundant in the general case, can also help determine if some bug has trashed the object between calls. =RB
Nov 12 2001
parent "Walter" <walter digitalmars.com> writes:
"Russell Borogove" <kaleja estarcion.com> wrote in message
news:3BF010A6.9EEE7EAD estarcion.com...
 John Nagle wrote:
    If you really know when control is outside the object, you
 don't have to check the invariant at entry, just at exit.
 Even better, you only have to check the parts of the invariant that
 mention variables changed in the function.  This allows big invariants
 with less overhead.

redundant in the general case, can also help determine if some bug has trashed the object between calls.

Yes. After all, D does have pointers, and you can still have pointer bugs <g>. Also, entry invariant checking when combined with the memory stomping of the delete operator should detect detect dangling reference bugs.
Nov 12 2001