Neil Kurzman said:
xarax said:
Kieran Simkin said:
what are reentrant functions? What are the characteristics of a
reentrant code ?
what things should be kept in mind while writing a reentrant code?
Smells of homework, but I'd like to know too, not for homework
Obviously homework, the same as the OP's other question
about position independent code.
Reentrancy is a characteristic of a program in memory
that does not modify itself in any way. Thus, multiple
threads (units of work) can concurrently execute the code
without interferring with each other. [...]
Huh???
the question was non-reentrant functions not non-reentrant programs.
non-reentrant function either call themselves or can be call from interrupts
other threads, task ect. Normally you do not have to do anything to the
function. But, the data it uses has limitations. You can not have static
variables in the function. You have to deal with access to globals. [...]
Ok people, everyone just sit down.
A non-reentrant function is a function that cannot properly reenter.
That is, it cannot by called again while a previous instance is still
considered live (i.e., its been called but has not yet returned). Its
not just that its non-recursive -- it cannot call something which
calls something else, etc and eventually call itself again. While the
static variables with multithreading is an obvious example of
non-reentrant functions, there are far simpler ones as well (otherwise
the CLC keystone cops would be in here claiming this was off topic).
For example, suppose qsort() was implemented to choose a completely
random element from each partition with which to pivot. And in the
call back comparison function, in addition to comparing the two
values, you sometimes (chosen on a random basis) recursively called
qsort on some proper (i.e., not identical to the original) subset of
the list. If you think about it, its clear to see that the partitions
will not retain their expected properties and therefore this will not
sort properly. So in a sense qsort is not re-entrant under these
conditions.
Another simple example is nested parsing. Suppose that you want to
split out substrings from a string and pass them to a callback
function (like qsort calls out to a user defined comparison function).
As parameters to your function (lets call it simpleParse()) you might
pass the source string, the string of divider tokens, and a callback
function which gets called on each subset string. Its not hard to see
how to do with very simply with strtok(). So a simple thing you might
like to do is divide the contents of a text file into lines, then
divide the lines into words. So you would think you could so
something like simpleParse(srcText,"\r\n",lines); where lines() would
basically call simpleParse(srcLine," \v\f\t",word). But because of a
defect in the design of the strtok() C library function there is no
way to make it work (because a static referenced by strtok is tracking
a singular string that it is tokenizing.)
The main thing to watch out for with non-reentrant functions is that
nesting calls to itself don't cause it to overwrite some kind of
shared context from any pair of live instances. One class of
functions to worry about are those that store results in
statics/globals that are reused either in subsequent calls or through
calls that it makes itself (through function pointers or other
mechanism.) But the overwriting state need not be static/global -- it
can literally be a context passed in as a parameter as well.
But that isn't to say that if a function uses a static/global or
potentially shared parameter that it will necessarily be
non-reentrant. For example, if it completes all usage of the shared
resource before re-entering, then there is no risk of reentrancy
problems. There may be other combinations of conditions that prevents
a re-entered instance of a function from modifying a shared resource
that a parent instance is modifying. And of course some shared
resources may easily survive randomly ordered modifications regardless
of reentrancy (like a global statistics counter or something like
that).
<OT> Interrupt Service Routines are simplest example of an
asynchronous call that might have reentrancy issues. The problem is
that an ISR can be "pre-empted" with another ISR call to the same call
at any time during its execution. So for shared memory usage, the ISR
doesn't have a choice about when another reentrant call my come to use
that same shared memory. Proper solutions to this are way off topic
in CLC.
Preemtive multithreading of shared memory in essence have the same
problem except that its usually just categorized as "thread safety"
rather than "reentrancy safety". Again, proper solutions to this are
way off topic in CLC, nevertheless sometimes solutions require only
design considerations that can be implemented in ANSI/ISO C.</OT>