Tech Support > Operating Systems > Linux / Variants > Leak in pthread_create() -> pthead_cancel()
Leak in pthread_create() -> pthead_cancel()
Posted by Nick Albion on August 8th, 2003


From the output of the program below, it seems that there might be a
leak in the pthreads library.
On each iteration, the thread "th" has a different adddress. I wonder
what happens after several hours?

Is there something that I've missed out of the initialisation of the
thread?
Do I have to call pthread_join() after pthread_cancel()?
Do I have to manually "free(th)"?

/************************************************** */
#include <sys/poll.h>
#include <pthread.h>

void test_pthreads( void )
{
printf( "hello " );
poll( NULL, NULL, 1000 );
}

int main( int argc, char **argv )
{
pthread_t th;
int i;

for( i = 0; i < 5; i++ )
{
pthread_create( &th, NULL, (void*)&test_pthreads, NULL );
printf( "Thread = 0x%X\n", th );
poll( NULL, NULL, 100 );
pthread_cancel( th );
}
}

Posted by Eric Sosman on August 8th, 2003


Nick Albion wrote:
You've created them with default thread attributes,
meaning that they're created as joinable and not as
detached.

Unless they've detached themselves after starting, yes.
Remember, pthread_cancel() doesn't cancel a thread "now,"
it cancels it when the next cancellation point is reached.

Absolutely not: it's an `auto' variable, not a piece
of dynamic memory obtained from malloc() and friends.

--
Eric.Sosman@sun.com

Posted by Nick Albion on August 11th, 2003


I've tried several combinations & permutations using the functions you
recommend:

pthread_create( &th, .... );
pthread_detach( th );


pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
pthread_create( &th, &attr, .... );


void myFunc(void) { pthread_detach( pthread_self() ); ... }
pthread_create( &th, ...., &myFunc, NULL );


pthread_cancel( th );
pthread_join( th );


None of these are working for me. Could you please provide a simple
example that is known to work?

Thanks,

Nick


Eric Sosman <Eric.Sosman@sun.com> wrote in message news:<3F33E650.ED395346@sun.com>...

Posted by dtak on August 11th, 2003


Eric Sosman wrote:
his job you want to have.


#include <sys/poll.h>
#include <pthread.h>

void test_pthreads( void )
{
printf( "hello\n" );
poll( NULL, NULL, 1000 );
}

int main( int argc, char **argv )
{
pthread_t th[5]; /* You need to build an array to register the newly
created threads */
int i;

for( i = 0; i < 5; i++ )
{
pthread_create( &th[i], NULL, (void*)&test_pthreads, NULL);
printf( "Thread = 0x%X\n", th );
poll( NULL, NULL, 100 );
}
}


Posted by Eric Sosman on August 11th, 2003


Nick Albion wrote:
First, I apologize for reading your original message
too hastily. What I wrote about it was correct, but far
from complete. There are other errors and misconceptions
that I should have addressed. Further quotes are from
the original message:

The `th' variable is a `pthread_t', that is, an ID for
the thread. It is not an address, but an identifier. On
some systems, your attempt to print it with "%X" won't
work at all; `pthread_t' need not even be a scalar type.
In short, conclusions drawn from inspecting `pthread_t'
values aren't at all portable and are likely to be wrong.
Going from "Different threads have different identifiers"
to "There's a hole in the bucket, dear Liza" is a stretch.

This is not a proper function to be used as the thread's
starting point, that is, this is not a proper function to be
passed as the third argument to pthread_create(). A thread's
starting function takes a single `void*' argument and returns
a `void*' value. Even if you don't actually use the argument
or the returned value, they must be there. Well, "must" is a
little strong, because many compilers will let you get away
with the incorrect code -- but you might also get away with
juggling vials of nitroglycerine. I'd say the chances are
slim that this has anything to do with any anomalous behavior
you might be seeing, but when you're confronted with something
inexplicable it's a good idea to "eliminate variables" by
making the rest of the program squeaky clean.

I'd suggest a newline character. Many systems will buffer
`stdout' output until there's a lot of it or until a complete
line is finished, which could delay the appearance of your
"hello" for quite a long time. Alternatives: fflush(stdout)
or use setvbuf() at the start of the program to make `stdout'
unbuffered.

This is wrong, R-0-N-G wrong, and the compiler should
have complained about it. As mentioned above, the third
argument to pthread_create() must be a function pointer, and
a specific type of function pointer at that. You're taking
a function pointer and converting it to a data pointer, which
is not guaranteed to work at all. Again, it's not likely that
this causes anything to go wrong -- but "squeaky clean in the
face of strangeness" is the principle to follow.

You ask for example code "known to work," but it's not at
all clear to me what you're trying to do: I don't know what
"work" means to you. Suggestion: clean up your code, crank
your compiler's warning levels up to the maximum and clean
up or at least investigate all the complaints, and then decide
whether you've still got a problem of some kind. If so, post
the revised code *and* a clear explanation of what you're seeing
and why you believe it's a problem.

