imagine I write a library in C. Further, imagine this library to be used from a multi-threaded environment. How do I make it thread-safe? More specific: How do I assure, that certain functions are executed only by one thread at a time?
In opposite to Java or C# for example, C has no means to deal with threads/locks/etc., nor does the C standard library. I know, that operating systems support threads, but using their api would restrict the compatibility of my library very much. Which possibilities do I have, to keep my library as compatible/portable as possible? (for example relying on OpenMP, or on Posix threads to keep it compatible with at least all unix-like operating systems?)
A threadsafe function protects shared resources from concurrent access by locks. Thread safety concerns only the implementation of a function and does not affect its external interface. In C language, local variables are dynamically allocated on the stack.
write() is certainly thread-safe. The problem is that a partial write() could require multiple calls in order to completely write the data, and while that is "thread-safe" it could result in interleaved data.
++ is not defined as thread-safe.
If you're running on a Unix-like OS, when open() is a direct system call, the answer is absolutely yes. Different threads can open files (or even the same file) at the same time just as different processes can.
You can create wrappers with #ifdef. It's really the best you can do. (Or you can use a third party library to do this).
I'll show how I did it as an example for windows and linux. It's in C++ and not C but again it's just an example:
#ifdef WIN32
typedef HANDLE thread_t;
typedef unsigned ThreadEntryFunction;
#define thread __declspec(thread)
class Mutex : NoCopyAssign
{
public:
Mutex() { InitializeCriticalSection(&mActual); }
~Mutex() { DeleteCriticalSection(&mActual); }
void Lock() { EnterCriticalSection(&mActual); }
void Unlock() { LeaveCriticalSection(&mActual); }
private:
CRITICAL_SECTION mActual;
};
class ThreadEvent : NoCopyAssign
{
public:
ThreadEvent() { Actual = CreateEvent(NULL, false, false, NULL); }
~ThreadEvent() { CloseHandle(Actual); }
void Send() { SetEvent(Actual); }
HANDLE Actual;
};
#else
typedef pthread_t thread_t;
typedef void *ThreadEntryFunction;
#define thread __thread
extern pthread_mutexattr_t MutexAttributeRecursive;
class Mutex : NoCopyAssign
{
public:
Mutex() { pthread_mutex_init(&mActual, &MutexAttributeRecursive); }
~Mutex() { pthread_mutex_destroy(&mActual); }
void Lock() { pthread_mutex_lock(&mActual); }
void Unlock() { pthread_mutex_unlock(&mActual); }
private:
pthread_mutex_t mActual;
};
class ThreadEvent : NoCopyAssign
{
public:
ThreadEvent() { pthread_cond_init(&mActual, NULL); }
~ThreadEvent() { pthread_cond_destroy(&mActual); }
void Send() { pthread_cond_signal(&mActual); }
private:
pthread_cond_t mActual;
};
inline thread_t GetCurrentThread() { return pthread_self(); }
#endif
/* Allows for easy mutex locking */
class MutexLock : NoAssign
{
public:
MutexLock(Mutex &m) : mMutex(m) { mMutex.Lock(); }
~MutexLock() { mMutex.Unlock(); }
private:
Mutex &mMutex;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With