cppthread 1.1.16
C++ Thread Library
Public Types | Public Member Functions | Private Member Functions | Private Attributes | Friends | List of all members
cppthread::thread Class Reference

A thread object that ensures proper usage of system threads. More...

Collaboration diagram for cppthread::thread:
Collaboration graph
[legend]

Public Types

typedef std::shared_ptr< threadpointer_t
 The shared pointer for a thread object.
 
typedef std::vector< pointer_tvector_t
 A vector of threads.
 

Public Member Functions

 thread (std::string const &name, runner *runner)
 Initialize the thread object.
 
 thread (std::string const &name, std::shared_ptr< runner > runner)
 Initialize the thread with a shared pointer to the runner.
 
 thread (thread const &rhs)=delete
 The copy operator is deleted.
 
 ~thread ()
 Delete a thread object.
 
std::exception_ptr get_exception () const
 Get the exception pointer.
 
bool get_log_all_exceptions () const
 Retrieve whether all exceptions get logged or not.
 
std::string const & get_name () const
 Retrieve the name of this process object.
 
runnerget_runner () const
 Get a pointer to this thread runner.
 
mutexget_thread_mutex () const
 Retrieve a reference to the thread mutex.
 
pid_t get_thread_tid () const
 Retrieve the thread identifier of this thread.
 
bool is_running () const
 Check whether the thread is considered to be running.
 
bool is_stopping () const
 Check whether the thread was asked to stop.
 
bool kill (int sig)
 Send a signal to this thread.
 
void mask_all_signals ()
 Block all signals for this thread.
 
void mask_signals (libexcept::sig_list_t list)
 Masks signals from this thread.
 
threadoperator= (thread const &rhs)=delete
 The assignment operator is deleted.
 
void set_log_all_exceptions (bool log_all_exceptions)
 Whether to log exceptions caught in the thread.
 
bool start ()
 Attempt to start the thread.
 
void stop (std::function< void(thread *)> callback=nullptr)
 Stop the thread.
 
void unmask_signals (libexcept::sig_list_t list)
 Unmask signals for this thread.
 

Private Member Functions

void init ()
 This private function initializes the thread.
 
bool internal_enter ()
 Enter the thread runner.
 
void internal_leave (leave_status_t status)
 Function called when leaving the thread runner.
 
bool internal_run ()
 Execute the run() function.
 
void internal_thread ()
 Run the thread process.
 

Private Attributes

std::exception_ptr f_exception = std::exception_ptr()
 An exception pointer.
 
bool f_log_all_exceptions = true
 Whether the runner exceptions should be logged or not.
 
mutex f_mutex = mutex()
 The thread mutex to guard various functions.
 
std::string const f_name = std::string()
 The name of this thread.
 
runnerf_runner = nullptr
 The actual thread object.
 
bool f_running = false
 The thread is running.
 
bool f_started = false
 The thread is started.
 
bool f_stopping = false
 The thread is currently in the stopping process.
 
pthread_attr_t f_thread_attr = pthread_attr_t()
 This thread attributes.
 
pthread_t f_thread_id = THREAD_UNDEFINED
 This thread identifier.
 
pid_t f_tid = PID_UNDEFINED
 This thread identifier.
 

Friends

void * func_internal_start (void *system_thread)
 Start the actual thread.
 

Detailed Description

This class is used to handle threads. It should NEVER be used, however, there are some very specific cases where a thread is necessary to make sure that main process doesn't get stuck. For example, the process environment using pipes requires threads to read and write pipes because otherwise the processes could lock up.

Definition at line 63 of file thread.h.

Member Typedef Documentation

◆ pointer_t

This type is used to hold a smart pointer to a thread.

This smart pointer is safe. It can be used to hold a thread object and when it goes out of scope, it properly ends the corresponding thread runner (the runner) and returns.

Be cautious because the smart pointer of a runner is not actually safe to delete without first stopping the thread. Make sure to manage all your threads in with two objects, making sure that the thread goes out of scope first so it can stop your thread before your thread object gets destroyed.

Definition at line 66 of file thread.h.

◆ vector_t

This type defines a vector of threads. Since each entry in the vector is a smart pointer, it is safe to use this type.

