randolf.ca  1.00
Randolf's C++ classes
randolf::rthread Class Referenceabstract

This rthread thread library provides an easier and sufficiently-complete object-oriented thread interface class for C++ that adds a properly-honoured destructor, and without terminating the entire application when the thread throws an exception (a virtual method for optionally catching and handling all exceptions is also provided). More...

+ Collaboration diagram for randolf::rthread:

Public Member Functions

 rthread () noexcept
 Default constructor. You can subclass this constructor and/or create parameterized constructors, any of which may throw exceptions (even though this default one doesn't). More...
 
virtual ~rthread () noexcept
 Default destructor. Called only after two conditions are met: More...
 
virtual void _rthread_exceptions (std::exception *e) noexcept
 Final rthread exception handler. If an uncaught exception is thrown from the run() method in a running rthread, then it will be handled by this method (which is still technically running on the underlying pthread). Override this method to handle known and unknown exceptions gracefully, before the destructor (or reinitiator) runs. More...
 
virtual void _rthread_reinitialize () noexcept
 Re-initialize internal variables, refresh resources, etc., before re-running a reuseable rthread. When utilizing the reuseable feature, this method can be thought of as having similar functionality to that of the constructor, except that it is used to do the following to bring internal variables and resources back to the same state that the constructor originally initialized them to, and thus reducing construction overhead (some variables may not need re-initializing, which can also help to further-reduce overhead): More...
 
rthreaddetach ()
 Daemonize this rthread. More...
 
bool detached () noexcept
 Find out whether this rthread is daemonized. More...
 
rthreadjoin ()
 Join this rthread to the current thread that called this method. (This is how non-daemonized threads are normally terminated.) More...
 
pthread_t pthread_id () noexcept
 Find out what the underlying pthread_id is. More...
 
bool reuseable () noexcept
 Find out whether this rthread can be re-used. More...
 
rthreadreuseable (bool flag) noexcept
 Find out whether this rthread can be re-used. More...
 
virtual void run ()=0
 Thread functionality (subclassing this method is required). More...
 
ulong run_count () noexcept
 Find out how many times this rthread was started. This method is only really meaningful for reuseable rthreads. More...
 
bool running () noexcept
 Find out whether this rthread is actively running. More...
 
rthreadstart ()
 Commence execution of the run() method as a separate rthread. More...
 
rthreadstart_detached ()
 Commence execution of the run() method as a separate thread in a daemonized state. More...
 
rthreadstop () noexcept
 Request the graceful termination of this rthread. More...
 

Detailed Description

This rthread thread library provides an easier and sufficiently-complete object-oriented thread interface class for C++ that adds a properly-honoured destructor, and without terminating the entire application when the thread throws an exception (a virtual method for optionally catching and handling all exceptions is also provided).

POSIX threads are the foundation of the rthread class, and the resulting C++ interface will probably seem familiar to those who are familliar with Java.

Features
This is meant to be a safe and easy-to-use thread class for C++, which is intended to make thread sub-classing straight-forward in a logical manner for developers. Some of the key features are:
  • Customizable constructor that doesn't automatically start the thread
    • Better control with the start() method to start a previously constructed rthread
  • Abstract run() method (required)
    • This method contains the code that will be run in its own thread
  • Customizable destructor that occurs only after the thread ends (which is particularly useful for daemon threads {a.k.a., detached threads})
  • Customizable exception handler method (just subclass it to use it)
  • Most methods are designed to be thread-safe (e.g., status methods)

Some advanced features are planned that exceed what the basic thread functions provide, but are also needed:

  • thread groups (separate class), possibly with support for including threads in multiple groups
  • thread pool (separate class)
Background
I created this class to make it easier to write internet server daemons. I started out using C-style thread functions (because C++ doesn't come with a thread class that can be sub-classed), and even with std::jthreads I ran into a serious problem with the thread's destructor executing while the thread was still runningd (the code that started it went out of scope) – this is not what I expected because the running thread should really be an additional criteria for thread destruction (this is a serious concern since the premature destruction of a running thread usually results in a segmentation fault or other unpredictable behaviour that has the potential to cause the Operating System to exhibit other strange behaviours or crash entire).

After looking for existing solutions (none of which resolved the ineherent problems with threads in C++), I embarked on creating this rthread class, which was highly educational but also wasn't difficult. The end result is a small class that's easy to maintain, eliminates the reliability concerns, and is easy to use primarily because C++ thoroughly supports subclassing.

My background in programming began when I was a young child, teaching myself BASIC and then machine language (when I found BASIC to be too limited) before moving on to other languages like Perl and Java many years later. Eventually I circled around to C (which I chose to learn the hard way by writing some PostgreSQL extensions) and then C++ a few years after that. I have a lot of experience with programming threadded applications on a variety of platforms that support pthreads (including Novell's NetWare), running application code I created that ran hundreds of thousands of threads successfully serving a similar number of users globally without slowdowns or crashes.

History
2022-Oct-22 rthread v1.00 by Randolf Richardson.

Constructor & Destructor Documentation

◆ rthread()

randolf::rthread::rthread ( )
inlinenoexcept

Default constructor. You can subclass this constructor and/or create parameterized constructors, any of which may throw exceptions (even though this default one doesn't).

This is particularly useful for performing pre-run setup before starting the thread (use the run() and start() methods for details), especially with threads that start running in response to external events (such as user interaction events, incoming internet socket connections, etc.).

◆ ~rthread()

virtual randolf::rthread::~rthread ( )
inlinevirtualnoexcept

Default destructor. Called only after two conditions are met:

  1. this rthread has gone out of scope
  2. termination of the underlying pthread (this is different from the standard C++ implementation of threads and jthreads, which don't have this criteria)

Exceptions are handled by the _rthread_exceptions() method before this destructor is activated.

You can add your own destructor to safely ensure that files and sockets are closed, and that resources are freed, deallocated, deleted, etc., which is essential to preventing various types of resource leaks if your thread code (in the run() method) exited early due to an unexpected exception (it is the developer's responsibility to ensure resources are managed properly, and this destructor provides a means of handling this reliably).

Logging, re-queuing, semaphore completions, etc., can also be handled in this method because it runs after the termination of the underlying pthread.

Warning
Do not throw exceptions from this method. If you're relying on a function or a class that might throw exceptions (intended or otherwise), then you need to take care to at least utilize a final try/catch block for std::exception.

Member Function Documentation

◆ _rthread_exceptions()

virtual void randolf::rthread::_rthread_exceptions ( std::exception *  e)
inlinevirtualnoexcept

Final rthread exception handler. If an uncaught exception is thrown from the run() method in a running rthread, then it will be handled by this method (which is still technically running on the underlying pthread). Override this method to handle known and unknown exceptions gracefully, before the destructor (or reinitiator) runs.

Warning
If e is nullptr it means an unknown exception was thrown (e.g., internally we literally caught (...) (other exception), in which case you'll need to use std::current_exception() to access it.
Note
For a reuseable rthread, the running status will be set to false during _rthread_reinitialize execution, which can be useful for handling those exceptions separately within this code (by testing for the running() condition).
Parameters
eExceptions (see warning, immediately above)

◆ _rthread_reinitialize()

virtual void randolf::rthread::_rthread_reinitialize ( )
inlinevirtualnoexcept

Re-initialize internal variables, refresh resources, etc., before re-running a reuseable rthread. When utilizing the reuseable feature, this method can be thought of as having similar functionality to that of the constructor, except that it is used to do the following to bring internal variables and resources back to the same state that the constructor originally initialized them to, and thus reducing construction overhead (some variables may not need re-initializing, which can also help to further-reduce overhead):

  • close any open files, sockets, etc., that need to be closed (or reset file pointers instead of re-opening files)
  • reset/clear variables and memory structures to their expected defaults (or free-and-allocate resources that can't be reset or cleared reliably)
  • re-add this to a thread pool or concurrent queue of ready rthreads
Note
If there is any clean-up, logging, etc., peformed in the destructor that also needs to be performed here, the best approach is to create a private method for those particular actions and then call it from this method as well as the destructor for better operational consistency.
See also
reuseable

◆ detach()

rthread* randolf::rthread::detach ( )
inline

Daemonize this rthread.

If this rthread was previously detached, then this method has no effect (but won't cause the randolf::rex::xEINVAL exception to be thrown; there may be other reasons for this exception to be thrown).

Threads
This method is thread-safe.
Exceptions
randolf::rex::xEINVALNot a joinable rthread
randolf::rex::xESRCHA pthread with the underlying pthread_id couldn't be found (underlying pthread doesn't exist)
Returns
The same rthread object so as to facilitate stacking
See also
start_detached

◆ detached()

bool randolf::rthread::detached ( )
inlinenoexcept

Find out whether this rthread is daemonized.

Threads
This method is thread-safe.
Returns
TRUE = daemonized
FALSE = not daemonized

◆ join()

rthread* randolf::rthread::join ( )
inline

Join this rthread to the current thread that called this method. (This is how non-daemonized threads are normally terminated.)

Note
This method blocks until this rthread's underlying pthread is terminated.
Threads
This method is thread-safe.
Exceptions
randolf::rex::xEDEADLKDeadlock detected because two threads are queued to join with each other, or the current rthread is trying to join with itself
randolf::rex::xEINVALNot a joinable rthread
randolf::rex::xEINVALA different thread is already queued to join with this rthread
randolf::rex::xESRCHA pthread with the underlying pthread_id couldn't be found (underlying pthread doesn't exist)
Returns
The same rthread object so as to facilitate stacking
See also
stop

◆ pthread_id()

pthread_t randolf::rthread::pthread_id ( )
inlinenoexcept

Find out what the underlying pthread_id is.

Advanced developers may want easy access to this ID for a variety of reasons.

Warning
This method is not thread-safe.
Returns
ID number of underlying pthread (0 = not assigned, which is what you should expect to find if this rthread didn't start() yet)

◆ reuseable() [1/2]

bool randolf::rthread::reuseable ( )
inlinenoexcept

Find out whether this rthread can be re-used.

Threads
This method is thread-safe.
Returns
TRUE = re-useable
FALSE = not re-useable

◆ reuseable() [2/2]

rthread* randolf::rthread::reuseable ( bool  flag)
inlinenoexcept

Find out whether this rthread can be re-used.

Threads
This method is thread-safe.
Returns
The same rthread object so as to facilitate stacking
See also
_rthread_reinitialize
Parameters
flagTRUE = Set this rthread to be re-useable
FALSE = Set this rthread to not be re-useable

◆ run()

virtual void randolf::rthread::run ( )
pure virtual

Thread functionality (subclassing this method is required).

Warning
Do not call this method directly. To properly begin rthread execution, use either of the start() or start_detached() methods. When subclassing rthread, you need to override this method with the code that is to be executed as a separate thread. (You don't have to move all of your code into this method; in fact, you can simply use this method to call out to code elsewhere, or call methods on instantiated classes that were referenced during the instantiation of your overriding rthread() constructor, etc.)
Note
Minimally, this is the only method that's required when sub-classing rthread.
See also
start
start_detached

◆ run_count()

ulong randolf::rthread::run_count ( )
inlinenoexcept

Find out how many times this rthread was started. This method is only really meaningful for reuseable rthreads.

Threads
This method is thread-safe.
Returns
Run count
See also
_rthread_reinitialize
reuseable

◆ running()

bool randolf::rthread::running ( )
inlinenoexcept

Find out whether this rthread is actively running.

Threads
This method is thread-safe.
Returns
TRUE = running
FALSE = not running
See also
start
stop

◆ start()

rthread* randolf::rthread::start ( )
inline

Commence execution of the run() method as a separate rthread.

Threads
This method is thread-safe.
Exceptions
randolf::rex::xEWOULDBLOCK(xEAGAIN) Insufficient resources to start the underlying pthread
randolf::rex::xEWOULDBLOCK(xEAGAIN) Maximum number-of-threads limit reached
Returns
The same rthread object so as to facilitate stacking
See also
start_detached

◆ start_detached()

rthread* randolf::rthread::start_detached ( )
inline

Commence execution of the run() method as a separate thread in a daemonized state.

Threads
This method is thread-safe.
Exceptions
randolf::rex::xEDEADLKDeadlock detected because two threads are queued to join with each other, or the current rthread is trying to join with itself
randolf::rex::xEINVALNot a joinable rthread
randolf::rex::xEINVALA different thread is already queued to join with this rthread
randolf::rex::xENOMEMInsufficient memory (on Linux this normally always succeeds, but there's not guarantee as the community suggests that this could change in the future)
randolf::rex::xESRCHA pthread with the underlying pthread_id couldn't be found (underlying pthread doesn't exist)
Returns
The same rthread object so as to facilitate stacking
See also
start

◆ stop()

rthread* randolf::rthread::stop ( )
inlinenoexcept

Request the graceful termination of this rthread.

Warning
One side-effect of this method is to daemonize this rthread because stop() serves as an alternative to the join() method.
Note
It is the responsibility of developers to include periodic checks throughout their code (assumedly at convenient points, which is common) by utilizing the running() method to determine whether to start winding things down.
Threads
This method is thread-safe.
Exceptions
randolf::rex::xEINVALNot a joinable rthread
randolf::rex::xESRCHA pthread with the underlying pthread_id couldn't be found (underlying pthread doesn't exist)
Returns
The same rthread object so as to facilitate stacking
See also
join

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