Re-enter thread protection

I know that a lot has been discussed in this forum. But one thing still bothers me. Wikipedia mentions that every re-code is thread safe. http://en.wikipedia.org/wiki/Reentrant_%28subroutine%29 And later, an example of a function that is repetitive but not thread safe is given.

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

It bothers me. Are all subscriber codes safe? In addition, all recursive functions are thread safe. I can not visualize the difference.

thanks

+4
source share
5 answers

The concepts of thread recovery and thread safety are related but not equivalent. You can write a re-entry function that is not thread safe, and a stream-independent function that is not re-entry. I will use C # for my examples:

Non-stream re-entry feature

This function changes the position of the array:

 void Reverse(int[] data) { if (data == null || data.Length < 2) return; for (var i = 0 ; i != data.Length/2 ; i++) { int tmp = data[i]; data[i] = data[data.Length-i-1]; data[data.Length-i-1] = tmp; } } 

This function is explicitly redirected because it does not reference external resources. However, the security of its stream is due to the fact that the same data from several streams does not pass it. If several streams simultaneously transmit Reverse the same array instance, an incorrect result may be obtained. One way to make this function unconditionally thread safe is to add a lock:

 void Reverse(int[] data) { if (data == null || data.Length < 2) return; lock (data) { for (var i = 0 ; i != data.Length/2 ; i++) { int tmp = data[i]; data[i] = data[data.Length-i-1]; data[data.Length-i-1] = tmp; } } } 

Non-Re-Entrant Continuous Function

This function calls the f() function c times and returns a value that was returned more time than other values.

 static int[] counts = new int[65536]; unsigned short MaxCount(Func<unsigned short> f, int c) { lock(counts) { Array.Clear(counts, 0, counts.Length); for (var i = 0 ; i != c ; i++) { counts[f()]++; } unsigned short res = 0; for (var i = 1 ; i != counts.Length ; i++) { if (counts[i] > counts[res]) { res = i; } } return res; } } 

This function is thread safe because it blocks the static array that it uses for counting. However, it is not repeated: for example, if the passed functor f was to call MaxCount , incorrect results will be returned.

+2
source

Are all backup codes streams safe?

Not.

Later in a Wikipedia article: ... The key to avoiding confusion is that the reenterium refers to only one stream. This is a concept from the time when multitasking operating systems did not exist.

In other words, the reentry concept does not pay attention to multithreaded execution (as the most common example). Only if this is an actual limitation in your system, then yes, the re-grantor will qualify as thread-safe (but in this environment there will also be only one thread).

IMO, the term "reentrant" should be used only in the context of qualification systems (does not apply to your desktop OS, but refers to some embedded systems).

Now, if you really want to force the use of the word "reentrance" in a multi-threaded environment: you can force re-entry into a multi-threaded system only if you also guaranteed that it is also thread safe. Perhaps it is better to ask the question: "To guarantee re-entry in a multi-threaded context, will it be understood that the function and all the data it refers to must also be thread safe?" - A: Yes, the function and everything that it refers to must also be thread safe and reentrant in order for the function to be reentrant in this context. Achieving this quickly becomes difficult and is the reason global variables are not a good idea.

In addition, all recursive functions are safe.

Not.

+1
source

This function is not thread safe because it accesses the resource t , which is outside the scope (not allocated on the stack) without any security mechanisms (for example, blocking) to provide access to t .

The Wikipedia page actually states:

A reentrant routine can provide thread safety, but this condition alone may not be enough in all situations.

(My emphasis).

UPDATE:

Reentrant (as defined in the article) simply means that one thread of execution (I think, DOS) can “break the instruction pointer” from the middle of the executable procedure, put it in another place and continue the linear thread through the new code (for example, the interrupt routine in DOS- days), and the same linear stream may return to the function a second time. In this limited scenario, the second function call SHOULD complete before control is transferred from the interrupt routine back to "normal" program execution, which resumes at the same point in the routine where the instruction code was originally deleted. This scenario does not allow arbitrary scheduling of flows, but rather an interrupt that switches control to a new point, which then ends and returns back to where it was interrupted.

Please note that in real life, things may not be so simple. I'm not quite sure (was ... 20 years?), But I think that one interrupt procedure can interrupt and interrupt a regular program that is already running (for example, interrupting a soft debugger can interrupt a timer interrupt, etc.).

0
source

The swap function is not reentrant because it uses the global state (for whit, the global variable "t").

0
source

If your routine does not affect external variables (without side effects), you can say that it is repeated. . Although not a rule, this is a good recommendation.
If the subroutine uses external variables and saves the state of external variables at the beginning and restores this state at the end, then the subroutine is a retry .

If the subroutine changes external variables and does not save their state in the first place and is interrupted, the state of these external variables can be changed, therefore, when the call returns to its original location in the subroutine, the external variable is not synchronized, which leads to an inconsistent state for the subroutine.

Re-entry functions retain their state (in their local stack, in a thread stack that does not use global variables).

In your case, you get access to the external variable, t , but it returns because the variable is saved and restored at the end of the subroutine.

Usually thread safe means re-entry, but again, there is no rule more than an approximate one. Note. Some locks in Java are returned, so you can recursively call a method and not be blocked by previous calls. (re-entry in this context means that threads can access any section locked with the same lock - a thread can re-enter any block of code for which it already holds a lock)

Solution / Answer:
If you protect t with atoms, you should get a subroutine with a stream. In addition, if you put t in each thread in the stack (make it local), the routine will become thread safe because there is no global data.

Also reentrant! = Recursive; you can also perform ISR in a recursive routine. Essentially, if you call a recursive routine of 2 threads, you will get garbage. To make it thread safe, protect the recursive routine with reentry locks (other locking locks will result in locking / blocking).

0
source

Source: https://habr.com/ru/post/1398333/


All Articles