0

I see saying global variable update makes a function non-reentrant, this makes sense. But I do not understand why making local copy and restore can resolve it.

Here is an example:

int global_n = 10;

1 void foo()
2 {
3    int local_n = global_n;
4    global_n *= 10;
5    if (global_n < 100) {
6       printf("double digits\n");
7    } else {
8       printf("not double digits\n");
9    }
10   printf("%d\n", global_n);
11   global_n = local_n;
12 }

Let's say the first run of foo() executed line 4, now global_n is 100 and the run is interrupted by another run of foo(), and line 4 of this 2nd run made global_n 1000. So this function is still not reentrant.

Am I right?

Reference: Here is the quote from GNU libc got me asking the above question- if you want to call in a handler a function that modifies a particular object in memory, you can make this safe by saving and restoring that object.

I replaced "memory" with global variable.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
my_question
  • 3,075
  • 2
  • 27
  • 44
  • Yes, you are correct. – Ted Lyngmo Jan 15 '23 at 20:07
  • 2
    Please don't put line numbers in code like this. It means if we need to use that code we need to go out of our way to remove them. – tadman Jan 15 '23 at 20:26
  • There's a limit to how many times you can run this before you blow up `int` and exceed `INT_MAX`. – tadman Jan 15 '23 at 20:27
  • 2
    By "reentrant" do you mean "thread safe" or something else? If you need to make something thread safe you need some kind of access control to a global variable, like a mutex. Here you just *assume* no changes have been made, which is incorrect. Another thread may well have altered that between the time you picked it up and put it back. – tadman Jan 15 '23 at 20:28
  • 1
    The usual way is to externalize state to the caller. So if you have n concurrent calls to `foo()` then each caller have their own copy of `n` that they pass in. It's less pain if you avoid global and static variables (both file scope and storage duration). – Allan Wind Jan 15 '23 at 20:31
  • A reentrant function usually means that it correct even if an interrupt handle is being serviced while the function was running. – Allan Wind Jan 15 '23 at 20:36
  • The libc advice just shields the place that was *interrupted* by foo(). Whatever foo() does: after the interrupt has been processed, and regular program flow continues at the interrupted location, `errno` (or some other global object) has had its value restored so that the interrupted code does not "detect" false errors from non-zero `errno` values (or some other global state). I think this would include [nested interrupts](https://stackoverflow.com/questions/34527763/linux-nested-interrupts). In effect a stack logic is manually built that resembles nested regular function calls. – Peter - Reinstate Monica Jan 16 '23 at 01:02

1 Answers1

0

When they say

if you want to call in a handler a function that modifies a particular object in memory, you can make this safe by saving and restoring that object.

They mean you can write the signal handler so that its call to the function is safe by saving and restoring that object in the signal handler.

You cannot make a non-reentrant function reentrant by saving and restoring global variables or other shared state in the function. If the function modifies shared state, it is not reentrant.

Signal handlers should not call non-reentrant functions. But the signal handler itself does not need to be reentrant. The signal mask will (normally) prevent the signal handler from being re-entered. That is, unless the signal handler is assigned to multiple signals without masking other those other signals, or unless it removes its signal from the mask. And, of course, only one signal handler can play this game with a particular piece of shared state (unless you take care to ensure that all of the group of signal handlers which share that state are mutually masked).

So if the signal handler needs to call a function which modifies shared state, and it is known exactly what shared state is being modified, then that shared state could be saved in the signal handler before calling the non-reentrant function, and then restored in the signal handler after the non-reentrant function returns. This is all subject to the caveats at the end of the previous paragraph, and probably others.

Whether this is advisable is another question. I wouldn't advise it, personally. In fact, I would be inclined to fail a code review for a program which attempted it. There are just too many corner cases which need to be checked. But YMMV.

rici
  • 234,347
  • 28
  • 237
  • 341