std::mutex can't be shared among std::thread - c++

In the following codes I suppose mLooperMutex can't be acquired by the children thread. But the program output is quite surprising. It looks that mLooperMutex captured in the std::thread is not the same one in main thread.
But if I changed the detach() call of std::thread to join(), this will lead to a deadlock since the mLooperMutex has been locked by the main thread.
Are there anything wrong with this program if I'd like to use the mLooperMutex among different threads?
a.out:
main: wait cond begin
child: acquiring lock begin
child: acquiring lock done
child: notify one begin
child: notify one done
main: wait cond done
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
int main()
{
std::condition_variable looperSet;
bool child_done = false;
std::mutex mLooperMutex;
cout << "main: acquiring lock begin" << endl;
std::unique_lock<std::mutex> lock(mLooperMutex);
cout << "main: acquiring lock done" << endl;
std::thread{[&mutex=mLooperMutex, &looperSet, &child_done] () {
cout << "child: acquiring lock begin" << endl;
std::unique_lock<std::mutex> lock(mutex);
cout << "child: acquiring lock done" << endl;
child_done = true;
lock.unlock();
cout << "child: notify one begin" << endl;
looperSet.notify_one();
cout << "child: notify one done" << endl;
}}.detach();
cout << "main: wait cond begin" << endl;
looperSet.wait(lock, [&child_done]{ return child_done; });
cout << "main: wait cond done" << endl;
return 0;
}

The reason why the mLooperMutex can be acquired in the child thread is because the lock is released by looperSet.wait:
// This unlocks "lock", and then locks it again afterwards.
looperSet.wait(lock, [&child_done]{ return child_done; });
The reason why this doesn't work with .join() is because .join() waits for the thread to finish before proceeding, and the thread can't finish until the lock is released, and looperSet.wait() which releases the lock won't run until .join() finishes.
Creating a thread and then immediately calling .join() is not very useful, you might as well run the code directly rather than using a thread.

Related

If a thread that is blocked on a std::condition_variable is notified, but the lock on the associated mutex has not been released what would be result?

Is it clear what would happen if a thread blocked on a std::condition_variable receives a notification but the lock on the associated mutex is not yet released, and the lock will be released 10 seconds later? Would the thread wait for the lock to be released or is the situation undefined?
I added a 15seconds sleep on purpose to make the lock unavailable during that time to see how waiting thread will be doing. It is doing OK but just wanted to be sure about it.
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
struct SharedResource
{
SharedResource() :
cv_mutex(), cv(), counter(0)
{
}
/*
This mutex is used for three purposes:
1) to synchronize accesses to counter
2) to synchronize accesses to std::cerr
3) for the condition variable cv
*/
std::mutex cv_mutex;
std::condition_variable cv;
int counter;
};
void waits(SharedResource& sharedRes)
{
std::unique_lock<std::mutex> lk(sharedRes.cv_mutex);
std::cerr << "Waiting... \n";
while (sharedRes.counter != 1)
{
sharedRes.cv.wait_for(lk,3s);
std::cerr << "Thread ID: " << std::this_thread::get_id() << " wakes up every 3 seconds.\n";
}
std::cerr << "...finished waiting." << "counter: " << sharedRes.counter << std::endl;
} //The lk object will be unlocked after this scope ends.
void signals(SharedResource& sharedRes)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lk(sharedRes.cv_mutex);
std::cerr << "Notifying...\n";
} // The lk object will be unlocked after this scope ends.
sharedRes.cv.notify_all();
std::this_thread::sleep_for(std::chrono::seconds(6));
{
std::lock_guard<std::mutex> lk(sharedRes.cv_mutex);
sharedRes.counter = 1;
std::cerr << "Notifying again...\n";
sharedRes.cv.notify_all();
std::this_thread::sleep_for(std::chrono::seconds(15));
}// The lk object will be unlocked after this scope ends.
}
int main()
{
SharedResource sharedRes;
std::thread
t1(waits, std::ref(sharedRes)),
t2(signals, std::ref(sharedRes));
t1.join();
t2.join();
}
If a thread that is blocked on a std::condition_variable is notified, but the lock on the associated mutex has not been released what would be result?
It will continue to wait / wait_for until it can reacquire the lock.
When std::condition_variable::wait and wait_for returns (for whatever reason), the lock is held again, so you don't have to worry about that.
It can even return from wait without having gotten any notifications (spurious wake-ups) - but, no matter what, the lock is reacquired when the call returns.

Trouble understanding the unique_lock

I have some trouble understanding the unique_lock and condition_variable in the code below.
#define _GLIBCXX_USE_NANOSLEEP
#include <chrono>
#include <mutex>
#include <thread>
#include <iostream>
#include <condition_variable>
using namespace std;
mutex m;
int i=1;
condition_variable cv;
void funcA()
{
cout << "FuncA Before lock" << endl;
unique_lock<mutex> lk(m);
cout << "FuncA After lock" << endl;
while(i != 2) { cv.wait(lk); }
//std::chrono::milliseconds dura(500);//make sure thread is running
//std::this_thread::sleep_for(dura); //this_thread::sleep_for(dura);
cout << "FuncA After sleep" << endl;
lk.unlock();
cv.notify_all();
}
void funcB()
{
cout << "FuncB Before lock" << endl;
unique_lock<mutex> lk(m);
cout << "FuncB After lock" << endl;
while(i != 1) { cv.wait(lk); }
//std::chrono::milliseconds dura(500);//make sure thread is running
//std::this_thread::sleep_for(dura); //this_thread::sleep_for(dura);
cout << "FuncB After sleep" << endl;
++i;
lk.unlock();
cv.notify_all();
}
int main(int argc, char* argv[])
{
auto a = thread(funcA), b=thread(funcB);
a.join(), b.join();
return 0;
}
As per https://en.cppreference.com/w/cpp/thread/unique_lock/unique_lock (when the constructor is called with a mutex) the constructor locks the associated mutex by calling m.lock()
Let us say, threadA/funcA gets scheduled first by the operating system. So, the following lines will get executed
cout << "FuncA Before lock" << endl;
unique_lock<mutex> lk(m);
cout << "FuncA After lock" << endl;
while(i != 2) { cv.wait(lk); }
Now, threadA has the mutex since the constructor for the unique_lock has called lock on it. However, since i!=2, the thread will stay in the while-loop waiting to be notified by the other thread.
Now, let us say the operating system switches the threads and funcB is called.
cout << "FuncB Before lock" << endl;
unique_lock<mutex> lk(m);
Now, this thread should get stuck here on the second line right? Because mutex m is already owned by threadA (since it has called m.lock() on it), and therefore the m.lock() call by unique_lock constructor should block since the mutex is not available.
So, there's a deadlock? The threadA is waiting in the infinite loop waiting for i to be incremented to 2, whereas threadB is waiting for the mutex owned by threadA.
However, nothing like that happens, and the code works fine.

condition_variable::notify_one does not instantly unblock wait?

