Recently, I heard that pthread_create will cause memory leaks, it is incredible, so the POSIX (NPTL) thread creation and destruction are analyzed.
Analysis results: If used incorrectly, it does cause a memory leak. Root cause: Pthread_create The thread created by default is detached. Prevention: either create a detached thread, or the thread thread start_routine before the end of detached, or join
The analysis process is as follows:
1. View the Pthread_create source code, the core code is as follows (NPTL/PTHREAD_CREATE.C):
Click (here) to collapse or open int
__pthread_create_2_1 (Newthread, attr, Start_routine, Arg)
pthread_t *newthread;
Const pthread_attr_t *ATTR;
void * (*start_routine) (void *);
void *arg;
{
Stack_variables;
const struct PTHREAD_ATTR *iattr = (struct pthread_attr *) attr;
if (iattr = NULL)
* Is this best idea? On NUMA machines This could mean
accessing Far-away memory. */
Iattr = &default_attr;
struct Pthread *pd = NULL;
int err = Allocate_stack (iattr, &PD);//Allocate memory for TCB
if (__builtin_expect (Err!= 0, 0))
/* something went wrong. Maybe a parameter of the attributes is
Invalid or we could not allocate memory. */
return err; ... err = Create_thread (PD, IATTR, Stack_variables_args); formally create thread
2. View CREATETHREAD.C (NPTL/SYSDEPS/PTHREAD/CREATETHREAD.C)
Click (here) to collapse or open the static int
Create_thread (struct pthread *pd, const struct PTHREAD_ATTR,
Stack_variables_parms)
{
#ifdef TLS_TCB_AT_TP
ASSERT (PD->HEADER.TCB!= NULL);
#endif///...
int res = Do_clone (PD, attr, Clone_flags, Start_thread, Stack_variables_args, 1);//clone a process 3. Then look at the start What did _thread (nptl/pthread_create.c) do?
Click (here) to collapse or open the static int
Start_thread (void *arg)
{
struct Pthread *pd = (struct pthread *) arg;
......
/* Run The code the user provided. */
#ifdef CALL_THREAD_FCT
Thread_setmem (PD, result, CALL_THREAD_FCT (PD));
#else
Thread_setmem (PD, result, Pd->start_routine (Pd->arg)); Officially start the execution of the thread and wait for execution to complete
#endif//... if (is_detached (PD))/* Free the TCB. //__FREE_TCB (PD);//If the detached flag is set, the content occupied by the TCB is released, otherwise it is returned directly to else if (__builtin_expect pd->cancelhandling & Setxid _bitmask, 0)) {/* Some other thread might-call all of the SETXID functions and expect us to reply. In the case wait until we did that. * * Do lll_futex_wait (&pd->setxid_futex, 0, lll_private); while (Pd->cancelhandling & Setxid_bitmask);
* Reset The value so this stack can be reused. * * Pd->setxid_futex = 0; From the above procedure, we can see that if the detached flag is not set when the thread is created, TCB memory will never be released
Next, let's look at what Pthread_detach (NPTH/PTHREAD_DETACH.C) did.
Click (here) to collapse or open int
Pthread_detach (TH)
pthread_t th;
{
struct Pthread *pd = (struct pthread *) th;
/* Make sure the descriptor is valid. */
if (Invalid_not_terminated_td_p (PD))
/* Not a valid thread handle. */
return esrch;
int result = 0;
* Mark the thread as detached. */
if (Atomic_compare_and_exchange_bool_acq (&pd->joinid, PD, NULL))
{
/* There are two possibilities here. The thread might
Already be detached. We return einval.
Otherwise there might already be a waiter. The standard does
Not mention what happens in the this case. */
if (is_detached (PD))
result = Einval;
}
Else
/* Check Whether the thread terminated meanwhile. In the case we
Would just free the TCB. */
if ((Pd->cancelhandling & Exiting_bitmask)!= 0)
/* Note This code in __FREE_TCB makes sure each thread
Control's freed only once. */
__FREE_TCB (PD);//through a series of fault-tolerant judgment, direct release TCB occupied memory
return result;
Finally, let's take a look at what Pthread_join (NPTL/PTHREAD_JOIN.C) did
Click (here) to collapse or open int
pthread_join (threadid, thread_return)
pthread _t ThreadID;
void **thread_return;
{
struct pthread *pd = (struct pthread *) threadid;
/* make sure the descriptor is valid. */
if (invalid_not_ terminated_td_p (PD))
/* not a valid thread handle. */
return Esrch