Definition at line 67 of file thread.h.

Constructor & Destructor Documentation

◆ thread() [1/3]

cppthread::thread::thread ( std::string const &  name,
runner runner 
)

This function saves the name of the thread. The name is generally a static string and it is used to distinguish between threads when managing several at once. The function makes a copy of the name.

The runner pointer is an object which has a run() function that will be called from another thread. That object becomes the "child" of this thread controller. However, if it is already assigned a thread controller, then the initialization of the thread fails. You may test whether a runner is already assigned a thread controller by calling its get_thread() function and see that it is not nullptr.

The pointer to the runner object cannot be nullptr.

Parameters
[in]nameThe name of the process.
[in]runnerThe runner (the actual thread) to handle.

Definition at line 89 of file thread.cpp.

References init().

Here is the call graph for this function:

◆ thread() [2/3]

cppthread::thread::thread ( std::string const &  name,
std::shared_ptr< runner runner 
)

At times you have a shared pointer to the runner, in which case you can directly use that pointer. This function otherwise works exactly like the other accepting a bare pointer. In other words, the thread does not keep a shared pointer of your runner. It is expected that you do not delete your runner before the thread is done with it.

Parameters
[in]nameThe name of the process.
[in]runnerThe runner (the actual thread) to handle.

Definition at line 108 of file thread.cpp.

References init().

Here is the call graph for this function:

◆ thread() [3/3]

cppthread::thread::thread ( thread const &  rhs)
delete

The thread object holds a pointer to a runner which is an OS thread. These would be really difficult to copy. Instead we prevent the operation altogether.

Parameters
[in]rhsThe right hand side.

◆ ~thread()

cppthread::thread::~thread ( )

The destructor of a Snap! C++ thread object ensures that the thread stops running before actually deleting the runner object.

Then it destroys the thread attributes and returns.

The destructor also removes the thread from the runner so the runner can be assigned to another thread controller and run again.

Definition at line 175 of file thread.cpp.

References cppthread::end(), f_runner, cppthread::runner::f_thread, f_thread_attr, cppthread::log, and stop().

Here is the call graph for this function:

Member Function Documentation

◆ get_exception()

std::exception_ptr cppthread::thread::get_exception ( ) const

When the thread runner raises an exception, it gets saved in the thread object. That exception can be retrieved using this get_exception() function.

If no exception occurred, then this pointer will be the nullptr. If an exception did occur, then it will be a pointer to that exception. It can be rethrown inside a try/catch in order to handle it.

std::exception_ptr e(my_thread->get_exception());
if(e != std::exception_ptr())
{
try
{
e->rethrow_exception();
}
catch(std::exception const & e)
{
std::cerr << "exception occurred: " << e.what() << std::endl;
}
}
Returns
A pointer to a standard exception (std::exception).

Definition at line 836 of file thread.cpp.

References f_exception, and f_mutex.

◆ get_log_all_exceptions()

bool cppthread::thread::get_log_all_exceptions ( ) const

This function returns true if all exceptions are to be logged. By default this flag is set to true. You can change it with the set_log_all_exceptions() function.

Internally, the flag is used to know whether we should log exceptions. It is very useful to do so if you do not join your thread on a constant basis (i.e. in a service where a thread runs as long as the service itself–you probably will want to know when the thread runs in a problem).

This also means you do not have to log the exception yourself unless you need some special handling to indicate all the possible messages and parameters found in the exception object. However, the logging doesn't prevent the system from re-throwing the exception once the thread was joined. In other words, whether you log the exception or not, you most certainly want to catch it or your process will be terminated.

Returns
true if the thread object is to log all exceptions.
See also
set_log_all_exceptions()

Definition at line 803 of file thread.cpp.

References f_log_all_exceptions.

◆ get_name()

std::string const & cppthread::thread::get_name ( ) const

This process object is given a name on creation. In most cases this is a static name that is used to determine which process is which.

Returns
The name of the process.

Definition at line 209 of file thread.cpp.

References f_name.

◆ get_runner()

runner * cppthread::thread::get_runner ( ) const

This function returns the pointer to the thread runner. There are cases where it is quite handy to be able to use this function rather than having to hold on the information in your own way.