I have a question about the notify_one function. At the following code,
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
#include <mutex>
std::condition_variable cv;
std::mutex m;
bool ready = false;
void f()
{
std::unique_lock<std::mutex> lk(m);
std::cout << "get into wait, ready=" << ready << std::endl;
cv.wait(lk, []() { return ready; });
std::cout << "get out of wait, ready=" << ready << std::endl;
}
int main()
{
std::thread a(f);
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::unique_lock<std::mutex> lk(m, std::defer_lock);
if (lk.try_lock()) {
std::cout << "main try_lock success, ready=" << ready << std::endl;
ready = true;
}
}
std::cout << "main notify, ready=" << ready << std::endl;
cv.notify_one();
// std::cout << "hello" << std::endl;
{
std::unique_lock<std::mutex> lk(m, std::defer_lock);
if (lk.try_lock()) {
std::cout << "main try_lock success, ready=" << ready << std::endl;
ready = true;
}
}
std::cout << "main notify, ready=" << ready << std::endl;
cv.notify_one();
a.join();
return 0;
}
I get a following result,
get into wait, ready=0
main try_lock success, ready=0
main notify, ready=1
main try_lock success, ready=1
main notify, ready=1
get out of wait, ready=1
But I expect a below result, because, according to the page, if notify_one is called wait is unblocked and lock of mutex(m) is reacquired.
get into wait, ready=0
main try_lock success, ready=0
main notify, ready=1
main notify, ready=1
get out of wait, ready=1
I found if I comment out the std::cout << "hello" << std::endl; I get expected result. Looks to me like notify_one does not immediately unblock the wait. It is correct?
Thank you very much!
Looks to me like notify_one does not immediately unblock the wait. It is correct?
Notification immediately unblocks the wait, meaning the wait is able to resume after the notification. That is, the sleeping thread is marked as runnable.
However, the scheduler doesn't necessarily re-start it immediately. If it would have to pre-empt some already running thread/process, it will probably wait until a yield, a syscall or some other cancellation point, and otherwise not even look at the newly-runnable thread until the end of the current timeslice.
Unblocking something is not the same as forcing an immediate context switch (which is lucky, as otherwise synchronization would be even more expensive).
After your first cv.notify_one(); you have no synchronization points (until a.join();) and the thread already running will most likely continue to run and reach a.join(); before the scheduler decides to give the other thread a spin.
As an experiment, you can yield the thread's execution slot by sleeping a little after notifying - and it may cause the order of the output to be what you expected.
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
Don't rely on it in production code though. This doesn't guarantee anything since there is no synchronization between the threads (other than the join()).

Thread Ownership of a mutex

According to several documentation examples the thread can't unlock a mutex unless it locked it explicitly. Here is an excerpt from man page for pthread_mutex_unlock at IBM.
The pthread_mutex_unlock() function unlocks the mutex specified. If
the calling thread does not currently hold the mutex (via a previous
call to pthread_mutex_lock(), pthread_mutex_trylock(), or
pthread_mutex_timedlock_np()) the unlock request fails with the EPERM
error.
Even the new C++ standard says something similar about the thread ownership, yet the following program was able to unlock a mutex locked on a different thread. On gcc & Linux systems the same behavior is seen both on pthread mutex as well as std::mutex (which I believe is implemented based on pthread_mutex anyway).
#include <iostream>
#include <thread>
#include <mutex>
#include <pthread.h>
std::mutex stmutex;
pthread_mutex_t pthrmutex = PTHREAD_MUTEX_INITIALIZER;
void thread1(int i)
{
stmutex.unlock();
std::cout << "Un Locked in thread 1" << std::this_thread::get_id() << std::endl;
}
void pthread1(int i)
{
pthread_mutex_unlock(&pthrmutex);
std::cout << "Un Locked Pthread mutex in thread 1" << std::this_thread::get_id() << std::endl;
}
void thread2(int i)
{
stmutex.lock();
std::cout << "Locked in thread 2" << std::this_thread::get_id() << std::endl;
}
void thread3(int i)
{
stmutex.unlock();
std::cout << "UNLocked in thread 3" << std::this_thread::get_id() << std::endl;
}
int main(int argc, char **argv)
{
try {
stmutex.lock();
std::cout << "Locked in main thread : " << std::this_thread::get_id() << std::endl;
std::thread t1(thread1,1);
t1.join();
stmutex.lock();
std::cout << "Locked in main thread after unlocking in thread1" << std::endl;
stmutex.unlock();
std::cout << "Un Locked in main thread " << std::endl;
pthread_mutex_lock(&pthrmutex);
std::cout << "Locked pthread mutex in main thread : " << std::this_thread::get_id() << std::endl;
std::thread t2(pthread1,1);
t2.join();
pthread_mutex_lock(&pthrmutex);
std::cout << "Locked Pthread mutext in main thread after unlocking in thread1" << std::endl;
pthread_mutex_unlock(&pthrmutex);
std::cout << "Un Locked Pthread mutext in main thread " << std::endl;
std::thread t3(thread2,1);
t3.join();
std::thread t4(thread3,1);
t4.join();
} catch (std::exception& ex)
{
std::cerr << "Exception In main thread: " << ex.what() << std::endl;
}
}
Am I missing anything in my understanding of mutex "Ownership" ?
The request fails with the EPERM error only for mutexes created with PTHREAD_MUTEX_ERRORCHECK.
See pthread_mutex_lock section RATIONALE:
... while being able to extract the thread ID of the owner of a mutex might be desirable, it would require storing the current thread ID when each mutex is locked, and this could incur unacceptable levels of overhead.
I.e. initialize your mutex with PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP.
Example:
void locker(pthread_mutex_t* mutex) {
if(int e = pthread_mutex_lock(mutex))
fprintf(stderr, "pthread_mutex_lock: (%d)%s\n", e, strerror(e));
}
void unlocker(pthread_mutex_t* mutex) {
if(int e = pthread_mutex_unlock(mutex))
fprintf(stderr, "pthread_mutex_unlock: (%d)%s\n", e, strerror(e));
}
int main() {
pthread_mutex_t a = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t b = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
std::thread(locker, &a).join();
std::thread(locker, &b).join();
std::thread(unlocker, &a).join();
std::thread(unlocker, &b).join(); // pthread_mutex_unlock: (1)Operation not permitted
}

