lvella
lvella

Reputation: 13413

Race condition on pthread_kill()?

Linux manual for pthread_kill() has the following paragraph:

POSIX.1-2008 recommends that if an implementation detects the use of a thread ID after the end of its lifetime, pthread_kill() should return the error ESRCH. The glibc implementation returns this error in the cases where an invalid thread ID can be detected. But note also that POSIX says that an attempt to use a thread ID whose lifetime has ended produces undefined behavior, and an attempt to use an invalid thread ID in a call to pthread_kill() can, for example, cause a segmentation fault.

The problem is, between checking the thread ID is valid and issuing the pthread_kill(), the thread might have terminated. Is it inherently unsafe to use pthread_kill(), as there is always a race condition that can turn into a undefined behavior?

How to ensure thread ID will be valid?

Upvotes: 1

Views: 392

Answers (2)

pilcrow
pilcrow

Reputation: 58524

How to ensure thread ID will be valid?

You must redesign so that your code knows this a priori.* Anything short of that is a TOCTOU race (CWE-367).

Fortunately, there's a lot of prior art from interprocess killing. Interprocess signaling doesn't run the terrifying risk of undefined behavior like pthread_kill does, but careful coders consider the risk of signaling a recycled PID just as unacceptable. (And thread IDs can be recycled, too.)

* OK, you could do it by checking some contrived state. For example, set a mutex-protected i_am_still_running flag to false at the very, very end of your thread routine. Then only pthread_kill that thread while holding the mutex and confirming that it is still running. Yuck.

Upvotes: 1

KamilCuk
KamilCuk

Reputation: 140900

Race condition on pthread_kill()?

When the thread is detached, always. But if thread ID is valid, no.

Is it inherently unsafe to use pthread_kill(), as there is always a race condition that can turn into a undefined behavior?

No, not always.

How to ensure thread ID will be valid?

From POSIX thread ID:

The lifetime of a thread ID ends after the thread terminates if it was created with the detachstate attribute set to PTHREAD_CREATE_DETACHED or if pthread_detach() or pthread_join() has been called for that thread.

Otherwise it's valid. So when the thread is not detached nor joined, thread ID is just valid and you always at any time can call pthread_kill() with it.

Generally, you should stop using a thread ID after pthread_detach or pthread_join. It's like free() in malloc() - you can't use memory allocated by malloc() after free(). The same way you can't use thread ID after detaching or joining, the thread ID just becomes invalid. Just with pthread_detach it becomes invalid "later", but you don't know when, so you can't use it anyway (well, unless you write your own synchronization). It might become invalid right after the call to pthread_detach. If you intent to do anything with a thread ID, do not detach and do not join it.

The call to pthread_kill with "inactive thread" (non-detached non-joined thread that terminated) is valid - the thread ID is still valid. We can read from pthread_kill posix:

Existing implementations vary on the result of a pthread_kill() with a thread ID indicating an inactive thread (a terminated thread that has not been detached or joined). Some indicate success on such a call, while others give an error of [ESRCH]. Since the definition of thread lifetime in this volume of POSIX.1-2017 covers inactive threads, the [ESRCH] error as described is inappropriate in this case. In particular, this means that an application cannot have one thread check for termination of another with pthread_kill().

FUTURE DIRECTIONS

A future version of this standard may require that pthread_kill() not fail with [ESRCH] in the case of sending signals to an inactive thread (a terminated thread not yet detached or joined), even though no signal will be delivered because the thread is no longer running.

The FUTURE DIRECTIONS looks like it's prefering that pthread_kill() with an inactive thread should just succeed and return 0. I personally like the ESRCH error in such case.

Upvotes: 1

Related Questions