I have my main process send pthread_cancel to another thread which is waiting for a condition to happen with cond_wait(&condition). On the pthread_cancel they are saying : Deferred cancel ability means that cancellation will be delayed until the thread next calls a function that is a cancellation point. But often those function are blocking function. Then my question is the thread cancelled only after that thread has been unblock (in my example by a broadcast or a signal) or it would see that i am currently blocking on a cancellation point and then cancelled my thread ?
I'm not familiar with cond_wait, but I presume it's from another library than the typically used pthread_cond_wait?
But yes, if a thread is blocked in a pthread_cond_wait and then cancelled, the thread will be woken up, reacquire it's mutex, and then be canceled.
There are thus two important points here to keep in mind when canceling threads that are blocked on a condition:
Make sure that the mutex is unlocked (or will be unlocked at some point in the future), before calling pthread_cancel. For instance, if thread A is waiting on a condition, and thread B locks the condition mutex, calls pthread_cancel and then pthread_join before unlocking the condition mutex, you'll deadlock.
Install a cleanup handler (see pthread_cleanup_push) to unlock your condition mutex before calling pthread_cond_wait - otherwise you'll cancel your thread and leave the mutex locked.
However, note also that the pthread condition variable implementation has had/has some bugs - so be sure to use an up-to-date glibc.
You might want to use pthread_cond_wait instead of cond_wait.
If you use pthread_cond_wait and based on this from man pthread_cond_wait(3)
A condition wait (whether timed or not) is a cancellation point. When the cancelability enable state of a thread is set to PTHREAD_CANCEL_DEFERRED, a side effect of acting upon a cancellation request while in a condition wait is that the mutex is (in effect) re-acquired before calling the first cancellation cleanup handler. The effect is as if the thread were unblocked, allowed to execute up to the point of returning from the call to pthread_cond_timedwait() or pthread_cond_wait(), but at that point notices the cancellation request and instead of returning to the caller of pthread_cond_timedwait() or pthread_cond_wait(), starts the thread cancellation activities, which includes calling cancellation cleanup handlers.
It looks like the thread will cancel on pthread_cond_wait even if it's currently blocked
Or you could set the cancellation type with pthread_setcanceltype to ASYNCHRONOUS. see comment below
But like most of the time, the best way to know for sure would be to try it with a test code.
Related
Looking at several videos and the documentation example, we unlock the mutex before calling the notify_all(). Will it be better to instead call it after?
The common way:
Inside the Notifier thread:
//prepare data for several worker-threads;
//and now, awaken the threads:
std::unique_lock<std::mutex> lock2(sharedMutex);
_threadsCanAwaken = true;
lock2.unlock();
_conditionVar.notify_all(); //awaken all the worker threads;
//wait until all threads completed;
//cleanup:
_threadsCanAwaken = false;
//prepare new batches once again, etc, etc
Inside one of the worker threads:
while(true){
// wait for the next batch:
std::unique_lock<std::mutex> lock1(sharedMutex);
_conditionVar.wait(lock1, [](){return _threadsCanAwaken});
lock1.unlock(); //let sibling worker-threads work on their part as well
//perform the final task
//signal the notifier that one more thread has completed;
//loop back and wait until the next task
}
Notice how the lock2 is unlocked before we notify the condition variable - should we instead unlock it after the notify_all() ?
Edit
From my comment below: My concern is that, what if the worker spuriously awakes, sees that the mutex is unlocked, super-quickly completes the task and loops back to the start of while. Now the slow-poke Notifier finally calls notify_all(), causing the worker to loop an additional time (excessive and undesired).
There are no advantages to unlocking the mutex before signaling the condition variable unless your implementation is unusual. There are two disadvantages to unlocking before signaling:
If you unlock before you signal, the signal may wake a thread that choose to block on the condition variable after you unlocked. This can lead to a deadlock if you use the same condition variable to signal more than one logical condition. This kind of bug is hard to create, hard to diagnose, and hard to understand. It is trivially avoided by always signaling before unlocking. This ensures that the change of shared state and the signal are an atomic operation and that race conditions and deadlocks are impossible.
There is a performance penalty for unlocking before signaling that is avoided by unlocking after signaling. If you signal before you unlock, a good implementation will know that your signal cannot possibly render any thread ready-to-run because the mutex is held by the calling thread and any thread affects by the condition variable necessarily cannot make forward progress without the mutex. This permits a significant optimization (often called "wait morphing") that is not possible if you unlock first.
So signal while holding the lock unless you have some unusual reason to do otherwise.
should we instead unlock it after the notify_all() ?
It is correct to do it either way but you may have different behavior in different situations. It is quite difficult to predict how it will affect performance of your program - I've seen both positive and negative effects for different applications. So it is better you profile your program and make decision on your particular situation based on profiling.
As mentioned here : cppreference.com
The notifying thread does not need to hold the lock on the same mutex
as the one held by the waiting thread(s); in fact doing so is a
pessimization, since the notified thread would immediately block
again, waiting for the notifying thread to release the lock.
That said, documentation for wait
At the moment of blocking the thread, the function automatically calls
lck.unlock(), allowing other locked threads to continue.
Once notified (explicitly, by some other thread), the function
unblocks and calls lck.lock(), leaving lck in the same state as when
the function was called. Then the function returns (notice that this
last mutex locking may block again the thread before returning).
so when notified wait will re-attempt to gain the lock and in that process it will get blocked again till original notifying thread releases the lock.
So I'll suggest that release the lock before calling notify. As done in example on cppreference.com and most importantly
Don't be Pessimistic.
David's answer seems to me wrong.
First, assuming the simple case of two threads, one waiting for the other on a condition variable, unlocking first by the notifier will not waken the other waiting thread, as the signal has not arrived. Then the notify call will immediately waken the waiting thread. You do not need any special optimizations.
On the other hand, signalling first has the potential of waking up a thread and making it sleep immediately again, as it cannot hold the lock—unless wait morphing is implemented.
Wait morphing does not exist in Linux at least, according to the answer under this StackOverflow question: Which OS / platforms implement wait morphing optimization?
The cppreference example also unlocks first before signalling: https://en.cppreference.com/w/cpp/thread/condition_variable/notify_all
It explicit says:
The notifying thread does not need to hold the lock on the same mutex as the one held by the waiting thread(s). Doing so may be a pessimization, since the notified thread would immediately block again, waiting for the notifying thread to release the lock, though some implementations recognize the pattern and do not attempt to wake up the thread that is notified under lock.
should we instead unlock it after the notify_all() ?
After reading several related posts, I've formed the opinion that it's purely a performance issue. If OS supports "wait morphing", unlock after; otherwise, unlock before.
I'm adding an answer here to augment that of #DavidSchwartz 's. Particularly, I'd like to clarify his point 1.
If you unlock before you signal, the signal may wake a thread that choose to block on the condition variable after you unlocked. This can lead to a deadlock if you use the same condition variable to signal more than one logical condition. This kind of bug is hard to create, hard to diagnose, and hard to understand. It is trivially avoided by always signaling before unlocking. This ensures that the change of shared state and the signal are an atomic operation and that race conditions and deadlocks are impossible.
The 1st thing I said is that, because it's a CV and not a Mutex, a better term for the so-called "deadlock" might be "sleep paralysis" - a mistake some programs make is that
a thread that's supposed to wake
went to sleep due to not rechecking the condition it's been waiting for before wait'ng again.
The 2nd thing is that, when waking some other thread(s),
the default choice should be broadcast/notify_all (broadcast is the POSIX term, which is equivalent to its C++ counterpart).
signal/notify is an optimized special case used for when there's only 1 other thread is waiting.
Finally 3rd, David is adamant that
it's better to unlock after notify,
because it can avoid the "deadlock" which I've been referring to as "sleep paralysis".
If it's unlock then notify, then there's a window where another thread (let's call this the "wrong" thread) may i.) acquire the mutex, ii.)going into wait, and iii.) wake up. The steps i. ii. and iii. happens too quickly, consumed the signal, leaving the intended (let's call it "correct") thread in sleep.
I discussed this extensively with David, he clarified that only when all 3 points are violated ( 1. condvar associated with several separate conditions and/or didn't check it before waiting again; 2. signal/notify only 1 thread when there're more than 1 other threads using the condvar; 3. unlock before notify creating a window for race condition ), the "sleep paralysis" would occur.
Finally, my recommendation is that, point 1 and 2 are essential for correctness of the program, and fixing issues associated with 1 and 2 should be prioritized over 3, which should only be a augmentative "last resort".
For the purpose of providing reference, manpage for signal/broadcast and wait contains some info from version 3 of Single Unix Specification that gave some explanations on point 1 and 2, and partly 3. Although specified for POSIX/Unix/Linux in C, it's concepts are applicable to C++.
As of this writing (2023-01-31), the 2018 edition of version 4 of Single Unix Specification is released, and the drafting of version 5 is underway.
I am trying to get my head around what exactly a cancellation point is in c++. I have read:
man page and What are pthread cancellation points used for
But I am still a little confused on certain points. For example, I am using the file write() function. Apparently this is a cancellation point. So when I call write(), I can see that another thread may start processing (so my code switches from the writing thread to another thread), this usually happens when the write-to buffer is full and needs to be emptied before the write() can succeed/complete.
But in my mind, this is not a cancellation of a thread, but merely a temporary blocking/suspend, and there is no thread "cleanup" to do...
So my question is, do cancellation points have relation to "blocking points"? - are they really the same thing, or is there some difference?
Any clear "top-level" description of what a cancellation point is would be really useful.
When your thread gets pulled from execution, its state is saved by the OS and that is not a cancellation of the thread. The cancellation means thread termination, on request, with the specific intent of letting everything in a final state when completed (aka. all resources are freed, all handlers are updated, etc.).
What you call blocking can happen to a thread while in mid-cancellation.
Example: The thread gets a cancellation request. The OS queues it until the thread becomes cancellable. When the thread becomes cancellable, and the thread is executing a cancel point, the thread can be cleaned and cancelled. The write function is a cancellation point, this meaning it is safe from the point of view of the OS to cancel the thread while this function is executed (the state of all related resources will be consistent).
While the cancellation procedure is running, the thread can be blocked as many times as the OS sees fit.
As an additional note, if you look at the POSIX requirement for cancellation points, virtually all blocking interfaces are required to be cancellation points. Otherwise, on any completely blocked thread (in such call), there would be no safe way to terminate that thread.
http://man7.org/linux/man-pages/man7/pthreads.7.html
When you want to terminate or cancel a thread from another thread (e.g., from the main thread) using pthread_cancel() the following happens (c.f.):
The pthread_cancel() function sends a cancellation request to the thread thread.
The target thread will not terminate at once, but rather when it reaches a cancellation point (c.f.):
POSIX.1 specifies that certain functions must, and certain other
functions may, be cancellation points. If a thread is cancelable,
its cancelability type is deferred, and a cancellation request is
pending for the thread, then the thread is canceled when it calls a
function that is a cancellation point.
Whether or not these functions that are a cancellation point may also block the execution of the thread, is not relevant at this point. There is a list of these functions in the documentation:
http://man7.org/linux/man-pages/man7/pthreads.7.html
Note that there are settings that can influence the behaviour and "cancellability" of a thread that I have left out here for simplicity. For further reading:
http://man7.org/linux/man-pages/man7/pthreads.7.html
http://man7.org/linux/man-pages/man3/pthread_cancel.3.html
http://man7.org/linux/man-pages/man3/pthread_setcancelstate.3.html
http://man7.org/linux/man-pages/man3/pthread_testcancel.3.html
i want to know how it is possible to wait for a work to done and then continue and create new one
while(!stop)
{
CreateWork();
waitForWorkToDone();
}
wait must not block calling thread
how i can achive this?
To achieve this, you can rely on the operating system providing a facility to block until notified with or without a timeout. Thus, your thread correctly does not use unnecessary CPU cycles by performing a busy wait, but is still able to respond to program state changes. With POSIX threads, you can use a condition timed wait. I'll illustrate with the boost implementation, but the concept extends generally.
do
{
boost::unique_lock<boost::mutex> lock(state_change_mutex);
boost::system_time const timeout = boost::get_system_time() + boost::posix_time::seconds(5);
state_change_cond.timed_wait(lock,timeout);
...
} while(!done);
Overall this thread will loop until the done sentinel value becomes true. Other threads can signal this thread by calling
state_change_cond.notify_all();
Or in this example if no signal happens in 5 seconds then the thread wakes up by itself.
Note that condition variables require locking by mutexes. This is to guarantee that the thread is awoken atomically and that it will behave correctly in a mutually exclusive section as inter-thread signaling implicitly is.
How about Creating a Signal. Create a handler that creates CreateWork() and signals when the job is done! Just a Suggestion
I am trying to write a faulty code that doesn't uses the pthread_cond variable. On the condition check, if the condition fails it waits/blocks forever. The thread B should post a signal to get the thread A unblock. I don't the API to use for block and unblock in Linux.
I want to demonstrate the conditional variable benefits. However, I want to first write a code that blocks on a condition, and it should unblock by the signal. In this, I will show how a signal is lost, and the race condition occurs.Please help me with the API's and usage.
You can use pthread_mutex instead.
First create a global mutex, and lock it into your main() function before creating the threads A & B.
Into thread A do a pthread_mutex_lock() that will wait until the mutex is unlocked.
Into thread B do a pthread_mutex_unlock() to signal your thread A to continue.
I have a pthread that I created and now I want that in a specific time interval the thread execute some code. But the user should also be able to cancel the thread. How can I cancel a thread and ensure that the thread is not cancelled when it execute the code?
In Java you handle this with
while(!isInterrupted)
Is there any similar solution with pthreads.
In the Question's example code you are checking some variable. This is not the normal pattern for interrupting threads in Java.
In Java, you interrupt a thread by calling the interrupt() method.
The thread then checks if it is interrupted inside IO and system calls (which can throw InterruptedException when this happens; this means a thread that is sleeping or waiting on IO can be awoken when interrupted) or by sampling the isInterrupted() flag (typically used in a condition in a loop, as in Question).
The distinction is important; checking some flag variable you've declared is only possible in loops and your own code; the Java interrupting system works for all threads and all non-CPU-blocking code without special effort on the part of the programmer.
Pthreads has the pthread_cancel() pattern which works like the Java interrupting pattern.
pthread_cancel is available for sending cancel requests:
A thread's cancellation type, determined by pthread_setcanceltype(3), may be
either asynchronous or deferred (the default for new threads). Asynchronous
cancelability means that the thread can be canceled at any time (usually
immediately, but the system does not guarantee this). Deferred cancelability
means that cancellation will be delayed until the thread next calls a function
that is a cancellation point. A list of functions that are or may be
cancellation points is provided in pthreads(7).
A thread's cancelability state, determined by pthread_setcancelstate(3), can
be enabled (the default for new threads) or disabled. If a thread has
disabled cancellation, then a cancellation request remains queued until the
thread enables cancellation. If a thread has enabled cancellation, then its
cancelability type determines when cancellation occurs.
So there are several options:
1: while value checking (works very well, but you don't have much control).
2: check the pthread_cancel manpage, it works to but with strict rules.
3: using pthread_signal, first you need to block, than signal for resume. It has the same issues as the second option.
Using pthreads cancel and signal will only work from within the thread that must be locked. So setting a variable to initiate the signal block. Unlocking can be done by any other thread.
The same can be done using mutex or semaphores (pthread_mutex, pthread_semaphore).
A site I recommend: http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
There's no specific function to cancel a threadYou can use pthread_cancel to cancel the thread, as mentioned (but I would advise against it, unless you know what you're doing), and you have to set up your own timers. But the while(!isInterrupted) is pretty acceptable way of doing it.
It should basically be like this:
while(!isInterrupted)
{
// whatever you want to do
sleep(howLongYouWantToWait);
}
// clean up and exit the thread function here
and in the main thread have a global (or other, see below)
volatile bool isInterrupted = false;
and set it to true when you're done, and pthread_join if you want to wait for the thread to finish.
Instead of global, you can use a class variable, or a flag pointer passed to the thread function, or any other way, global is the simplest and the least preferable.
Of course, if you want to cancel the thread while it waits, and not to have it canceled only after it finishes the whole loop, then you need to deal with signals, and other stuff, but I think you're not looking for that.