Why it does NOT occur deadlock?

The code is acquiring the same mutex from two different threads at the same time.
I understand that a deadlock should occur. Why it is not happening?
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
template <typename T>
class SafeQueue
{
public:
T pop()
{
std::unique_lock<std::mutex> mlock(mutex_);
std::cout << "lock pop()" << std::endl;
while (queue_.empty())
{
cond_.wait(mlock);
std::cout << "lock pop awake. Items: " << queue_.size() << std::endl;
}
auto item = queue_.front();
queue_.pop();
std::cout << "returning from pop" << std::endl;
return item;
}
void push(const T& item)
{
std::unique_lock<std::mutex> mlock(mutex_);
std::cout << "lock push()" << std::endl;
queue_.push(item);
mlock.unlock();
cond_.notify_one();
}
private:
std::queue<T> queue_;
mutable std::mutex mutex_;
std::condition_variable cond_;
};
SafeQueue<int> queue;
void pop()
{
std::cout << "popping..." << std::endl;
std::cout << "popped: " << queue.pop() << std::endl;
}
int main()
{
std::thread consumerThread(pop);
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "main thread will push" << std::endl;
queue.push(2);
std::cout << "pushed" << std::endl;
consumerThread.join();
std::cout << "end" << std::endl << std::endl;
}
My output is:
popping...
lock pop()
main thread will push
lock push()
pushed
lock pop awake. Items: 1
returning from pop
popped: 2
end
This statement:
cond_.wait(mlock);
actually unlocks the mutex during the wait, and reacquires the lock once signaled. That's why you don't have any deadlock.
What is happening is that std::condition_variable::wait is releasing the mutex. The thread then waits until the notify_one call, that will release the condition and re-acquire the mutex.
http://en.cppreference.com/w/cpp/thread/condition_variable/wait
"There must be at least one thread that is waiting for a condition to become true. The waiting thread must first acquire a unique_lock. This lock is passed to the wait() method, that releases the mutex and suspends the thread until the condition variable is signaled. When that happens the thread is awaken and the lock is re-acquired."
http://www.codeproject.com/Articles/598695/Cplusplus-threads-locks-and-condition-variables
A deadlock requires TWO mutexes to be acquired in different orders in different threads. I only see a single mutex in your code.