You will probably have to dynamic_cast<>() the result to your own object type.

Note
The thread constructor ensures that this pointer is never nullptr. Therefore this function never returns a null pointer. However, the dynamic_cast<>() function may return a nullptr.
Returns
The runner object attached to this thread.

Definition at line 231 of file thread.cpp.

References f_runner.

◆ get_thread_mutex()

mutex & cppthread::thread::get_thread_mutex ( ) const

This function returns a reference to this thread mutex. Note that the runner has its own mutex as well.

Returns
This thread's mutex.

Definition at line 740 of file thread.cpp.

References f_mutex.

◆ get_thread_tid()

pid_t cppthread::thread::get_thread_tid ( ) const

Under Linux, threads are tasks like any others. Each task is given a pid_t value. This function returns that pid_t for this thread.

When the thread is not running this function returns PID_UNDEFINED. Note, however, that the value is set a little after the thread started and cleared a little before the thread exits. This is not a good way to know whether the thread is running. Use the is_running() function instead.

Returns
The thread identifier (tid) or PID_UNDEFINED if the thread is not running.

Definition at line 726 of file thread.cpp.

References f_mutex, and f_tid.

Referenced by cppthread::runner::enter(), cppthread::runner::gettid(), and cppthread::runner::leave().

Here is the caller graph for this function:

◆ init()

void cppthread::thread::init ( )
private

We can create the thread with a bare pointer or a shared pointer. This function does the remaining of the initialization.

Definition at line 121 of file thread.cpp.

References cppthread::end(), f_runner, cppthread::runner::f_thread, f_thread_attr, cppthread::runner::get_name(), and cppthread::log.

Referenced by thread(), and thread().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ internal_enter()

bool cppthread::thread::internal_enter ( )
private

This function signals that the thread runner is about to be entered. This is often used as an initialization function.

If the runner::enter() function raises an std::exception, then the function saves that exception for the thread owner, emits a log, and returns false.

Note
The log is not emitted if set_log_all_exceptions() was called with false.
Returns
true if the runner::enter() function returned as expected.

Definition at line 434 of file thread.cpp.

References cppthread::end(), cppthread::runner::enter(), f_exception, f_log_all_exceptions, f_runner, and cppthread::log.

Referenced by internal_thread().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ internal_leave()

void cppthread::thread::internal_leave ( leave_status_t  status)
private

Whenever the thread runner leaves, we want to send a signal to the runner owner through the runner::leave() function. This is the thread function which makes sure that the runner::leave() function get called.

The function is called with a status which tells us what failed (i.e. the reason for the call).

The function is std::exception safe. Unknown exceptions are ignored here since they will be caught by the internal_thread() function.

std::exceptions are reported and either ignored (another exception occurred earlier) or reported back to the thread owner after the owner joins with the thread.

Parameters
[in]statusThe status when the internal_leave() function gets called.

Definition at line 528 of file thread.cpp.

References cppthread::end(), f_exception, f_log_all_exceptions, f_runner, cppthread::runner::leave(), and cppthread::log.

Referenced by internal_thread().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ internal_run()

bool cppthread::thread::internal_run ( )
private

This function specifically calls the run() function in an exception safe manner.

If no exception occurs, the function returns true meaning that everything worked as expected.

When an std::exception occurs, the function returns false after saving the exception so it can be reported to this thread owner. (i.e. it gets re-thrown whenever the thread owner joins with the thread).

