Can a thread be logically interruptible while waiting for a mutex? - multithreading

I was reading R&R's Unix system programming, I encounter a question about mutex. For the following paragraph stated in that book. When he said a thread that waits for a mutex is not logically interruptible, does it mean when a thread wait for a mutex, it won't be able to do a context switch? Can someone elaborate it?
A thread that waits for a mutex is not logically interruptible except
by termination of the process, termination of a thread with
pthread_exit (from a signal handler), or asynchronous cancellation
(which is normally not used).

No, it doesn't mean that it can't context switch. On the contrary, a thread waiting for a mutex that is already acquired almost always will context switch away, perhaps after a short delay.
All it means is that the pthread_mutex_lock() call won't return EINTR or similar - it will either successfully acquire the mutex, or return persistent failure.

Related

Can it be assumed that `pthread_cond_signal` will wake the signaled thread atomically with regards to the mutex bond to the condition variable?

Quoting POSIX:
The pthread_cond_broadcast() or pthread_cond_signal() functions may be called by a thread whether or not it currently owns the mutex that threads calling pthread_cond_wait() or pthread_cond_timedwait() have associated with the condition variable during their waits; however, if predictable scheduling behavior is required, then that mutex shall be locked by the thread calling pthread_cond_broadcast() or pthread_cond_signal().
"If predictable scheduling behavior is required". This could/would hint that locking the mutex bound to the condition variable right before calling pthread_cond_signal() should guarantee that the signaled thread will be woken up before any other thread manages to lock this mutex. Is this correct?
We will se if any PThreads guru has a more comprehensive answer, but as far as I can see, at least in the Linux manpage, you do not get fully predictable behavior. What you do get is a guarantee that if two threads wait on the same condition variable, the higher-prio thread gets to go first (at least, that should be true on Linux if one thread is SCHED_OTHER and the other is real-time SCHED_FIFO). That holds if you lock mutex before signalling (with reservation for errors after a quick read of the manpage).
See
https://linux.die.net/man/3/pthread_cond_signal
No, there is no guarantee the signalled thread will be waken up. Worse, if in the signalling thread you have sequence:
while(run_again) {
pthread_mutex_lock(&mutex);
/* prepare data */
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);
}
there is reasonable chance control would never be passed to other threads waiting on mutex because of logic in the scheduler. Some examples to play with you can find in this answer.
No.
The best reference I have found regarding the predictability is this one:
https://austin-group-l.opengroup.narkive.com/lKcmfoRI/predictable-scheduling-behavior-in-pthread-cond-broadcast
Basically, people want to guard against the possibility that threads do not get a fair chance to run. Apparently, it is not a problem for most producer-consumer scenarios, and it does not apply to pthread_cond_broadcast as well. I would say, it is useful only in limited cases.
Cppreference.com actually considers unlocking after notifying may be a pessimization:
https://en.cppreference.com/w/cpp/thread/condition_variable/notify_all

How can I grant ownership of a mutex to a specific thread?

Imagine I have a mutex locked. There is unlimited number of other threads waiting to lock the mutex. When I unlock the mutex, one of those threads will be chosen to enter the critical section. However I have no control over which one. What if I want specific thread to enter the critical section?
I am prety sure this cannot be done using POSIX mutex, however, can I emulate the behaviour using different synchronisation object(s)?
You can use a mutex, a condition variable and a thread id to achive that.
Before unlocking the mutex the thread sets the target thread id, broadcasts the condition variable and releases the mutex. The waiting threads wake up, lock the mutex and check whether the target thread id equals to this thread id. If not the thread goes back to wait.
An optimization to this method to avoid waking up all waiting threads just to check the target thread id and then go back to wait would be to use a separate condition variable for each waiting thread. This way the signaling thread would notify the condition variable of the particular target thread.
Another option is to use signals sent to a particular thread. Let's say we use SIGRTMINfor this purpose. First, all threads block this signal at the start, so that the signal becomes pending and doesn't get lost when the thread isn't waiting for it. When a thread wants to lock the mutex it first calls sigwait() which atomically unblocks SIGRTMIN and waits for it or delivers an already pending one. Once the thread received the signal it can proceed and lock the mutex. The signaling thread uses pthread_kill(target_thread_id, SIGRTMIN) to wake up a particular thread.

