天天看點

thread safe vs reentrant1.Reentrant2.Thread safe3.Thread safe vs Reentrant

1.Reentrant

1.1Definition

Reentrant is an adjectivethat describes a computer program or routine thatis written so that the same copy in memory  canbe shared by multiple users. Reentrant code is commonly required in operatingsystems and in applicationsintended to be shared in multi-use systems. A programmer writes a reentrantprogram by making sure that no instructionsmodify the contents of variable values in other instructions within theprogram. Each time the program is entered for a user, a data area is obtained in which to keep all the variable values for that user. The data area is inanother part of memory from the program itself. When the program is interruptedto give another user a turn to use the program, information about the data areaassociated with that user is saved. When the interrupted user of the program isonce again given control of the program, information in the saved data area isrecovered and the program can be reentered without concern that the previoususer has changed some instruction within the program.[Citing from GNU]

In computing, a computerprogram or subroutine is called reentrant if it can be interrupted in the middle of itsexecution and then safely called again ("re-entered") before itsprevious invocations complete execution. The interruption could be caused by aninternal action such as a jump or call, or by an external action such as a hardwareinterrupt or signal. Once the reentered invocation completes, theprevious invocations will resume correct execution.[Citing from Wikipedia]

1.2.Rulesfor reentrancy

[Citing from Wikipedia]

①Reentrant code may not hold any static (orglobal) non-constant data.

Reentrant functions can work withglobal data. For example, a reentrant interrupt service routine could grab apiece of hardware status to work with (e.g. serial port read buffer) which isnot only global, but volatile. Still, typical use of static variables andglobal data is not advised, in the sense that only atomic read-modify-writeinstructions should be used in these variables (it should not be possible foran interrupt or signal to come during the execution of such an instruction).

②Reentrant code may not modify its own code.

The operating system might allow aprocess to modify its code. There are various reasons for this (e.g., blitting graphics quickly) but this would cause a problemwith reentrancy, since the code might not be the same next time.

It may, however, modify itself if itresides in its own unique memory. That is, if each new invocation uses adifferent physical machine code location where a copy of the original code ismade, it will not affect other invocations even if it modifies itself duringexecution of that particular invocation (thread).

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

Multiple levels of'user/object/process priority' and/or multiprocessing usuallycomplicate the control of reentrant code. It is important to keep track of anyaccess and or side effects that are done inside a routine designed to bereentrant.

2.Thread safe

[Citing from Wikipedia]

2.1Definition

Thread safety is a computer programming concept applicable in the context of multi-threaded programs. A piece of code is thread-safe if it only manipulates shared datastructures in a manner that guarantees safe execution by multiple threads atthe same time. There are various strategies for making thread-safe datastructures.

如果一個函數在同一時刻可以被多個線程安全地調用,就稱該函數時線程安全的。

2.2Rules for thread safety

There areseveral approaches for avoiding race conditions to achieve threadsafety. The first class of approaches focuses on avoiding shared state, andincludes:

①Reentrancy 

Writingcode in such a way that it can be partially executed by a thread, re-executedby the same thread or simultaneously executed by another thread and stillcorrectly complete the original execution. This requires the saving of state information invariables local to each execution, usually on a stack, instead of in static or global variables or other non-local state. All non-localstate must be accessed through atomic operations and the data-structures mustalso be reentrant.

②Thread-local storage 

Variablesare localized so that each thread has its own private copy. These variablesretain their values across subroutine and other code boundaries, and are thread-safesince they are local to each thread, even though the code which accesses themmight be executed simultaneously by another thread.

The secondclass of approaches are synchronization-related, and are used in situationswhere shared state cannot be avoided:

③Mutual exclusion

Access toshared data is serialized usingmechanisms that ensure only one thread reads or writes to the shared data atany time. Incorporation of mutual exclusion needs to be well thought out, sinceimproper usage can lead to side-effects like deadlocks, live locks and resource starvation.

④Atomic operations 

Shared dataare accessed by using atomic operations which cannot be interrupted by otherthreads. This usually requires using special machine language instructions, which might be availablein a runtime library. Since theoperations are atomic, the shared data are always kept in a valid state, nomatter how other threads access it. Atomic operations form the basis of manythread locking mechanisms, and are used to implement mutual exclusionprimitives.

⑤Immutable objects 

The stateof an object cannot be changed after construction. This implies both that onlyread-only data is shared and that inherent thread safety is attained. Mutable(non-const) operations can then be implemented in such a way that they createnew objects instead of modifying existing ones.

