thread safety in thirty part libraries - c++

I want to use a library developed by someone else, of which I only have the library file, not the source code. My question is this: the library provides a class with a number of functionalities. The class itself is not thread safe. I wanted to make it thread safe and I was wondering if this code works
// suppose libobj is the class provided by the library
class my_libobj : public libobj {
// code
};

This only inherits from libobj, which may or may not "work" depending on whether the class was designed for inheritance (has at least a virtual destructor).
In any case, it won't buy you thread-safety for free. The easiest way to get that is to add mutexes to the class and lock those when entering a critical section:
class my_obj {
libobj obj; // inheritance *might* work too
boost::mutex mtx;
void critical_op()
{
boost::unique_lock lck(mtx);
obj.critical_op();
}
};
(This is very coarse-grained design with a single mutex; you may to able to make it more fine-grained if you know the behavior of the various operations. It's also not fool-proof, as #dribeas explains.)

Retrofitting thread safety -- and BTW, there are different level -- in a library which hasn't be designed for is probably impossible without knowing how it has be implemented if you aren't content with just serializing all calls to it, and even then it can be problematic if the interface is bad enough -- see strtok for instance.

This is impossible to answer without knowledge of at least the actual interface of the class. In general the answer would be no.
From the practical C++ point of view, if the class was not designed to be extended, every non-virtual method will not be overriden and as such you might end up with a mixture of some thread-safe and some non-thread safe methods.
Even if you decide to wrap (without inheritance) and force delegation only while holding a lock, the approach is still not valid in all cases. Thread safety requires not only locking, but an interface that can be made thread safe.
Consider a stack implementation as in the STL, by just adding a layer of locking (i.e. making every method thread safe, you will not guarantee thread safety on the container. Consider a few threads adding elements to the stack and two threads pulling information:
if ( !stack.empty() ) { // 1
x = stack.top(); // 2
stack.pop(); // 3
// operate on data
}
There are a number of possible things that can go wrong here: Both threads might perform test [1] when the stack has a single element and then enter sequentially, in which case the second thread will fail in [2] (or obtain the same value and fail in [3]), even in the case where there are multiple objects in the container, both threads could execute [1] and [2] before any of them executing [3], in which case both threads would be consuming the same object, and the second element in the stack would be discarded without processing...
Thread safety requires (in most cases) changes to the API, in the example above, an interface that provides bool pop( T& v ); implemented as:
bool stack::try_pop( T& v ) { // argument by reference, to provide exception safety
std::lock<std:mutex> l(m);
if ( s.empty() ) return false; // someone consumed all data, return failure
v = s.top(); // top and pop are executed "atomically"
s.pop();
return true; // we did consume one datum
}
Of course there are other approaches, you might not return failure but rather wait on a condition in a pop operation that is guaranteed to lock until a datum is ready by making use of a conditional variable or something alike...

The simplest solution is to create a single thread uniquely for that library, and only access the library from that thread, using message queues to pass requests and return parameters.

Related

C++ thread safety of static resources

Say I have the following classes:
public class Item {
public:
CString name;
int id;
UINT type;
bool valid;
void invalidate(){
valid = false;
}
...
}
public class itemPool {
public:
static std::vector<Item*> items ;
void invalidateOfType(UINT type){
for( auto iter : items )
if ( iter->type == type )
iter->invalidate();
}
...
}
Can I call the "invalidateOfType(UINT type)" - method from different threads?
Is there any possibility of "undefined behaviour" ? In other words, can I use static resources from in multiple threads ( make parallel calls to that resource ) ?
Static resources are no different than any shared resources. Unless their methods are thread-safe, they should not be called from multiple threads simultaneously. In your particular case, it boils down to the question of invalidate() being thread safe. Iterating over vector itself is thread-safe.
Quite unexpectedly (to me!) the question turned out into something very interesting and educational. Following are points of interest to remember here. In explaining those, I will take the code at the face value. I will also operate under the assumption (actually clarified by OP in some of the comments) that no code is READING while the invalidation takes place.
The code as written would iterate over the same vector at the same time. Since iterating the vector which is not modified during iteration is thread safe, this part is thread safe and needs no further discussion.
The second question is 'can two or more threads execute invalidateOfType for the same type at the same type'? If the answer is NO - every thread has it's own type - than again, the code is 100% thread safe, since same objects are not accessed from more than one thread and no further discussion is neccessary.
If the answer to the above question is 'YES', than we have a conondrum. Effectively it boils down to the question 'when two or more threads set the same memory location to the same value at the same time, is it going to produce unexpected results'? Precise reading of standards does not give a straight answer.
No, you cannot. This could result in two threads executing valid = false; at the same time on the same valid. It is not permissible to modify an object in one thread while another thread is, or might be, accessing it. (To be sure, check the docs for the particular threading model or library you are using, but most have this rule.)
I would consider this okay on Windows, because everyone does it. It's unlikely that some subsequent change to the platform will break everyone's code. I wouldn't do this on POSIX platforms because the documentation is pretty clear that it's not allowed and it's not commonly done.
If your question is, calling invalidateOfType() simultaniously from different threads lead to data curruption (one thread reading the other writing to the same object), then the answer is yes.
But you can protect the resource, in this example the items vector, with a std::mutex and std::lock_guard like:
class itemPool {
public:
static std::vector<Item*> items;
static std::mutex items_mutex;
void invalidateOfType(UINT type) {
std::lock_guard< std::mutex > scoped_lock(items_mutex);
for (auto iter : items)
if (iter->type = type)
{
iter->invalidate();
}
}
...
}
If thread1 is just executing invalidateOfType and thread2 does a call to invalidateOfType, then thread2 has to wait until thread1 has left the function and the items_mutex is unlocked.
Do this with every resource you share accross threads to prevent corruption.
Can I call the "invalidateOfType(UINT type)" - method from different threads?
Dear god, no! Simply touching that array from another thread while the invalidateOfType function is running is sufficient to crash your program instantly. There's no locks anywhere.
At the very least you should lock (ie mutex) access to the array itself.

Thread safety of multiple-reader/single-writer class

I am working on a set that is frequently read but rarely written.
class A {
boost::shared_ptr<std::set<int> > _mySet;
public:
void add(int v) {
boost::shared_ptr<std::set<int> > tmpSet(new std::set<int>(*_mySet));
tmpSet->insert(v); // insert to tmpSet
_mySet = tmpSet; // swap _mySet
}
void check(int v) {
boost::shared_ptr<std::set<int> > theSet = _mySet;
if (theSet->find(v) != theSet->end()) {
// do something irrelevant
}
}
};
In the class, add() is only called by one thread and check() is called by many threads. check() does not care whether _mySet is the latest or not. Is the class thread-safe? Is it possible that the thread executing check() would observe swap _mySet happening before insert to tmpSet?
This is an interesting use of shared_ptr to implement thread safety.
Whether it is OK depends on the thread-safety guarantees of
boost::shared_ptr. In particular, does it establish some sort of
fence or membar, so that you are guaranteed that all of the writes in
the constructor and insert functions of set occur before any
modification of the pointer value becomes visible.
I can find no thread safety guarantees whatsoever in the Boost
documentation of smart pointers. This surprizes me, as I was sure that
there was some. But a quick look at the sources for 1.47.0 show none,
and that any use of boost::shared_ptr in a threaded environment will
fail. (Could someone please point me to what I'm missing. I can't
believe that boost::shared_ptr has ignored threading.)
Anyway, there are three possibilities: you can't use the shared pointer
in a threaded environment (which seems to be the case), the shared
pointer ensures its own internal consistency in a threaded environment,
but doesn't establish ordering with regards to other objects, or the
shared pointer establishes full ordering. Only in the last case will
your code be safe as is. In the first case, you'll need some form of
lock around everything, and in the second, you'll need some sort of
fences or membar to ensure that the necessary writes are actually done
before publishing the new version, and that they will be seen before
trying to read it.
You do need synchronization, it is not thread safe. Generally it doesn't matter, even something as simple as shared += value; is not thread safe.
look here for example with regards to thread safety of shared_ptr: Is boost shared_ptr <XXX> thread safe?
I would also question your allocation/swapping in add() and use of shared_ptr in check()
update:
I went back and re-rad dox for shared_ptr ... It is most likely thread-safe in your particular since the reference counting for shared_ptr is thread-safe. However you are doing (IMHO) unnecessary complexity by not using read/write lock.
Eventually this code should be thread safe:
atomic_store(&_my_set,tmpSet);
and
theSet = atomic_load(&_mySet);
(instead of simple assignments)
But I don't know the current status of atomicity support for shared_ptr.
Note, that adding atomicity to shared_ptr in lock-free manner is really dificult thing; so even atomicity is implemented it may relay on mutexes or usermode spinlocks and, therefore, may sometimes suffer from performance issues
Edit: Perhaps, volatile qualifier for _my_set member variable should also be added.. but I'm not sure that it is strictly required by semantics of atomic operations

How to detect circular calls?

I've been looking for causes for deadlocks and strategies/tools to avoid and detect them.
Another potential cause for deadlocks is to have blocking functions calling other blocking functions in a circular way, so that eventually a call never returns.
Sometimes this is hard to discover, specially in very large projects.
So, are there any tools/libraries/techiques that allow to automate the detection of circular calls in a program?
EDIT:
I code mostly in C and C++ so, if possible, give any information about the topic that is applicable to those languages.
Nevertheless, it seems this topic is scarcely covered in SO, so answers for other languages are ok too. although maybe those deserve a topic of its own if someone finds it relevant
Thanks.
Circular (or recursive) calls that try to acquire the same non-reentrant lock are one of the easiest to debug blocking scenarios: locking is deterministic, and can be easily checked. When the application locks, fire up the debugger and look at the stack trace to understand what locks are held and why.
As to general solutions for the problem of locking... you can look into some libraries that provide mutex ordering, and detect when you are trying to lock on a mutex out of order. This type of solutions might be complex to implement correctly, but once in place it ensures that you cannot enter a deadlock condition, as it forces all processes to obtain the locks in the same order (i.e. if process A holds lock La, and it tries to acquire lock Lb for which the ordering is correct, then it can either succeed or lock, but whichever process is holding lock Lb cannot try to lock La as the ordering constraint would not be met).
If you are on Linux there 2 Valgrind tools for detecting deadlocks and race conditions: Helgrind, DRD. They both complement each other and it's worth to check for thread errors by both of them.
In linux you can use valgrind to detect deadlocks, use --tool=helgrind.
Best way to detect deadlocks (IMO) is to make a test program that calls all the functions in a random order in like 30 different threads 10000s of times.
If you get a deadlock you can use VS2010 "Parallel Stacks" window. Debug->Windows->Parallel Stacks
This window will show you all the stacks, so you can find the methods that are deadlocking.
A simple strategy I use to write thread-safe objects:
A thread safe object should be safe when its public methods are called, so you don't get deadlocks when it is used.
So, the idea is to just lock all the public methods that access the object's data.
Besides that you need to insure that within the class' code you never call a public method. If you need to use one of the public methods, then make that method private, and wrap the private method with a public method that locks and then calls it.
If you want better lock granularity you could just create objects for each part that has its own lock, and lock it like I suggested. Then use encapsulation to combine those classes to the one class.
Example:
class Blah {
MyData data;
Lock lock;
public:
DataItem GetData(int index)
{
ReadLock read(lock);
return LocalGetData(index);
}
DataItem FindData(string key)
{
ReadLock read(lock);
DataItem item;
//find the item, can use LocalGetData() to get the item without deadlocking
return item;
}
void PutData(DataItem item)
{
ReadLock write(lock);
//put item in database
}
private:
DataItem LocalGetData(int index)
{
return data[index];
}
}
You could find a tool that builds a call graph, and check the graph for cycles.
Otherwise, there are a number of strategies for detecting deadlocks or other circularities, but they all depend on having some sort of supporting infrastructure in place.
There are deadlock avoidance strategies, having to do with assigning lock priorities and ordering the locks according to priority. These require code changes and enforcing the standards, though.

C++ objects in multithreading

I would like to ask about thread safety in C++ (using POSIX threads with a C++ wrapper for ex.) when a single instance/object of a class is shared between different threads. For example the member methods of this single object of class A would be called within different threads. What should/can I do about thread safety?
class A {
private:
int n;
public:
void increment()
{
++n;
}
void decrement()
{
--n;
}
};
Should I protect class member n within increment/decrement methods with a lock or something else? Also static (class variables) members have such a need for lock?
If a member is immutable, I do not have to worry about it, right?
Anything that I cannot foreseen now?
In addition to the scenario with a single object within multithreads, what about multiple object with multiple threads? Each thread owns an instance of a class. Anything special other than static (class variables) members?
These are the things in my mind, but I believe this is a large topic and I would be glad if you have good resources and refer previous discussions about that.
Regards
Suggestion: don't try do it by hand. Use a good multithread library like the one from Boost: http://www.boost.org/doc/libs/1_47_0/doc/html/thread.html
This article from Intel will give you a good overview: http://software.intel.com/en-us/articles/multiple-approaches-to-multithreaded-applications/
It's a really large topic and probably it's impossible to complete the topic in this thread.
The golden rule is "You can't read while somebody else is writing."
So if you have an object that share a variable you have to put a lock in the function that access the shared variable.
There are very few cases when this is not true.
The first case is for integer number you can use the atomic function as showed by c-smile, in this case the CPU will use an hardware lock on the cache, so other cores can't modify the variables.
The second cases are lock free queue, that are special queue that use the compare and excange function to assure the atomicity of the instruction.
All the other cases are MUST be locked...
the first aproach is to lock everything, this can lead to a lot of problem when more object are involved (ObjA try to read from ObjB but, ObjB is using the variable and also is waiting for ObjC that wait ObjA) Where circular lock can lead to indefinite waiting (deadlock).
A better aproach is to minimize the point where thread share variable.
For example if you have and array of data, and you want to parallelize the computation on the data you can launch two thread and thread one will work only on even index while thread two will work on the odd. The thread are working on the same set of data, but as long the data don't overlap you don't have to use lock. (This is called data parallelization)
The other aproch is to organize the application as a set of "work" (function that run on a thread a produce a result) and make the work communicate only with messages. You only have to implement a thread safe message system and a work sheduler you are done. Or you can use libray like intel TBB.
Both approach don't solve deadlock problem but let you isolate the problem and find bugs more easily. Bugs in multithread are really hard to debug and sometime are also difficoult to find.
So, if you are studing I suggest to start with the thery and start with pThread, then whe you are learned the base move to a more user frendly library like boost or if you are using Gcc 4.6 as compiler the C++0x std::thread
yes, you should protect the functions with a lock if they are used in a multithreading environment. You can use boost libraries
and yes, immutable members should not be a concern, since a such a member can not be changed once it has been initialized.
Concerning "multiple object with multiple threads".. that depends very much of what you want to do, in some cases you could use a thread pool which is a mechanism that has a defined number of threads standing by for jobs to come in. But there's no thread concurrency there since each thread does one job.
You have to protect counters. No other options.
On Windows you can do this using these functions:
#if defined(PLATFORM_WIN32_GNU)
typedef long counter_t;
inline long _inc(counter_t& v) { return InterlockedIncrement(&v); }
inline long _dec(counter_t& v) { return InterlockedDecrement(&v); }
inline long _set(counter_t &v, long nv) { return InterlockedExchange(&v, nv); }
#elif defined(WINDOWS) && !defined(_WIN32_WCE) // lets try to keep things for wince simple as much as we can
typedef volatile long counter_t;
inline long _inc(counter_t& v) { return InterlockedIncrement((LPLONG)&v); }
inline long _dec(counter_t& v) { return InterlockedDecrement((LPLONG)&v); }
inline long _set(counter_t& v, long nv) { return InterlockedExchange((LPLONG)&v, nv); }

What exactly is a reentrant function?

Most of the times, the definition of reentrance is quoted from Wikipedia:
A computer program or routine is
described as reentrant if it can be
safely called again before its
previous invocation has been completed
(i.e it can be safely executed
concurrently). To be reentrant, a
computer program or routine:
Must hold no static (or global)
non-constant data.
Must not return the address to
static (or global) non-constant
data.
Must work only on the data provided
to it by the caller.
Must not rely on locks to singleton
resources.
Must not modify its own code (unless
executing in its own unique thread
storage)
Must not call non-reentrant computer
programs or routines.
How is safely defined?
If a program can be safely executed concurrently, does it always mean that it is reentrant?
What exactly is the common thread between the six points mentioned that I should keep in mind while checking my code for reentrant capabilities?
Also,
Are all recursive functions reentrant?
Are all thread-safe functions reentrant?
Are all recursive and thread-safe functions reentrant?
While writing this question, one thing comes to mind:
Are the terms like reentrance and thread safety absolute at all i.e. do they have fixed concrete definitions? For, if they are not, this question is not very meaningful.
1. How is safely defined?
Semantically. In this case, this is not a hard-defined term. It just mean "You can do that, without risk".
2. If a program can be safely executed concurrently, does it always mean that it is reentrant?
No.
For example, let's have a C++ function that takes both a lock, and a callback as a parameter:
#include <mutex>
typedef void (*callback)();
std::mutex m;
void foo(callback f)
{
m.lock();
// use the resource protected by the mutex
if (f) {
f();
}
// use the resource protected by the mutex
m.unlock();
}
Another function could well need to lock the same mutex:
void bar()
{
foo(nullptr);
}
At first sight, everything seems ok… But wait:
int main()
{
foo(bar);
return 0;
}
If the lock on mutex is not recursive, then here's what will happen, in the main thread:
main will call foo.
foo will acquire the lock.
foo will call bar, which will call foo.
the 2nd foo will try to acquire the lock, fail and wait for it to be released.
Deadlock.
Oops…
Ok, I cheated, using the callback thing. But it's easy to imagine more complex pieces of code having a similar effect.
3. What exactly is the common thread between the six points mentioned that I should keep in mind while checking my code for reentrant capabilities?
You can smell a problem if your function has/gives access to a modifiable persistent resource, or has/gives access to a function that smells.
(Ok, 99% of our code should smell, then… See last section to handle that…)
So, studying your code, one of those points should alert you:
The function has a state (i.e. access a global variable, or even a class member variable)
This function can be called by multiple threads, or could appear twice in the stack while the process is executing (i.e. the function could call itself, directly or indirectly). Function taking callbacks as parameters smell a lot.
Note that non-reentrancy is viral : A function that could call a possible non-reentrant function cannot be considered reentrant.
Note, too, that C++ methods smell because they have access to this, so you should study the code to be sure they have no funny interaction.
4.1. Are all recursive functions reentrant?
No.
In multithreaded cases, a recursive function accessing a shared resource could be called by multiple threads at the same moment, resulting in bad/corrupted data.
In singlethreaded cases, a recursive function could use a non-reentrant function (like the infamous strtok), or use global data without handling the fact the data is already in use. So your function is recursive because it calls itself directly or indirectly, but it can still be recursive-unsafe.
4.2. Are all thread-safe functions reentrant?
In the example above, I showed how an apparently threadsafe function was not reentrant. OK, I cheated because of the callback parameter. But then, there are multiple ways to deadlock a thread by having it acquire twice a non-recursive lock.
4.3. Are all recursive and thread-safe functions reentrant?
I would say "yes" if by "recursive" you mean "recursive-safe".
If you can guarantee that a function can be called simultaneously by multiple threads, and can call itself, directly or indirectly, without problems, then it is reentrant.
The problem is evaluating this guarantee… ^_^
5. Are the terms like reentrance and thread safety absolute at all, i.e. do they have fixed concrete definitions?
I believe they do, but then, evaluating a function is thread-safe or reentrant can be difficult. This is why I used the term smell above: You can find a function is not reentrant, but it could be difficult to be sure a complex piece of code is reentrant
6. An example
Let's say you have an object, with one method that needs to use a resource:
struct MyStruct
{
P * p;
void foo()
{
if (this->p == nullptr)
{
this->p = new P();
}
// lots of code, some using this->p
if (this->p != nullptr)
{
delete this->p;
this->p = nullptr;
}
}
};
The first problem is that if somehow this function is called recursively (i.e. this function calls itself, directly or indirectly), the code will probably crash, because this->p will be deleted at the end of the last call, and still probably be used before the end of the first call.
Thus, this code is not recursive-safe.
We could use a reference counter to correct this:
struct MyStruct
{
size_t c;
P * p;
void foo()
{
if (c == 0)
{
this->p = new P();
}
++c;
// lots of code, some using this->p
--c;
if (c == 0)
{
delete this->p;
this->p = nullptr;
}
}
};
This way, the code becomes recursive-safe… But it is still not reentrant because of multithreading issues: We must be sure the modifications of c and of p will be done atomically, using a recursive mutex (not all mutexes are recursive):
#include <mutex>
struct MyStruct
{
std::recursive_mutex m;
size_t c;
P * p;
void foo()
{
m.lock();
if (c == 0)
{
this->p = new P();
}
++c;
m.unlock();
// lots of code, some using this->p
m.lock();
--c;
if (c == 0)
{
delete this->p;
this->p = nullptr;
}
m.unlock();
}
};
And of course, this all assumes the lots of code is itself reentrant, including the use of p.
And the code above is not even remotely exception-safe, but this is another story… ^_^
7. Hey 99% of our code is not reentrant!
It is quite true for spaghetti code. But if you partition correctly your code, you will avoid reentrancy problems.
7.1. Make sure all functions have NO state
They must only use the parameters, their own local variables, other functions without state, and return copies of the data if they return at all.
7.2. Make sure your object is "recursive-safe"
An object method has access to this, so it shares a state with all the methods of the same instance of the object.
So, make sure the object can be used at one point in the stack (i.e. calling method A), and then, at another point (i.e. calling method B), without corrupting the whole object. Design your object to make sure that upon exiting a method, the object is stable and correct (no dangling pointers, no contradicting member variables, etc.).
7.3. Make sure all your objects are correctly encapsulated
No one else should have access to their internal data:
// bad
int & MyObject::getCounter()
{
return this->counter;
}
// good
int MyObject::getCounter()
{
return this->counter;
}
// good, too
void MyObject::getCounter(int & p_counter)
{
p_counter = this->counter;
}
Even returning a const reference could be dangerous if the user retrieves the address of the data, as some other portion of the code could modify it without the code holding the const reference being told.
7.4. Make sure the user knows your object is not thread-safe
Thus, the user is responsible to use mutexes to use an object shared between threads.
The objects from the STL are designed to be not thread-safe (because of performance issues), and thus, if a user want to share a std::string between two threads, the user must protect its access with concurrency primitives;
7.5. Make sure your thread-safe code is recursive-safe
This means using recursive mutexes if you believe the same resource can be used twice by the same thread.
"Safely" is defined exactly as the common sense dictates - it means "doing its thing correctly without interfering with other things". The six points you cite quite clearly express the requirements to achieve that.
The answers to your 3 questions is 3× "no".
Are all recursive functions reentrant?
NO!
Two simultaneous invocations of a recursive function can easily screw up each other, if
they access the same global/static data, for example.
Are all thread-safe functions reentrant?
NO!
A function is thread-safe if it doesn't malfunction if called concurrently. But this can be achieved e.g. by using a mutex to block the execution of the second invocation until the first finishes, so only one invocation works at a time. Reentrancy means executing concurrently without interfering with other invocations.
Are all recursive and thread-safe functions reentrant?
NO!
See above.
The common thread:
Is the behavior well defined if the routine is called while it is interrupted?
If you have a function like this:
int add( int a , int b ) {
return a + b;
}
Then it is not dependent upon any external state. The behavior is well defined.
If you have a function like this:
int add_to_global( int a ) {
return gValue += a;
}
The result is not well defined on multiple threads. Information could be lost if the timing was just wrong.
The simplest form of a reentrant function is something that operates exclusively on the arguments passed and constant values. Anything else takes special handling or, often, is not reentrant. And of course the arguments must not reference mutable globals.
Now I have to elaborate on my previous comment. #paercebal answer is incorrect. In the example code didn't anyone notice that the mutex which as supposed to be parameter wasn't actually passed in?
I dispute the conclusion, I assert: for a function to be safe in the presence of concurrency it must be re-entrant. Therefore concurrent-safe (usually written thread-safe) implies re-entrant.
Neither thread safe nor re-entrant have anything to say about arguments: we're talking about concurrent execution of the function, which can still be unsafe if inappropriate parameters are used.
For example, memcpy() is thread-safe and re-entrant (usually). Obviously it will not work as expected if called with pointers to the same targets from two different threads. That's the point of the SGI definition, placing the onus on the client to ensure accesses to the same data structure are synchronised by the client.
It is important to understand that in general it is nonsense to have thread-safe operation include the parameters. If you've done any database programming you will understand. The concept of what is "atomic" and might be protected by a mutex or some other technique is necessarily a user concept: processing a transaction on a database can require multiple un-interrupted modifications. Who can say which ones need to be kept in sync but the client programmer?
The point is that "corruption" doesn't have to be messing up the memory on your computer with unserialised writes: corruption can still occur even if all individual operations are serialised. It follows that when you're asking if a function is thread-safe, or re-entrant, the question means for all appropriately separated arguments: using coupled arguments does not constitute a counter-example.
There are many programming systems out there: Ocaml is one, and I think Python as well, which have lots of non-reentrant code in them, but which uses a global lock to interleave thread acesss. These systems are not re-entrant and they're not thread-safe or concurrent-safe, they operate safely simply because they prevent concurrency globally.
A good example is malloc. It is not re-entrant and not thread-safe. This is because it has to access a global resource (the heap). Using locks doesn't make it safe: it's definitely not re-entrant. If the interface to malloc had be design properly it would be possible to make it re-entrant and thread-safe:
malloc(heap*, size_t);
Now it can be safe because it transfers the responsibility for serialising shared access to a single heap to the client. In particular no work is required if there are separate heap objects. If a common heap is used, the client has to serialise access. Using a lock inside the function is not enough: just consider a malloc locking a heap* and then a signal comes along and calls malloc on the same pointer: deadlock: the signal can't proceed, and the client can't either because it is interrupted.
Generally speaking, locks do not make things thread-safe .. they actually destroy safety by inappropriately trying to manage a resource that is owned by the client. Locking has to be done by the object manufacturer, thats the only code that knows how many objects are created and how they will be used.
The "common thread" (pun intended!?) amongst the points listed is that the function must not do anything that would affect the behaviour of any recursive or concurrent calls to the same function.
So for example static data is an issue because it is owned by all threads; if one call modifies a static variable the all threads use the modified data thus affecting their behaviour. Self modifying code (although rarely encountered, and in some cases prevented) would be a problem, because although there are multiple thread, there is only one copy of the code; the code is essential static data too.
Essentially to be re-entrant, each thread must be able to use the function as if it were the only user, and that is not the case if one thread can affect the behaviour of another in a non-deterministic manner. Primarily this involves each thread having either separate or constant data that the function works on.
All that said, point (1) is not necessarily true; for example, you might legitimately and by design use a static variable to retain a recursion count to guard against excessive recursion or to profile an algorithm.
A thread-safe function need not be reentrant; it may achieve thread safety by specifically preventing reentrancy with a lock, and point (6) says that such a function is not reentrant. Regarding point (6), a function that calls a thread-safe function that locks is not safe for use in recursion (it will dead-lock), and is therefore not said to be reentrant, though it may nonetheless safe for concurrency, and would still be re-entrant in the sense that multiple threads can have their program-counters in such a function simultaneously (just not with the locked region). May be this helps to distinguish thread-safety from reentarncy (or maybe adds to your confusion!).
The answers your "Also" questions are "No", "No" and "No". Just because a function is recursive and/or thread safe it doesn't make it re-entrant.
Each of these type of function can fail on all the points you quote. (Though I'm not 100% certain of point 5).
non reentrant function means that there will be a static context, maintained by function. when first time entering, there will be create new context for you. and next entering, you don't send more parameter for that, for convenient to token analyze, . e.g. strtok in c. if you have not clear the context, there might be some errors.
/* strtok example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ,.-");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
on the contrary of non-reentrant, reentrant function means calling function in anytime will get the same result without side effect. because there is none of context.
in the view of thread safe, it just means there is only one modification for public variable in current time, in current process. so you should add lock guard to ensure just one change for public field in one time.
so thread safety and reentrant are two different things in different views.reentrant function safety says you should clear context before next time for context analyze. thread safety says you should keep visit public field order.
The terms "Thread-safe" and "re-entrant" mean only and exactly what their definitions say. "Safe" in this context means only what the definition you quote below it says.
"Safe" here certainly doesn't mean safe in the broader sense that calling a given function in a given context won't totally hose your application. Altogether, a function might reliably produce a desired effect in your multi-threaded application but not qualify as either re-entrant or thread-safe according to the definitions. Oppositely, you can call re-entrant functions in ways that will produce a variety of undesired, unexpected and/or unpredictable effects in your multi-threaded application.
Recursive function can be anything and Re-entrant has a stronger definition than thread-safe so the answers to your numbered questions are all no.
Reading the definition of re-entrant, one might summarize it as meaning a function which will not modify any anything beyond what you call it to modify. But you shouldn't rely on only the summary.
Multi-threaded programming is just extremely difficult in the general case. Knowing which part of one's code re-entrant is only a part of this challenge. Thread safety is not additive. Rather than trying to piece together re-entrant functions, it's better to use an overall thread-safe design pattern and use this pattern to guide your use of every thread and shared resources in the your program.