diff mbox

Update catch(...) handlers to deal with __forced_unwind

Message ID 20140514193707.GE10556@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely May 14, 2014, 7:37 p.m. UTC
Failing to rethrow a __forced_unwind exception is very bad.

This patch ensures we rethrow them in async tasks, and makes the
shared state ready with a broken_promise so that waiting threads
don't block forever. That seems reasonable to me, does anyone have any
better ideas?

Tested x86_64-linux, will wait for feedback before committing.

Comments

Jonathan Wakely June 2, 2014, 3:45 p.m. UTC | #1
On 14/05/14 20:37 +0100, Jonathan Wakely wrote:
>Failing to rethrow a __forced_unwind exception is very bad.
>
>This patch ensures we rethrow them in async tasks, and makes the
>shared state ready with a broken_promise so that waiting threads
>don't block forever. That seems reasonable to me, does anyone have any
>better ideas?
>
>Tested x86_64-linux, will wait for feedback before committing.

Committed to trunk.


>commit 8d4a49e0391269380b160bd277339f740716de0c
>Author: Jonathan Wakely <jwakely@redhat.com>
>Date:   Tue May 13 15:35:29 2014 +0100
>
>    	* include/std/condition_variable (condition_variable_any::_Unlock): Do
>    	not swallow __forced_unwind.
>    	* include/std/future (__future_base::_Task_setter): Likewise.
>    	(__future_base::_Async_state_impl): Turn __forced_unwind into broken
>    	promise and rethrow.
>    	* include/std/mutex (try_lock): Likewise.
>    	* testsuite/30_threads/async/forced_unwind.cc: New.
>    	* testsuite/30_threads/packaged_task/forced_unwind.cc: New.
>
>diff --git a/libstdc++-v3/include/std/condition_variable b/libstdc++-v3/include/std/condition_variable
>index fc111dd..921cb83 100644
>--- a/libstdc++-v3/include/std/condition_variable
>+++ b/libstdc++-v3/include/std/condition_variable
>@@ -189,7 +189,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	~_Unlock() noexcept(false)
> 	{
> 	  if (uncaught_exception())
>-	    __try { _M_lock.lock(); } __catch(...) { }
>+	    {
>+	      __try
>+	      { _M_lock.lock(); }
>+	      __catch(const __cxxabiv1::__forced_unwind&)
>+	      { __throw_exception_again; }
>+	      __catch(...)
>+	      { }
>+	    }
> 	  else
> 	    _M_lock.lock();
> 	}
>diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future
>index 717ce71..8972ecf 100644
>--- a/libstdc++-v3/include/std/future
>+++ b/libstdc++-v3/include/std/future
>@@ -1231,6 +1231,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	  {
> 	    _M_result->_M_set(_M_fn());
> 	  }
>+	__catch(const __cxxabiv1::__forced_unwind&)
>+	  {
>+	    __throw_exception_again; // will cause broken_promise
>+	  }
> 	__catch(...)
> 	  {
> 	    _M_result->_M_error = current_exception();
>@@ -1250,6 +1254,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	  {
> 	    _M_fn();
> 	  }
>+	__catch(const __cxxabiv1::__forced_unwind&)
>+	  {
>+	    __throw_exception_again; // will cause broken_promise
>+	  }
> 	__catch(...)
> 	  {
> 	    _M_result->_M_error = current_exception();
>@@ -1510,7 +1518,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       : _M_result(new _Result<_Res>()), _M_fn(std::move(__fn))
>       {
> 	_M_thread = std::thread{ [this] {
>-	  _M_set_result(_S_task_setter(_M_result, _M_fn));
>+	    __try
>+	      {
>+		_M_set_result(_S_task_setter(_M_result, _M_fn));
>+	      }
>+	    __catch (const __cxxabiv1::__forced_unwind&)
>+	      {
>+		// make the shared state ready on thread cancellation
>+		if (static_cast<bool>(_M_result))
>+		  this->_M_break_promise(std::move(_M_result));
>+		__throw_exception_again;
>+	      }
>         } };
>       }
> 
>diff --git a/libstdc++-v3/include/std/mutex b/libstdc++-v3/include/std/mutex
>index 3d70754..f6b851c 100644
>--- a/libstdc++-v3/include/std/mutex
>+++ b/libstdc++-v3/include/std/mutex
>@@ -44,6 +44,7 @@
> #include <bits/functexcept.h>
> #include <bits/gthr.h>
> #include <bits/move.h> // for std::swap
>+#include <bits/cxxabi_forced.h>
> 
> #ifdef _GLIBCXX_USE_C99_STDINT_TR1
> 
>@@ -631,6 +632,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       auto __locks = std::tie(__l1, __l2, __l3...);
>       __try
>       { __try_lock_impl<0>::__do_try_lock(__locks, __idx); }
>+      __catch(const __cxxabiv1::__forced_unwind&)
>+      { __throw_exception_again; }
>       __catch(...)
>       { }
>       return __idx;
>diff --git a/libstdc++-v3/testsuite/30_threads/async/forced_unwind.cc b/libstdc++-v3/testsuite/30_threads/async/forced_unwind.cc
>new file mode 100644
>index 0000000..7b0a492
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/async/forced_unwind.cc
>@@ -0,0 +1,45 @@
>+// { dg-do run { target *-*-linux* *-*-gnu* } }
>+// { dg-options " -std=gnu++11 -pthread" { target *-*-linux* *-*-gnu* } }
>+// { dg-require-cstdint "" }
>+// { dg-require-gthreads "" }
>+// { dg-require-atomic-builtins "" }
>+
>+// Copyright (C) 2014 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// Test (non-standard) handling of __forced_unwind exception.
>+
>+#include <future>
>+#include <stdexcept>
>+#include <pthread.h>
>+#include <testsuite_hooks.h>
>+
>+void f() { pthread_exit(nullptr); }
>+
>+int main()
>+{
>+  auto fut = std::async(std::launch::async, f);
>+  try
>+  {
>+    fut.get();
>+    throw std::logic_error("Unreachable");
>+  }
>+  catch (const std::future_error& e)
>+  {
>+    VERIFY( e.code() == std::future_errc::broken_promise );
>+  }
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/packaged_task/forced_unwind.cc b/libstdc++-v3/testsuite/30_threads/packaged_task/forced_unwind.cc
>new file mode 100644
>index 0000000..235ab17
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/packaged_task/forced_unwind.cc
>@@ -0,0 +1,48 @@
>+// { dg-do run { target *-*-linux* *-*-gnu* } }
>+// { dg-options " -std=gnu++11 -pthread" { target *-*-linux* *-*-gnu* } }
>+// { dg-require-cstdint "" }
>+// { dg-require-gthreads "" }
>+// { dg-require-atomic-builtins "" }
>+
>+// Copyright (C) 2014 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// Test (non-standard) handling of __forced_unwind exception.
>+
>+#include <future>
>+#include <stdexcept>
>+#include <pthread.h>
>+#include <testsuite_hooks.h>
>+
>+void f() { pthread_exit(nullptr); }
>+
>+int main()
>+{
>+  std::packaged_task<void()> p(f);
>+  auto fut = p.get_future();
>+  std::thread t(std::move(p));
>+  try
>+  {
>+    fut.get();
>+    throw std::logic_error("Unreachable");
>+  }
>+  catch (const std::future_error& e)
>+  {
>+    VERIFY( e.code() == std::future_errc::broken_promise );
>+  }
>+  t.join();
>+}
diff mbox

Patch

commit 8d4a49e0391269380b160bd277339f740716de0c
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue May 13 15:35:29 2014 +0100

    	* include/std/condition_variable (condition_variable_any::_Unlock): Do
    	not swallow __forced_unwind.
    	* include/std/future (__future_base::_Task_setter): Likewise.
    	(__future_base::_Async_state_impl): Turn __forced_unwind into broken
    	promise and rethrow.
    	* include/std/mutex (try_lock): Likewise.
    	* testsuite/30_threads/async/forced_unwind.cc: New.
    	* testsuite/30_threads/packaged_task/forced_unwind.cc: New.

diff --git a/libstdc++-v3/include/std/condition_variable b/libstdc++-v3/include/std/condition_variable
index fc111dd..921cb83 100644
--- a/libstdc++-v3/include/std/condition_variable
+++ b/libstdc++-v3/include/std/condition_variable
@@ -189,7 +189,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	~_Unlock() noexcept(false)
 	{
 	  if (uncaught_exception())
-	    __try { _M_lock.lock(); } __catch(...) { }
+	    {
+	      __try
+	      { _M_lock.lock(); }
+	      __catch(const __cxxabiv1::__forced_unwind&)
+	      { __throw_exception_again; }
+	      __catch(...)
+	      { }
+	    }
 	  else
 	    _M_lock.lock();
 	}
diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future
index 717ce71..8972ecf 100644
--- a/libstdc++-v3/include/std/future
+++ b/libstdc++-v3/include/std/future
@@ -1231,6 +1231,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  {
 	    _M_result->_M_set(_M_fn());
 	  }
+	__catch(const __cxxabiv1::__forced_unwind&)
+	  {
+	    __throw_exception_again; // will cause broken_promise
+	  }
 	__catch(...)
 	  {
 	    _M_result->_M_error = current_exception();
@@ -1250,6 +1254,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  {
 	    _M_fn();
 	  }
+	__catch(const __cxxabiv1::__forced_unwind&)
+	  {
+	    __throw_exception_again; // will cause broken_promise
+	  }
 	__catch(...)
 	  {
 	    _M_result->_M_error = current_exception();
@@ -1510,7 +1518,17 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       : _M_result(new _Result<_Res>()), _M_fn(std::move(__fn))
       {
 	_M_thread = std::thread{ [this] {
-	  _M_set_result(_S_task_setter(_M_result, _M_fn));
+	    __try
+	      {
+		_M_set_result(_S_task_setter(_M_result, _M_fn));
+	      }
+	    __catch (const __cxxabiv1::__forced_unwind&)
+	      {
+		// make the shared state ready on thread cancellation
+		if (static_cast<bool>(_M_result))
+		  this->_M_break_promise(std::move(_M_result));
+		__throw_exception_again;
+	      }
         } };
       }
 
diff --git a/libstdc++-v3/include/std/mutex b/libstdc++-v3/include/std/mutex
index 3d70754..f6b851c 100644
--- a/libstdc++-v3/include/std/mutex
+++ b/libstdc++-v3/include/std/mutex
@@ -44,6 +44,7 @@ 
 #include <bits/functexcept.h>
 #include <bits/gthr.h>
 #include <bits/move.h> // for std::swap
+#include <bits/cxxabi_forced.h>
 
 #ifdef _GLIBCXX_USE_C99_STDINT_TR1
 
@@ -631,6 +632,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       auto __locks = std::tie(__l1, __l2, __l3...);
       __try
       { __try_lock_impl<0>::__do_try_lock(__locks, __idx); }
+      __catch(const __cxxabiv1::__forced_unwind&)
+      { __throw_exception_again; }
       __catch(...)
       { }
       return __idx;
diff --git a/libstdc++-v3/testsuite/30_threads/async/forced_unwind.cc b/libstdc++-v3/testsuite/30_threads/async/forced_unwind.cc
new file mode 100644
index 0000000..7b0a492
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/async/forced_unwind.cc
@@ -0,0 +1,45 @@ 
+// { dg-do run { target *-*-linux* *-*-gnu* } }
+// { dg-options " -std=gnu++11 -pthread" { target *-*-linux* *-*-gnu* } }
+// { dg-require-cstdint "" }
+// { dg-require-gthreads "" }
+// { dg-require-atomic-builtins "" }
+
+// Copyright (C) 2014 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
+// <http://www.gnu.org/licenses/>.
+
+// Test (non-standard) handling of __forced_unwind exception.
+
+#include <future>
+#include <stdexcept>
+#include <pthread.h>
+#include <testsuite_hooks.h>
+
+void f() { pthread_exit(nullptr); }
+
+int main()
+{
+  auto fut = std::async(std::launch::async, f);
+  try
+  {
+    fut.get();
+    throw std::logic_error("Unreachable");
+  }
+  catch (const std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::broken_promise );
+  }
+}
diff --git a/libstdc++-v3/testsuite/30_threads/packaged_task/forced_unwind.cc b/libstdc++-v3/testsuite/30_threads/packaged_task/forced_unwind.cc
new file mode 100644
index 0000000..235ab17
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/packaged_task/forced_unwind.cc
@@ -0,0 +1,48 @@ 
+// { dg-do run { target *-*-linux* *-*-gnu* } }
+// { dg-options " -std=gnu++11 -pthread" { target *-*-linux* *-*-gnu* } }
+// { dg-require-cstdint "" }
+// { dg-require-gthreads "" }
+// { dg-require-atomic-builtins "" }
+
+// Copyright (C) 2014 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
+// <http://www.gnu.org/licenses/>.
+
+// Test (non-standard) handling of __forced_unwind exception.
+
+#include <future>
+#include <stdexcept>
+#include <pthread.h>
+#include <testsuite_hooks.h>
+
+void f() { pthread_exit(nullptr); }
+
+int main()
+{
+  std::packaged_task<void()> p(f);
+  auto fut = p.get_future();
+  std::thread t(std::move(p));
+  try
+  {
+    fut.get();
+    throw std::logic_error("Unreachable");
+  }
+  catch (const std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::broken_promise );
+  }
+  t.join();
+}