3.Thread safe vs Reentrant

[citing from http://www.thegeekstuff.com/]

1. Thread SafeCode

As the namesuggests, a piece of code is thread safe when more than one thread can executethe same code without causing synchronization problems. Let’s look at thefollowing code snippet :

chararr[10];

intindex=0;

intfunc(char c)

{

    int i=0;

    if(index >= sizeof(arr))

    {

        printf("\n No storage\n");

        return -1;

    }

    arr[index] = c;

    index++;

    return index;

}

The abovefunction populates the array ‘arr’ with the character value passed to it asargument and then updates the ‘index’ variable so that subsequent calls to thisfunction write on the updated index of the array.

Suppose thisfunction is being used by two threads. Now, lets assume that thread one callsthis function and updates the array index with value ‘c’. Now, before updatingthe ‘index’ suppose second thread gets the execution control and it also callsthis function. Now since the index was not updated by thread one , so thisthread writes on the same index and hence overwrites the value written bythread one.

So we see thatlack of synchronization between the threads was the root cause of this problem.

Now, lets makethis function thread safe :

chararr[10];

intindex=0;

Mutexmutex;

intfunc(char c)

{

int i=0;

mutex.lock();

    if(index >= sizeof(arr))

    {

        printf("\n No storage\n");

        return -1;

    }

    arr[index] = c;

    index++;

mutex.unlock();

    return index;

}

What we did above is that we made the array and indexupdates an atomic operation using the mutex locks. Now even if multiple threadsare trying to use this function, there would be no synchronization problems asany thread which acquire the mutex will complete both the operations (array andindex update) before any other thread acquires the mutex.

So now the above piece of code becomes thread-safe.

2. ReentrantCode

The concept of reentrant code isslightly different from thread safe code. Usually in a single thread ofexecution if a function is called then before the completion of execution of thatparticular function, the flow cannot move ahead. But, there are some situationswhere in a single thread also the execution of a function can be interrupted bya call to same function again. So a piece of code that can successfully handlethe this scenario is known as a re-entrant code. Let’s look at the examplebelow :

char *s;

voidfunc()

{

    int new_length = 0;

    // initialize 'new_length'

    // with some new value here

    char *ptr = realloc(s, new_length);

    if(ptr)

    {

        s= ptr;

    }

    else

    {

        //Report Failure

    }

    // do some stuff here

}

if we analyze the re-entrancecapability of the above code, we find that this code is not re-entrant. This isbecause of the fact that the above code is buggy in the sense that if the samefunction is being used by a signal handler (in response to handling of somesignals) then in the situation where a call to function func() was between realloc()and the ‘if’ condition next to it and then this execution is interrupted by acall to this function from signal handler. In this scenario since ‘s’ is notupdated with new allocated address so realloc might fail (or program might evencrash).

So we see that the above code is notre-entrant. A re-entrant code is least expected to work with global variables.Following is an example of a re-entrant code:

intexchange_values(int *ptr1, int *ptr2)

{

    int tmp;

    tmp = *ptr1;

    *ptr1 = *ptr2;

    *ptr2 = *tmp;

    return 0;

}

3. Thread Safe but not Re-entrant

Apiece of code can be thread safe but it’s not necessary that its re-entrant.Look at the following code :

int func()

{

    int ret = 0;

mutex.lock();

    // Play with some

    // global data structures here  

mutex.unlock();

    return ret;

}

In the exampleabove, since the critical section is protected by mutex so the code above isthread safe but its not re-entrant because if the execution of the above functionis interrupted through some signal handler (calling the same function while handlinga signal) then (if non-recursive mutexes are being used) the first execution isinterrupted while the second execution will wait forever to acquire mutex. So overallthe complete program will hang.

4. Re-entrantbut not Thread Safe

Re-entrance is something which isassociated with a function whose first execution gets interrupted by secondcall to it (from within the same thread) and this first execution resumes whenthe second execution completes. This is not the case with threads which cankeep on stepping onto another thread’s toes multiple times. So if a function isre-entrant then it does not guarantee that its thread safe.

The following(somewhat contrived) modification of the swap function, which is careful toleave the global data in a consistent state at the time it exits, is perfectlyreentrant; however, it is not thread-safe since it does not ensure the globaldata is in a consistent state during execution:

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);

}

整理隻為學習之用,可能會有錯誤,請自行甄别,歡迎留言指出錯誤

繼續閱讀