thread.h
Go to the documentation of this file.
/* Copyright (C) 2005 to 2013 Chris Vine
The library comprised in this file or of which this file is part is
distributed by Chris Vine under the GNU Lesser General Public
License as follows:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, 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
Lesser General Public License, version 2.1, for more details.
You should have received a copy of the GNU Lesser General Public
License, version 2.1, along with this library (see the file LGPL.TXT
which came with this source code package in the c++-gtk-utils
sub-directory); if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef CGU_THREAD_H
#define CGU_THREAD_H
#include <memory> // for std::unique_ptr or std::auto_ptr
#include <utility> // for std::move
#include <pthread.h>
#include <c++-gtk-utils/callback.h>
#include <c++-gtk-utils/mutex.h>
#include <c++-gtk-utils/cgu_config.h>
namespace Cgu {
namespace Thread {
/**
* @class Cgu::Thread::Thread thread.h c++-gtk-utils/thread.h
* @brief A class representing a pthread thread.
* @sa Thread::Mutex Thread::Mutex::Lock Thread::Cond Thread::Future Thread::JoinableHandle
*
* The Thread class encapsulates a pthread thread. It can start, join
* and cancel a thread.
*
* The Thread class, and the associated CancelBlock class, can be used
* interchangeably with (and mixed with) GThread objects and
* functions, and GMutex, GStaticMutex, GStaticRecMutex and GCond, as
* they all use pthreads underneath on POSIX and other unix-like OSes.
* In addition it can be used with threads started with the C++11
* threading facilities, as in C++11 on unix-like OSes these
* facilities will be built on top of pthreads (for which purpose
* C++11 provides the std::native_handle_type type and
* std::thread::native_handle() function). Even where they are not,
* they will use the same threading primitives provided by the kernel.
*
* @anchor ThreadsAnchor
* @b c++-gtk-utils @b library @b and @b C++11 @b threads
*
* As mentioned above, the thread facilities provided by this library
* can be freely interchanged with the threading facilities provided
* by C++11.
*
* The main features available from this library and not C++11 are
* thread cancellation and the associated Cgu::Thread::CancelBlock
* class, the Cgu::Thread::JoinableHandle class for scoped joinable
* thread handling and the Cgu::Thread::TaskManager class for running
* and composing tasks on a thread pool.
*
* C++11 does not provide thread cancellation or interruption support,
* and C++ will never be able to do so on a complete basis because to
* do so requires support from the underlying OS, which therefore
* makes it platform specific (in this case, POSIX specific):
* cancellation is only of limited use if it cannot reliably interrupt
* blocking system calls. The POSIX specification sets out the
* interruptible cancellation points in System Interfaces, section
* 2.9.5, Cancellation Points, and in effect specifies all the system
* calls which can block as cancellation points.
*
* Whether, in C++ programs, destructors of local objects in the
* cancelled thread are called is also system specific and is not
* specified by POSIX. Most modern commercial unixes, and recent
* linux/BSD distributions based on NPTL (in the case of Linux, those
* based on 2.6/3.* kernels), will unwind the stack and call
* destructors on thread cancellation by means of a pseudo-exception,
* but older distributions relying on the former linuxthreads
* implementation will not. Therefore for maximum portability
* cancellation would only be used where there are plain data
* structures/built-in types in existence in local scope when it
* occurs, and if there is anything in free store to be released some
* clean-ups would be implemented with
* pthread_cleanup_push()/pthread_cleanup_pop(). This should be
* controlled with pthread_setcancelstate() and/or the CancelBlock
* class to choose the cancellation point.
*
* One of the (perhaps odd) features of C++11 threads is that if the
* destructor of a std::thread object is called which represents a
* joinable thread which has not been detach()ed or join()ed, the
* whole program is terminated with a call to std::terminate(), which
* makes it difficult to use in the presence of exceptions. Often
* what is wanted however is for join() to be called on a joinable
* thread where the associated thread object goes out of scope, or
* (provided it is done carefully and knowingly) for detach() to be
* called. The Cgu::Thread::JoinableHandle class can be used where
* either of these two is the appropriate response to this situation.
*
* In addition, the c++-gtk-utils library provides the following which
* are not present in C++11: a guaranteed monotonic clock on timed
* condition variable waits where the operating system supports them;
* read-write locks; and a Cgu::Thread::Future object which is more
* intuitive to use than C++11 futures and features a built in
* Cgu::SafeEmitter object which emits when the particular task has
* completed, and (since version 2.0.2) has associated
* Cgu::Thread::Future::when() methods for passing the result to a
* glib main loop.
*
* @b c++-gtk-utils @b library @b and @b gthreads
*
* As mentioned above, the thread facilities provided by this library
* can be freely interchanged with the threading facilities provided
* by glib.
*
* The main features available with this thread implementation and not
* GThreads are thread cancellation, the mutex scoped locking classes
* Cgu::Thread::Mutex::Lock and Cgu::Thread::RecMutex::Lock, the
* joinable thread scoped management class Cgu::Thread::JoinableHandle
* and the Cgu::Thread::Future class (abstracting thread functions
* which provide a result).
*
* There is no need from the perspective of this class to call
* g_thread_init() before Cgu::Thread::Thread::start() is called, but
* prior to glib version 2.32 glib itself is not thread-safe without
* g_thread_init(), so where this class is used with glib < 2.32,
* g_thread_init() should be called at program initialization.
*
* See @ref Threading for particulars about GTK+ thread safety.
*/
class Thread {
pthread_t thread;
// private constructor - this class can only be created with Thread::start
Thread() {}
public:
/**
* This class cannot be copied: it is intended to be held by
* std::unique_ptr. The copy constructor is deleted.
*/
Thread(const Thread&) = delete;
/**
* This class cannot be copied: it is intended to be held by
* std::unique_ptr. The assignment operator is deleted.
*/
Thread& operator=(const Thread&) = delete;
/**
* Cancels the thread represented by this Thread object. It can be
* called by any thread. The effect is undefined if the thread
* represented by this Thread object has both (a) already terminated
* and (b) been detached or had a call to join() made for it.
* Accordingly, if the user is not able to establish from the program
* logic whether the thread has terminated, the thread must be created
* as joinable and cancel() must not be called after a call to
* detach() has been made or a call to join() has returned. A
* Thread::JoinableHandle object can used to ensure this. It does not
* throw.
* @note Use this method with care - sometimes its use is unavoidable
* but destructors for local objects may not be called if a thread
* exits by virtue of a call to cancel() (that depends on the
* implementation). Most modern commercial unixes, and recent
* linux/BSD distributions based on NPTL, will unwind the stack and
* call destructors on thread cancellation by means of a
* pseudo-exception, but older distributions relying on the former
* linuxthreads implementation will not. Therefore for maximum
* portability only have plain data structures/built-in types in
* existence in local scope when it occurs and if there is anything in
* free store to be released implement some clean-ups with
* pthread_cleanup_push()/pthread_cleanup_pop(). This should be
* controlled with pthread_setcancelstate() and/or the CancelBlock
* class to choose the cancellation point.
* @sa Cgu::Thread::Exit
*/
void cancel() {pthread_cancel(thread);}
/**
* Joins the thread represented by this Thread object (that is, waits
* for it to terminate). It can be called by any thread other than
* the one represented by this Thread object. The result is undefined
* if the thread is or was detached or join() has already been called
* for the thread (a Thread::JoinableHandle object will however give a
* defined result in such cases for threads originally started as
* joinable). It does not throw.
*/
void join() {pthread_join(thread, 0);}
/**
* Detaches the thread represented by this Thread object where it is
* joinable, so as to make it unjoinable. The effect is unspecified
* if the thread is already unjoinable (a Thread::JoinableHandle
* object will however give a defined result in such cases for threads
* originally started as joinable). It does not throw.
*/
void detach() {pthread_detach(thread);}
/**
* Specifies whether the calling thread is the same thread as is
* represented by this Thread object. The effect is undefined if the
* thread represented by this Thread object has both (a) already
* terminated and (b) been detached or had a call to join() made for
* it. Accordingly, if the user is not able to establish from the
* program logic whether the thread has terminated, the thread must be
* created as joinable and is_caller() must not be called after a call
* to detach() has been made or a call to join() has returned. A
* Thread::JoinableHandle object can used to ensure this. This method
* does not throw.
* @return Returns true if the caller is in the thread represented by
* this Thread object.
*/
bool is_caller() {return pthread_equal(thread, pthread_self());}
/**
* Starts a new thread. It can be called by any thread.
* @param cb A callback object (created by Callback::make(),
* Callback::make_ref() or Callback::lambda()) encapsulating the
* function to be executed by the new thread. The Thread object
* returned by this function will take ownership of the callback: it
* will automatically be deleted either by the new thread when it has
* finished with it, or by this method in the calling thread if the
* attempt to start a new thread fails (including if std::bad_alloc is
* thrown).
* @param joinable Whether the join() method may be called in relation
* to the new thread.
* @return A Thread object representing the new thread which has been
* started, held by a std::unique_ptr object as it has single
* ownership semantics. The std::unique_ptr object will be empty
* (that is std::unique_ptr<Cgu::Thread::Thread>::get() will return 0)
* if the thread did not start correctly, which would mean that memory
* is exhausted, the pthread thread limit has been reached or pthread
* has run out of other resources to start new threads.
* @exception std::bad_alloc This method might throw std::bad_alloc if
* memory is exhausted and the system throws in that case. (This
* exception will not be thrown if the library has been installed
* using the \--with-glib-memory-slices-no-compat configuration
* option: instead glib will terminate the program if it is unable to
* obtain memory from the operating system.) If this exception is
* thrown, the thread will not have started.
* @note 1. The thread will keep running even if the return value of
* start() goes out of scope (but it will no longer be possible to
* call any of the methods in this class for it, which is fine if the
* thread is not started as joinable and it is not intended to cancel
* it).
* @note 2. If the thread is started with the joinable attribute, the
* user must subsequently either call the join() or the detach()
* method, as otherwise a resource leak may occur (the destructor of
* this class does not call detach() automatically). Alternatively,
* the return value of this method can be passed to a
* Thread::JoinableHandle object which will do this automatically in
* the Thread::JoinableHandle object's destructor.
* @note 3. Any Thread::Exit exception thrown from the function
* executed by the new thread will be caught and consumed. The thread
* will safely terminate and unwind the stack in so doing.
* @note 4. If any uncaught exception other than Thread::Exit is
* allowed to propagate from the initial function executed by the new
* thread, the exception is not consumed (NPTL's forced stack
* unwinding on cancellation does not permit catching with an ellipsis
* argument without rethrowing, and even if it did permit it, the
* result would be an unreported error). The C++11 standard requires
* std::terminate() to be called in such a case and so the entire
* program terminated. Accordingly, a user must make sure that no
* exceptions, other than Thread::Exit or any cancellation
* pseudo-exception, can propagate from the initial function executed
* by the new thread. This includes ensuring that, for any argument
* passed to that function which is not a built-in type and which is
* not taken by the function by const or non-const reference, the
* argument type's copy constructor does not throw.
* @note 5. If the library is compiled using the \--with-auto-ptr
* configuration option, then this function will return a
* Thread::Thread object by std::auto_ptr instead of std::unique_ptr
* in order to retain compatibility with the 1.2 series of the
* library.
*/
#ifdef CGU_USE_AUTO_PTR
static std::auto_ptr<Cgu::Thread::Thread> start(const Cgu::Callback::Callback* cb,
bool joinable);
#else
static std::unique_ptr<Cgu::Thread::Thread> start(const Cgu::Callback::Callback* cb,
bool joinable);
#endif
#ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
CGU_GLIB_MEMORY_SLICES_FUNCS
#endif
};
/**
* @class Cgu::Thread::JoinableHandle thread.h c++-gtk-utils/thread.h
* @brief A class wrapping a Thread::Thread object representing a
* joinable thread.
* @sa Thread::Thread Thread::Future
*
* This class enables a joinable thread to be made more easily
* exception safe. It can also be used to provide that a joinable
* thread is not detached or joined while other methods dependent on
* that might still be called, and to provide a defined result where
* there are multiple calls to join() and/or detach(). When it is
* destroyed, it will either detach or join the thread represented by
* the wrapped Thread::Thread object unless it has previously been
* detached or joined using the detach() or join() methods, so
* avoiding thread resource leaks. Whether it will detach() or join()
* on destruction depends on the Thread::JoinableHandle::Action
* argument passed to the
* Thread::JoinableHandle::JoinableHandle(std::unique_ptr<Thread::Thread>,
* Action) constructor.
*
* Passing Thread::JoinableHandle::detach_on_exit to that argument is
* not always the correct choice where the thread callback has been
* bound to a reference argument in local scope and an exception might
* be thrown, because the thread will keep running after the
* Thread::JoinableHandle object and other local variables have
* (because of the exception) gone out of scope. Consider the
* following trivial parallelized calculation example:
*
* @code
* std::vector<int> get_readings();
* void get_mean(const std::vector<int>& v, int& result);
* void get_std_deviation(const std::vector<int>& v, int& result); // might throw
* void show_result(int mean, int deviation);
*
* using namespace Cgu;
* void do_calc() {
* int i, j;
* std::vector<int> v = get_readings();
* // with bound reference arguments, Callback::make() requires explicit type instantation
* std::unique_ptr<Thread::Thread> t =
* Thread::Thread::start(Callback::make<const std::vector<int>&, int&>(&get_mean, v, i), true);
* if (t.get()) { // checks whether thread started correctly
* get_std_deviation(v, j);
* t->join();
* show_result(i, j);
* }
* }
* @endcode
*
* If get_std_deviation() throws, as well as there being a potential
* thread resource leak by virtue of no join being made, the thread
* executing get_mean() will continue running and attempt to access
* variable v, and put its result in variable i, which may by then
* both be out of scope. To deal with such a case, the thread could
* be wrapped in a Thread::JoinableHandle object which joins on exit
* rather than detaches, for example:
*
* @code
* ...
* using namespace Cgu;
* void do_calc() {
* int i, j;
* std::vector<int> v = get_readings();
* // with reference arguments, Callback::make() requires explicit type instantation
* Thread::JoinableHandle t(Thread::Thread::start(Callback::make<const std::vector<int>&, int&>(&get_mean, v, i), true),
* Thread::JoinableHandle::join_on_exit);
* if (t.is_managing()) { // checks whether thread started correctly
* get_std_deviation(v, j);
* t.join();
* show_result(i, j);
* }
* }
* @endcode
*
* Better still, however, would be to use Cgu::Thread::Future in this
* kind of usage, namely a usage where a worker thread is intended to
* provide a result for inspection.
*
* @note These examples assume that the std::vector library
* implementation permits concurrent reads of a vector object by
* different threads. Whether that is the case depends on the
* documentation of the library concerned (if designed for a
* multi-threaded environment, most will permit this).
*/
class JoinableHandle {
public:
enum Action {detach_on_exit, join_on_exit};
private:
Mutex mutex; // make this the first member so the constructors are strongly exception safe
Action action;
bool detached;
std::unique_ptr<Cgu::Thread::Thread> thread;
public:
/**
* Cancels the thread represented by the wrapped Thread::Thread
* object. It can be called by any thread. The effect is undefined
* if when called the thread represented by the wrapped Thread::Thread
* object has both (a) already terminated and (b) had a call to join()
* or detach() made for it. Accordingly, if the user is not able to
* establish from the program logic whether the thread has terminated,
* cancel() must not be called after a call to detach() has been made
* or a call to join() has returned: this can be ensured by only
* detaching or joining via this object's destructor (that is, by not
* using the explicit detach() and join() methods). This method does
* not throw.
* @note Use this method with care - see Thread::cancel() for further
* information.
*/
void cancel();
/**
* Joins the thread represented by the wrapped Thread::Thread object
* (that is, waits for it to terminate), unless the detach() or join()
* method has previously been called in which case this call does
* nothing. It can be called by any thread other than the one
* represented by the wrapped Thread::Thread object, but only one
* thread can wait on it: if one thread (thread A) calls it while
* another thread (thread B) is already blocking on it, thread A's
* call to this method will return immediately and return false. It
* does not throw.
* @return true if a successful join() has been accomplished (that is,
* detach() or join() have not previously been called), otherwise
* false.
*/
bool join();
/**
* Detaches the thread represented by this Thread::Thread object, so
* as to make it unjoinable, unless the detach() or join() method has
* previously been called in which case this call does nothing. It
* does not throw.
*/
void detach();
/**
* Specifies whether the calling thread is the same thread as is
* represented by the wrapped Thread::Thread object. It can be called
* by any thread. The effect is undefined if the thread represented
* by the wrapped Thread::Thread object has both (a) already
* terminated and (b) had a call to join() or detach() made for it.
* Accordingly, if the user is not able to establish from the program
* logic whether the thread has terminated, is_caller() must not be
* called after a call to detach() has been made or a call to join()
* has returned: this can be ensured by only detaching or joining via
* this object's destructor (that is, by not using the explicit
* detach() and join() methods). This method does not throw.
* @return Returns true if the caller is in the thread represented by
* the wrapped Thread::Thread object. If not, or this JoinableHandle
* does not wrap any Thread object, then returns false.
*/
bool is_caller();
/**
* Specifies whether this JoinableHandle object has been initialized
* with a Thread::Thread object representing a correctly started
* thread in respect of which neither JoinableHandle::detach() nor
* JoinableHandle::join() has been called. It can be called by any
* thread. It is principally intended to enable the constructor
* taking a std::unique_ptr<Cgu::Thread::Thread> object to be directly
* initialized by a call to Thread::Thread::start(), by providing a
* means for the thread calling Thread::Thread::start() to check
* afterwards that the new thread did, in fact, start correctly. Note
* that this method will return true even after the thread has
* finished, provided neither the join() nor detach() method has been
* called.
* @return Returns true if this object has been initialized by a
* Thread::Thread object representing a correctly started thread in
* respect of which neither JoinableHandle::detach() nor
* JoinableHandle::join() has been called, otherwise false.
*/
bool is_managing();
/**
* Moves one JoinableHandle object to another JoinableHandle object.
* This is a move operation which transfers ownership to the assignee,
* as the handles store their Thread::Thread object by
* std::unique_ptr<>. Any existing thread managed by the assignee
* prior to the move will be detached if it has not already been
* detached or joined. This method will not throw.
* @param h The assignor/movant, which will cease to hold a valid
* Thread::Thread object after the move has taken place.
* @return A reference to the assignee JoinableHandle object after
* assignment.
* @note 1. This method is thread safe as regards the assignee (the
* object assigned to), but no synchronization is carried out with
* respect to the rvalue assignor/movant. This is because temporaries
* are only visible and accessible in the thread carrying out the move
* operation and synchronization for them would represent pointless
* overhead. In a case where the user uses std::move to force a move
* from a named object, and that named object's lifetime is managed by
* (or the object is otherwise accessed by) a different thread than
* the one making the move, the user must carry out her own
* synchronization with respect to that different thread, as the named
* object will be mutated by the move.
* @note 2. If the library is compiled using the \--with-auto-ptr
* configuration option, then this operator's signature is
* JoinableHandle& operator=(JoinableHandle& h) in order to retain
* compatibility with the 1.2 series of the library.
*/
#ifdef CGU_USE_AUTO_PTR
JoinableHandle& operator=(JoinableHandle& h);
#else
JoinableHandle& operator=(JoinableHandle&& h);
#endif
/**
* This constructor initializes a new JoinableHandle object with a
* std::unique_ptr<Thread::Thread> object, as provided by
* Thread::Thread::start(). This is a move operation which transfers
* ownership to the new object.
* @param thr The initializing Thread::Thread object (which must have
* been created as joinable) passed by a std::unique_ptr smart
* pointer. This is a move operation.
* @param act Either Thread::JoinableHandle::detach_on_exit (which
* will cause the destructor to detach the thread if it has not
* previously been detached or joined) or
* Thread::JoinableHandle::join_on_exit (which will cause the
* destructor to join the thread if it has not previously been
* detached or joined).
* @exception Cgu::Thread::MutexError Throws this exception if
* initialization of the internal mutex fails. The constructor is
* strongly exception safe: if Cgu::Thread::MutexError is thrown, the
* initializing std::unique_ptr<Cgu::Thread::Thread> object will be
* left unchanged. (It is often not worth checking for this
* exception, as it means either memory is exhausted or pthread has
* run out of other resources to create new mutexes.)
* @note 1. It is not necessary to check that the thread parameter
* represents a correctly started thread (that is, that thr.get() does
* not return 0) before this constructor is invoked, because that can
* be done after construction by calling JoinableHandle::is_managing()
* (a JoinableHangle object can safely handle a case where thr.get()
* does return 0). This enables a JoinableHandle object to be
* directly initialized by this constructor from a call to
* Thread::Thread::start().
* @note 2. No synchronization is carried out with respect to the
* initializing std::unique_ptr object. This is because such an
* object is usually passed to this constructor as a temporary, which
* is only visible and accessible in the thread carrying out the move
* operation, in which case synchronization would represent pointless
* overhead. In a case where the user uses std::move to force a move
* from a named std::unique_ptr object, and that named object's
* lifetime is managed by (or the object is otherwise accessed by) a
* different thread than the one making the move, the user must carry
* out her own synchronization with respect to that different thread,
* as the initializing std::unique_ptr object will be mutated by the
* move.
* @note 3. If the library is compiled using the \--with-auto-ptr
* configuration option, then this constructor's signature is
* JoinableHandle(std::auto_ptr<Cgu::Thread::Thread> thr, Action act)
* in order to retain compatibility with the 1.2 series of the library
* @sa JoinableHandle::is_managing().
*/
#ifdef CGU_USE_AUTO_PTR
JoinableHandle(std::auto_ptr<Cgu::Thread::Thread> thr, Action act): action(act), detached(false), thread(thr.release()) {}
#else
JoinableHandle(std::unique_ptr<Cgu::Thread::Thread> thr, Action act): action(act), detached(false), thread(std::move(thr)) {}
#endif
/**
* This constructor initializes a new JoinableHandle object with an
* existing JoinableHandle object. This is a move operation which
* transfers ownership to the new object.
* @param h The initializing JoinableHandle object, which will cease
* to hold a valid Thread::Thread object after the initialization has
* taken place.
* @exception Cgu::Thread::MutexError Throws this exception if
* initialization of the internal mutex fails. The constructor is
* strongly exception safe: if Cgu::Thread::MutexError is thrown, the
* initializing Cgu::Thread::JoinableHandle object will be left
* unchanged. (It is often not worth checking for this exception, as
* it means either memory is exhausted or pthread has run out of other
* resources to create new mutexes.)
* @note 1. No synchronization is carried out with respect to the
* initializing rvalue. This is because temporaries are only visible
* and accessible in the thread carrying out the move operation and
* synchronization for them would represent pointless overhead. In a
* case where a user uses std::move to force a move from a named
* object, and that named object's lifetime is managed by (or the
* object is otherwise accessed by) a different thread than the one
* making the move, the user must carry out her own synchronization
* with respect to that different thread, as the named object will be
* mutated by the move.
* @note 2. If the library is compiled using the \--with-auto-ptr
* configuration option, then this constructor's signature is
* JoinableHandle(JoinableHandle& h) in order to retain compatibility
* with the 1.2 series of the library.
*/
#ifdef CGU_USE_AUTO_PTR
JoinableHandle(JoinableHandle& h): action(h.action), detached(h.detached), thread(std::move(h.thread)) {}
#else
JoinableHandle(JoinableHandle&& h): action(h.action), detached(h.detached), thread(std::move(h.thread)) {}
#endif
/**
* The default constructor. Nothing is managed until the move
* assignment operator has been called.
* @exception Cgu::Thread::MutexError Throws this exception if
* initialization of the internal mutex fails. (It is often not worth
* checking for this exception, as it means either memory is exhausted
* or pthread has run out of other resources to create new mutexes.)
*
* Since 2.0.8
*/
JoinableHandle(): action(detach_on_exit), detached(true) {}
/**
* The destructor will detach a managed thread (if the
* Thread::JoinableHandle::detach_on_exit flag is set) or join it (if
* the Thread::JoinableHandle::join_on_exit flag is set), unless it
* has previously been detached or joined with the detach() or join()
* methods. The destructor is thread safe (any thread may destroy the
* JoinableHandle object). The destructor will not throw.
*/
~JoinableHandle();
/* Only has effect if --with-glib-memory-slices-compat or
* --with-glib-memory-slices-no-compat option picked */
CGU_GLIB_MEMORY_SLICES_FUNCS
};
/**
* @class CancelBlock thread.h c++-gtk-utils/thread.h
* @brief A class enabling the cancellation state of a thread to be
* controlled.
*
* A class enabling the cancellation state of a thread to be
* controlled, so as to provide exception safe cancellation state
* changes. When a CancelBlock object goes out of scope, the thread's
* cancellation state is returned to the state it was in immediately
* prior to the object's construction.
*
* Cancellation state can be changed before a CancelBlock object goes
* out of scope by calling its block() and unblock() methods.
* However, care should be taken if calling unblock() for the purpose
* of enabling thread cancellation while the CancelBlock object is
* still in existence: this should normally only be done if the
* thread's cancellation state at the time the CancelBlock object was
* constructed (which is the cancellation state to which the thread
* will be restored when the object goes out of scope) was
* PTHREAD_CANCEL_DISABLE. This is because when a thread begins
* cancellation the POSIX standard states that it will automatically
* switch itself into a PTHREAD_CANCEL_DISABLE state (see System
* Interfaces, section 2.9.5, Thread Cancellation Cleanup Handlers),
* and the POSIX standard further states that the behaviour is
* undefined if a cancellation handler attempts to enable cancellation
* again while the thread is cleaning up - and any thread
* implementation such as NPTL which unwinds the stack on cancellation
* will do so if the CancelBlock's destructor would restore to
* PTHREAD_CANCEL_ENABLE state. Whilst it is to be expected that any
* cancellation stack unwinding implementation will behave sensibly in
* these circumstances, this is not mandated by POSIX, so making code
* relying on this less portable.
*
* For these reasons, the same care should be exercised if passing
* 'false' to the CancelBlock constructor's 'blocking' argument.
*/
class CancelBlock {
int starting_state;
public:
/**
* This class cannot be copied. The copy constructor is deleted.
*/
CancelBlock(const CancelBlock&) = delete;
/**
* This class cannot be copied. The assignment operator is deleted.
*/
CancelBlock& operator=(const CancelBlock&) = delete;
/**
* Makes the thread uncancellable, even if the code passes through a
* cancellation point, while the CancelBlock object exists (when the
* CancelBlock object ceases to exist, cancellation state is returned
* to the state prior to it being constructed). It should only be
* called by the thread which created the CancelBlock object. This
* method will not throw.
* @param old_state Indicates the cancellation state of the calling
* thread immediately before this call to block() was made, either
* PTHREAD_CANCEL_ENABLE (if the thread was previously cancellable) or
* PTHREAD_CANCEL_DISABLE (if this call did nothing because the thread
* was already uncancellable).
* @return 0 if successful, else a value other than 0.
*/
static int block(int& old_state) {return pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);}
/**
* Makes the thread uncancellable, even if the code passes through a
* cancellation point, while the CancelBlock object exists (when the
* CancelBlock object ceases to exist, cancellation state is returned
* to the state prior to it being constructed). It should only be
* called by the thread which created the CancelBlock object. This
* method will not throw.
* @return 0 if successful, else a value other than 0.
*/
static int block() {int old_state; return block(old_state);}
/**
* Makes the thread cancellable while the CancelBlock object exists
* (when the CancelBlock object ceases to exist, cancellation state is
* returned to the state prior to it being constructed). It should
* only be called by the thread which created the CancelBlock object.
* This method will not throw. The 'Detailed Description' section
* above has information about the issues to be taken into account if
* a call to this method is to be made.
* @param old_state Indicates the cancellation state of the calling
* thread immediately before this call to unblock() was made, either
* PTHREAD_CANCEL_DISABLE (if the thread was previously uncancellable)
* or PTHREAD_CANCEL_ENABLE (if this call did nothing because the
* thread was already cancellable).
* @return 0 if successful, else a value other than 0.
*/
static int unblock(int& old_state) {return pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state);}
/**
* Makes the thread cancellable while the CancelBlock object exists
* (when the CancelBlock object ceases to exist, cancellation state is
* returned to the state prior to it being constructed). It should
* only be called by the thread which created the CancelBlock object.
* This method will not throw. The 'Detailed Description' section
* above has information about the issues to be taken into account if
* a call to this method is to be made.
* @return 0 if successful, else a value other than 0.
*/
static int unblock() {int old_state; return unblock(old_state);}
/**
* Restores cancellation state to the state it was in immediately
* before this CancelBlock object was constructed. It should only be
* called by the thread which created the CancelBlock object. This
* method will not throw.
* @param old_state Indicates the cancellation state of the calling
* thread immediately before this call to restore() was made, either
* PTHREAD_CANCEL_DISABLE (if the thread was previously uncancellable)
* or PTHREAD_CANCEL_ENABLE (if this thread was previously
* cancellable).
* @return 0 if successful, else a value other than 0.
*/
int restore(int& old_state) {return pthread_setcancelstate(starting_state, &old_state);}
/**
* Restores cancellation state to the state it was in immediately
* before this CancelBlock object was constructed. It should only be
* called by the thread which created the CancelBlock object. This
* method will not throw.
* @return 0 if successful, else a value other than 0.
*/
int restore() {int old_state; return restore(old_state);}
/**
* The constructor will not throw.
* @param blocking Whether the CancelBlock object should start in
* blocking mode. The 'Detailed Description' section above has
* information about the issues to be taken into account if 'false' is
* passed to this parameter.
*/
CancelBlock(bool blocking = true);
/**
* The destructor will put the thread in the cancellation state that
* it was in immediately before the CancelBlock object was constructed
* (which might be blocking). It will not throw.
*/
~CancelBlock() {restore();}
/* Only has effect if --with-glib-memory-slices-compat or
* --with-glib-memory-slices-no-compat option picked */
CGU_GLIB_MEMORY_SLICES_FUNCS
};
/**
* @class Exit thread.h c++-gtk-utils/thread.h
* @brief A class which can be thrown to terminate the throwing
* thread.
*
* This class can be thrown (instead of calling pthread_exit()) when a
* thread wishes to terminate itself and also ensure stack unwinding,
* so that destructors of local objects are called. It is caught
* automatically by the implementation of Cgu::Thread::Thread::start()
* so that it will only terminate the thread throwing it and not the
* whole process. See the Cgu::Thread::Thread::cancel() method above,
* for use when a thread wishes to terminate another one, and the
* caveats on the use of Cgu::Thread::Thread::cancel().
*
* Do not throw a Cgu::Thread::Exit object in a program with more than
* one main loop in order to terminate one of the threads which has
* its own main loop. Instead, just cause its main loop to terminate
* by, say, calling g_main_loop_quit() on it.
*/
class Exit {};
} // namespace Thread
} // namespace Cgu
#endif