[net.lang.c++] Re-entrancy Re-visited

nathan@orstcs.UUCP (nathan) (03/03/86)

"That turns out not to be the case."	-Niven & Pournelle

Under certain FORTRAN compilers, I can write a clean, recursive
Hoare quicksort.  It would not be at all portable.  FORTRAN is
not a recursive language.

Under certain C compilers, I can write a complex-number library
whose functions are usable both in main-line code and in signal
routines.  It would not be at all portable.  C is not a fully
re-entrant language.

What galls is that *most* of the language is!  It's only in the
area of structure handling that the specification is too loose
to permit portability of programs which need re-entrancy.

If, in the language specification, it doesn't say that
interrupts won't mess up mainline code's variables, then I as
a programmer can't count on that fact -- and any program I write
which needs it is immediately, irreparably nonportable.

C++ was intended to address the needs of implementors
of large systems.  The Unix kernel is one such system.
The Unix kernel avoids use of structs as return values.
Are we to avoid returning "class" items in C++ programs,
except in constructors?

I understand that addressing this issue raises some sticky
implementation issues, *all* related to use of C as the
intermediate language representation.  I do not think that it
therefore deserves to be swept under a rug.

After all, the issue is not intractable.  Let's call a bug a bug.
For instance, one could define an explicit subset of C++, called
"C++ but not re-entrant".  Compilers that bypass the C stage, or
are built around a re-entrant C compiler, need not be so hobbled
and could then be called full "C++".

A more difficult solution would be to work around the (quite prevalent)
bug.  For example, the C code generated could allocate return-value
space in the caller's block and generate an extra pointer argument
for the C function whose C++ "antecedent" returns a struct.
The C function would copy its "return value" out via this pointer.
The calling function would use the returned pointer to access
the return value.  Then even pcc-based compilers would work right.
The extra argument wouldn't bother (most) pcc library routines.
C-library calls still wouldn't be re-entrant, but C++ ones would.
The only difficulty would be in object compatibility with other
(unbuggy) C++ compilers.

A note to Dr. Stroustrup:  I hope that in harping on this point I am
not making an enemy.  I think C++ is a tremendous product that
may rescue us all from almost certain Ada-ization.  But as a builder
of real-time systems, I'd love to see some indication that it will
be useful (that is, portable) in what I and my kind do.

	Nathan C. Myers
	{ tektronix | hplabs!hp-pcd }!orstcs!nathan  OR nathan@oregon-state

jack@boring.UUCP (03/07/86)

The fact the functions returning structures are non-reentrant
is not the only problem.

*any* intelligent I/O subsystem will have to keep some sort
of internal data structures, and an interrupt routine will
immedeately blow the program away as soon as it tries to do
I/O. Or, even worse, it will blow the program away two years
later, when you've long gone somewhere else.
The only safe things to do in an interrupt routine are
- setting the InterruptHasOccurred flag,
- longjmp
- exit.

By the way, can anyone point me to an application where I would
want to calculate with complex numbers in an *interrupt routine*:-)
-- 
	Jack Jansen, jack@mcvax.UUCP
	The shell is my oyster.

jimc@ucla-cs.UUCP (03/07/86)

In article <34200005@orstcs.UUCP> nathan@orstcs.UUCP (nathan) writes:
>Under certain C compilers, I can write a complex-number library
>whose functions are usable both in main-line code and in signal
>routines.  It would not be at all portable.  C is not a fully
>re-entrant language.
>
>What galls is that *most* of the language is!  It's only in the
>area of structure handling that the specification is too loose
>to permit portability of programs which need re-entrancy.
>
>If, in the language specification, it doesn't say that
>interrupts won't mess up mainline code's variables, then I as
>a programmer can't count on that fact -- and any program I write
>which needs it is immediately, irreparably nonportable.
>
The draft "C" standard, X3J11/85-138, section B.2.3 "Signals and Exceptions",
states:
   Functions must be implemented such that they may be interrupted at any time
by a signal, and may be called by a signal handler with no alteration to data
with automatic storage duration belonging to earlier invocations...
   The functions in the standard library are not guaranteed to be reentrant...

As for the latter part, I believe it refers to things like asctime that
store results in a static string.  I have recommended a wording change to
"Certain library functions, explicitly noted in the standard, may not be
reentrant."

-- 
James F. Carter            (213) 206-1306
UCLA-SEASnet; 2567 Boelter Hall; 405 Hilgard Ave.; Los Angeles, CA 90024
UUCP:...!{ihnp4,ucbvax,{hao!cepu}}!ucla-cs!jimc  ARPA:jimc@locus.UCLA.EDU

brooks@lll-crg.ARpA (Eugene D. Brooks III) (03/08/86)

>By the way, can anyone point me to an application where I would
There aren't many interrupt routines that need complex arithmetic.
There are however a few parallel applications which use complex arithmetic
and as long as structure return values are not properly reentrant you have
a serious problem with them in a parallel coding environment.

gwyn@brl-smoke.UUCP (03/09/86)

In article <6812@boring.UUCP> jack@mcvax.UUCP (Jack Jansen) writes:
>*any* intelligent I/O subsystem will have to keep some sort
>of internal data structures, and an interrupt routine will
>immedeately blow the program away as soon as it tries to do
>I/O. Or, even worse, it will blow the program away two years
>later, when you've long gone somewhere else.

Well, if one can implement an atomic semaphore (I know how to
do this for a PDP-11 or a VAX), one can implement "conditional
critical regions" to protect data structures from being clobbered
by asynchronous interrupts.  The 3B20A UNIX kernel uses semaphores
for this purpose (it's the only symmetric multiprocessor implementation
of UNIX that I am aware of).  SVR2 STDIO makes a valiant attempt to
permit use of STDIO routines from interrupt handlers.  However, ...

>The only safe things to do in an interrupt routine are
>- setting the InterruptHasOccurred flag,
>- longjmp
>- exit.

... I agree with these sentiments, mostly.  I wouldn't even say
that longjmp is safe, since while the (non-interrupt) manipulation
of data structures is occurring, they will be in an inconsistent
state that could cause trouble later if the manipulations are not
allowed to complete.

To implement operating systems, however, the problem of asynchronous
interrupts must be solved.  Methods for parallel processing have
been published in the past decade or so that are relevant to this
issue.  Ultimately one needs an atomic synchronizer and a queue
to put blocked tasks on when they attempt to enter a locked region.
(A blocked task is allowed to proceed when the locker leaves the
shared region.)  This seems like far too much complexity for normal
user-mode applications, though, unless implemented as a package or
class (probably with a small assembly-language locking module).

henry@utzoo.UUCP (Henry Spencer) (03/12/86)

> The only safe things to do in an interrupt routine are
> - setting the InterruptHasOccurred flag,
> - longjmp
> - exit.

Actually, neither longjmp nor exit is 100% safe.  Longjmp will get you
out fairly safely (unless your function-calling sequence has an interrupt
window, like the V7 pdp11 one did [fixed in SysV]), but things may still
be in a mess inside whatever was interrupted, with disastrous results the
next time you call it.  And exit invokes stdio's cleanup routine, which
might run afoul of the same problem.  _exit should be safe.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry