tedj@hpcilzb.HP.COM (Ted Johnson) (07/29/88)
Can someone please explain the difference between "reentrant" and "non-reentrant" code? Thanks! -Ted
roy@phri.UUCP (Roy Smith) (07/31/88)
In article <1670001@hpcilzb.HP.COM> tedj@hpcilzb.HP.COM (Ted Johnson) writes: > Can someone please explain the difference between "reentrant" and > "non-reentrant" code? Thanks! Reentrant code is, quite simply, code you can reenter without ill effect. Usually this means that each invocation of the code gets its own set of variables, although this is not a sufficient condition to make something reentrant. Take a typical example in C, a recursve factorial routine: fact (i) int i; { return (i<2 ? i : i*fact(i-1)); } If you call fact(5), the first instance of i is 5. Fact calls itself with an argument of i=4. Now you have two instances of fact in the process of being executed. This works because i is automatic; each invocation of fact gets a new piece of memory in which to store i. Now, change the routine to look like: fact (i) int i; { static j; j = i; return (j<2 ? j : j*fact(j-1)); } and you end up with a bit of non-reentrant code: each invocation of fact still has its own instance of i, but they all share the same j. The second time fact gets called, it overwrites the value of j still being used. Actually, what I've just shown is a bit of code which is not recursively reentrant, which is not quite the same thing as not being reentrant. A better example would be something like: sayfoo() { static char *str = "foo\n"; while (*str) { putchar (*str); str++; } } The first time you call sayfoo(), it prints "foo\n", but each other time you call it, it just returns without printing anything. Any code with static variables in it is likely to be non-reentrant. This generally means Fortran code is non-reentrant since Fortran uses static storage. Self modifying code is also almost certainly non-reentrant. Sometimes code is reentrant except for certain windows of vulnerability which much be locked around. A typical example is a Unix device driver. -- Roy Smith, System Administrator Public Health Research Institute {allegra,philabs,cmcl2,rutgers}!phri!roy -or- phri!roy@uunet.uu.net "The connector is the network"
anton@postgres.uucp (Jeff Anton) (08/01/88)
In article <3409@phri.UUCP> roy@phri.UUCP (Roy Smith) writes: >In article <1670001@hpcilzb.HP.COM> tedj@hpcilzb.HP.COM (Ted Johnson) writes: >> Can someone please explain the difference between "reentrant" and >> "non-reentrant" code? Thanks! > > Reentrant code is, quite simply, code you can reenter without ill >effect. Usually this means that each invocation of the code gets its own >set of variables, although this is not a sufficient condition to make >something reentrant. Take a typical example in C, a recursve factorial >routine: > Reentrantcy is generally not a problem for most code since most code deals with one thread of execution. However, you might run against the problem if you have a signal handler which uses code which you also use durring normal execution. Bewarned, recursive is not reentrant. Code can be recursive without being reentrant. Since recursion is part of the design of a code sample, that code can be written to do recursion only when the environment of the code is ready. You could, for example, have a static variable in a recursive routine so long as you no longer use that variable after you make the recursive call, or only use it after the last recursive call. Also, you can have reentrant code without being recursive. This is very common and most C code is reentrant. Recursion is part of the design of code. For example: /* recursive and reentrant */ unsigned fact1(i) unsigned i; { if (i < 3) return(i); return (i*fact1(i-1)); } /* reentrant */ unsigned fact2(i) unsigned i; { unsigned v; if (i < 3) return(i); v = 1; while (i > 1) v *= i--; return(v); } /* recursive - kids, don't try this at home */ unsigned fact3(i) unsigned i; { static unsigned v; if (i < 3) return(v = i); fact3(i-1); return(v *= i); } /* badness */ unsigned fact4(i) unsigned i; { static unsigned v; if (i < 3) return(i); v = 1; while (i > 1) v *= i--; return(v); } main() { printf("fact1(5) = %u\n", fact1(5)); printf("fact2(5) = %u\n", fact2(5)); printf("fact3(5) = %u\n", fact3(5)); printf("fact4(5) = %u\n", fact4(5)); } -------------------------------------------------------------- At the end of the journey don't except me to stay - Pink Floyd Obscured by Clouds Jeff Anton anton@postgres.berkeley.edu
paul@csnz.nz (Paul Gillingwater) (08/01/88)
In article <1670001@hpcilzb.HP.COM> tedj@hpcilzb.HP.COM (Ted Johnson) writes: > >Can someone please explain the difference between "reentrant" and >"non-reentrant" code? Thanks! Many of us down-under probably don't bother replying to questions because the delays means that it's already been answered, but what the heck, here goes anyway.... ;-) Back in 1979 when I was writing multi-tasking operating systems for M6800's for process control applications, I found out the hard way the difference. When I wrote modules that might need to be called concurrently to respond to different sources of interrupts, I found that if an interrupt was currently being processed (and it had to be done real fast so no use setting a flag to process later) when another interrupt came in, the code would be executed again (i.e. re-entered) before the first call could finish and clean up after itself. The result of this was that any local variables (read absolute addresses to RAM - this is M6800 assembler, remember!) were overwritten. The solution was simple - just use the X register to maintain a heap - sort of like a stack, only starting at the bottom of RAM. Any local variables then became just offsets from this pointer (which WAS maintained at a fixed place, and had to be accessed under special Test-and-set semaphore control. Of course any decent HLL will look after local variables in a clean manner. So, to summarise, re-entrant code doesn't mind being entered any number of times (subject to stack/heap space), and won't do things that can't be undone. It cleans up after itself (if well written), whilst non-re-entrant code (like the IBM ROM BIOS) doesn't and isn't. MS-DOS - Just say Gnu! -- Paul Gillingwater, Computer Sciences Call this BBS - Magic Tower (24 hours) paul@csnz.nz (vuwcomp!dsiramd!csnz) NZ +64 4 753 561 8N1 TowerNet software P.O.Box 929, Wellington, NEW ZEALAND V21/V23/V22/V22bis/Bell 103/Bell 212A Vox: +64 4 846194, Fax: +64 4 843924 "All things must parse"-ancient proverb
bzs@prep.ai.mit.edu (Barry Shein) (08/06/88)
The simplest way to understand re-entrant code is to just understand that functionally it requires that all variables accessed (plus or minus intended global side-effects, a special case) are private to the invocation of the subroutine, like automatic variables (a C routine with no statics or globals is re-entrant because all automatics are stack relative, perhaps somewhere there is an implementation which violates that but it would be a questionable implementation.) If you think of it in psuedo-assembler consider: ADD A,B,C # add A to B yielding C A: .WORD B: .WORD C: .WORD NOT re-entrant since a subsequent entry to this code would modify the same A,B,C locations. ADD 0(FP),4(FP),8(FP) If FP is a pointer to an area provided on entry to the code and every entry will have its own FP value (typically a stack area, but not necessarily) then no matter how many times you are simultaneously executing that area of code the source and destinations will be to/from different areas of memory, voila', re-entrant. There's nothing more to it. The usefulness is obvious, sometimes while running a piece of code you can get an interrupt (eg. running an output routine and a timer goes off, say you were doing a printf() when the clock DINGed) and then, before the interrupt is finished, it calls the same code (eg. the timer handler uses printf().) If printf() were re-entrant then it would work out, returning from the interrupt later would continue with your original printf().) Note: printf() is inherently non-re-entrant (why? because it will always modify the stdout buffer, thus you could get output interleaved or worse if FILE * items weren't updated correctly when the interrupt occurred.) That example is purposely bad to cause you to think thru the issues. -Barry Shein, Whereabouts Unknown