The std::exception can be logged by calling the set_log_all_exceptions() function with true, which is the default (i.e. don't call it with false if you want to get the logs).

Other exceptions are ignored (they will be caught by the internal_thread() function).

Returns
true if the run worked as expected; false if an exception was caught.

Definition at line 483 of file thread.cpp.

References cppthread::end(), f_exception, f_log_all_exceptions, f_runner, cppthread::log, and cppthread::runner::run().

Referenced by internal_thread().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ internal_thread()

void cppthread::thread::internal_thread ( )
private

This function is called by the func_internal_start() so we run from within the thread class. (i.e. the func_internal_start() function itself is static.)

The function marks the thread as started which allows the parent start() function to return.

Note
The function catches standard exceptions thrown by any of the functions called by the thread. When that happens, the thread returns early after making a copy of the exception. The stop() function will then rethrow that exception and it can be managed as if it had happened in the main process (if a thread creates another thread, then it can be propagated multiple times between all the threads up to the main process.)

Definition at line 318 of file thread.cpp.

References cppthread::end(), f_exception, f_log_all_exceptions, f_mutex, f_runner, f_running, f_started, f_tid, cppthread::runner::get_name(), cppthread::gettid(), internal_enter(), internal_leave(), internal_run(), cppthread::log, cppthread::PID_UNDEFINED, cppthread::set_current_thread_name(), and cppthread::mutex::signal().

Here is the call graph for this function:

◆ is_running()

bool cppthread::thread::is_running ( ) const

This flag is used to know whether the thread is running.

Todo:
We need to save the pid of the process that creates threads. This tells us whether the thread is running in this process. i.e. if a fork() happens, then the child process does not have any threads so is_running() has to return false. Also, any other function that checks whether a thread is running needs to also check the pid. We have to save the pid at the time we start the thread and then when we check whether the thread is running.
Returns
true if the thread is still considered to be running.

Definition at line 253 of file thread.cpp.

References f_mutex, and f_running.

◆ is_stopping()

bool cppthread::thread::is_stopping ( ) const

The thread is using three status flags. One of them is f_stopping which is set to false (which is also the default status) when start() is called and to true when stop() is called. This function is used to read that flag status from the continue_running() function.

Returns
true if the stop() function was called, false otherwise.

Definition at line 269 of file thread.cpp.

References f_mutex, and f_stopping.

Referenced by cppthread::runner::continue_running().

Here is the caller graph for this function:

◆ kill()

bool cppthread::thread::kill ( int  sig)

This function sends a signal to this specific thread.

You have to be particularly careful with Unix signal and threads as they do not always work as expected. This is yet particularly useful if you want to send a signal such as SIGUSR1 and SIGUSR2 to a thread so it reacts one way or another (i.e. you are using poll() over a socket and need to be stopped without using a possibly long time out, you can use the signalfd() function to transform SIGUSR1 into a poll-able signal.)

Note
Obviously, if the thread is not running, nothing happens.
Parameters
[in]sigThe signal to send to this thread.
Returns
true if the signal was sent, false if the signal could not be sent (i.e. the thread was already terminated...)

Definition at line 862 of file thread.cpp.

References f_mutex, f_running, and f_thread_id.

◆ mask_all_signals()

void cppthread::thread::mask_all_signals ( )

After this call, all Unix signals will be masked (except for the few that can never be masked such as SIGKILL).

Note that by default a thread always starts with all its signals blocked. In most cases, you want to leave them that way. This function is useful only if you call the unmask_signals() to unblock some signals for some time.

Note
The start() function currently masks all the signals before starting the thread.
Todo:
This won't work well in our test environment. This is due to the fact that there are a few signals that are used by the catch2 and sanitizer tools. See the scoped_signal_mask implementation in libexcept for additional information about this.
See also
mask_signals()
unmask_signals()

Definition at line 927 of file thread.cpp.

◆ mask_signals()

void cppthread::thread::mask_signals ( libexcept::sig_list_t  list)

The thread has a signal mask that can be used to prevent delivery of signals to that thread. In our event dispatcher environment, all threads should have their signals masked because in most cases the threads will just die on a signal. This is because the event dispatcher system uses the fdsignal() and thus does not make use of handler. The default handler for most signal will terminate the whole process.

Parameters
[in]listThe list of signals to block.
See also
mask_all_signals()
unmask_signals()

Definition at line 892 of file thread.cpp.

◆ operator=()

cppthread::thread::operator= ( thread const &  rhs)
delete

The thread object holds a pointer to a runner which is an OS thread. These would be really difficult to assign. Instead we prevent the operation altogether.

Parameters
[in]rhsThe right hand side.
Returns
A reference to this object.

◆ set_log_all_exceptions()

void cppthread::thread::set_log_all_exceptions ( bool  log_all_exceptions)

We catch exceptions that happen in your threads. By default, we log them with the basic cppthread log mechanism. If you do not want to have this intermediate logging kick in, you can set this flag to false.

This flag is defaulted to true because in many cases you are not joining a thread when the exception occurs. That means your code would never see the exception. Instead, you'd have a dangling process as it is likely to expect things are happening in the thread and if not, it gets stuck. At least, in this way, by default you get a message in your logs.

Note that in case of the leave() callback, it may also generate an exception when one of enter() or run() may already have generated an exception. In that case, the log is always generated since otherwise you would lose that information.

Note
Unknown exceptions (i.e. those not derived from std::exception), are always logged and re-thrown (which has the effect of terminating your process). Those exceptions are always logged, whatever the state of this "log all exceptions" flag.
Parameters
[in]log_all_exceptionsWhether to log (true) all exceptions or not (false).
See also
get_log_all_exceptions()

Definition at line 775 of file thread.cpp.

References f_log_all_exceptions.

◆ start()

bool cppthread::thread::start ( )

This function is used to start running the thread code. If the thread is already running, then the function returns false.

The function makes use of a condition to wait until the thread is indeed started. The function will not return until the thread is started or something failed.

Note that by default all signals are blocked for all our threads. This is much safer because otherwise the default signal handler kicks in and is likely to terminate the whole process. If you want a thread to have some signals unblocked, call the unmask_signals() function as required.

Returns
true if the thread successfully started, false otherwise.

Definition at line 575 of file thread.cpp.

References cppthread::end(), f_exception, f_mutex, f_runner, f_running, f_started, f_stopping, f_thread_attr, f_thread_id, func_internal_start, cppthread::gettid(), cppthread::runner::is_ready(), cppthread::log, and cppthread::mutex::wait().

Referenced by cppthread::life::life().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ stop()

void cppthread::thread::stop ( std::function< void(thread *)>  callback = nullptr)

This function requests the thread to stop. Note that the function does not actually forcibly stop the thread. It only turns on a flag (namely it makes the is_stopping() function return true) meaning that the thread should stop as soon as possible. This gives the thread the time necessary to do all necessary cleanup before quitting.

The stop function blocks until the thread is done.

Warning
This function throws the thread exceptions that weren't caught in your run() function. This happens after the thread has completed. The exception is then removed from the thread (i.e. it won't re-throw a second time and a call to get_exception() returns a null pointer).
Parameters
[in]callbackA function to call after the thread is marked as stopping but before calling join. Useful to send a signal to the child if you could not have done so earlier.

