RE-ENTRANCY IN OPERATING SYSTEM

- By Surya Seetharaman What is Re-entrant code??

➔ A computer program is called re-entrant, if it can be interrupted in the middle of its execution by another actor before the earlier invocation has finished execution, without affecting the path that the first actor would have taken through the code. That is, it is possible to “re-enter” the code while it is already running and still produce correct results.

➔ In most cases the actors are threads.

➔ It is also called pure procedure or sharable code. Continued...

➔ It is a computer program that is written so that the same copy is shared by multiple users.

➔ The could be caused due to internal action like a jump or call, or by an external action like a hardware interrupt or .

➔ Once the reentered invocation completes, the previous actor would continue with its execution from the point where the interrupt aroused.

➔ A programmer writes the reentrant program by making sure that no instructions modify the contents of variable values in other instructions within the program. Uses of Re-entrant code

➔ This is manily used in operating systems and in applications where it is intended to be shared and in multi-use systems.

➔ Since it is a programming routine that can be used by multiple programs simultaneously, it is used in system softwares as well as in multithreading, where concurrent events are taking place.

➔ It is written so that none of its code is modifiable (no values are changed) and it does not keep track of anything. The calling programs keep track of their own progress (variables, flags, etc.), thus one copy of the re-entrant routine can be shared by any number of users or processes. Example 1

Re-entrant code Non Re-entrant code int t; int t; void swap(int *x, int *y) void swap(int *x, int *y) { { int s; t = *x; s = t; // save *x = *y; t = *x; // hardware interrupt might invoke isr() here! *x = *y; *y = t; // hardware interrupt might invoke isr() here! } *y = t; t = s; // restore global variable } void isr() void isr() { { int x = 1, y = 2; int x = 1, y = 2; swap(&x, &y); swap(&x, &y); } } Explanation

Local data are not shared by any, re-entering or not, routines; therefore, they do not affect re-entrance. Global data are defined outside functions, and can be accessed by more than one function, either in form of global variables (data shared between all functions), or as static variables (data shared by all functions of the same name). Rules for Re-entrancy

Reentrant code may not hold any static (or global) non-constant data.

➔ Reentrant functions can work with global data. For example, a reentrant interrupt service routine could grab a piece of hardware status to work with (e.g. serial port read buffer) which is not only global, but volatile.

➔ Still, typical use of static variables and global data is not advised, in the sense that only atomic read-modify-write instructions should be used in these variables (it should not be possible for an interrupt or signal to come during the execution of such an instruction). Rule 2

Reentrant code may not modify its own code.

➔ The operating system might allow a to modify its code, as this would cause a problem with reentrancy, since the code might not be the same next time.

➔ It may, however, modify itself if it resides in its own unique memory. That is, if each new invocation uses a different physical machine code location where a copy of the original code is made, it will not affect other invocations even if it modifies itself during execution of that particular invocation (thread). Rule 3

Reentrant code may not call non-reentrant computer programs or routines.

➔ Multiple levels of 'user/object/process priority' and/or multiprocessing usually complicate the control of reentrant code. It is important to keep track of any access and or side effects that are done inside a routine designed to be reentrant. Re-entrant Interrupt Handler

➔ A "reentrant interrupt handler" is an interrupt handler that re-enables early in the interrupt handler.

➔ This may reduce interrupt latency. In general, while programming interrupt service routines, it is recommended to re-enable interrupts as soon as possible in the interrupt handler. This practice helps to avoid losing interrupts. Another Example :- f() depends on a non-constant global variable g_var; thus, if two threads execute it and access g_var concurrently, then the result varies depending on the timing of the execution. Hence, f is not reentrant. Neither is g; it calls f, which is not reentrant.

Non- reentrant code: Reentrant code: int g_var = 1; int f() int f(int i) { { g_var = g_var + 2; return i + 2; return g_var; } } int g() int g(int i) { { return f() + 2; return f(i) + 2; } }