(By the way: Is cross-posting to five groups sufficient?
Consider adding a few dozen more ;-)

--
Eric.Sosman@sun.com

Posted by Nick Albion on August 11th, 2003


Sorry, you've missed my point.

I want to be able to create-use-cancel threads & should therefore
should not have to build a fixed-sized array.

However, I can't figure out how to completely destroy all trace of a
thread after calling pthread_cancel(). When I run my test program,
the value of th changes on each iteration, and after X attempts,
pthread_create() fails.


Posted by Loic Domaigne on August 11th, 2003


Hi Nick!

Eric (Sosman) gave you a bunch of salient advices that are more than
just "good programming practices", especially if you care about
portability. OK, on Linux I do not expect much problems. But, as said
by Eric: "you are juggling vials of nitroglycerine."

However, it seems that your original question hasn't been answered
yet. I shall try to fill the gap now.


Probably ;-)

Why Do you think there is a memory leak in the implementation? Just
because the implementation doesn't reuse the same thread ID? It
doesn't have to.

Consider for instance the process id of a program. Start several times
your program, you will notice that the PID is never the same (it
probably increases, and at a given point, it will wrap-up). Altought
your program has terminated, and that (theoritically) the same PID
could be re-used. But it doesn't mean that your process is still
somewhere in your memory. That's pretty much the same for the thread
ID.

Running your example on an IA32 with LinuxThreads (LT) v0.10, the
thread ID of the first thread is 0x402, the second 0x802,... But in
fact, the thread 0x802 really reuses all the resources that has been
freed when the thread 0x402 got cancelled!!! (If you are really
curious, the formula for the thread ID in this particular case is
2+(i+1)*PTHREAD_THREADS_MAX).

You really should consider the thread ID as an opaque object that just
"puts a name (ID)" to your thread, not more, not less. In fact, for
the implementation, it might be more than just an id. It might derive
from it a lot of information that are needed internally. But these
aspects are hidden to us, Pthread users.


As already explained, you don't have to free the thread resource by
hand (except of course, if you are doing explicit memory allocation in
the thread). However, yes, you might want to detach your thread, in
particular if you are not interested by the returned value. The 2
possibilities are:

- use pthread_detach(). You had already the right solution (up to the
prototype problem):

| void* myFunc(void* arg) { pthread_detach( pthread_self() ); ... }
| pthread_create( &th, ...., &myFunc, NULL );


- setting the thread attribute to PTHREAD_CREATE_DETACHED. Your code
was almost OK, up to one thing: initialize the attribute first!

| pthread_attr_init (&attr);
| pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
| pthread_create( &th, &attr, .... );


Oh yes, one last thing if you are using LT. AFAIK, the poll() is _NOT_
yet implemented as a cancellation point (SUSv3 defines it as mandatory
cancellation point, though). Your code is working with LT, because
poll() gets interrupted.

My last advice: always check for error conditions! It will save you
unpleasant surprises...


HTH,
Loic.

Posted by Patrick TJ McPhee on August 12th, 2003


In article <3c4d10ba.0308110928.3eadb152@posting.google.com>,
Nick Albion <nick.albion@sde.eu.sony.com> wrote:

% I want to be able to create-use-cancel threads & should therefore
% should not have to build a fixed-sized array.

You've got to provide a spot for the pthread_t which persists until
you no longer need the pthread_t. In your case, if you were to
create detached threads or have the thread detach itself, you wouldn't
need an array, you could just put all the pthread_ts in the same
spot.

% However, I can't figure out how to completely destroy all trace of a
% thread after calling pthread_cancel().

pthread_join(). Even that doesn't guarantee that all trace of the thread
is destroyed, but it does guarantee that all of the code in your thread
function has finished running, so you can use the results.

% > for( i = 0; i < 5; i++ )
% > {
% > pthread_create( &th[i], NULL, (void*)&test_pthreads, NULL);
% > printf( "Thread = 0x%X\n", th );
% > poll( NULL, NULL, 100 );
% I want to cancel here.
% > }

One thing about this -- there's no reason to believe that the cancel will
take place before the next pthread_create() is called. The cancellation is
acted on when the target thread is scheduled to run, which is likely
after this thread's timeslice is done. I would expect most systems to
run this loop through to the end before scheduling any of the new
threads. You could put a join at the end of the loop, which will stop
this thread until the new thread has run, but that would be stupid and
pointless.

You have another problem -- once this thread finishes this loop, it
returns from main(). Returning from main() causes the process to stop
running, so it's likely that your back-ground threads will never run
at all.
--

Patrick TJ McPhee
East York Canada
ptjm@interlog.com


Similar Posts