From: Wolfgang Bangerth Date: Wed, 14 Jan 2009 14:29:46 +0000 (+0000) Subject: Use boost threads instead of doing everything ourselves by hand calling POSIX and... X-Git-Tag: v8.0.0~8117 X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=73f135d76d9b79f681e42cda4e5a0c1f9260d5b1;p=dealii.git Use boost threads instead of doing everything ourselves by hand calling POSIX and what not. Makes our code a lot simpler as well :-) git-svn-id: https://svn.dealii.org/trunk@18218 0785d39b-7218-0410-832d-ea1e28bc413d --- diff --git a/deal.II/base/include/base/std_cxx0x/thread.h b/deal.II/base/include/base/std_cxx0x/thread.h new file mode 100644 index 0000000000..48328e8687 --- /dev/null +++ b/deal.II/base/include/base/std_cxx0x/thread.h @@ -0,0 +1,36 @@ +//--------------------------------------------------------------------------- +// $Id: std_cxx0x_tuple.h 16094 2008-05-14 19:18:25Z bangerth $ +// Version: $Name$ +// +// Copyright (C) 2009 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- +#ifndef __deal2__std_cxx0x_thread_h +#define __deal2__std_cxx0x_thread_h + + +#include + +#ifdef DEAL_II_CAN_USE_CXX0X + +# include + +#else + +# include + +DEAL_II_NAMESPACE_OPEN +namespace std_cxx0x +{ + using boost::thread; +} +DEAL_II_NAMESPACE_CLOSE + +#endif + +#endif diff --git a/deal.II/base/include/base/thread_management.h b/deal.II/base/include/base/thread_management.h index 0a912d50e1..2c0ddb3249 100644 --- a/deal.II/base/include/base/thread_management.h +++ b/deal.II/base/include/base/thread_management.h @@ -21,6 +21,10 @@ #include #include +#if (DEAL_II_USE_MT == 1) +# include +#endif + #include #include #include @@ -1187,143 +1191,111 @@ namespace Threads namespace internal { #if (DEAL_II_USE_MT == 1) -# if defined(DEAL_II_USE_MT_POSIX) - /** - * @internal - * Base class describing a - * thread. This is the basic - * class abstracting the - * operating system's POSIX - * implementation into a C++ - * class. It provides a mechanism - * to start a new thread, as well - * as for joining it. - * - * @author Wolfgang Bangerth, 2003 - */ - struct thread_description_base + /** + * A class that represents threads. For + * each thread, we create exactly one of + * these objects -- exactly one because + * it carries the returned value of the + * function called on the thread. + * + * While we have only one of these + * objects per thread, several + * Threads::Thread objects may refer to + * this descriptor. + */ + template + struct ThreadDescriptor { - private: - /** - * Variable holding the data - * the operating system needs - * to work with a thread. - */ - pthread_t thread; - - /** - * Store whether the - * join() member function - * as already been called. If - * true, then join - * will return immediately, - * otherwise it needs to go - * through a call to the - * operating system. - * - * This class is generated - * exactly once per thread, - * but is kept in the - * background: user's should - * not directly access this - * class. Access to it is - * performed through counted - * pointers, both from - * Thread<> objects as - * well as from the thread - * entry point function on - * the new thread. It is only - * deleted when all users - * have freed it, which means - * that also the new thread - * has ended. - */ - mutable volatile bool was_joined; - - /** - * Mutex used to synchronise - * calls to the join() - * function. - */ - mutable ThreadMutex join_mutex; - - public: + /** + * An object that represents the + * thread started. + */ + std_cxx0x::thread thread; - /** - * Constructor. - */ - thread_description_base (); - - /** - * Destructor. - */ - virtual ~thread_description_base (); - - /** - * Create a new thread with - * the given thread entry - * point and arguments. Store - * the result of the - * operation in the - * thread member variable - * for further use. - */ - void create (void * (*p) (void *), void *d); - - /** - * Join a thread, i.e. wait - * for it to finish. This - * function can safely be - * called from different - * threads at the same time, - * and can also be called - * more than once. - */ - void join () const; + /** + * An object that will hold the value + * returned by the function called on + * the thread. + */ + return_value ret_val; /** - * Exception + * Constructor. Start the thread and + * let it put its return value into + * the ret_val object. */ - DeclException1 (ExcThreadCreationError, - int, - << "pthread_create return code is " << arg1); - }; + ThreadDescriptor (const std_cxx0x::function &function) + : + thread (thread_entry_point, function, &ret_val) + {} -# else // some other threading model -# error Not Implemented -# endif // defined(DEAL_II_USE_MT_POSIX) -#else // no threading enabled + /** + * Wait for the thread to end. + */ + void join () + { + thread.join (); + } + + private: - struct thread_description_base - { - static void join () {} + /** + * The function that runs on the + * thread. + */ + static + void thread_entry_point (const std_cxx0x::function function, + return_value *ret_val) + { + call (function, *ret_val); + } }; - -#endif - /** - * @internal - * Class derived from - * thread_description_base() - * that also provides the - * possibility to store a return - * value. - * - * @author Wolfgang Bangerth, 2003 - */ +#else + /** + * A class that represents threads. For + * each thread, we create exactly one of + * these objects -- exactly one because + * it carries the returned value of the + * function called on the thread. + * + * While we have only one of these + * objects per thread, several + * Threads::Thread objects may refer to + * this descriptor. + */ template - struct thread_description : public thread_description_base + struct ThreadDescriptor { + /** + * An object that will hold the value + * returned by the function called on + * the thread. + */ return_value ret_val; - }; - - // forward declare another class - template struct ThreadStarter; - } + /** + * Constructor. Start the thread and + * let it put its return value into + * the ret_val object. + */ + ThreadDescriptor (const std_cxx0x::function &function) + { + call (function, ret_val); + } + /** + * Wait for the thread to end. + */ + void join () + {} + }; + +#endif + } + /** * User visible class describing a @@ -1342,39 +1314,22 @@ namespace Threads * return value, you can omit the * template argument. * - * @author Wolfgang Bangerth, 2003 + * @author Wolfgang Bangerth, 2003, 2009 + * @ingroup threads */ template class Thread { + public: /** * Construct a thread object - * with a pointer to an - * internal thread object. This - * is the constructor used to - * the spawn family of - * functions. - * - * We would like to make this - * constructor private and only - * grant the - * fun_wrapper::fire_up - * function friendship, but - * granting friendship to - * functions in other - * namespaces doesn't work with - * some compilers, so only do - * so if the configure script - * decided that this is safe. + * with a function object. */ -#if defined(DEAL_II_NAMESP_TEMPL_FRIEND_BUG2) || defined(DEAL_II_NAMESP_TEMPL_FRIEND_BUG) - public: -#endif - Thread (const std_cxx0x::shared_ptr > &td) - : thread_descriptor (td) {} + Thread (const std_cxx0x::function &function) + : + thread_descriptor (new internal::ThreadDescriptor(function)) + {} - public: - /** * Default constructor. You * can't do much with a thread @@ -1382,7 +1337,7 @@ namespace Threads * except for assigning it a * thread object that holds * data created by the - * spawn functions. + * new_thread functions. */ Thread () {} @@ -1451,210 +1406,10 @@ namespace Threads * as long as there is at least * one subscriber to it. */ - std_cxx0x::shared_ptr > thread_descriptor; - -#if !defined(DEAL_II_NAMESP_TEMPL_FRIEND_BUG2) && !defined(DEAL_II_NAMESP_TEMPL_FRIEND_BUG) - template friend struct internal::ThreadStarter; -#endif + std_cxx0x::shared_ptr > thread_descriptor; }; - - - namespace internal - { - /** - * @internal - * Wrap the arguments to a - * non-member or static member - * function and provide an entry - * point for a new thread that - * unwraps this data and calls - * the function with them. - * - * @author Wolfgang Bangerth, 2003 - */ - template - struct ThreadStarter - { - public: - /** - * Start a new thread, wait - * until it has copied the - * data out of this object, - * and return the thread - * descriptor. - * - * In the non-multithreaded case, - * simply call the function. - */ - static - Thread start_thread (const std_cxx0x::function &function) - { - ThreadStarter wrapper (function); - wrapper.thread_descriptor = - std_cxx0x::shared_ptr > - (new internal::thread_description()); - -#if (DEAL_II_USE_MT == 1) - ThreadMutex::ScopedLock lock (wrapper.mutex); - wrapper.thread_descriptor->create (&ThreadStarter::entry_point, - (void *)&wrapper); - - // wait for the newly created - // thread to indicate that it has - // copied the function - // descriptor. note that the - // POSIX description of - // pthread_cond_wait says that - // the function can return - // spuriously and may have to be - // called again; we guard against - // this using the - // data_has_been_copied variable - // that indicates whether the - // condition has actually been - // met - do - { - wrapper.condition.wait (wrapper.mutex); - } - while (wrapper.data_has_been_copied == false); -#else - call (function, wrapper.thread_descriptor->ret_val); -#endif - return wrapper.thread_descriptor; - } - - private: - /** - * Constructor. Store the - * necessary information - * about the function to be - * called and with which - * arguments. - * - * Pass down the address of - * this class's thread entry - * point function. This way, - * we can ensure that object - * and thread entry point - * function always are in - * synch with respect to - * their knowledge of the - * types involved. - */ - ThreadStarter (const std_cxx0x::function &function) - : - function (function), - data_has_been_copied (false) - {} - - /** - * Shared pointer to the - * unique object describing a - * thread on the OS level. - */ - std_cxx0x::shared_ptr > - thread_descriptor; - - /** - * Mutex and condition - * variable used to - * synchronise calling and - * called thread. - */ - mutable ThreadMutex mutex; - mutable ThreadCondition condition; - - /** - * Pointer to the function to - * be called on the new - * thread. - * - * Note that it is enough to store a - * reference to the function object - * because of the way the creation of - * threads is staged. - */ - const std_cxx0x::function &function; - - /** - * A variable that indicates whether - * the called thread has copied the - * function descriptor to its own - * stack. - */ - mutable bool data_has_been_copied; - - /** - * Entry point for the new - * thread. - */ - static void * entry_point (void *arg) - { - const ThreadStarter *wrapper - = reinterpret_cast*> (arg); - - // copy information from - // the stack of the - // calling thread - std_cxx0x::function function = wrapper->function; - - std_cxx0x::shared_ptr > - thread_descriptor = wrapper->thread_descriptor; - - // signal the fact that - // we have copied all the - // information that is - // needed - // - // note that we indicate success - // of the operation not only - // through the condition variable - // but also through - // data_has_been_copied because - // pthread_cond_wait can return - // spuriously and we need to - // check there whether the return - // is spurious or not - { - wrapper->data_has_been_copied = true; - ThreadMutex::ScopedLock lock (wrapper->mutex); - wrapper->condition.signal (); - } - - // call the - // function. since an - // exception that is - // thrown from one of the - // called functions will - // not propagate to the - // main thread, it will - // kill the program if - // not treated here - // before we return to - // the operating system's - // thread library - internal::register_thread (); - try - { - internal::call (function, thread_descriptor->ret_val); - } - catch (const std::exception &exc) - { - internal::handle_std_exception (exc); - } - catch (...) - { - internal::handle_unknown_exception (); - } - internal::deregister_thread (); - - return 0; - } - }; - } - + namespace internal { /** @@ -1751,7 +1506,7 @@ namespace Threads Thread operator() () { - return ThreadStarter::start_thread (function); + return Thread (function); } private: @@ -1780,7 +1535,7 @@ namespace Threads operator() (typename std_cxx0x::tuple_element<0,ArgList>::type arg1) { return - ThreadStarter::start_thread + Thread (std_cxx0x::bind (function, internal::maybe_make_ref::type>::act(arg1))); } @@ -1812,7 +1567,7 @@ namespace Threads typename std_cxx0x::tuple_element<1,ArgList>::type arg2) { return - ThreadStarter::start_thread + Thread (std_cxx0x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2))); @@ -1846,7 +1601,7 @@ namespace Threads typename std_cxx0x::tuple_element<2,ArgList>::type arg3) { return - ThreadStarter::start_thread + Thread (std_cxx0x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), @@ -1882,7 +1637,7 @@ namespace Threads typename std_cxx0x::tuple_element<3,ArgList>::type arg4) { return - ThreadStarter::start_thread + Thread (std_cxx0x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), @@ -1920,7 +1675,7 @@ namespace Threads typename std_cxx0x::tuple_element<4,ArgList>::type arg5) { return - ThreadStarter::start_thread + Thread (std_cxx0x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), @@ -1960,7 +1715,7 @@ namespace Threads typename std_cxx0x::tuple_element<5,ArgList>::type arg6) { return - ThreadStarter::start_thread + Thread (std_cxx0x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), @@ -2002,7 +1757,7 @@ namespace Threads typename std_cxx0x::tuple_element<6,ArgList>::type arg7) { return - ThreadStarter::start_thread + Thread (std_cxx0x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), @@ -2046,7 +1801,7 @@ namespace Threads typename std_cxx0x::tuple_element<7,ArgList>::type arg8) { return - ThreadStarter::start_thread + Thread (std_cxx0x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), @@ -2092,7 +1847,7 @@ namespace Threads typename std_cxx0x::tuple_element<8,ArgList>::type arg9) { return - ThreadStarter::start_thread + Thread (std_cxx0x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), diff --git a/deal.II/base/source/boost_threads.cc b/deal.II/base/source/boost_threads.cc new file mode 100644 index 0000000000..6632f20cae --- /dev/null +++ b/deal.II/base/source/boost_threads.cc @@ -0,0 +1,24 @@ +//--------------------------------------------------------------------------- +// $Id: thread_management.cc 18205 2009-01-13 13:53:08Z bangerth $ +// Version: $Name$ +// +// Copyright (C) 2009 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- + + +// include all the files that form BOOST's thread implementation so that we +// don't have to build BOOST itself only to get at this small part of it. it +// also ensures that we use the correct compiler and flags + +#define BOOST_THREAD_POSIX +#define BOOST_THREAD_BUILD_LIB 1 + +#include <../libs/thread/src/pthread/once.cpp> +#include <../libs/thread/src/pthread/exceptions.cpp> +#include <../libs/thread/src/pthread/thread.cpp> diff --git a/deal.II/base/source/thread_management.cc b/deal.II/base/source/thread_management.cc index ae6af3c6dc..8166a37d66 100644 --- a/deal.II/base/source/thread_management.cc +++ b/deal.II/base/source/thread_management.cc @@ -275,169 +275,7 @@ namespace Threads return_values[i].second = end; }; return return_values; - } - - -#if (DEAL_II_USE_MT == 1) && defined(DEAL_II_USE_MT_POSIX) - - namespace internal - { - thread_description_base::thread_description_base () - : - was_joined (false) - {} - - - - thread_description_base::~thread_description_base () - { - // if we are here, then the - // last listener is just about - // to cease to exists. there - // are two possibilities (since - // there are two types of - // possible listeners, and - // exactly one listener is - // around): - // - // 1. there are no Thread<> - // objects around any more, but - // we are at the end of the - // entry_point function of the - // new thread; the thread is - // still running, though, and - // we are called from this new - // thread. - // - // if this is the case, - // apparently none of the - // orginal listeners has called - // join(), otherwise the thread - // would not be running any - // more. since there are no - // listeners any more, nobody - // will ever call join() on - // this thread any - // more. however, to avoid a - // resource leak, somebody has - // to do something like that -- - // on the other hand, we can't - // since calling pthread_join() - // on ourself will create a - // deadlock, of course. so - // detach the thread. - // - // note that in this case, - // since nobody has ever called - // join(), was_joined must be - // false - // - // 2. the thread has already - // ended. in this case, the - // last Thread<> object - // refering to it just went out - // of scope. If this is the - // case, to prevent a memory - // leak, we have to call join - // on the thread, if this has - // not yet happened, i.e. if - // was_joined is false. - // - // if we are in case 2, then - // the destructor is called - // from another than the - // presently running thread - // - // - // now: how do we find out - // which case we're in? we - // could either call - // pthread_join and see whether - // we get an error back, or if - // we get back an error on - // pthread_detach. from the man - // pages, the second way seems - // a little bit safer, so go it: - if (was_joined == false) - { - // assume case 1: - int error = pthread_detach (thread); - if (error == 0) - return; - - // ouch, could not - // detach. see if thread - // could not be found any - // more: - if (error == ESRCH) - // ok, this is the - // case. then we are in - // branch 2, and need to - // join the thread - join (); - else - // something went - // terribly wrong - AssertThrow (false, ExcInternalError()); - } - } - - - void - thread_description_base::create (void * (*p) (void *), void *d) - { - // start new thread. retry until - // we either succeed or get an - // error other than EAGAIN - int error = 0; - do - { - error = pthread_create (&thread, 0, p, d); - } - while (error == EAGAIN); - - AssertThrow (error == 0, - ExcThreadCreationError(error)); - } - - - - void - thread_description_base::join () const - { - // use Schmidt's double - // checking pattern: if thread - // was already joined, then - // return immediately - if (was_joined) - return; - - // otherwise make sure that - // only one thread can enter - // the following section at a - // time - ThreadMutex::ScopedLock lock(join_mutex); - // while getting the lock, - // another thread may have set - // was_joined to true, so check - // again (this is the double - // checking pattern) and only - // if we are really sure that - // this has not happened, call - // pthread_join - if (!was_joined) - { - const int error = pthread_join (thread, 0); - AssertThrow (error == 0, ExcInternalError()); - } - - // set the flag - was_joined = true; - } - - } // end namespace internal - -#endif // (DEAL_II_USE_MT == 1) && defined(DEAL_II_USE_MT_POSIX) + } } // end namespace Thread