From: Jonathan Wakely Date: Mon, 19 Dec 2011 00:34:29 +0000 (+0000) Subject: re PR libstdc++/50862 (deadlock in std::condition_variable_any) X-Git-Tag: releases/gcc-4.6.3~246 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d10d3240d7d6998b22bd7b62cd48eabacef85773;p=thirdparty%2Fgcc.git re PR libstdc++/50862 (deadlock in std::condition_variable_any) PR libstdc++/50862 * include/std/condition_variable (condition_variable_any::wait): Fix deadlock and ensure _Lock::lock() is called on exit. * testsuite/30_threads/condition_variable_any/50862.cc: New. From-SVN: r182467 --- diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 62a2d4c18e95..5978d7b7a066 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,10 @@ +2011-12-19 Jonathan Wakely + + PR libstdc++/50862 + * include/std/condition_variable (condition_variable_any::wait): Fix + deadlock and ensure _Lock::lock() is called on exit. + * testsuite/30_threads/condition_variable_any/50862.cc: New. + 2011-12-18 Jonathan Wakely PR libstdc++/51540 diff --git a/libstdc++-v3/include/std/condition_variable b/libstdc++-v3/include/std/condition_variable index a0a3c08794a1..ff65dc49ce44 100644 --- a/libstdc++-v3/include/std/condition_variable +++ b/libstdc++-v3/include/std/condition_variable @@ -198,10 +198,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void wait(_Lock& __lock) { - unique_lock __my_lock(_M_mutex); - __lock.unlock(); - _M_cond.wait(__my_lock); - __lock.lock(); + // scoped unlock - unlocks in ctor, re-locks in dtor + struct _Unlock { + explicit _Unlock(_Lock& __lk) : _M_lock(__lk) { __lk.unlock(); } + ~_Unlock() noexcept(false) + { + if (uncaught_exception()) + __try { _M_lock.lock(); } __catch(...) { } + else + _M_lock.lock(); + } + _Lock& _M_lock; + }; + + unique_lock __my_lock(_M_mutex); + _Unlock __unlock(__lock); + // _M_mutex must be unlocked before re-locking __lock so move + // ownership of _M_mutex lock to an object with shorter lifetime. + unique_lock __my_lock2(std::move(__my_lock)); + _M_cond.wait(__my_lock2); } diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable_any/50862.cc b/libstdc++-v3/testsuite/30_threads/condition_variable_any/50862.cc new file mode 100644 index 000000000000..b85a5e136266 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/condition_variable_any/50862.cc @@ -0,0 +1,80 @@ +// { dg-do run { target *-*-freebsd* *-*-netbsd* *-*-linux* *-*-solaris* *-*-cygwin *-*-darwin* alpha*-*-osf* mips-sgi-irix6* } } +// { dg-options " -std=gnu++0x -pthread" { target *-*-freebsd* *-*-netbsd* *-*-linux* alpha*-*-osf* mips-sgi-irix6* } } +// { dg-options " -std=gnu++0x -pthreads" { target *-*-solaris* } } +// { dg-options " -std=gnu++0x " { target *-*-cygwin *-*-darwin* } } +// { dg-require-cstdint "" } +// { dg-require-gthreads "" } +// { dg-require-sched-yield "" } + +// Copyright (C) 2011 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +#include +#include +#include +#include +#include + +struct scoped_thread +{ + ~scoped_thread() { if (t.joinable()) t.join(); } + std::thread t; +}; + +int main() +{ + typedef std::unique_lock Lock; + + std::mutex m; + std::condition_variable_any cond; + unsigned int product = 0; + const unsigned int count = 10; + + // writing to stream causes timing changes which makes deadlock easier + // to reproduce - do not remove + std::ostringstream out; + + // create consumers + std::array threads; + for (std::size_t i = 0; i < threads.size(); ++i) + threads[i].t + = std::thread( [&] + { + for (unsigned int i = 0; i < count; ++i) + { + std::this_thread::yield(); + Lock lock(m); + while(product == 0) + cond.wait(lock); + out << "got product " + << std::this_thread::get_id() + << ' ' << product << std::endl; + --product; + } + } ); + + // single producer + for (std::size_t i = 0; i < threads.size() * count; ++i) + { + std::this_thread::yield(); + Lock lock(m); + ++product; + out << "setting product " << std::this_thread::get_id() + << ' ' << product << std::endl; + cond.notify_one(); + } +}