Definition at line 656 of file thread.cpp.

References f_exception, f_mutex, f_running, f_started, f_stopping, and f_thread_id.

Referenced by cppthread::life::~life(), and ~thread().

Here is the caller graph for this function:

◆ unmask_signals()

void cppthread::thread::unmask_signals ( libexcept::sig_list_t  list)

After this call, all the signals defined in list will be unmasked. This means those signals may be sent to this thread instead of the main application. In most cases, unless you properly define a signal handler, the signals will terminate the whole process.

Parameters
[in]listThe list of signals to unblock.
See also
mask_signals()
mask_all_signals()

Definition at line 947 of file thread.cpp.

Friends And Related Symbol Documentation

◆ func_internal_start

void * func_internal_start ( void *  system_thread)
friend

This function is called when starting the thread. This is a static function since pthread can only accept such a function pointer.

The function then calls the internal_thread().

Note
The function parameter is a void * instead of thread because that way the function signature matches the signature the pthread_create() function expects.
Parameters
[in]system_threadThe thread pointer.
Returns
We return a null pointer, which we do not use because we expect users to pass results in a different way (i.e. using the fifo).

Definition at line 293 of file thread.cpp.

Referenced by start().

Member Data Documentation

◆ f_exception

cppthread::thread::f_exception = std::exception_ptr()
private

The exception pointer to the exception that was raised in the runner. By default this pointer is null.

This pointer is reset back to a null pointer each time the start() is called.

This exception must be caught by your function when calling the stop() function. If you don't catch these, then it will stop your process.

Note
Only standard exceptions can be caught in this way. You should derive all your exception from std::exception (or use the libexcept).

Definition at line 112 of file thread.h.

Referenced by get_exception(), internal_enter(), internal_leave(), internal_run(), internal_thread(), start(), and stop().

