![]() | This article is rated Start-class on Wikipedia's
content assessment scale. It is of interest to the following WikiProjects: | ||||||||||||||||||||||||||||||||||
|
|
This definition is way too narrow. Wouter Lievens 09:46, 8 Apr 2005 (UTC)
The mention of FreeBSD's VFS is relevant, however the follow up about DragonFlyBSD seems to me to be off-topic. —Preceding unsigned comment added by 62.253.64.17 ( talk • contribs)
I don't think recursion is correct in this context. A function can be re-entered from more than one thread without being called recursively. Recursive implies the function calls itself. Peter Ritchie 19:48, 5 October 2006 (UTC)
Shouldn't it be "multiple threads" instead of "multiple processes"?
Here is a function which is idempotent but not reentrant:
int global; int f(int * i) { global++; *i = global; global--; }
Or am I misunderstanding something? —Preceding unsigned comment added by 199.172.169.86 ( talk) 10:15, 25 February 2011 (UTC)
I don't know very much about functional programming, but aren't functional programming languages reentrant (variables and functions, and even syntax (in Scheme for example) ? —Preceding unsigned comment added by 83.214.221.148 ( talk • contribs)
This article is inaccurate. Someone more generous than I should get out an OS textbook and revise it. JBW012307
The lisp dialects are re-entrant they just don't seem to have a need to call it other than recursion :). W/re the usage of re-entrant as presented here, isn't Scheme's call/cc an example of what is being discussed? If so, I don't understand why there is an implicit suggestion that re-entrant subrs are directly correlated with thread safety and `concurrency' issues i.e. w/ call/cc the stack is elsewhere thread or no. Lambda-mon key ( talk) 01:05, 5 March 2010 (UTC)
Back in the day, I remember the concept of serially reentrant. A subroutine was serially reentrant even though it manipulated global variables because a semaphore guaranteed at most one thread was actively executing in the subroutine at a given time. The global variables had to be protected by the semaphore in order for this work properly. Static local variables (those not allocated in a stack frame or from a heap) would similarly be protected. —Preceding unsigned comment added by 70.108.186.46 ( talk • contribs)
I think the definition is too sloppy: Consider the case of an object oriented language. An object x could refer to another object y. Now a method in the class of object x returns a value which depends on the state of object y. If the class of object y is mutable, then this is a side effect which influences the result of the method.
Now the problem with the definition is that in its strict sense, this method *is* re-entrant because there is no static data involved in this example. But I am led to believe that in its true meaning the method is *not* reentrant because the return value depends on side effects.
So I suggest the following definition: A reentrant piece of code is a pure function in mathematical terms, which means that its output solely depends on its input parameters, without any side effects.
This implies that the piece of code cannot use static, global or any other data which is not an input parameter. It also implies that the code cannot call other code which does not obeye these requirements. Finally, it implies that the piece of code is thread safe, because the code can only use variables which are local to the current thread (which are usually held on the stack by most programming languages). —Preceding unsigned comment added by 217.83.33.125 ( talk • contribs)
int f(int i) { int priv = i; priv = priv + 2; return priv; } int g(int i) { int priv = i; return f(priv) + 2; } int main() { g(1); return 0; }
Why is this example reentrant? Is priv
unique to each simultaneous process/thread or is it shared by all? --
Abdull
10:40, 13 July 2007 (UTC)
priv
is unique to each execution of f()
and g()
because priv is allocated on the stack of the current thread. Possibly this could be mentioned in the article, but I don't think it's necessary, either. --
Johngiors
06:50, 26 September 2007 (UTC)int f(int i) { return i + 2; } int g(int i) { return f(i) + 2; } int main() { g(1); return 0; }
the parameter values are unique for each call. —Preceding unsigned comment added by 85.144.94.32 ( talk) 13:59, 1 May 2008 (UTC)
Agreed. I have re-written the reentrant version without temporary variables, as I think they only served to obfuscate the code. TOGoS ( talk) 23:00, 17 June 2008 (UTC)
"Despite a common misconception, this is not the same as being designed in such a way that a single copy of the program's instructions, in memory, can be shared."
-- Jerome Potts 17:16, 19 August 2007 (UTC)
Sections of this article are almost redundant copies of the first external link: http://www.ibm.com/developerworks/linux/library/l-reent.html
Compare:
Reentrance and thread-safety are separate concepts: a function can be either reentrant, thread-safe, both, or neither...
and
Non-reentrant functions are thread-unsafe. Furthermore, it may be impossible to make a non-reentrant function thread-safe.
to:
Don't confuse reentrance with thread-safety. From the programmer perspective, these two are separate concepts: a function can be reentrant, thread-safe, both, or neither. Non-reentrant functions cannot be used by multiple threads. Moreover, it may be impossible to make a non-reentrant function thread-safe.
TheShagg 20:26, 12 October 2007 (UTC)
I don't think the following statement is correct:
"Non-reentrant functions are thread-unsafe. Furthermore, it may be impossible to make a non-reentrant function thread-safe."
I think you can have a thread safe function that is non reentrant. Maybe the write meant something like this?
"Thread-unsafe functions are non-reentrant. Furthermore, it may be [or even is] impossible to make a thread-unsafe function reentrant" —Preceding unsigned comment added by Hnassif ( talk • contribs) 17:30, 19 October 2007 (UTC)
The article seems to be assuming a different definition of reentrancy from the one with which I am familiar -- in particular, the definition with which I'm familiar says that a function is reentrant iff it's safe for the function to be called multiple times simultaneously from the same thread of execution. Using that definition, the notions of thread-safety and reentrancy are orthogonal (a function can be one, the other, both or neither). If this (useful!) notion is not called reentrancy, what /is/ it called? 212.44.26.44 ( talk) 17:05, 24 August 2009 (UTC)
Async-signal-safe, maybe? 68.255.109.73 ( talk) 18:18, 4 December 2010 (UTC)
Ad ads ;). There's been mentioned 2 guys for no aparent reason. It is nice they recomend something, sure. Your favourite OS teacher doesn't?
Explanations. I thought it might be nice to add some explanations for girls, who cram, and guys, who are just mildly curious. I also called it a derivation. It might not be a best name. Feel free to change mine mistakes ;).
Simplification. There we're 2 sentences which I thought would better be swapped. To me the second seemed more like natural explanation. Hmm
Citations. There are few claims in "derivation and explanation of rules" that I have no support. I am too lazy to look it up and ,hey, wiki-kids might practice google hunting. 86.61.232.26 ( talk) 23:56, 26 April 2009 (UTC)
We are given two examples of best practise: 're-enable interrupts as soon as possible', and 'avoid early re-enabling of interrupts unless it is necessary'. These would appear to be mutually exclusive.
On a tangent: why does the article use 're-enable' and 're-entered', but then use the dubious 'reentrant'? Shouldn't it be re-entrant? —Preceding unsigned comment added by 59.95.22.6 ( talk) 18:36, 20 November 2009 (UTC)
It does seem that the correct usage here is 're-entrant'. Lambda-mon key ( talk) 00:51, 5 March 2010 (UTC)
Is there a difference between the concepts of a re-entrant function a pure function? What about locks to singleton objects - is that a point of difference?
If they are the same, then this should be pointed out in both articles (see pure function). Otherwise, I think the difference ought to be explained, as it would make the definition richer.
Does anyone know the difference (and a few definitive sources to cite)? 203.171.122.38 ( talk) 09:46, 23 October 2010 (UTC)
No idea about definitive sources, but:
Hope that helps. 68.255.109.73 ( talk) 18:22, 4 December 2010 (UTC)
The long runs of italic text in the Derivations section is hard to read. Are these a quote? If so, that should be explicit. Normal text with perhaps a numbered list or bullet list would be easier to read. Sluggoster ( talk) 17:51, 9 November 2010 (UTC)
The current version of this article is so factually wrong I can't fathom where it could be pulled from. Not to mention being completely unreferenced -- the only paragraph with references is unrelated to the rest of the article.
Let's start with debunking every single of the conditions listed:
var counter:integer; function serial():integer; assembler; asm MOV EAX, 1 LOCK XADD counter, EAX end; { like counter++ in C but atomic -- sorry for having no clue about AT&T syntax }
This function is re-entrant for every definition I am aware of.
int get_queue(int i) { return &queues[i]; }
Example 1.
May use any such locks as long as it handles it being taken by other instances.
void search_village_for_the_grail() { while(get_an_undug_spot()) { start: do_the_digging(); if (grail_found) *((char*)start) = RET; } }
This code works with both unthreaded and threaded reentrancy.
Or do it with proper care -- like queuing the calls and coordinating somehow. Users of the reentrant outer function don't need to know about the complexity.
The definition I have been taught on MIM UW is concerned with a single thread. While the function is being executed, it may be called again, either normally or spontaneously (via an interrupt/signal/etc), and execution of the first call is completely blocked until the second finishes. In this case, there is no option of waiting for the first call to release any lock, it is often possible, though, to save any global state on the stack and restore it before returning.
This is especially important for Unix signals, when you cannot use non-reentrant functions like malloc() (in most implementations) which severely hinders what the program can try to do. Note that this definition does not imply thread safety.
Another definition some sources use is having the function calleable from different thread but not from the same one. This is equivalent to thread safety, and thus uninteresting.
The third possible definition is being both reentrant and thread-safe. This means that anyone can call the function in any case, regardless of which thread is executing. These concepts go largely orthogonal if efficiency is not vital -- and can be tricky to obtain together if it is.
In any case, the definition provided by the article is useless. It basically boils down to (mostly) pure functions with allowed output but not input.
KiloByte ( talk) 15:22, 26 November 2010 (UTC)
A yet another glaring factual error: this version claimed reentrancy is a requirement for recursion. It's not, for two reasons:
void f(int x) { acquire_global_data(); mess_with_global_data(x); release_global_data(); if (x > 0) f(x - 1); }
void process_dir_and_parents(char *path) { if (!path || *path != '/') path = get_absolute_path(path); // not reentrant process_dir(path); chop_last_element(path); if (*path) process_dir_and_parents(path); // here, the path is always absolute }
KiloByte ( talk) 13:36, 26 May 2012 (UTC)
The page gives an example of a function.
int g_var = 1;
int f()
{
g_var = g_var + 2;
return g_var;
}
f is not thread-safe. Then the article literally concludes from the non-thread-safety "Hence, f is not reentrant". First, the article itself explains that functions can be reentrant and not thread-safe. Second, Qt's page on reentrancy [1] gives basically the same code as an example of a reentrant function. It does not return the value, but that is not part of the definition of reentrancy, and the member variable might as well be global static if you use a static instance of the class.
class Counter
{
int n;
public:
void increment() { ++n; }
}
80.153.93.87 ( talk) 15:46, 13 January 2016 (UTC)
void swap(int *x, int *y)
{
int s;
s = *x;
*x = *y;
// hardware interrupt might invoke isr() here!
*y = s;
}
void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}
This would make swap() a pure function without any side effects (as long as addresses of x and y are not shared). But with shared addresses consider
int x = 1, y = 2;
void swap(int *x, int *y)
{
int s;
s = *x;
*x = *y;
// hardware interrupt might invoke isr() here!
*y = s;
}
void isr()
{
int z = 3;
swap(&x, &z);
}
main()
{
swap(&x, &y);
}
When isr() finishes, x==3 and z==2. When then swap() finishes the second time, x==3 and y==1. Hardly what was expected as the result of swap() in main(). So I don't think this last code is an example of a reentrant routine, which does its task atomically.-- H3xc0d3r ( talk) 14:45, 26 January 2017 (UTC)
"The point is that 'corruption' doesn't have to be messing up the memory on your computer with unserialised writes: corruption can still occur even if all individual operations are serialised. It follows that when you're asking if a function is thread-safe, or re-entrant, the question means for all appropriately separated arguments: using coupled arguments does not constitute a counter-example."
Reentrancy (computing)#Rules for reentrancy is incorrect in toto:
Note: a refreshable procedure, or even a pure procedure, is not necessarily reentrant; it still needs appropriate serialization. - Shmuel (Seymour J.) Metz Username:Chatul ( talk) 16:19, 25 May 2018 (UTC)
SSM
with code that acquired and released locks.
[a]LINK
macro with no explicit serialization; LINK provides the required serialization.Notes
ENQ
/DEQ
. Acquiring and releasing a lock does not involve using the SVC
instruction.
@
KiloByte: do you think that the last paragraph about the atomicity requirement in the current introduction of the article is correct? To me atomicity is not necessary, as demonstrated by the 3rd example in the article (
Reentrant but not thread-safe), where tmp
is a global variable that is not modified atomically.
The claim It is a concept from the time when no multitasking operating systems existed. is false. The concept originated with OS/360 [1] Shmuel (Seymour J.) Metz Username:Chatul ( talk) 15:03, 20 August 2018 (UTC)
Reenterable: A reenterable module can be used by. more than one task at the same time; i.e., a task may begin executing a reenterable module before a previous task' has finished executing it. A reenterable module is not modified during execution.
When was the concept of reentrant subroutines first developed? Was it before or after the widespread support for 1. hardware interrupts and 2. recursive subroutines? I'm guessing it preceded the introduction of preemptive multitasking OS. Cesiumfrog ( talk) 06:42, 12 September 2019 (UTC)
I have been IBM-CE in the 60ies, with beginning of OS/360 for those CPUs too, and later teached MFT/MVT (+Compiler) Lessons some years starting 1969/70. Until the first true Multi-CPU-Systems IBM had two main-definitions for executable programs, running in separate storage regions (variable size, MVT) or storage partitions (fixed size, MFT), each program-work named as task:
Just at this early years, remember, all interrupts have been handled by only one CPU without problems until sometime at end 70ies the first (tightly coupled) MP-Systems had been delivered - 2 CPUs. Any interrupt handler did the houskeeping via some standard (minor) register conventions. The interrupt handler routines must have been reentrant, because stored within operating system and serving all tasks.
With multiple CPUs each code not bounded to one processor has to be reentrant, because after each interrupt the next free CPU continues running that program. After teaching years (rotation job) and moving to service at customer locations i had some mightmares of first installations to serve running programms on Dual-Processor systems which have been linked with attribute reentrant, but did not regard reentrant-rules. The most common error was the use of (register-)save-areas in static storage inside program instead of dynamically allocations. Each processor has its own register-set, so .... results in garbage if save-areas get mixed. This relates to subtasks as well as to programs(tasks) in different partitions/regions.
In first time of small /360 Systems Customer are running programs within 8 to 16kiloBytes without operating system one program at a time. So common coding uses the initialization code later for data-workarea. Those coding was later on also kept for a while with small storage systems. A 512kB Mainstorage /360 was a big system, and MVT/MFT has to handle Programs within this "big" storage. This changes with first virtuell storage systems, but storage constraints are always real until today, just only other measures. Those spare-storage-programs are neither "serial reusable" nor "reentrant", but ..... they are still living somewhere .....
BTW: just pointing it here - "serial reentrant" ist not a correct term and was never used in old times. Either a program is reentrant and interruptable anywhere (from anyone) or otherwise it is no more reentrant. Serial reusable means - scrap the old copy and reload again, start always at defined entrypoint. serial reusable has to be locked on one processor only, but is still interruptable. Recurrency is just an other independent term, but if you run a recurrent function with non-reentrant code, you will get the code and work-areas multiplied with each (single) run of the loop, which will kill your system very quickly running out of storage.
Just a bit history, sorry if already known. It's just for sorting some of the terminologie discussed here by their original meaning. regards Peter -- Pqz602 ( talk) 18:24, 19 May 2022 (UTC)
Sorry, just an add-on: "task" as used in my post means the same as "thread" in these wiki-programming posts. Thread was at least not used by the former time main frame programmers. (and task is still used in Win10, most NT based Windows Systems are using the architecture similar to OS/360 and follower) -- Pqz602 ( talk) 18:56, 19 May 2022 (UTC):
References
3. A virtual partitioned data set is used to com bine individually organized data groups into a single data set. Each group of data is called a member, and each member is identified by a unique name. The member name may consist of from one to eight alphameric characters; the first character must be alphabetic. The partitioned organization allows the user to refer to either the entire data set (via the partitioned data set's fully qualified name) or to any member of that data set (via a name consisting of the fully qualified name of the data set suffixed by the member name in parentheses). ... The partitioned data set may be composed of virtual sequential or virtual index sequential members or a mixture of both. Individual members, however, cannot be of mixed organization.
{{
cite book}}
: |work=
ignored (
help)
The code in Reentrancy (computing)#Reentrant and thread-safe is not reentrant; it can execute concurrently on two different processors.
void swap(int* x, int* y)
{
int tmp;
tmp = *x;
*x = *y; /* if the other processor is running swap concurrently, you'll get incorrect results */
*y = tmp; /* Hardware interrupt might invoke isr() here. */
}
void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}
The code needs serialization to be reentrant. Shmuel (Seymour J.) Metz Username:Chatul ( talk) 20:17, 15 September 2019 (UTC)
Notes
I haven't contributed to Wikipedia in forever, so hopefully I'm doing this Talk thing correctly (and if not, it's an honest mistake!), but...
This article confuses the heck out of me. I'm not a computer scientist, but perhaps for that very reason I can be helpful. My observation is that most confusion and frustration comes about when there is a misunderstanding about the common ground. It's like when you don't quite hear everything your wife/husband/partner says, all you hear is "blah blah blah grocery store blah blah blah" and you ask them to repeat it, and they say "blah blah blah GROCERY STORE!!! blah blah," … and you are like, look, I got the GROCERY STORE part, I didn't get what came before and after that!!!
So, just to begin:
After fifteen minutes, I still have no idea what the first two sentences even mean. Take sentence one:
"In computing, a computer program or subroutine is called reentrant if multiple invocations can safely run concurrently."
I don't know what does this means. What is an "invocation?" Is an "invocation" the same as a "thread"? If so, does that mean we're just talking about multi-threaded programming? If not, how does this apply to other situations I might encounter?
How about "concurrently"? What does that mean? "Concurrently" can not mean "at the same time" (as in time on a wall clock), because the next sentence says "The concept applies even on a single processor system." Obviously, a single processor can't possibly be executing one machine-level instruction at a time, therefore, logically, "concurrently" must not mean "at the same time."
Next, still in sentence two, a re-entrant procedure is interrupted in the middle of its execution and then called again. What does this mean? Interrupted how? You mean by the OS or by something internal to the code itself? How can it be called again if it was interrupted? In my little universe, if I interrupt a running code by typing ^C, that's it. Game over, there's no picking back up. So what does "interrupt" mean here? Is it the same as pausing? And when we say it can be called again, do we mean, called again by the same thread, after it comes back and finishes the first time? Or a different thread? Or a different process?
I'm sure all of this is very basic and fundamental stuff, but at the moment it leaves me thinking that the underlying concepts probably aren't nearly as complex or confusing as the terms being used to try to describe them. I hope that my comments are taken at intended, which is to explain - to people who actually know this stuff and who might be baffled why this article is confusing - why it is, in fact, confusing, at least to some people. Thanks. Petwil ( talk) 13:38, 7 February 2020 (UTC)
With multiple CPUs
To a first approximation you need the same code to make foo reentrant whether the first (single CPU) or second (multiple CPU) scenario applies. Shmuel (Seymour J.) Metz Username:Chatul ( talk) 18:32, 7 February 2020 (UTC)
I have a computer science background but am new to this concept. I found
Reentrancy_(computing)#Reentrant_but_not_thread-safe to be unnecessarily confusing because of the recently added comment in the code: /*If hardware interrupt occurs here then it will fail to keep the value of tmp. So this is also not a reentrant example*/
This is self-contradictory because the name of the section and the description preceding the code example say that it is reentrant. I see in the edit history that this comment was added, removed, readded within the last few months.
I think we can agree that the article as a whole would be better if there isn't a part that says two opposing things simultaneously, right? Rather than leaving it as it is now, someone who knows this stuff better than me should clarify the ambiguities so that it's no longer possible to interpret the description and example in different "contexts" to arrive at different conclusions about whether it's reentrant or not. Maybe adding to the description something like "assuming that the ISR itself can't be interrupted and there is only 1 processor, so the ISR will finish and swap its 2 ints correctly before restoring tmp so that the interrupted instance will still see the value it expects in tmp"
Citizenofinfinity ( talk) 11:11, 19 February 2021 (UTC)
The code in #Reentrant but not thread-safe is not reentrant unless it is running on a uniprocessor with interrupts disabled.
int tmp;
void swap(int* x, int* y)
{
/* Save global variable. */
int s;
s = tmp;
tmp = *x;
*x = *y;
*y = tmp; /* Hardware interrupt might invoke isr() here. */
/* Restore global variable. */
tmp = s;
}
void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}
There is no serialization on the global variable tmp between s = tmp;
and tmp = s;
. --
Shmuel (Seymour J.) Metz Username:Chatul (
talk)
18:03, 13 February 2022 (UTC)
I cannot believe this sentence. If a program once is modified in its code, a second caller will never get the original coding. Serialization does not prohibit the code-change nor restores the original code. Those code may even not be "serial reusable", must be refreshed by reloading und always start "initially" again.
A selfmodifying code will never be reentrant in terms of reentrant's original meaning. -- Pqz602 ( talk) 22:05, 19 May 2022 (UTC)
@ OlliverWithDoubleL: A recent edit added an incorrect short description. I was going to correct it but then I realized that I had come up with text that was much two long. So, the question is, how to word it so that it covers both scenarios below without being verbose?
In both cases Baz has to deal with serialization of data. Shmuel (Seymour J.) Metz Username:Chatul ( talk) 13:57, 19 February 2023 (UTC)
I’m ashamed to say I’m not a computer scientist, I just skimmed the introduction and tried to summarize it as best I could. If the concept is that complicated it's probably better to just set the short description as "concept in computer programming" and leave it at that OlliverWithDoubleL ( talk) 23:24, 19 February 2023 (UTC)
![]() | This article is rated Start-class on Wikipedia's
content assessment scale. It is of interest to the following WikiProjects: | ||||||||||||||||||||||||||||||||||
|
|
This definition is way too narrow. Wouter Lievens 09:46, 8 Apr 2005 (UTC)
The mention of FreeBSD's VFS is relevant, however the follow up about DragonFlyBSD seems to me to be off-topic. —Preceding unsigned comment added by 62.253.64.17 ( talk • contribs)
I don't think recursion is correct in this context. A function can be re-entered from more than one thread without being called recursively. Recursive implies the function calls itself. Peter Ritchie 19:48, 5 October 2006 (UTC)
Shouldn't it be "multiple threads" instead of "multiple processes"?
Here is a function which is idempotent but not reentrant:
int global; int f(int * i) { global++; *i = global; global--; }
Or am I misunderstanding something? —Preceding unsigned comment added by 199.172.169.86 ( talk) 10:15, 25 February 2011 (UTC)
I don't know very much about functional programming, but aren't functional programming languages reentrant (variables and functions, and even syntax (in Scheme for example) ? —Preceding unsigned comment added by 83.214.221.148 ( talk • contribs)
This article is inaccurate. Someone more generous than I should get out an OS textbook and revise it. JBW012307
The lisp dialects are re-entrant they just don't seem to have a need to call it other than recursion :). W/re the usage of re-entrant as presented here, isn't Scheme's call/cc an example of what is being discussed? If so, I don't understand why there is an implicit suggestion that re-entrant subrs are directly correlated with thread safety and `concurrency' issues i.e. w/ call/cc the stack is elsewhere thread or no. Lambda-mon key ( talk) 01:05, 5 March 2010 (UTC)
Back in the day, I remember the concept of serially reentrant. A subroutine was serially reentrant even though it manipulated global variables because a semaphore guaranteed at most one thread was actively executing in the subroutine at a given time. The global variables had to be protected by the semaphore in order for this work properly. Static local variables (those not allocated in a stack frame or from a heap) would similarly be protected. —Preceding unsigned comment added by 70.108.186.46 ( talk • contribs)
I think the definition is too sloppy: Consider the case of an object oriented language. An object x could refer to another object y. Now a method in the class of object x returns a value which depends on the state of object y. If the class of object y is mutable, then this is a side effect which influences the result of the method.
Now the problem with the definition is that in its strict sense, this method *is* re-entrant because there is no static data involved in this example. But I am led to believe that in its true meaning the method is *not* reentrant because the return value depends on side effects.
So I suggest the following definition: A reentrant piece of code is a pure function in mathematical terms, which means that its output solely depends on its input parameters, without any side effects.
This implies that the piece of code cannot use static, global or any other data which is not an input parameter. It also implies that the code cannot call other code which does not obeye these requirements. Finally, it implies that the piece of code is thread safe, because the code can only use variables which are local to the current thread (which are usually held on the stack by most programming languages). —Preceding unsigned comment added by 217.83.33.125 ( talk • contribs)
int f(int i) { int priv = i; priv = priv + 2; return priv; } int g(int i) { int priv = i; return f(priv) + 2; } int main() { g(1); return 0; }
Why is this example reentrant? Is priv
unique to each simultaneous process/thread or is it shared by all? --
Abdull
10:40, 13 July 2007 (UTC)
priv
is unique to each execution of f()
and g()
because priv is allocated on the stack of the current thread. Possibly this could be mentioned in the article, but I don't think it's necessary, either. --
Johngiors
06:50, 26 September 2007 (UTC)int f(int i) { return i + 2; } int g(int i) { return f(i) + 2; } int main() { g(1); return 0; }
the parameter values are unique for each call. —Preceding unsigned comment added by 85.144.94.32 ( talk) 13:59, 1 May 2008 (UTC)
Agreed. I have re-written the reentrant version without temporary variables, as I think they only served to obfuscate the code. TOGoS ( talk) 23:00, 17 June 2008 (UTC)
"Despite a common misconception, this is not the same as being designed in such a way that a single copy of the program's instructions, in memory, can be shared."
-- Jerome Potts 17:16, 19 August 2007 (UTC)
Sections of this article are almost redundant copies of the first external link: http://www.ibm.com/developerworks/linux/library/l-reent.html
Compare:
Reentrance and thread-safety are separate concepts: a function can be either reentrant, thread-safe, both, or neither...
and
Non-reentrant functions are thread-unsafe. Furthermore, it may be impossible to make a non-reentrant function thread-safe.
to:
Don't confuse reentrance with thread-safety. From the programmer perspective, these two are separate concepts: a function can be reentrant, thread-safe, both, or neither. Non-reentrant functions cannot be used by multiple threads. Moreover, it may be impossible to make a non-reentrant function thread-safe.
TheShagg 20:26, 12 October 2007 (UTC)
I don't think the following statement is correct:
"Non-reentrant functions are thread-unsafe. Furthermore, it may be impossible to make a non-reentrant function thread-safe."
I think you can have a thread safe function that is non reentrant. Maybe the write meant something like this?
"Thread-unsafe functions are non-reentrant. Furthermore, it may be [or even is] impossible to make a thread-unsafe function reentrant" —Preceding unsigned comment added by Hnassif ( talk • contribs) 17:30, 19 October 2007 (UTC)
The article seems to be assuming a different definition of reentrancy from the one with which I am familiar -- in particular, the definition with which I'm familiar says that a function is reentrant iff it's safe for the function to be called multiple times simultaneously from the same thread of execution. Using that definition, the notions of thread-safety and reentrancy are orthogonal (a function can be one, the other, both or neither). If this (useful!) notion is not called reentrancy, what /is/ it called? 212.44.26.44 ( talk) 17:05, 24 August 2009 (UTC)
Async-signal-safe, maybe? 68.255.109.73 ( talk) 18:18, 4 December 2010 (UTC)
Ad ads ;). There's been mentioned 2 guys for no aparent reason. It is nice they recomend something, sure. Your favourite OS teacher doesn't?
Explanations. I thought it might be nice to add some explanations for girls, who cram, and guys, who are just mildly curious. I also called it a derivation. It might not be a best name. Feel free to change mine mistakes ;).
Simplification. There we're 2 sentences which I thought would better be swapped. To me the second seemed more like natural explanation. Hmm
Citations. There are few claims in "derivation and explanation of rules" that I have no support. I am too lazy to look it up and ,hey, wiki-kids might practice google hunting. 86.61.232.26 ( talk) 23:56, 26 April 2009 (UTC)
We are given two examples of best practise: 're-enable interrupts as soon as possible', and 'avoid early re-enabling of interrupts unless it is necessary'. These would appear to be mutually exclusive.
On a tangent: why does the article use 're-enable' and 're-entered', but then use the dubious 'reentrant'? Shouldn't it be re-entrant? —Preceding unsigned comment added by 59.95.22.6 ( talk) 18:36, 20 November 2009 (UTC)
It does seem that the correct usage here is 're-entrant'. Lambda-mon key ( talk) 00:51, 5 March 2010 (UTC)
Is there a difference between the concepts of a re-entrant function a pure function? What about locks to singleton objects - is that a point of difference?
If they are the same, then this should be pointed out in both articles (see pure function). Otherwise, I think the difference ought to be explained, as it would make the definition richer.
Does anyone know the difference (and a few definitive sources to cite)? 203.171.122.38 ( talk) 09:46, 23 October 2010 (UTC)
No idea about definitive sources, but:
Hope that helps. 68.255.109.73 ( talk) 18:22, 4 December 2010 (UTC)
The long runs of italic text in the Derivations section is hard to read. Are these a quote? If so, that should be explicit. Normal text with perhaps a numbered list or bullet list would be easier to read. Sluggoster ( talk) 17:51, 9 November 2010 (UTC)
The current version of this article is so factually wrong I can't fathom where it could be pulled from. Not to mention being completely unreferenced -- the only paragraph with references is unrelated to the rest of the article.
Let's start with debunking every single of the conditions listed:
var counter:integer; function serial():integer; assembler; asm MOV EAX, 1 LOCK XADD counter, EAX end; { like counter++ in C but atomic -- sorry for having no clue about AT&T syntax }
This function is re-entrant for every definition I am aware of.
int get_queue(int i) { return &queues[i]; }
Example 1.
May use any such locks as long as it handles it being taken by other instances.
void search_village_for_the_grail() { while(get_an_undug_spot()) { start: do_the_digging(); if (grail_found) *((char*)start) = RET; } }
This code works with both unthreaded and threaded reentrancy.
Or do it with proper care -- like queuing the calls and coordinating somehow. Users of the reentrant outer function don't need to know about the complexity.
The definition I have been taught on MIM UW is concerned with a single thread. While the function is being executed, it may be called again, either normally or spontaneously (via an interrupt/signal/etc), and execution of the first call is completely blocked until the second finishes. In this case, there is no option of waiting for the first call to release any lock, it is often possible, though, to save any global state on the stack and restore it before returning.
This is especially important for Unix signals, when you cannot use non-reentrant functions like malloc() (in most implementations) which severely hinders what the program can try to do. Note that this definition does not imply thread safety.
Another definition some sources use is having the function calleable from different thread but not from the same one. This is equivalent to thread safety, and thus uninteresting.
The third possible definition is being both reentrant and thread-safe. This means that anyone can call the function in any case, regardless of which thread is executing. These concepts go largely orthogonal if efficiency is not vital -- and can be tricky to obtain together if it is.
In any case, the definition provided by the article is useless. It basically boils down to (mostly) pure functions with allowed output but not input.
KiloByte ( talk) 15:22, 26 November 2010 (UTC)
A yet another glaring factual error: this version claimed reentrancy is a requirement for recursion. It's not, for two reasons:
void f(int x) { acquire_global_data(); mess_with_global_data(x); release_global_data(); if (x > 0) f(x - 1); }
void process_dir_and_parents(char *path) { if (!path || *path != '/') path = get_absolute_path(path); // not reentrant process_dir(path); chop_last_element(path); if (*path) process_dir_and_parents(path); // here, the path is always absolute }
KiloByte ( talk) 13:36, 26 May 2012 (UTC)
The page gives an example of a function.
int g_var = 1;
int f()
{
g_var = g_var + 2;
return g_var;
}
f is not thread-safe. Then the article literally concludes from the non-thread-safety "Hence, f is not reentrant". First, the article itself explains that functions can be reentrant and not thread-safe. Second, Qt's page on reentrancy [1] gives basically the same code as an example of a reentrant function. It does not return the value, but that is not part of the definition of reentrancy, and the member variable might as well be global static if you use a static instance of the class.
class Counter
{
int n;
public:
void increment() { ++n; }
}
80.153.93.87 ( talk) 15:46, 13 January 2016 (UTC)
void swap(int *x, int *y)
{
int s;
s = *x;
*x = *y;
// hardware interrupt might invoke isr() here!
*y = s;
}
void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}
This would make swap() a pure function without any side effects (as long as addresses of x and y are not shared). But with shared addresses consider
int x = 1, y = 2;
void swap(int *x, int *y)
{
int s;
s = *x;
*x = *y;
// hardware interrupt might invoke isr() here!
*y = s;
}
void isr()
{
int z = 3;
swap(&x, &z);
}
main()
{
swap(&x, &y);
}
When isr() finishes, x==3 and z==2. When then swap() finishes the second time, x==3 and y==1. Hardly what was expected as the result of swap() in main(). So I don't think this last code is an example of a reentrant routine, which does its task atomically.-- H3xc0d3r ( talk) 14:45, 26 January 2017 (UTC)
"The point is that 'corruption' doesn't have to be messing up the memory on your computer with unserialised writes: corruption can still occur even if all individual operations are serialised. It follows that when you're asking if a function is thread-safe, or re-entrant, the question means for all appropriately separated arguments: using coupled arguments does not constitute a counter-example."
Reentrancy (computing)#Rules for reentrancy is incorrect in toto:
Note: a refreshable procedure, or even a pure procedure, is not necessarily reentrant; it still needs appropriate serialization. - Shmuel (Seymour J.) Metz Username:Chatul ( talk) 16:19, 25 May 2018 (UTC)
SSM
with code that acquired and released locks.
[a]LINK
macro with no explicit serialization; LINK provides the required serialization.Notes
ENQ
/DEQ
. Acquiring and releasing a lock does not involve using the SVC
instruction.
@
KiloByte: do you think that the last paragraph about the atomicity requirement in the current introduction of the article is correct? To me atomicity is not necessary, as demonstrated by the 3rd example in the article (
Reentrant but not thread-safe), where tmp
is a global variable that is not modified atomically.
The claim It is a concept from the time when no multitasking operating systems existed. is false. The concept originated with OS/360 [1] Shmuel (Seymour J.) Metz Username:Chatul ( talk) 15:03, 20 August 2018 (UTC)
Reenterable: A reenterable module can be used by. more than one task at the same time; i.e., a task may begin executing a reenterable module before a previous task' has finished executing it. A reenterable module is not modified during execution.
When was the concept of reentrant subroutines first developed? Was it before or after the widespread support for 1. hardware interrupts and 2. recursive subroutines? I'm guessing it preceded the introduction of preemptive multitasking OS. Cesiumfrog ( talk) 06:42, 12 September 2019 (UTC)
I have been IBM-CE in the 60ies, with beginning of OS/360 for those CPUs too, and later teached MFT/MVT (+Compiler) Lessons some years starting 1969/70. Until the first true Multi-CPU-Systems IBM had two main-definitions for executable programs, running in separate storage regions (variable size, MVT) or storage partitions (fixed size, MFT), each program-work named as task:
Just at this early years, remember, all interrupts have been handled by only one CPU without problems until sometime at end 70ies the first (tightly coupled) MP-Systems had been delivered - 2 CPUs. Any interrupt handler did the houskeeping via some standard (minor) register conventions. The interrupt handler routines must have been reentrant, because stored within operating system and serving all tasks.
With multiple CPUs each code not bounded to one processor has to be reentrant, because after each interrupt the next free CPU continues running that program. After teaching years (rotation job) and moving to service at customer locations i had some mightmares of first installations to serve running programms on Dual-Processor systems which have been linked with attribute reentrant, but did not regard reentrant-rules. The most common error was the use of (register-)save-areas in static storage inside program instead of dynamically allocations. Each processor has its own register-set, so .... results in garbage if save-areas get mixed. This relates to subtasks as well as to programs(tasks) in different partitions/regions.
In first time of small /360 Systems Customer are running programs within 8 to 16kiloBytes without operating system one program at a time. So common coding uses the initialization code later for data-workarea. Those coding was later on also kept for a while with small storage systems. A 512kB Mainstorage /360 was a big system, and MVT/MFT has to handle Programs within this "big" storage. This changes with first virtuell storage systems, but storage constraints are always real until today, just only other measures. Those spare-storage-programs are neither "serial reusable" nor "reentrant", but ..... they are still living somewhere .....
BTW: just pointing it here - "serial reentrant" ist not a correct term and was never used in old times. Either a program is reentrant and interruptable anywhere (from anyone) or otherwise it is no more reentrant. Serial reusable means - scrap the old copy and reload again, start always at defined entrypoint. serial reusable has to be locked on one processor only, but is still interruptable. Recurrency is just an other independent term, but if you run a recurrent function with non-reentrant code, you will get the code and work-areas multiplied with each (single) run of the loop, which will kill your system very quickly running out of storage.
Just a bit history, sorry if already known. It's just for sorting some of the terminologie discussed here by their original meaning. regards Peter -- Pqz602 ( talk) 18:24, 19 May 2022 (UTC)
Sorry, just an add-on: "task" as used in my post means the same as "thread" in these wiki-programming posts. Thread was at least not used by the former time main frame programmers. (and task is still used in Win10, most NT based Windows Systems are using the architecture similar to OS/360 and follower) -- Pqz602 ( talk) 18:56, 19 May 2022 (UTC):
References
3. A virtual partitioned data set is used to com bine individually organized data groups into a single data set. Each group of data is called a member, and each member is identified by a unique name. The member name may consist of from one to eight alphameric characters; the first character must be alphabetic. The partitioned organization allows the user to refer to either the entire data set (via the partitioned data set's fully qualified name) or to any member of that data set (via a name consisting of the fully qualified name of the data set suffixed by the member name in parentheses). ... The partitioned data set may be composed of virtual sequential or virtual index sequential members or a mixture of both. Individual members, however, cannot be of mixed organization.
{{
cite book}}
: |work=
ignored (
help)
The code in Reentrancy (computing)#Reentrant and thread-safe is not reentrant; it can execute concurrently on two different processors.
void swap(int* x, int* y)
{
int tmp;
tmp = *x;
*x = *y; /* if the other processor is running swap concurrently, you'll get incorrect results */
*y = tmp; /* Hardware interrupt might invoke isr() here. */
}
void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}
The code needs serialization to be reentrant. Shmuel (Seymour J.) Metz Username:Chatul ( talk) 20:17, 15 September 2019 (UTC)
Notes
I haven't contributed to Wikipedia in forever, so hopefully I'm doing this Talk thing correctly (and if not, it's an honest mistake!), but...
This article confuses the heck out of me. I'm not a computer scientist, but perhaps for that very reason I can be helpful. My observation is that most confusion and frustration comes about when there is a misunderstanding about the common ground. It's like when you don't quite hear everything your wife/husband/partner says, all you hear is "blah blah blah grocery store blah blah blah" and you ask them to repeat it, and they say "blah blah blah GROCERY STORE!!! blah blah," … and you are like, look, I got the GROCERY STORE part, I didn't get what came before and after that!!!
So, just to begin:
After fifteen minutes, I still have no idea what the first two sentences even mean. Take sentence one:
"In computing, a computer program or subroutine is called reentrant if multiple invocations can safely run concurrently."
I don't know what does this means. What is an "invocation?" Is an "invocation" the same as a "thread"? If so, does that mean we're just talking about multi-threaded programming? If not, how does this apply to other situations I might encounter?
How about "concurrently"? What does that mean? "Concurrently" can not mean "at the same time" (as in time on a wall clock), because the next sentence says "The concept applies even on a single processor system." Obviously, a single processor can't possibly be executing one machine-level instruction at a time, therefore, logically, "concurrently" must not mean "at the same time."
Next, still in sentence two, a re-entrant procedure is interrupted in the middle of its execution and then called again. What does this mean? Interrupted how? You mean by the OS or by something internal to the code itself? How can it be called again if it was interrupted? In my little universe, if I interrupt a running code by typing ^C, that's it. Game over, there's no picking back up. So what does "interrupt" mean here? Is it the same as pausing? And when we say it can be called again, do we mean, called again by the same thread, after it comes back and finishes the first time? Or a different thread? Or a different process?
I'm sure all of this is very basic and fundamental stuff, but at the moment it leaves me thinking that the underlying concepts probably aren't nearly as complex or confusing as the terms being used to try to describe them. I hope that my comments are taken at intended, which is to explain - to people who actually know this stuff and who might be baffled why this article is confusing - why it is, in fact, confusing, at least to some people. Thanks. Petwil ( talk) 13:38, 7 February 2020 (UTC)
With multiple CPUs
To a first approximation you need the same code to make foo reentrant whether the first (single CPU) or second (multiple CPU) scenario applies. Shmuel (Seymour J.) Metz Username:Chatul ( talk) 18:32, 7 February 2020 (UTC)
I have a computer science background but am new to this concept. I found
Reentrancy_(computing)#Reentrant_but_not_thread-safe to be unnecessarily confusing because of the recently added comment in the code: /*If hardware interrupt occurs here then it will fail to keep the value of tmp. So this is also not a reentrant example*/
This is self-contradictory because the name of the section and the description preceding the code example say that it is reentrant. I see in the edit history that this comment was added, removed, readded within the last few months.
I think we can agree that the article as a whole would be better if there isn't a part that says two opposing things simultaneously, right? Rather than leaving it as it is now, someone who knows this stuff better than me should clarify the ambiguities so that it's no longer possible to interpret the description and example in different "contexts" to arrive at different conclusions about whether it's reentrant or not. Maybe adding to the description something like "assuming that the ISR itself can't be interrupted and there is only 1 processor, so the ISR will finish and swap its 2 ints correctly before restoring tmp so that the interrupted instance will still see the value it expects in tmp"
Citizenofinfinity ( talk) 11:11, 19 February 2021 (UTC)
The code in #Reentrant but not thread-safe is not reentrant unless it is running on a uniprocessor with interrupts disabled.
int tmp;
void swap(int* x, int* y)
{
/* Save global variable. */
int s;
s = tmp;
tmp = *x;
*x = *y;
*y = tmp; /* Hardware interrupt might invoke isr() here. */
/* Restore global variable. */
tmp = s;
}
void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}
There is no serialization on the global variable tmp between s = tmp;
and tmp = s;
. --
Shmuel (Seymour J.) Metz Username:Chatul (
talk)
18:03, 13 February 2022 (UTC)
I cannot believe this sentence. If a program once is modified in its code, a second caller will never get the original coding. Serialization does not prohibit the code-change nor restores the original code. Those code may even not be "serial reusable", must be refreshed by reloading und always start "initially" again.
A selfmodifying code will never be reentrant in terms of reentrant's original meaning. -- Pqz602 ( talk) 22:05, 19 May 2022 (UTC)
@ OlliverWithDoubleL: A recent edit added an incorrect short description. I was going to correct it but then I realized that I had come up with text that was much two long. So, the question is, how to word it so that it covers both scenarios below without being verbose?
In both cases Baz has to deal with serialization of data. Shmuel (Seymour J.) Metz Username:Chatul ( talk) 13:57, 19 February 2023 (UTC)
I’m ashamed to say I’m not a computer scientist, I just skimmed the introduction and tried to summarize it as best I could. If the concept is that complicated it's probably better to just set the short description as "concept in computer programming" and leave it at that OlliverWithDoubleL ( talk) 23:24, 19 February 2023 (UTC)