michael.po...@gmail.com
unread,Dec 11, 2015, 10:55:11 AM12/11/15You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to
Hi Everyone,
This post is related to the thread library of C++11.
There is a somewhere special function in C++
void std::notify_all_at_thread_exit( std::condition_variable& cond,
std::unique_lock<std::mutex> lk );
which is defined as to 'schedule' an execution of the following code fragment:
line 1: lk.unlock();
line 2: cond.notify_all();
at the very end of the thread when all the thread-local variables are already destructed.
I think that this particular order (first releasing the mutex and only then signalling the cond-var) may be dangerous for some particular scenarios and lead to accessing memory which is either freed or where destructor has been applied (that very memory which was previously allocated by the cond-var).
To demonstrate that, I'll consider a promise-future class pair, in which the promise class implements
std::promise::set_value_at_thread_exit(const T& val) function.
The scenario is simple:
Thread B calls promise.set_value_at_thread_exit(val);
while Thread A calls val=future.get();
and then immediately destroys the 'future' instance (say, it is going out of the scope).
I will now follow the Microsoft C++ implementation, which uses notify_all_at_thread_exit() in rather obvious and straightforward way.
The promise and future class instances share a common state which can store a value (which promise will pass to the future) and with a mutex+cond_var to control synchronization. So, when we call set_value_at_thread_exit() on the promise in the thread B, the mutex will be locked, then the value will be assigned, some readiness boolean flag will be set and then the releasing of the mutex and signalling of the cond-var will be scheduled with a call to notify_all_at_thread_exit(). While the thread B is running, the mutex will be locked and so other threads will not yet "see" the respective 'future' ready.
The thread A which possesses the future instance will do its call to
val = future.get();
which awaits for the result from the thread B. This wait is implemented as locking the same mutex, verifying the readiness flag and if it is not yet set, starting a busy-wait on the cond-var. Finally, when the readiness flag is seen to be set, reading the value and releasing the mutex. Pretty standard arrangement.
Now suppose that the thread A started its wait just at the moment when thread B is finishing, precisely after the thread B calls lk.unlock() (line 1) and BEFORE it calls cond.notify_all(). Then the wait will immediately succeed, the future.get() call will see the value ready and future.get() will finish. Then thread A will destruct its 'future' instance (remember, we made it going out of scope) which will destroy the shared (between promise and future) state as well (the 'promise' instance has been presumably destructed by the time thread B finishes). So, the shared state is destructed, the condition variable is destructed and yet we still have thread B which is now going to call cond.notify_all() on the object 'cond' (line 2) which has been already destructed.
This problem is not a feature of promise-future implementation, it may happen with some custom call to notify_all_at_thread_exit() unless the provided conditional variable does not reside in the global memory.
I suppose that if notify_all_at_thread_exit() would make its actions in opposite way, i.e. first signalling the condvar and then releasing the mutex, this problem would have gone.
Regards, Michael