◆ f_log_all_exceptions

cppthread::thread::f_log_all_exceptions = true
private

Whenever an exception occurs in a runner, the exception pointer is saved in the runner so it can be re-thrown after we join with that thread.

The main problem here is that certain threads are not joined until we are done with an application. So the exception lingers and there is absolutely no trace of it, especially if you hit Ctlr-C to exit your software, the exception will 100% be lost.

Instead, we have this flag to determine whether the exception should be logged at the time it gets caught instead of just saving it in the pointer. By default the flag is set to true which means we will log the exception immediately. This makes it easy to not forget.

If you use threads to run a quick process and then return/join (i.e. stop()), then setting this flag to false is okay.

See also
get_log_all_exceptions()
set_log_all_exceptions()

Definition at line 108 of file thread.h.

Referenced by get_log_all_exceptions(), internal_enter(), internal_leave(), internal_run(), internal_thread(), and set_log_all_exceptions().

◆ f_mutex

cppthread::thread::f_mutex = mutex()
mutableprivate

The mutex is used whenever a variable that may be accessed by different threads is used. Especially, it makes sure that the variables do not get changed too early or too late (i.e. avoid race conditions).

Also the mutex is used to send signals. For example, the thread waits on the runner to start. Once the runner is started, it signals the main thread which can then wake up and return as everything is now well defined.

Definition at line 104 of file thread.h.

Referenced by get_exception(), get_thread_mutex(), get_thread_tid(), internal_thread(), is_running(), is_stopping(), kill(), start(), and stop().

◆ f_name

cppthread::thread::f_name = std::string()
private

For debug purposes, you can give each one of your threads a different name. It gets saved in this string.

Definition at line 102 of file thread.h.

Referenced by get_name().

◆ f_runner

cppthread::thread::f_runner = nullptr
private

The runner is the object which holds the system thread and runs the commands. We have a separate object because that way we can make sure the runner destructor isn't called while the thread is still running. If that were to happen, then all the virtual functions would be invalid at that point and the system could crash.

So the thread object holds a runner allowing the thread to be destroyed first, which calls the stop() function before the runner gets destroyed.

Definition at line 103 of file thread.h.

Referenced by ~thread(), get_runner(), init(), internal_enter(), internal_leave(), internal_run(), internal_thread(), and start().

◆ f_running

cppthread::thread::f_running = false
private

When the thread is running, this flag is set to true. The start() function sets this flag to true before starting the thread and the internal_run() function sets the flag back to false right before the thread exits.

In other words, the flag is true early and false early. It's not a way to know whether the actual system thread is running or not.

Definition at line 105 of file thread.h.

Referenced by internal_thread(), is_running(), kill(), start(), and stop().

◆ f_started

cppthread::thread::f_started = false
private

The start() function returns only after the thread is started. To control that state, we use this flag. The caller will wait until the child thread is started (i.e. f_started is true).

Definition at line 106 of file thread.h.

Referenced by internal_thread(), start(), and stop().

◆ f_stopping

cppthread::thread::f_stopping = false
private

When the stop() function is called, the flag is set to true until after the caller joined with the thread.

The is_stopping() function returns the current value of that field.

Definition at line 107 of file thread.h.

Referenced by is_stopping(), start(), and stop().

◆ f_thread_attr

cppthread::thread::f_thread_attr = pthread_attr_t()
private

When we create a thread we create attributes to assign to the thread on creation. These are the attributes.

These are created once at the time the thread object is created and released when the object is destroyed. The same attributes are reused to re-start the thread over and over again (i.e. start() / stop() sequences).

Definition at line 111 of file thread.h.

Referenced by ~thread(), init(), and start().

◆ f_thread_id

cppthread::thread::f_thread_id = THREAD_UNDEFINED
private

Each thread is assigned a unique identifier by the C-library. This is that identifier. Under Linux, it is an IP address.

Definition at line 110 of file thread.h.

Referenced by kill(), start(), and stop().

◆ f_tid

cppthread::thread::f_tid = PID_UNDEFINED
private

Each thread is assigned a unique identifier by the OS.

Definition at line 109 of file thread.h.

Referenced by get_thread_tid(), and internal_thread().


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

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.