POSIX Threads: Semi-FAQ Revision 5.2
© 2001-2006 Michael M. Lampkin
email: michael.lampkin<at>ieee.org
© 2001-2006 Michael M. Lampkin
email: michael.lampkin<at>ieee.org
In addition to the other various programming interfaces that POSIX defines, it also provides those related to threaded programming.
This section details the basic methods to discover if a system implements POSIX Threads, also known as pthreads, and how to create and execute a simple pthread.
Threads are defined as optional by the POSIX standard which means that once we determine that the OS is POSIX compliant as described in section 2.2, another check must be done to see if threads are implemented. The determination can be made by looking in the header files unistd.h for the symbolic constant _POSIX_THREADS and checking it’s value.
If _POSIX_THREADS is undefined or has a value of 0 or -1, this indicates that POSIX threads are not implemented.
If the symbolic constant _POSIX_THREADS is defined to a value greater than 0, this indicates that POSIX threads are implemented.
In particular, if the value is 200112L, this is an indication that pthreads are not only implemented but also compliant with the most recent POSIX specification.
As with determining if a system is POSIX compliant using sysconf( ), we can also determine if pthreads are implemented using sysconf( ) at runtime with the name value of _SC_THREADS as shown below:
#define _POSIX_C_SOURCE 200112L
#include <unistd.h>
#include <stdio.h>
int main( int argv, char ** argc )
{
long x = sysconf(_SC_THREADS);
printf( "%li\n", x );
return 0;
}
Compared to a non-threaded program, you only have to add two items to the compile line and that is an inclusion of the POSIX libraries. The library name can vary from system to system but compiling on Linux with gcc the command line would look like:
gcc -lpthread -lrt -D_REENTRANT -o [OUTPUT NAME] [SOURCE NAME]
The -lpthread says to link with the system pthread library while the -lrt says to include the real time components. Sometimes you will not need real time components, for example if you don't use POSIX clocks, and the -lrt can be removed. On the same note, always including it will NOT hurt your code.
You may define _REENTRANT within a header file or on the command line as shown and it indicates that you are specifying explicitly that you want all available re-entrant versions of functions exposed AND that errno's will be thread specific.
To quickly cover what a re-entrant function is, consider the functions which use static buffers to hold data. Because two threads calling such a function at the same time may end up even in the best of situations, overwriting each others data, alternative versions of those functions must be provided if they are to be usable by multi-threaded programs. The "re-entrant" functions can usually be found by looking up the normal function name with a trailing _r adde
If you define _POSIX_C_SOURCE >= 199506 then _REENTRANT should automatically be defined for you, but my motto is better safe than sorry.
Other systems and options:
AIX with their compiler -
Make certain that pthread.h is the FIRST include file in your source code.
xlc_r -o [OUTPUT NAME] [SOURCE NAME] for ANSI level code
cc_r7 -o [OUTPUT NAME] [SOURCE NAME] for Extended level code
Solaris with their compiler -
cc -lpthread -lrt -D_REENTRANT -o [OUTPUT NAME] [SOURCE NAME]
or ...
cc -mt -lpthread -lrt -D_REENTRANT -o [OUTPUT NAME] [SOURCE NAME]
The first step in defining code which will execute in its own thread is to make certain that your system is POSIX compliant and also supplies a pthread implementation. The methods by which this can be verified have been previously covered in sections 2.2 and 4.1 respectively.
The next step is including the following line in your code:
#include <pthread.h>
You must also define a start function to serve as the entry point for the new thread. All start functions must follow the prototype:
void * [START_FUNCTION_NAME] ( void * arg )
A short example of such a function would be:
void * start
(
void * arg
)
{
printf( "I am a thread!\n" );
return NULL;
}
If you create a thread start function / entry point as detailed in section 4.3 and you call it directly from your program, you end up with nothing but a single single non-threaded process. To start the function code executing in it’s own thread, you need to utilize the pthread_create( ) function.
The pthread_create( ) function prototype is:
int pthread_create ( pthread_t * thread, const pthread_attr_t * attr, void * (* start_func ) ( void * ), void * arg );
The arguments you need to supply are:
thread: A pointer to a pthread_t variable supplied by the caller within which will be placed the identifier for the newly created thread if the call was successful.
attr: A NULL pointer or (constant) pointer to a pthread_attr_t variable which contains the desired attributes for the newly created thread. The setting of thread attributes is covered in more detail later in this section.
start_func: The address of the function which will be called when the thread is started e.g. our test_thread function previously given in this section.
arg: A pointer to a void variable which will be passed to the function start_func when it is called.
If the call to pthread_create( ) is successful it will set the pthread * thread parameter so that it uniquely identifies the new thread and return a value of zero.
It is possible for the call to fail and return one of the following non-zero error codes:
EAGAIN: The system lacked the necessary resources to create another thread, or the system-imposed limit on the total number of threads in a process, as defined by PTHREAD_THREADS_MAX, would be exceeded. See section 4.6 and 4.7 for more information.
EINVAL: The value specified by attr is invalid.
EPERM: The caller does not have appropriate permission to set the required scheduling parameters or scheduling policy (see the section on thread scheduling attributes for further details).
An very simple example with no error checking is:
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <pthread.h>
void * start
(
void * arg
)
{
printf( "I am a thread!\n" );
return NULL;
}
int main( int argc, char ** argv )
{
pthread_t thread;
( void ) pthread_create( & thread, NULL, & start, NULL );
pthread_join( thread, NULL );
return 0;
}
You may notice the use of the pthread_join( ) function. This function and descriptions of joined and unjoined threads are given in another section of the documen
There are four primary ways that you can exit from a thread or cause it to terminate. Those methods are:
The last item is the only one which typically requires an in depth explanation. Since cancellation can be a complex topic with a many dependencies, it will be covered in its own section later in the FAQ.
Though not listed, a thread can simply crash or even be killed if it receives a normally unblockable signal such as SIGSEGV. Since such an event is not normally considered proper behavior, it wasn't placed in the list.
The number of simultaneous threads allowed is defined by the constant PTHREAD_THREADS_MAX in the limits.h header file.
It may also be retrieved during runtime using a call to sysconf( ) with a name value of _SC_THREAD_THREADS_MAX as shown below:
#define _POSIX_C_SOURCE 200112L
#include <limits.h>
#include <unistd.h>
#include <stdio.h>
int main( int argc, char ** argv )
{
long x = sysconf( _SC_THREAD_THREADS_MAX );
printf( "%li\n", x );
return 0;
}
In this case the value returned by the sysconf( ) call may be negative one (-1) instead of the expected positive long value. If a -1 is returned, this means that there is "technically" no limit to the number of simultaneous threads.
Even though _PTHREAD_THREADS_MAX or sysconf( _SC_THREAD_THREADS_MAX ) may return an extremely large value, this value indicates only the maximum number of threads the system could handle with unlimited memory and resources. The actual number of maximum threads allowed is really limited by the available system resources and these values should only be used as a guideline.
The pthread_create( ) function will return EAGAIN if you are attempting to create more simultaneous threads than allowed by PTHREAD_THREADS_MAX or if it runs out of resources. Since we are dealing with a situation where we have not yet hit the max threads limit, lets consider the following scenarios:
a) An application (or an implementation) that specifies that all created threads being given a system level scope. In such a situation, the threads must be handled by the kernel instead of a second stage scheduler. The maximum count may be based on the number of simultaneous threads the second stage scheduler can handle and it may be able to handle more entities than the kernel scheduler.
b) An application that specifies that all created threads are joinable (which is, after all, the default). If a thread is joinable then additional information, and as a result additional memory, must be maintained by the system for each thread. The added resource consumption may be causing your application thread max limit to be lower than the given maximum.
c) An application that specifies a custom stack size, larger than the default. As with the situation outline in (b), the additional resource consumption may skew the real maximum to a lower value than the system indicates.
The point is (again) that the implementation max values are often (very) optimistic and you may only be able to create that number under 100% ideal conditions. Because of this you should only use the values as a guide and nothing more.
A thread attribute is nothing more than an indicator of some aspect concerning the behavior of a thread. The thread attributes specified by the current POSIX standard are:
While a few thread attributes may be manipulated directly using a function which takes a thread id, more often you will probably find yourself using a pthread_attr_t type variable during thread creation. Creating and initializing a pthread_attr_t type variable is covered in the here and in section 5.
A thread attribute variable is of type pthread_attr_t so one simply includes lines similar to:
pthread_attr_t [SOME_VARIABLE_NAME];
The attribute variable is NOT yet usable. You must initialize it by calling the function:
int pthread_attr_init ( pthread_attr_t * attr );
This initialization MUST be performed prior to the pthread_attr_t variable’s first use. This function allocates the necessary resources for the thread attribute object and fills it’s interior fields with the default attributes used during thread creation.
Since memory may be allocated to a pthread_attr_t type variable, you must free that memory using the function described in section 4.9 or risk a memory leak in you're program.
When a thread attribute object is initialized, it is (or more precisely, may have been) allocated system resources. When the attribute object is no longer needed, these resources should always be freed by performing the function call:
int pthread_attr_destroy ( pthread_attr_t * attr );
Once the attribute is "destroyed", you must re-initialize via a call to pthread_attr_init( ) to re-use in any manner. To do otherwise is to ask for (at a minimum) unpredictable behavior.
A thread may be identified by calling the pthread_self() function defined in the pthread.h header.
The pthread_self() function prototype is:
pthread_t pthread_self ( void );
The returned pthread_t will uniquely identify the thread among those currently in existence in the process and will be equivalent to the pthread_t object initialized when pthread_create() was called to start the thread.
Now for a word of warning: there is NO guarantee that a specific thread id will not be re-used once a thread with that id has finished execution and is cleaned up. In other words, unless you are careful, it is possible to call this function and retrieve an id and think you are dealing with a thread that has actually already exited.
I have often seen code which compares threads using their pthread_t objects directly. This is incorrect since even though many systems will define pthread_t as a simple numeric value, POSIX does not in fact specify a required type for pthread_t and says that it could actually be a structure which would make a direct == comparison invalid.
Instead the function pthread_equal() should be used if comparison of two pthread_t variables are desired.
The pthread_equal() function is defined in pthread.h and has the following prototype:
int pthread_equal ( pthread_t t1, pthread_t t2 );
Where the parameters t1 and t2 are the two VALID pthread_t objects to compare for equality. If either t1 or t2 are not valid pthread_t objects then undefined behavior may result on certain systems.
If the t1 and t2 are equal then the function will return a non-zero value, while if they are not equal a zero (0) will be returned.
When a thread is joinable, it indicates that other threads may wait for it to complete execution by making a call to the pthread_join( ) function. Not only that, but any system resources allocated to the thread during creation will not be freed until some other thread successfully calls pthread_join( ) on thread or the thread is detached using attribute settings or by making a call to pthread_detach( ).
Threads are, by default, created in a joinable state.
If you want to make certain that your system is really creating a joinable thread then you could explicitly set it during thread creation by using a thread attribute and the function pthread_attr_setdetachstate.
The function pthread_attr_setdetachstate is defined as:
int pthread_attr_setdetachstate ( pthread_attr_t * attr, int detachstate );
Where attr is a previously initialized pthread attribute and the detachstate parameter should be set to PTHREAD_CREATE_JOINABLE.
A short code example with no error checking is:
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <pthread.h>
void * start
(
void * arg
)
{
printf( "I am a thread!\n" );
return NULL;
}
int main( int argc, char ** argv )
{
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init( & attr );
pthread_attr_setdetachstate( & attr, PTHREAD_CREATE_JOINABLE );
pthread_create( & thread, & attr, & start, NULL );
pthread_join( thread, NULL );
return 0;
}
Unfortunately, once a thread has had its state changed from joinable to anything else you cannot revert it back to a joinable state.
The only other currently valid state is detached which is covered in another section.
In a prior we stated that the resources allocated to a thread which is joinable are not released until another thread joins it or it is detached. If our desire is to join a thread then that is accomplished using the obviously named function pthread_join. This function suspends execution of the calling thread until the target thread terminates. In the case where the target thread has already terminated, the function will return immediately.
The pthread_join() function has the following prototype:
int pthread_join ( pthread_t thread, void ** value_ptr );
Where the parameters are specified as:
thread: the target thread the calling thread wants to join.
value_ptr: a NULL value or if non-null then the value returned by the terminating thread's start function or passed by it to pthread_exit() will be made available here.
It is possible for the pthread_join() call to fail and return one of the following non-zero error codes:
ESRCH: The given thread parameter is invalid.
EINVAL: The implementation has detected that the value specified by thread does not refer to a joinable thread. All threads are created as joinable by default but may be detached through the manipulation of thread attributes or a call to pthread_detach function.
EDEADLK: A deadlock condition was detected or the value of thread specifies the same thread as the one which called the function.
Under almost every circumstance you should either join or detach, as described in the next sections, all threads to make certain that their allocated memory is freed and that terminated threads are removed from the current active thread count.
If you don't want to tie up a thread simply to wait for other threads to exit and don't care about their return value you can put the threads into a detached state. A thread in a detached state will have its allocated resources cleaned up automatically without requiring a call by some other thread to pthread_join.
Threads are, by default, created in a joinable state. To force the creation of a thread in a detached state you must use a thread attribute and the function pthread_attr_setdetachstate.
The function pthread_attr_setdetachstate is defined as:
int pthread_attr_setdetachstate ( pthread_attr_t * attr, int detachstate );
Where attr is a previously initialized pthread attribute and the detachstate parameter should be set to PTHREAD_CREATE_DETACHED.
A short code example with no error checking is:
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <pthread.h>
#include <sys/select.h>
void * start
(
void * arg
)
{
printf( "I am a thread!\n" );
return NULL;
}
int main( int argc, char ** argv )
{
pthread_t thread;
pthread_attr_t attr;
struct timeval timeout;
pthread_attr_init( & attr );
pthread_attr_setdetachstate( & attr, PTHREAD_CREATE_DETACHED );
pthread_create( & thread, & attr, & start, NULL );
timeout.tv_sec = 3;
timeout.tv_usec = 0;
select( 0, NULL, NULL, NULL, & timeout );
return 0;
}
One item of note about the example is that we use the select function but not for it's normal purpose. Here we use it as a timer to make the parent do the equivalent of sleep for 3 seconds prior to termination. Select is used instead of the normal sleep function because POSIX allows sleep to utilize signals and signals act very differently in threaded programs.
The reason for performing the short pause is required is that otherwise the parent may exit prior to the created thread finishing execution. If this were to happen then the created thread would also be immediately terminated and never output the intended message.
An existing thread is detached using the function pthread_detach. The prototype for pthread_detach is:
int pthread_detach
(
pthread_t thread
);
Where the parameters are specified as:
thread: the thread to detach.
It is possible for the pthread_detach() call to fail and return one of the following non-zero error codes:
ESRCH: The given thread parameter is invalid.
EINVAL: The specified thread does not refer to a joinable thread e.g. the thread has previously been detached.
Threads may also be created in a detached state using thread attributes as discussed in the previous section.
Under almost every circumstance you should either detach or join all threads to make certain that their allocated memory is freed and that terminated threads are removed from the current active thread count.
There is no way to directly query an existing thread to determine whether it is joinable or detached even though you may encounter code such as the following:
int is_detached( )
{
if ( EDEADLK == pthread_join( pthread_self( ) )
{
return 1;
}
/* Result of pthread_join was EINVAL == detached thread */
return 0;
}
The idea is that if the thread is joinable and tries to join itself, then the deadlock condition would be indicated by pthread_join( ) call’s return value. There is one slight problem though. Even though most pthread implemenations follow this behavior the specification states only that the call MAY fail with EDEADLK under such conditions provides no guarantee.
So if you want to use this type of method you should make certain the target platform exhibits the desired behavior. Also understand that you may be introducing cross platform compatability issues.
On a more positive note, the detach state of a thread can be reliably obtained by querying the thread attribute object which was used during the creation of the thread as long as the attribute has not been re-used / modified.
The function defined to discover the Detach State is:
int pthread_attr_getdetachstate ( const pthread_attr_t * attr, int * detachstate );
Where the current detach state held in the thread attribute object attr is written to the integer location specified by the argument detachstate.
On success (a return value of 0), detach state will contain either the value PTHREAD_CREATE_JOINABLE or PTHREAD_CREATE_DETACHED; both of which are self explanatory.
A call to this function should never return an error code though unpredictable behavior should be expected if either supplied pointer is invalid.
The answer I have seen most often to this question is to perform a call the function:
int pthread_kill ( pthread_t thread, int sig );
Where the parameter thread is given the ID of the thread you want to check and sig is a value of 0. If a thread with the given ID is alive, the function will return a 0 and if it is "dead" (no longer exists) it will return an error value of ESRCH.
There is one issue about using this functionality and that is the fact that the POSIX specification allows thread IDs to be re-used. This can result in the predicament where your code is attempting to check a particular thread (by ID) and the thread has exited but the pthread_kill( ) function returns valid (a value of 0) because the ID your code has possession of has already been re-used.
The end result is that it is very hard, if not impossible, to track the status of a thread in a reliable using just the POSIX threading API. If your application requires this type of functionality, it may be better for you to implement a custom mechanism directly within your code.
In the simplest terms, these functions allow access to and manipulation of the level of mapping from upper level threads to lower kernel level entities; those entities being known as kernel threads, light weight processes and a variety of other names depending on their actual implementation.
Without going into detailed explanations or pointing fingers, I will simply state that some second level scheduler implementations do NOT create / assign enough lower level entities. The result is that you may, with the default setup, see behavior where your application's main( ) thread executes but none of it’s child threads is ever scheduled any CPU time.
If you run into this issue you might try adding the following to your code with the pthread_setconcurrency( ) function called prior to the creation of any threads:
/** Defining _XOPEN_SOURCE 600 exposes the concurrency functions **/ #define _XOPEN_SOURCE 600 #define _POSIX_C_SOURCE 200112L ... pthread_setconcurrency( VALUE_GT_ONE );
There is a pthread_getconcurrency( ) function to retrieve the current concurrency level. The problem with retrieving the concurrency level is that a call to pthread_getconcurrency( ) will return zero unless you have previously set the concurrency level. This means there is no way to really retrieve the default concurrency level of the system on which your application is executing.
I have also heard the suggestion that instead of setting the concurrency level, you could simply set all threads to PTHREAD_SCOPE_SYSTEM level using pthread attributes. The rationale being that if the thread is being scheduled at system scope, you should be by-passing the second level scheduler and concurrency would be assured. The problem with this suggestion is that the POSIX specification says an implementation must support process or system scope but not necessarily both, making it unviable if you want true cross platform compatibility.
By giving up the processor, we mean that the currently executed thread will release it so that for an application with multiple threads, the next thread in the queue can begin executing. For this functionality, the following POSIX function prototype is provided in the sched.h header:
int sched_yield ( void );
Normally the scheduler will take care of the details for you and calling sched_yield( ) will not be necessary. There are some "special" cases where you may want to try sprinkling it in your code. One such case, as the title of this question indicates, might be when you have a thread which is 100% or even just heavily CPU bound. In a CPU bound thread the sched_yield( ) call would indicate or force a break to the scheduler which can be used to switch to another thread, much in the same way that an IO bound thread provides such visible breaks during blocking IO calls.
There is another personal warning concerning the use of this function. Remember that if a thread has it’s scope set to the system level then that means it is being scheduled by the kernel along with other processes. If that thread yields it is just as likely on many implementations for another process to get the CPU instead of another thread in the caller’s process.
Here are the basic reasons why you might have segfaults or odd behavior when using calls to the sleep() function inside threaded code:
* The sleep function is allowed to use signals and quite simply, signals act much differently in threaded programs as compared to multi-threaded programs.
* If your system uses SIGALRM or similar, they may be a limit to the total number of pending signals queued of that type e.g. 8. Exceeding this could cause a crash, a signal to be lost so that a thread is never woken or other strange behavior.
There are many solutions to this but the one I normally use since it seems to work on ANY system which also has the BSD networking core proper or any derivative of it is calling the select function with all null FD_SETs.
For example in the follow code the call to select( ) would cause the main( ) code block to sleep for 3 seconds:
#define _POSIX_C_SOURCE 200112L
#include <sys/select.h>
int main( int argc, char ** argv )
{
struct timeval timeout;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
select( 0, NULL, NULL, NULL, & timeout );
return 0;
}
This is certainly not the only solution. You could use nanosleep() if you knew all the systems supported the REALTIME extension just to name one.
See "4.24 Why does threaded code sometimes segfault when using sleep( )?" for a related discussion on sleep, it's potential use of signals and likely results.
Another way to handle a heavily CPU bound thread is to modify it's priority level instead of constantly making calls to the sched_yield( ) function. You can modify the priority of an existing thread if the system defines _POSIX_THREAD_PRIORITY_SCHEDULING to 200112L in the unistd.h header file.
The function to set thread priority is:
int pthread_setschedprio ( pthread_t thread, int prio );
Where the thread parameter is the ID of the thread who's priority you wish to modify and the parameter prio is the new priority level.
This function returns 0 on success and:
ESRCH if the ID specified by the thread parameter does not refer to an existing thread.
EINVAL if the value of the prio parameter is invalid for the scheduling policy of the specified thread.
ENOTSUP if an attempt was made to set the priority to an unsupported value.
EPERM if the implementation does not allow the application to modify the priority to the value specified.
EPERM if the caller does not have the appropriate permission to set the scheduling policy of the specified thread.
Setting and getting priority values during thread creation is covered in the pthread attributes section.
It is a common situation where you will set the priority of a thread only to notice that is no longer at the specified value after a period of time. The reason for this is that many operating systems and thread implementations actually modify the priority of a thread during its lifetime, with IO bound threads slowly getting their priority raised and CPU bound threads getting their priority lowered.
This may not make sense at first glance but consider that a truly IO bound thread is using very little CPU time BUT quite often wants its operations completed quickly when there is data available for receipt or transmission. One such case would be an interactive program with one thread handling user IO such as keyboard entry; we do not want the to hazard the risk that the IO bound process has too low of a priority and it taking seconds (or longer) for the data to be processed. To avoid such a risk of partial "starvation", IO bound threads (and processes for that matter) will often have their priorities raised over a period of time. Note that as long as the IO is sporadic, this automatic raising of priority will not normally degrade the execution of the CPU bound threads.
First off, this should NOT occur but does with some implementations where pthreads exist almost entirely if not completely in user space. In such cases the implementations in question do not have full blown second level schedulers for threads but instead rely on the priority of the parent process. This means that when you change the priority of a thread you are in reality modifying the priority of it’s parent process. When the parent process has it’s priority modified that modification is then applied to all of the threads which the parent has spawned.
Probably the OS most well known for this problem, even though there are a number of others which have exhibited the same problem, is older versions of BSD. If you are using say FreeBSD 4.7 or lower and you want to control the priority of individual threads then your best bet is to update to a new release. Failing that, try using a third party threading library like LinuxThreads instead. Of course, most of the older third party libraries have their own set of issues.
Probably the easiest way to explain race conditions is to give a very simple example of one. Imagine you have a threaded program with the following function:
static int i = 0;
void some_func( )
{
++ i;
if ( 2 == i )
{
printf( "some message..." );
}
}
Now take two threads, we will call them A and B, entering the function and executing in the following sequence:
If we are used to single threaded programming that called the function twice we would expect one and only one message to be output to the user. Instead when using threading we see that the potential exists for 2 ( or more ) messages to be output. This unexpected behavior when using threads would be considered a race condition.
Race conditions like the one shown above can happen because threads execute in an indeterminate manner and for all intent and purpose "simultaneously". The solution is to use proper synchronization on code where obtaining the proper result from an operation(s) requires that it be performed by a single thread at a time.
Synchronization and exclusive access methods are covered in more detail in the section on pthread mutexes.
Yes you can since, by either planning or happy coincidence, the internal signature of a static class function is ( usually ) the same as the signature of a normal C function. So that does result in one restriction which is that the class function you are going to use is required to be declared as static.
A short example is:
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <pthread.h>
class Foo
{
public:
static void * start( void * arg )
{
printf( "I am a thread!\n" );
// We don't have a this so still need to create
// a Foo object to manipulate here!!!
Foo f;
// etc. ...
return NULL;
}
};
int main( int argc, char ** argv )
{
pthread_t thread;
( void ) pthread_create( & thread, NULL, & Foo::start, NULL );
pthread_join( thread, NULL );
return 0;
}
Many people feel that "if you can do it, then do it" but my own thoughts are it is just as easy to use a regular C function for the thread then have that function create any required classes. The reason for this is I feel why take the risk that some compiler out there does not represent a static C++ class function the same as a C function and breaks my code.
This is another common problem with some pthread implementations which exist in user space libraries instead of in the kernel. The only real solution is the same one that was given when discussing the issue of setting individual thread priorites for a user space library. That solution is to update the version of your OS to one which implements these calls properly or to use a third party library.
There really are not any other options...
There is a difference between using exit( ) to end a process and pthread_exit( ) to exit from a thread in a process. In both cases no clean up is performed and in the former case this doesn't matter since on exit the system will free all memory that has been allocated to the program. On the other hand, when doing a pthread_exit the main process continues to exist and the memory assigned to the thread remains allocated and becomes a memory leak.
So it should be clear that you need to manually free memory allocated on the heap for a C program prior to calling doing a pthread_exit. What may not be as obvious is what happens when you are dealing with complex C++ objects such as those from STL class libraries allocated on the stack. Doing a pthread_exit will mean that the object destructors won't be called and again memory will be leaked. Under these circumstances you will be required to manually call the destructors for the stack objects prior to using pthread_exit.
Due to the protential of accidentally forgetting to free / destroy memory I would suggest you avoid using pthread_exit unless it is absolutely necessary or you are certain there is no allocated heap memory or complex objects. Instead I think the best would be to simply write your code using flags or a similar mechanism so that the path of execution unwinds in an orderly manner back to the thread’s start function and return from there. If you do this the system will automatically perform all clean up for you and it will be very hard to accidentally introduce a memory leak.
Here are a few items to check before you panic:
a) Make certain you are actually calling pthread_create somewhere in your code and that it is returning a zero (success).
b) During compilation, make certain you are telling the compiler to include the pthread library, for example: gcc -lpthread source_name.c. Some systems / compilers will gladly say the code is compiled and complete when in fact it has replaced the pthread calls with NOP stubs.
c) Remember that when the process (your main( ) function) exits, all threads created in the process are destroyed. Try inserting a test call to select() or a similar call in your main function prior to where it exits. If the thread(s) now execute properly before the program terminates, make (more) appropriate modifications like doing pthread_join calls.
d) You system may require explicit setup of thread concurrency or your threads may be highly CPU bound and require periodic yielding. Try adding the line:
#define _XOPEN_SOURCE 600
at the top of your source code prior to the inclusion of any headers and then, at the top of your main( ) function immediately after your variable declarations add:
( void ) pthread_setconcurrency( int );
Where the argument in is equal 2 or greater. The first line says to expose the X/Open extensions to your application, which is where pthread_setconcurrency is defined. The pthread_setconcurrency function call itself provides a hint to the system as to the number of underlying kernel light weight processes your application process requires at a minimum.
e) Get away from the computer and relax for a bit. It may just be temporary code blindness that will be cured by taking a break.