It appears that glib provides mutexes and conditions as thread synchronization primitives, but what about generic semaphores (in the sense that they support the original P and V operations?) Am I correct in understanding a GCond as equivalent to a binary semaphore, with g_cond_signal being equivalent to P, and g_cond_wait being equivalent to V? But what about semaphores not restricted to a maximum value of 1?
I thought of something like this:
struct semaphore {
int n;
GMutex sem_lock;
GCond sem_cond;
}
Where the P operation would now look something like this:
void semaphore_P (struct semaphore *sem)
{
g_mutex_lock(sem->sem_lock);
while (sem->n == 0)
g_cond_wait(sem->sem_cond, sem->sem_lock);
--sem->n;
g_mutex_unlock(sem->sem_lock);
}
Is there a simpler way to get at the functionality of pthreads' sem_wait and sem_post from within glib?
An asynchronous queue can be used as a semaphore:
initialization: GAsyncQueue *queue = g_async_queue_new();
the V operation: g_async_queue_push(queue, GINT_TO_POINTER(1));
the P operation: g_async_queue_pop(queue);
The size of the queue serves as the counter of the semaphore.
The second parameter to g_async_queue_push may be any pointer except for NULL.
However, if you want to use the semaphore for some consumer/producer task, then sending in a pointer to some data will be useful.
In some cases, a thread pool may fit even better.
Related
I found that there's a macro called PTHRED_MUTEX_ADAPTIVE_NP which is somehow given as a value to a mutex so that the mutex does an adaptive spinning, meaning that it spins in the magnitude of an immediate wakeup through the kernel would last. But how do I utilize this configuration-macro to a thread ?
And as I've developed an improved shared readers-writer lock (it needs only one atomic operation at best in contrast to the three operations given in the Wikipedia-solution) with relative writer-priority (further readers are stalled when there's a writer and the readers before are allowed to proceed) which could also make use of adaptive spinning: how is the number of spinning-cycles calculated ?
I found that there's a macro called PTHRED_MUTEX_ADAPTIVE_NP
Some pthreads implementations provide a macro PTHREAD_MUTEX_ADAPTIVE_NP (note spelling) that is one of the possible values of the kind_np mutex attribute, but neither that attribute nor the macro are standard. It looks like at least BSD and AIX have them, or at least did at one time, but this is not something you should be using in new code.
But how do I utilize this configuration-macro to a thread ?
You don't. Even if you are using a pthreads implementation that supports it, this is the value of a mutex attribute, not a thread attribute. You obtain a mutex with that attribute value by explicitly requesting it when you initialize the mutex. It would look something like this:
pthread_mutexattr_t attr;
pthread_mutex_t mutex;
int rval;
// Return-value checks omitted for brevity and clarity
rval = pthread_mutexattr_init(&attr);
rval = pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
rval = pthread_mutex_init(&mutex, &attr);
There are other mutex attributes that you can set in analogous ways, which is one of the reasons I wrote this answer. Although you should not be using the kind_np attribute, you can follow this general model for other mutex attributes. There are also thread attributes, which work similarly.
I found the code in the glibc:
That's the "adaptive" mutex locking code of pthread_mutex_lock
in the glibc 2.31:
else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)
== PTHREAD_MUTEX_ADAPTIVE_NP, 1))
{
if (! __is_smp)
goto simple;
if (LLL_MUTEX_TRYLOCK (mutex) != 0)
{
int cnt = 0;
int max_cnt = MIN (max_adaptive_count (),
mutex->__data.__spins * 2 + 10);
do
{
if (cnt++ >= max_cnt)
{
LLL_MUTEX_LOCK (mutex);
break;
}
atomic_spin_nop ();
}
while (LLL_MUTEX_TRYLOCK (mutex) != 0);
mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8;
}
assert (mutex->__data.__owner == 0);
}
So the spin count is doubled up to a maximum plus 10 first (system configurable or 1000 if thre's no configuration) and after the locking the difference between the actual spins and the predefined spins divided by 8 is added to the next spin-count.
I'm trying to create a small class that will allow me to facilitate a communication between two threads.
Those threads most probably will outlive the context in which the above mentioned class was created as they are queued onto a thread pool.
What I have tried so far (on coliru as well):
class A
{
public:
A(int maxVal) : maxValue(maxVal) {}
bool IsOverMax() const { return cur >= maxValue; }
void Increase() { cur++; }
private:
const int maxValue;
atomic_int cur{ 0 };
};
possible usage:
void checking(const shared_ptr<A> counter)
{
while(!counter->IsOverMax())
{
cout<<"Working\n"; // do work
std::this_thread::sleep_for(10ms);
}
}
void counting(shared_ptr<A> counter)
{
while (!counter->IsOverMax())
{
cout<<"Counting\n";
counter->Increase(); // does this fall under `...uses a non-const member function of shared_ptr then a data race will occur`? http://en.cppreference.com/w/cpp/memory/shared_ptr/atomic
std::this_thread::sleep_for(9ms);
}
}
int main()
{
unique_ptr<thread> t1Ptr;
unique_ptr<thread> t2Ptr;
{
auto aPtr = make_shared<A>(100); // This might be out of scope before t1 and t2 end
t1Ptr.reset(new thread(checking, aPtr)); // To simbolize that t1,t2 will outlive the scope in which aPtr was originaly created
t2Ptr.reset(new thread(counting, aPtr));
}
t2Ptr->join();
t1Ptr->join();
//cout<< aPtr->IsOverMax();
}
The reason I'm concerned is that the documentation says that:
If multiple threads of execution access the same std::shared_ptr object without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur unless all such access is performed through these functions, which are overloads of the corresponding atomic access functions (std::atomic_load, std::atomic_store, etc.)
So Increase is a non const function, are the copies of aPtr are the same std::shared_ptr for this context or not ?
Is this code thread-safe?
Would this be OK for a non atomic object (say using an std::mutex to lock around reads and writes to a regular int)?
In any case why?
So Increase is a non const function, are the copies of aPtr are the same std::shared_ptr for this context or not ?
At std::thread creation, aPtr is passed by value. Therefore, it is guaranteed that:
You don't introduce a data race since each thread gets its own instance of shared_ptr (although they manage the same object A).
The documentation you are referring to describes a scenario whereby multiple threads operate on the same shared_ptr instance.
In that case, only const member functions can be called (see below), or synchronization is required.
shared_ptr reference-count is incremented before aPtr goes out of scope in main
So yes, this is a correct way to use shared_ptr.
Is this code thread-safe?
Your code does not introduce a data race, neither with access to shared_ptr instances, nor with access to the managed object A.
This means that there are no conflicting, non-atomic, read and write operations to the same memory location performed by multiple threads.
However, keep in mind that, in checking(), the call to IsOverMax() is separated from the actual work that follows
(Increase() could be called by the second thread after IsOverMax() but before 'do work'). Therefore, you could 'do work' while cur has gone over its maximum.
Whether or not that is a problem depends on your specification, but it is called a race condition which is not necessarily a programming error (unlike a data race which causes undefined behavior).
Would this be OK for a non atomic object (say using an std::mutex to lock around reads and writes to a regular int)?
cur can be a regular int (non-atomic) if you protect it with a std::mutex. The mutex must be locked for both write and read access in order to prevent a data race.
One remark on calling const member functions on objects shared by multiple threads.
The use of const alone does not guarantee that no data race is introduced.
In this case, the guarantee applies to shared_ptr const member functions, because the documentation says so.
I cannot find in the C++ standard whether that guarantee applies to all const member functions in the Standard Library
That documentation is talking about the member functions of shared_ptr, not the member functions of your class. Copies of shared_ptr objects are different objects.
I believe the code is thread safe, because the only changing variable written and read on different threads is cur, and that variable is atomic.
If cur was not atomic and access to it in both Increase() and IsOverMax() was protected by locking a std::mutex, that code would also be thread safe.
If I set the value of a variable in one thread and read it in another, I protect it with a lock to ensure that the second thread reads the value most recently set by the first:
Thread 1:
lock();
x=3;
unlock();
Thread 2:
lock();
<use the value of x>
unlock();
So far, so good. However, suppose I have a c++ object that sets the value of x in an initializer:
theClass::theClass() : x(3) ...
theClass theInstance;
Then, I spawn a thread that uses theInstance. Is there any guarantee that the newly spawned thread will see the proper value of x? Or is it necessary to place a lock around the declaration of theInstance? I am interested primarily in c++ on Linux.
Prior to C++11, the C++ standard had nothing to say about multiple threads of execution and so made no guarantees of anything.
C++11 introduced a memory model that defines under what circumstances memory written on one thread is guaranteed to become visible to another thread.
Construction of an object is not inherently synchronized across threads. In your particular case though, you say you first construct the object and then 'spawn a thread'. If you 'spawn a thread' by constructing an std::thread object and you do it after constructing some object x on the same thread then you are guaranteed to see the proper value of x on the newly spawned thread. This is because the completion of the thread constructor synchronizes-with the beginning of your thread function.
The term synchronizes-with is a specific term used in defining the C++ memory model and it's worth understanding exactly what it means to understand more complex synchronization but for the case you outline things 'just work' without needing any additional synchronization.
This is all assuming you're using std::thread. If you're using platform threading APIs directly then the C++ standard has nothing to say about what happens but in practice you can assume it will work without needing a lock on any platform I know of.
You seem to have a misconception on locks:
If I set the value of a variable in one thread and read it in another,
I protect it with a lock to ensure that the second thread reads the
value most recently set by the first.
This is incorrect. Locks are used to prevent data races. Locks do not schedule the instructions of Thread 1 to happen before the instructions of Thread 2. With your lock in place, Thread 2 can still run before Thread 1 and read the value of x before Thread 1 changes the value of x.
As for your question:
If your initialization of theInstance happens-before the initialization/start of a certain thread A, then thread A is guaranteed to see the proper value of x.
Example
#include <thread>
#include <assert.h>
struct C
{
C(int x) : x_{ x } {}
int x_;
};
void f(C const& c)
{
assert(c.x_ == 42);
}
int main()
{
C c{ 42 }; // A
std::thread t{ f, std::ref(c) }; // B
t.join();
}
In the same thread: A is sequenced-before B, therefore A happens-before B. The assert in thread t will thus never fire.
If your initialization of 'theInstance' inter-thread happens-before its usage by a certain thread A, then thread A is guaranteed to see the proper value of x.
Example
#include <thread>
#include <atomic>
#include <assert.h>
struct C
{
int x_;
};
std::atomic<bool> is_init;
void f0(C& c)
{
c.x_ = 37; // B
is_init.store(true); // C
}
void f1(C const& c)
{
while (!is_init.load()); // D
assert(c.x_ == 37); // E
}
int main()
{
is_init.store(false); // A
C c;
std::thread t0{ f0, std::ref(c) };
std::thread t1{ f1, std::ref(c) };
t0.join();
t1.join();
}
The inter-thread happens-before relationship occurs between t0 and t1. As before, A happens-before the creation of threads t0 and t1.
The assignment c.x_ = 37 (B) happens-before the store to the is_init flag (C). The loop in f1 is the source of the inter-thread happens-before relationship: f1 only proceeds once is_init is set, therefore C happens before E. Since these relationships are transitive, B inter-thread happens-before D. Thus, the assert will never fire in f1.
First of all, your example above doesn't warrant any locks. All you need to do is to declare your variable atomic. No locks, no worries.
Second, your question does not really make a lot of sence. Since you can not use your object (instance of the class) before it is constructed, and construction is happening within single thread, there is no need to lock anything which is done in class constructor. You simply can not access non-constructed class from multiple threads, it is impossible.
https://stackoverflow.com/a/5524120/462608
If you want to call functions recursively, which lock the same mutex, then they either
have to use one recursive mutex, or
have to unlock and lock the same non-recursive mutex again and again (beware of concurrent threads!), or
have to somehow annotate which mutexes they already locked (simulating recursive ownership/mutexes).
Can in any case this be a "sensible" design decision to make function recursive which already has a mutex lock?
Well, one possibility is that the resource you're using lends itself naturally to recursive algorithms.
Think of searching a binary tree, while preventing everyone else from using (especially modifying) the tree out with a mutex.
If you use a recursive mutex, you can simply have one function search() that you pass the root node in to. It then recursively calls itself as per a normal binary tree search but the first thing it does in that function is to lock the mutex (while this looks like Python, that's really just because Python is an ideal basis for pseudo-code):
def search (haystack, mutex, needle):
lock mutex recursively
if haystack == NULL:
unlock mutex and return NULL
if haystack.payload == needle:
unlock mutex and return haystack
if haystack.payload > needle:
found = search (haystack.left, mutex, needle)
else:
found = search (haystack.right, mutex, needle)
unlock mutex and return found
The alternative is to separate the mutex lock and search into two separate functions like search() (public) and search_while_locked() (most likely private):
def private search_while_locked (haystack, needle):
if haystack == NULL:
return NULL
if haystack.payload == needle:
return haystack
if haystack.payload > needle:
return search_while_locked (haystack.left, needle)
return search_while_locked (haystack.right, needle)
def search (haystack, mutex, needle):
lock mutex non-recursively
found = search_while_locked (haystack.right, needle)
unlock mutex and return found
While that sort of defeats the elegance of the recursive solution, I actually prefer it since it reduces the amount of work that needs to be done (however small that work is, it's still work).
And languages that lend themselves easily to public/private functions can encapsulate the details well. The user of your class has no knowledge (or need of knowledge) as to how you do things within your class, they just call the public API.
Your own functions, however, have access to all the non-public stuff as well as full knowledge as to what locks need to be in place for certain operations.
Another possibility is very much related to that but without being recursive.
Think of any useful operation you may want users to perform on your data which requires that no-one else be using it during that time. So far, you have just the classic case for a non-recursive mutex. For example, clearing all of the entries out of a queue:
def clearQueue():
lock mutex
while myQueue.first <> null:
myQueue.pop()
unlock mutex
Now let's say you find that rather useful and want to call it from your destructor, which already locks the mutex:
def destructor():
lock mutex
clearQueue()
doSomethingElseNeedingLock()
unlock mutex
Obviously, with a non-recursive mutex, that's going to lock up on the first line of clearQueue after your destructor calls it, which may be one reason why you'd want a recursive mutex.
You could use the afore-mentioned method of providing a locking public function and a non-locking private one:
def clearQueueLocked():
while myQueue.first <> null:
myQueue.pop()
def clearQueue():
lock mutex
clearQueueLocked():
unlock mutex
def destructor():
lock mutex
clearQueueLocked():
doSomethingElseNeedingLock()
unlock mutex and return
However, if there are a substantial number of these public/private function pairs, it may get a little messy.
In addition to paxdiablo's exmaple using an actual recursive funciotn, don't forget that using a mutex recursively doesn't necessarily mean that the functions involved are recursive. I've found use for recursive mutexes for dealing with a situation where you have complex operations which need to be atomic with respect to some data structure, with those complex operations rely on more fundamental operations that still need to use the mutex since the fundamental operations can be used on their own as well. An example might be something like the following (note that the code is illustrative only - it doesn't use proper error handling or transactional techniques that might really be necessary when dealing with accounts and logs):
struct account
{
mutex mux;
int balance;
// other important stuff...
FILE* transaction_log;
};
void write_timestamp( FILE*);
// "fundamental" operation to write to transaction log
void log_info( struct account* account, char* logmsg)
{
mutex_acquire( &account->mux);
write_timestamp( account->transaction_log);
fputs( logmsg, account->transaction_log);
mutex_release( &account->mux);
}
// "composed" operation that uses the fundamental operation.
// This relies on the mutex being recursive
void update_balance( struct account* account, int amount)
{
mutex_acquire( &account->mux);
int new_balance = account->balance + amount;
char msg[MAX_MSG_LEN];
snprintf( msg, sizeof(msg), "update_balance: %d, %d, %d", account->balance, amount, new_balance);
// the following call will acquire the mutex recursively
log_info( account, msg);
account->balance = new_balance;
mutex_release( &account->mux);
}
To do something more or less equivalent without recursive mutexes means that the code would need to take care not to reacquire the mutex if it already held it. One option is to add some sort of flag (or thread ID) to the data structure to indicate if the mutex is already held. In this case, you're essentially implementing recursive mutexes - a trickier bit of work than it might seem at first to get right. An alternative is to pass a flag indicating you already acquired the mutex to functions as a parameter (easier to implement and get right) or simply have even more fundamental operations that assume the mutex is already acquired and call those from the higher level functions that take on the responsibility of acquiring the mutex:
// "fundamental" operation to write to transaction log
// this version assumes that the lock is already held
static
void log_info_nolock( struct account* account, char* log msg)
{
write_timestamp( account->transaction_log);
fputs( logmsg, account->transaction_log);
}
// "public" version of the log_info() function that
// acquires the mutex
void log_info( struct account* account, char* logmsg)
{
mutex_acquire( &account->mux);
log_info_nolock( account, logmsg);
mutex_release( &account->mux);
}
// "composed operation that uses the fundamental operation
// since this function acquires the mutex, it much call the
// "nolock" version of the log_info() function
void update_balance( int amount)
{
mutex_acquire( &account->mux);
int new_balance = account->balance + amount;
char msg[MAX_MSG_LEN];
snprintf( msg, sizeof(msg), "update_balance: %d, %d, %d", account->balance, amount, new_balance);
// the following call assumes the lock is already acquired
log_info_nolock( account, msg);
account->balance = new_balance;
mutex_release( &account->mux);
}
I'm interested in doing something like(single thread update, multiple threads read banneedURLs):
atomic<bannedURLList*> bannedURLs;//global variable pointing to the currently used instance of struct
void updateList()
{
//no need for mutex because only 1 thread updates
bannedURLList* newList= new bannedURLList();
bannedURLList* oldList=bannedURLs;
newList->initialize();
bannedURLs=newList;// line must be after previous line, because list must be initialized before it is ready to be used
//while refcnt on the oldList >0 wait, then delete oldList;
}
reader threads do something like this:
{
bannedURLs->refCnt++;
//use bannedURLs
bannedURLs->refCnt--;
}
struct memeber refCnt is also atomic integer
My question is how to prevent reordering of this 2 lines:
newList->initialize();
bannedURLs=newList;
Can it be done in std:: way?
Use bannedURLs.store(newList); instead of bannedURLs=newList;. Since you didn't pass a weak ordering specifier, this forces full ordering in the store.