Topic: Is object return guaranteed to be reentrant?
Author: derway@cco.caltech.edu (Duane D. Erway)
Date: Mon, 22 Jun 1992 15:30:35 GMT Raw View
One problem with returning a struct in C is that many implementations, (pcc
for instance), did the return in a non-reentrant fashion. This doesn't hurt so
much if you are running in an environment where every execution thread is
running in it's own address space, but in a simple multi-tasking environment,
where all tasks, and interrupt handlers are linked together into one space it
is absolutely necessary.
Has this issue been addressed in C++? Are compiler implementations forced to
do struct/class returns in a reentant way?
They certainly should be.
-Don Erway Opinions expressed herein apparently
don%ndc.uucp@csvax.cs.caltech.edu spontaneously organized themselves.
Author: jss@lucid.com (Jerry Schwarz)
Date: Tue, 23 Jun 92 00:08:37 GMT Raw View
In article <1992Jun22.153035.2663@cco.caltech.edu>, derway@cco.caltech.edu (Duane D. Erway) writes:
|>
|> One problem with returning a struct in C is that many implementations, (pcc
|> for instance), did the return in a non-reentrant fashion. This doesn't hurt so
|> much if you are running in an environment where every execution thread is
|> running in it's own address space, but in a simple multi-tasking environment,
|> where all tasks, and interrupt handlers are linked together into one space it
|> is absolutely necessary.
|>
|> Has this issue been addressed in C++? Are compiler implementations forced to
|> do struct/class returns in a reentant way?
|>
|> They certainly should be.
I agree that compilers ought to generate reentrant code, but I think
it is outside the charter of the ANSI/ISO standardization committee.
Nothing in the language or library as currently being discussed
requires threads or tasks. So it isn't even clear that there is
a way to describe reentrancey.
Introducing threads would require a major revision of the program model.
Even with a sequential program model there is already a
lot of confusion and disagreement about order of
evaluation. Addressing these problems in the presence of multiple
threads would be even more difficult.
Implementations that want to support multiple threads
will have a lot of work to do. Code generation is only one
aspect and probably a minor one. I think library support is
probably more difficult. For example what should happen if more
than one thread tries to insert stuff in cout.
-- Jerry Schwarz
Author: derway@cco.caltech.edu (Duane D. Erway)
Date: Tue, 23 Jun 1992 15:10:08 GMT Raw View
In article <1992Jun23.000837.5879@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
>I agree that compilers ought to generate reentrant code, but I think
>it is outside the charter of the ANSI/ISO standardization committee.
>Nothing in the language or library as currently being discussed
>requires threads or tasks. So it isn't even clear that there is
>a way to describe reentrancey.
>
>Introducing threads would require a major revision of the program model.
>Even with a sequential program model there is already a
>lot of confusion and disagreement about order of
>evaluation. Addressing these problems in the presence of multiple
>threads would be even more difficult.
I don't want to "Introduce threads into the program model". All I want is to
not be prevented from doing it myself. In C, by following some simple rules, I
can know that a function or subroutine is callable reentrantly. One of these
rules is "Do not read and write, (without some sort of mutal exclusion), any
static or global variable".
In C++ and the programming styles it encourages, returning objects is frequent
and crucial. In C, if structure return was known to be non-reentrant for a
particular compiler, it was no big deal to add the rule, "Reentrant functions
may not return structures." In C++ this is a crippling limitation.
>Implementations that want to support multiple threads
>will have a lot of work to do. Code generation is only one
>aspect and probably a minor one. I think library support is
>probably more difficult. For example what should happen if more
>than one thread tries to insert stuff in cout.
>
> -- Jerry Schwarz
Not that much more work than for a regular C library. I modified our library,
which started out as Oasys / Greenhills. 99% of the library was reentrant to
begin with. Some statically allocated buffers existed just to save stack
space, and could easily be fixed. As for cout, the same problem exists with
stdout. I/O devices are sort of inherently non-reentrant, as they produce
different results depending on the order the various threads access them in.
So if you want multiple threads to be able to use a device, you have to put
some sort of access control in place. This should not be taken up by the C++
language or library standard.
Just please don't let compiler implemenations make it impossible!
-Don Erway Opinions expressed herein apparently
don%ndc.uucp@csvax.cs.caltech.edu spontaneously organized themselves.
Author: pop@goedel.mitre.org (Paul O. Perry)
Date: 23 Jun 92 18:02:34 GMT Raw View
> In C, by following some simple rules, I can know that a function or
> subroutine is callable reentrantly. One of these rules is "Do not read
> and write, (without some sort of mutal exclusion), any static or
> global variable".
While not particularly adding to this thread of discussion, I was
interested in knowing about the rest of the rules (given the fact that
structure returns are non-reentrant was news to me). Is there a short
list on this topic, or a book that I should refer to ?
Thanks, Paul.
--
Paul O. Perry Phone: (617) 271-5230
MITRE Corporation Fax: (617) 271-5255
202 Burlington Road pop@mitre.org
Bedford, MA 01730-1420
Author: afscian@watmsg.waterloo.edu (Anthony Scian)
Date: Wed, 24 Jun 1992 00:24:11 GMT Raw View
In article <1992Jun23.151008.28396@cco.caltech.edu> derway@cco.caltech.edu (Duane D. Erway) writes:
>In article <1992Jun23.000837.5879@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
>>I agree that compilers ought to generate reentrant code, but I think
>>it is outside the charter of the ANSI/ISO standardization committee.
>>Nothing in the language or library as currently being discussed
>>requires threads or tasks. So it isn't even clear that there is
>>a way to describe reentrancey.
The ANSI/ISO C standard requires re-entrant calling conventions via a paragraph
that states that a function must work even if an asychronous signal causes a
function to be called again anytime during its execution (re-entrancy).
Any compiler that claims to adhere to the ISO/ANSI C standard must support
this feature. Unfortunately it is almost impossible to test this in a test
suite.
The only compilers that I've seen that support this are GNU C (some option
turns it on), WATCOM C, and JPI C. Borland and Microsoft C/C++ do not
implement a re-entrant calling convention (I could be wrong but I think
returning a double is not re-entrant for Borland and Microsoft).
Anthony
Author: kendall@centerline.com (Sam Kendall)
Date: Wed, 24 Jun 1992 04:20:53 GMT Raw View
Function call and return are completely reentrant on the major RISC
workstations. Structure returning is not reentrant (it uses a static
area) on some 68k boxes including Sun-3's; but even on those machines it
is reentrant to return an object of a class with an explicit copy
constructor, since most C++ compilers do not actually return a C
structure in that case.
afscian@watmsg.waterloo.edu (Anthony Scian) writes:
Unfortunately it is almost impossible to test this [reentrancy] in a
test suite.
On a multiprocessing machine this kind of thing is pretty easy, since
you can have one process looping forever and another process firing
signals at it.
----
Sam Kendall
CenterLine Software, Inc. (formerly Saber Software)
Author: steve@taumet.com (Steve Clamage)
Date: Wed, 24 Jun 1992 15:47:42 GMT Raw View
afscian@watmsg.waterloo.edu (Anthony Scian) writes:
|The ANSI/ISO C standard requires re-entrant calling conventions ...
|The only compilers that I've seen that support this are GNU C (some option
|turns it on), WATCOM C, and JPI C. Borland and Microsoft C/C++ do not
|implement a re-entrant calling convention ...
Oregon C++, a product of my company, has always (since 1987) used
re-entrant conventions for both C and C++. On some systems we offer
non-re-entrant structure return as a compiler option for backward
compatibility with the local C compiler.
--
Steve Clamage, TauMetric Corp, steve@taumet.com
Vice Chair, ANSI C++ Committee, X3J16
Author: kanze@us-es.sel.de (James Kanze (R.30)
Date: Wed, 24 Jun 92 12:13:38 GMT Raw View
In article <1992Jun23.151008.28396@cco.caltech.edu>, derway@cco.caltech.edu (Duane D. Erway) writes:
|> In article <1992Jun23.000837.5879@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
|> >I agree that compilers ought to generate reentrant code, but I think
|> >it is outside the charter of the ANSI/ISO standardization committee.
|> >Nothing in the language or library as currently being discussed
|> >requires threads or tasks. So it isn't even clear that there is
|> >a way to describe reentrancey.
[...]
|> In C, by following some simple rules, I
|> can know that a function or subroutine is callable reentrantly. One of these
|> rules is "Do not read and write, (without some sort of mutal exclusion), any
|> static or global variable".
|>
|> In C++ and the programming styles it encourages, returning objects is frequent
|> and crucial. In C, if structure return was known to be non-reentrant for a
|> particular compiler, it was no big deal to add the rule, "Reentrant functions
|> may not return structures." In C++ this is a crippling limitation.
|>
|> > -- Jerry Schwarz
[...]
|> -Don Erway Opinions expressed herein apparently
|> don%ndc.uucp@csvax.cs.caltech.edu spontaneously organized themselves.
If my memory serves me correctly, the ANSI C committee addressed this issue
(at least partially) without introducing the notion of thread/task, etc.
After all, signals partially introduce the problem.
Basically, it requires the implementation to state what the smallest
(volatile) object that can successfully be modified in a signal handler
without re-entrancy problems (and implicitly, requires that such an object
exist).
I'm afraid I can't remember what was said about functions that return struct's.
If my memory is correct (but I'm not sure), I think it guarentees that a
function returning a struct *can* be invoked inside a signal handler. I seem
in fact to recall hearing that this meant that a lot of compilers were
broken.
Does anyone know the ANSI C++ attitude to this. I don't remember reading
anything about the problem in ARM, but then, since the ARM ignores libraries,
the "signal" problem doesn't come up.
--
James Kanze GABI Software, Sarl.
email: kanze@us-es.sel.de 8 rue du Faisan
67000 Strasbourg
France
Author: derway@cco.caltech.edu (Duane D. Erway)
Date: Wed, 24 Jun 1992 16:21:50 GMT Raw View
In article <BqBqGB.2v@math.waterloo.edu> afscian@watmsg.waterloo.edu (Anthony Scian) writes:
>The ANSI/ISO C standard requires re-entrant calling conventions via a
>paragraph that states that a function must work even if an asychronous signal
>causes a function to be called again anytime during its execution
>(re-entrancy). Any compiler that claims to adhere to the ISO/ANSI C standard
>must support this feature. Unfortunately it is almost impossible to test
>this in a test suite.
>
>The only compilers that I've seen that support this are GNU C (some option
>turns it on), WATCOM C, and JPI C. Borland and Microsoft C/C++ do not
>implement a re-entrant calling convention (I could be wrong but I think
>returning a double is not re-entrant for Borland and Microsoft).
>
>Anthony
-----------------------------------------------------------------
Reentrant calling conventions are easy. Just pass all arguments on the stack,
or even in registers if you save them first. I can't imagine that even
compilers for intel architecture gets this wrong, (though it would surely be
easier to do there).
However, no compiler can guarantee that a function is reentrant. It can only
allow the programmer to write a reentrant function, or prevent the programmer
from doing so. No compiler can make this function reentrant:
void foo(void) {
static int x = 0;
x = x + 3; /* death to reentrancy! */
cout << x << endl;
}
-----------------------------------------------------------------
In article <KENDALL.92Jun23232053@pen.centerline.com> kendall@centerline.com (Sam Kendall) writes:
>Function call and return are completely reentrant on the major RISC
>workstations. Structure returning is not reentrant (it uses a static
>area) on some 68k boxes including Sun-3's; but even on those machines it
>is reentrant to return an object of a class with an explicit copy
>constructor, since most C++ compilers do not actually return a C
>structure in that case.
>----
>Sam Kendall
>CenterLine Software, Inc. (formerly Saber Software)
-----------------------------------------------------------------
For what compilers? It is not, (or not only) a hardware architecture issue. It
is a compiler implementation issue.
Still I am intrigued by the second part of your statement - that a class with
an explicit copy constructor is automatically returned reentrantly. Does this
really seem true? I think you are depending on the compiler making the
optimization of leaving out the temporary copy of a returned object. After all
a byte wise copy constructor is no different from the "built in" copy
constructor. If I have:
class bar {
int x;
public:
bar& bar(bar& orig) {x = orig.x;};
bar& operator=(bar& orig) {x = orig.x;};
setX(int y) {x = y;};
getX(void) {return x;};
}
bar foo(int a, int b) {
bar y;
y.setX(a*a + b*b);
return y;
}
main () {
bar z = foo(47, 89);
cout << z.getX() << endl;
}
So what happens between the "return y" and the initialization of z? I think
most implementations are going to have to copy y somewhere that the caller can
find it. If this somewhere is a single static somewhere, we cannot call foo
reentrantly with safety. If it is somehow arranged to be on the stack, or off
the heap we should be ok.
-----------------------------------------------------------------
In article <1992Jun24.121338.25226@us-es.sel.de> kanze@us-es.sel.de (James Kanze (R.30) writes:
>If my memory serves me correctly, the ANSI C committee addressed this issue
>(at least partially) without introducing the notion of thread/task, etc.
>After all, signals partially introduce the problem.
>
>Basically, it requires the implementation to state what the smallest
>(volatile) object that can successfully be modified in a signal handler
>without re-entrancy problems (and implicitly, requires that such an object
>exist).
>
-----------------------------------------------------------------
So if the ANSI C committee was able to address this issue, perhaps the ANSI
C++ committe will be able to do so as well?
-----------------------------------------------------------------
-Don Erway Opinions expressed herein apparently
don%ndc.uucp@csvax.cs.caltech.edu spontaneously organized themselves.
Author: tom@sco.COM (Tom Kelly)
Date: 24 Jun 92 16:14:52 GMT Raw View
In <1992Jun23.000837.5879@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
> In article <1992Jun22.153035.2663@cco.caltech.edu>, derway@cco.caltech.edu (Duane D. Erway) writes:
> |>
> |> Has this issue been addressed in C++? Are compiler implementations forced to
> |> do struct/class returns in a reentant way?
> |>
> |> They certainly should be.
>
> I agree that compilers ought to generate reentrant code, but I think
> it is outside the charter of the ANSI/ISO standardization committee.
> -- Jerry Schwarz
Author: jss@lucid.com (Jerry Schwarz)
Date: Thu, 25 Jun 92 20:49:36 GMT Raw View
In article <1992Jun24.161452.4838@sco.COM>, tom@sco.COM (Tom Kelly) writes:
Tom points out that I had overlooked a section of the C standard
that expressly addresses the question of "reentrancy" in terms
of signals. I retract my mistatement.
|>
|> The decision to explicitly address signals in the standard
|> was not a trivial one for X3J11, as I recall, since our
|> execution model included only one execution thread. The
|> requirement above was intended, I think, to provide the
|> minimum necessary to support common uses of signals on Unix,
|> and to allow thread simulations to be built on top of C.
|>
|> I suspect that this is one place where it would not be advisable
|> for X3J16 to take a different stance than X3J11. If X3J16
|> is not persuaded to make such a requirement, it should probably
|> be noted in the "Differences from ANSI C" section.
The considerations with regard to signal for X3J16 seem similar
to that for X3J11. But from the point of independent compiler
vendors (such as Lucid) X3J11's approach is flawed.
Making our compiler generate reentrant code is no problem,
but changing the calling sequence is. If we port to a platform
with a "native" non-rentrant calling sequence we would
certainly conform to the calling sequence rather than the printed
standard.
I'm not involved with code generation, so I'm not sure what
platforms have such calling sequences. But I would expect that
no one would want a conforming compiler on such platforms.
-- Jerry Schwarz