summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libcxx/include/thread50
-rw-r--r--libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp66
2 files changed, 84 insertions, 32 deletions
diff --git a/libcxx/include/thread b/libcxx/include/thread
index 6857e9edb68..c3eb4378530 100644
--- a/libcxx/include/thread
+++ b/libcxx/include/thread
@@ -339,21 +339,21 @@ public:
#ifndef _LIBCPP_HAS_NO_VARIADICS
-template <class _Fp, class ..._Args, size_t ..._Indices>
+template <class _TSp, class _Fp, class ..._Args, size_t ..._Indices>
inline _LIBCPP_INLINE_VISIBILITY
void
-__thread_execute(tuple<_Fp, _Args...>& __t, __tuple_indices<_Indices...>)
+__thread_execute(tuple<_TSp, _Fp, _Args...>& __t, __tuple_indices<_Indices...>)
{
- __invoke(_VSTD::move(_VSTD::get<0>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...);
+ __invoke(_VSTD::move(_VSTD::get<1>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...);
}
template <class _Fp>
-void*
-__thread_proxy(void* __vp)
+void* __thread_proxy(void* __vp)
{
- __thread_local_data().reset(new __thread_struct);
+ // _Fp = std::tuple< unique_ptr<__thread_struct>, Functor, Args...>
std::unique_ptr<_Fp> __p(static_cast<_Fp*>(__vp));
- typedef typename __make_tuple_indices<tuple_size<_Fp>::value, 1>::type _Index;
+ __thread_local_data().reset(_VSTD::get<0>(*__p).release());
+ typedef typename __make_tuple_indices<tuple_size<_Fp>::value, 2>::type _Index;
__thread_execute(*__p, _Index());
return nullptr;
}
@@ -363,9 +363,13 @@ template <class _Fp, class ..._Args,
>
thread::thread(_Fp&& __f, _Args&&... __args)
{
- typedef tuple<typename decay<_Fp>::type, typename decay<_Args>::type...> _Gp;
- _VSTD::unique_ptr<_Gp> __p(new _Gp(__decay_copy(_VSTD::forward<_Fp>(__f)),
- __decay_copy(_VSTD::forward<_Args>(__args))...));
+ typedef unique_ptr<__thread_struct> _TSPtr;
+ _TSPtr __tsp(new __thread_struct);
+ typedef tuple<_TSPtr, typename decay<_Fp>::type, typename decay<_Args>::type...> _Gp;
+ _VSTD::unique_ptr<_Gp> __p(
+ new _Gp(std::move(__tsp),
+ __decay_copy(_VSTD::forward<_Fp>(__f)),
+ __decay_copy(_VSTD::forward<_Args>(__args))...));
int __ec = pthread_create(&__t_, 0, &__thread_proxy<_Gp>, __p.get());
if (__ec == 0)
__p.release();
@@ -376,22 +380,34 @@ thread::thread(_Fp&& __f, _Args&&... __args)
#else // _LIBCPP_HAS_NO_VARIADICS
template <class _Fp>
-void*
-__thread_proxy(void* __vp)
+struct __thread_invoke_pair {
+ // This type is used to pass memory for thread local storage and a functor
+ // to a newly created thread because std::pair doesn't work with
+ // std::unique_ptr in C++03.
+ __thread_invoke_pair(_Fp& __f) : __tsp_(new __thread_struct), __fn_(__f) {}
+ unique_ptr<__thread_struct> __tsp_;
+ _Fp __fn_;
+};
+
+template <class _Fp>
+void* __thread_proxy_cxx03(void* __vp)
{
- __thread_local_data().reset(new __thread_struct);
std::unique_ptr<_Fp> __p(static_cast<_Fp*>(__vp));
- (*__p)();
+ __thread_local_data().reset(__p->__tsp_.release());
+ (__p->__fn_)();
return nullptr;
}
template <class _Fp>
thread::thread(_Fp __f)
{
- std::unique_ptr<_Fp> __p(new _Fp(__f));
- int __ec = pthread_create(&__t_, 0, &__thread_proxy<_Fp>, __p.get());
+
+ typedef __thread_invoke_pair<_Fp> _InvokePair;
+ typedef std::unique_ptr<_InvokePair> _PairPtr;
+ _PairPtr __pp(new _InvokePair(__f));
+ int __ec = pthread_create(&__t_, 0, &__thread_proxy_cxx03<_InvokePair>, __pp.get());
if (__ec == 0)
- __p.release();
+ __pp.release();
else
__throw_system_error(__ec, "thread constructor failed");
}
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp
index 3de15af8929..2489bfc972c 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.constr/F.pass.cpp
@@ -20,23 +20,28 @@
#include <thread>
#include <new>
+#include <atomic>
#include <cstdlib>
#include <cassert>
#include "test_macros.h"
-unsigned throw_one = 0xFFFF;
+std::atomic<unsigned> throw_one(0xFFFF);
+std::atomic<unsigned> outstanding_new(0);
+
void* operator new(std::size_t s) throw(std::bad_alloc)
{
if (throw_one == 0)
throw std::bad_alloc();
--throw_one;
+ ++outstanding_new;
return std::malloc(s);
}
void operator delete(void* p) throw()
{
+ --outstanding_new;
std::free(p);
}
@@ -94,27 +99,58 @@ public:
#endif
-int main()
-{
+// Test throwing std::bad_alloc
+//-----------------------------
+// Concerns:
+// A Each allocation performed during thread construction should be performed
+// in the parent thread so that std::terminate is not called if
+// std::bad_alloc is thrown by new.
+// B std::threads constructor should properly handle exceptions and not leak
+// memory.
+// Plan:
+// 1 Create a thread and count the number of allocations, 'N', it performs.
+// 2 For each allocation performed run a test where that allocation throws.
+// 2.1 check that the exception can be caught in the parent thread.
+// 2.2 Check that the functor has not been called.
+// 2.3 Check that no memory allocated by the creation of the thread is leaked.
+// 3 Finally check that a thread runs successfully if we throw after 'N+1'
+// allocations.
+void test_throwing_new_during_thread_creation() {
+ throw_one = 0xFFF;
{
std::thread t(f);
t.join();
- assert(f_run == true);
}
- f_run = false;
- {
- try
- {
- throw_one = 0;
+ const int numAllocs = 0xFFF - throw_one;
+ // i <= numAllocs means the last iteration is expected not to throw.
+ for (int i=0; i <= numAllocs; ++i) {
+ throw_one = i;
+ f_run = false;
+ unsigned old_outstanding = outstanding_new;
+ try {
std::thread t(f);
- assert(false);
- }
- catch (...)
- {
- throw_one = 0xFFFF;
- assert(!f_run);
+ assert(i == numAllocs); // Only final iteration will not throw.
+ t.join();
+ assert(f_run);
+ } catch (std::bad_alloc const&) {
+ assert(i < numAllocs);
+ assert(!f_run); // (2.2)
}
+ assert(old_outstanding == outstanding_new); // (2.3)
+ }
+ f_run = false;
+ throw_one = 0xFFF;
+}
+
+int main()
+{
+ test_throwing_new_during_thread_creation();
+ {
+ std::thread t(f);
+ t.join();
+ assert(f_run == true);
}
+
{
assert(G::n_alive == 0);
assert(!G::op_run);
OpenPOWER on IntegriCloud