How can many threads wait on a condition variable if we place a mutex before it?

pthread_cond_broadcast is used to wake up several threads waiting on a condition variable. But, at the same time it is also said that we should place a mutex before the condition variable to avoid race conditions.
So, if a mutex is there, and one thread already holds it and thus waits on the variable, how can any other thread hold the same mutex (to enter to the waiting part)?
When a thread calls pthread_cond_wait() it needs to hold the associated mutex. The API will release the mutex while it blocks the thread. Once the API decides the thread needs to be released, it will acquire the mutex before returning from the API.
Holding the mutex is required because checking the condition then calling pthread_cond_wait() isn't an atomic operation. The mutex (and the proper use of the mutex) prevents the thread from missing the condition becoming true in the short period between checking it and calling the wait.
But the short answer to the specific question (how can another thread obtain the mutex) is that while the thread is blocked in pthread_cond_wait(), the mutex is not held.

On linux, how to make sure to unlock a mutex which was locked in a thread that dies/terminates?

This is an interview question.
On linux, how to make sure to unlock a POSIX mutex which was locked in a POSIX thread that dies/terminates?
My idea:
Linux will release it automatically when it send kill or termination signal to the program ? But, I cannot find more details about how OS do this ?
thanks
A robust mutex can be used to handle the case where the owner of the mutex is terminated while holding the mutex lock, so that a deadlock does not occur. These have more overhead than a regular mutex, and require that all clients locking the mutex be prepared to handle the error code EOWNERDEAD. This indicates that the former owner has died and that the client receiving this error code is the new owner and is responsible for cleaning up any inconsistent state.
A robust mutex is a mutex with the robust attribute set. It is set using the POSIX.1-2008 standard function pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST).
Further details and example code can be found on the Linux manual page for pthread_mutexattr_getrobust.
If it's not a process-shared mutex, it doesn't matter. When one thread dies, the process dies, and the mutex goes away.
If it's a process-shared mutex, you're asking the wrong question. You wouldn't want to unlock the mutex if a thread died while holding it. The reason a thread holds a mutex is so that it can manipulate shared data through states that must not be seen by other threads. If a thread dies while holding a mutex, it is likely that the data was left in such an inconsistent state. Unlocking the mutex would just allow other threads to see the invalid/corrupt data.

synchronising threads with mutexes

In Qt, I have a method which contains a mutex lock and unlock. The problem is when the mutex is unlock it sometimes take long before the other thread gets the lock back. In other words it seems the same thread can get the lock back(method called in a loop) even though another thread is waiting for it. What can I do about this? One thread is a qthread and the other thread is the main thread.
You can have your thread that just unlocked the mutex relinquish the processor. On Posix, you do that by calling pthread_yield() and on Windows by calling Sleep(0).
That said, there is no guarantee that the thread waiting on the lock will be scheduled before your thread wakes up again.
It shouldn't be possible to release a lock and then get it back if some other thread is already waiting on it.
Check that you actually releasing the lock when you think you do. Check that waiting thread actually waits (and not spins a loop with a trylock tests and sleeps, I actually done that once and was very puzzled at first :)).
Or if waiting thread really never gets time to even reach locking code, try QThread::yieldCurrentThread(). This will stop current thread and give scheduler a chance to give execution to somebody else. Might cause unnecessary switching depending on tightness of your loop.
If you want to make sure that one thread has priority over the other ones, an option is to use a QReadWriteLock. It's adapted to a typical scenario where n threads are going to read a value in a infinite loop, with only one thread updating it. I think it's the scenario you described.
QReadWriteLock offers two ways to lock: lockForRead() and lockForWrite(). The threads depending on the value will use the latter, while the thread updating the value (typically via the GUI) will use the former (lockForWrite()) and will have top priority. You won't need to sleep or yield or whatever.
Example code
Let's say you have a QReadWrite lock; somewhere.
"Reader" thread
forever {
lock.lockForRead();
if (condition) {
do_stuff();
}
lock.unlock();
}
"Writer" thread
// external input (eg. user) changes the thread
lock.lockForWrite(); // will block as soon as the reader lock ends
update_condition();
lock.unlock();

Resources