c++-gtk-utils
Public Member Functions | Static Public Member Functions | List of all members
Cgu::Thread::Thread Class Reference

A class representing a pthread thread. More...

#include <c++-gtk-utils/thread.h>

Public Member Functions

 Thread (const Thread &)=delete
 
Threadoperator= (const Thread &)=delete
 
void cancel () noexcept
 
void join () noexcept
 
void detach () noexcept
 
bool is_caller () noexcept
 

Static Public Member Functions

static std::unique_ptr< Cgu::Thread::Threadstart (const Cgu::Callback::Callback *cb, bool joinable)
 
template<class F , class = typename std::enable_if<!std::is_convertible<typename std::remove_reference<F>::type, const Cgu::Callback::Callback*>::value>::type>
static std::unique_ptr< Cgu::Thread::Threadstart (F &&func, bool joinable)
 

Detailed Description

A class representing a pthread thread.

See also
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 other thread related classes provided by this library such as Mutex, RecMutex, RWLock, CancelBlock and Cond, can be used interchangeably with (and mixed with) GThread objects and functions, and with GMutex, GRecMutex, GRWLock, GCond and similar, as they all use pthreads underneath on POSIX and other unix-like OSes.

In addition, the thread related classes provided by this library can be used in conjunction with threads started with C++11/14 threading facilities, and vice versa, as in C++11/14 on unix-like OSes these facilities are likely to be built on top of pthreads (for which purpose C++11/14 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: ยง30.3 of the C++11 standard states, albeit non-normatively, that "These threads [std::thread threads] are intended to map one-to-one with operating system threads".

If in doubt, always use this library's thread related classes as they are guaranteed to be compatible with glib/gtk and with the native platform libraries. However, such doubts are in practice unnecessary: it is in practice inconceivable that C++11/14's threading implementation will be incompatible with the platform's native threading implementation (pthreads), because any practical program using C++11/14 threads is going to call into platform libraries, and those libraries may themselves be threaded or make thread related calls. So use whichever appears to suit the particular case better.

c++-gtk-utils library and C++11/14 threads

As mentioned above, the thread facilities provided by this library can be freely interchanged with the threading facilities provided by C++11/14.

The main features available from this library and not C++11/14 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/14 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.x/4.x 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 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/14 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 and/or C++14: a guaranteed monotonic clock on timed condition variable waits where the operating system supports them; read-write locks/shared mutexes (present in C++14 but not C++11); a Cgu::Thread::Future object which is more intuitive to use than C++11/14 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; and (since version 2.2.2) Cgu::Thread::parallel_for_each() and Cgu::Thread::parallel_transform() functions for use with Cgu::Thread::TaskManager objects.

c++-gtk-utils library and 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 Writing multi-threaded programs using c++-gtk-utils for particulars about GTK+ thread safety.

Constructor & Destructor Documentation

◆ Thread()

Cgu::Thread::Thread::Thread ( const Thread )
delete

This class cannot be copied: it is intended to be held by std::unique_ptr. The copy constructor is deleted.

Member Function Documentation

◆ cancel()

void Cgu::Thread::Thread::cancel ( )
inlinenoexcept

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
1. 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 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.
2. When using thread cancellation, do not allow a cancellation pseudo-exception to propagate through a function with a 'noexcept' or 'noexcept(true)' specifier, as the cancellation pseudo-exception would be incompatible with such a specifier and std::terminate may be called.
See also
Cgu::Thread::Exit

◆ detach()

void Cgu::Thread::Thread::detach ( )
inlinenoexcept

Detaches the thread represented by this Thread object where it is joinable, so as to make it unjoinable. The effect is undefined 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.

◆ is_caller()

bool Cgu::Thread::Thread::is_caller ( )
inlinenoexcept

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.

Returns
Returns true if the caller is in the thread represented by this Thread object.

◆ join()

void Cgu::Thread::Thread::join ( )
inlinenoexcept

Joins the thread represented by this Thread object (that is, waits for it to terminate). It can only be called by one thread, which can be any thread other than the one represented by this Thread object. The result is undefined if the thread represented by this Thread object is or was detached or join() has already been called for it (a Thread::JoinableHandle object will however give a defined result in such cases for threads originally started as joinable). It does not throw.

◆ operator=()

Thread& Cgu::Thread::Thread::operator= ( const Thread )
delete

This class cannot be copied: it is intended to be held by std::unique_ptr. The assignment operator is deleted.

◆ start() [1/2]

static std::unique_ptr<Cgu::Thread::Thread> Cgu::Thread::Thread::start ( const Cgu::Callback::Callback cb,
bool  joinable 
)
static

Starts a new thread. It can be called by any thread.

Parameters
cbA 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).
joinableWhether the join() method may be called in relation to the new thread.
Returns
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.
Exceptions
std::bad_allocThis 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).
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.
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.
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.

◆ start() [2/2]

template<class F , class = typename std::enable_if<!std::is_convertible<typename std::remove_reference<F>::type, const Cgu::Callback::Callback*>::value>::type>
static std::unique_ptr<Cgu::Thread::Thread> Cgu::Thread::Thread::start ( F &&  func,
bool  joinable 
)
inlinestatic

Starts a new thread. It can be called by any thread.

Parameters
funcA callable object, such as formed by a lambda expression or the result of std::bind, which will be executed by the new thread.
joinableWhether the join() method may be called in relation to the new thread.
Returns
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.
Exceptions
std::bad_allocThis 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. This function may also throw if the copy or move constructor of the callable object throws. If that happens, the thread will not have started.
2. 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).
3. 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.
4. 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.
5. 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 bound 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.

Since 2.1.0


The documentation for this class was generated from the following file: