Thread Safety : a function is called thread-safe (thread-safe), and it always produces the correct result when it is called repeatedly by multiple concurrent processes. If a function is not thread-safe, we say it is thread insecure (thread-unsafe). We define four types of (intersecting) thread unsafe functions.
Class 1th: Functions that do not protect shared variables
Turning such thread unsafe functions into thread-safe is relatively easy: protect shared variables with synchronous operations such as P and V operations. The advantage of this method is that there is no need to make any changes in the calling program, and the disadvantage is that synchronization slows down the execution time of the program.
Class 2nd: Maintaining state functions that span multiple calls
A pseudo-random number generator is a simple example of such unsafe functions.
unsigned int next = 1;
int rand (void)
{
Next = next * 1103515245 + 12345;
return (unsigned int) (next/65536)% 32768;
}
??
void Srand (unsigned int seed)
{
Next = seed;
}
The RAND function is thread insecure because the result of the current call depends on the intermediate result of the previous call. When we call Srand to set a seed for Rand, we repeatedly call Rand from a single thread, and we can expect a repeatable sequence of random numbers. However, if there are multiple threads calling the RAND function at the same time, this assumption is not true.
The only way to make the RAND function thread safe is to rewrite it so that it no longer uses any static data and instead relies on the caller to pass state information in the parameters. The disadvantage is that programmers are now forced to change the code of the calling program.
Class 3rd: Returns a function that points to a static variable pointer
Some functions, such as gethostbyname, place the result of the calculation in a static structure and return a pointer to the structure. If we call these functions from a concurrent thread, a disaster can occur because the results being used by one thread are silently overwritten by another thread.
There are two ways to handle this type of thread unsafe function. One is to choose an overriding function that allows the caller to pass the structure address where the result is stored. This eliminates all shared data, but it requires the programmer to rewrite the caller's code as well.
If the thread unsafe function is difficult to modify or not modifiable (for example, it is linked from one library), the other option is to use Lock-and-copy (locking-copy) technology. This concept links thread unsafe functions to mutex locks. At each invocation location, the mutex is locked, the function unsafe function is called, the result is dynamically non-matching memory, the result returned by the function is copied to the memory location, and then the mutex is unlocked. An appealing change is the definition of a thread-safe encapsulation (wrapper) function that executes lock-and-copy and then calls this marshal function to replace all unsafe functions of the thread. For example, the following gethostbyname is a thread-safe function.
struct hostent* gethostbyname_ts (char* host)
{
struct hostent* gkfx, * UNSHAREDP;
UNSHAREDP = Malloc (sizeof (struct hostent));
P (&mutex)
shared = gethostbyname (hostname);
*UNSHAREDP = * GKFX;
V (&mutex);
return UNSHAREDP;
}
Class 4th: Functions that invoke thread unsafe functions
If the function f calls the thread unsafe function g, then f is the thread unsafe? Not necessarily. If G is a class 2 function that relies on a state that spans multiple invocations, then f is also unsafe, and there is no way to rewrite G. However, if G is a class 1th or 3rd function, F may still be thread safe as long as the call location and any resulting shared data are secured with a mutex. Like the gethostbyname_ts above.
reentrant functions
reentrant functions: Reentrant functions are one of the thread-safe functions, characterized by the fact that they are called by multiple threads and do not reference any shared data.
Reentrant functions are generally more efficient than non-reentrant thread-safe functions because they do not require synchronous operations. Further, the only way to convert a class 2nd thread unsafe function into a thread-safe function is to rewrite it so that it can be re-entered. We can simply draw their relationship.
Below is a reentrant version of the RAND function
int Rand_r (unsigned int* nextp)
{
*NEXTP = *nextp * 1103515245 + 12345;
return (unsigned int) (*nextp/65536)% 32768;
}
explicit reentrant functions: If all function arguments are passed (without pointers), and all data references are local auto-stack variables (that is, no static or global variables are referenced), then the function is displayed reentrant, that is, no matter how it is called, We can all assert that it can be re-entered.
implicitly reentrant functions: Some parameters in reentrant functions are reference passing (using pointers), that is, when the calling thread is carefully passing pointers to unshared data, it is reentrant. For example, Rand_r is implicitly reentrant.
We use Reentrant (reentrant) to include an explicit reentrant function and an implicit reentrant function. However, reentrant is sometimes a property common to both the caller and the callee, not just the individual properties of the callee.
?
Excerpt from:http://blog.xiaoheshang.info/?cat=11
Thread safety and Reentrant functions