15

I'm wondering if it is safe to call pthread_cancel() on a terminated thread. I couldn't find any hints in the manual page. Thanks in advance for any hints.

Edit: Maybe I wasn't accurate enough. I'm not talking about threads terminated by an earlier pthread_cancel() but about threads that simply returned from their thread function.

domachine
  • 1,119
  • 1
  • 12
  • 20

4 Answers4

12

I think it needs to be safe, or pthread_cancel would be problematic (next to unusable).

Indeed, if it wouldn't be safe, every call to pthread_cancel would have to be enormously complicated by checking the thread is alive (and ensuring it stays alive until you get to cancel it). A simple "are you still there" wouldn't do.

In conclusion, I believe pthread_cancel must be safe if the thread has terminated. Of course, this might not be the case for a terminated and joined thread.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • 8
    Simple rule: A pthread_t is valid until the thread terminates (if it is detached) or is joined (if it is not). This means you can safely pthread_cancel a detached thread only if you know it has not terminated. You can safely pthread_cancel a non-detached thread so long as you know it has not been pthread_join'ed. – David Schwartz Aug 30 '11 at 23:06
8

There is a hint actually:

ERRORS top

  ESRCH  No thread with the ID thread could be found.

And MKS gives a bit other description:

ESRCH

thread does not specify a currently running thread in the process.

OpenGroup recommends:

If an implementation detects use of a thread ID after the end of its lifetime, it is recommended that the function should fail and report an [ESRCH] error.

UPDATE

in NPTL there is a check for double cancel or cancel after exit:

  /* We are canceled now.  When canceled by another thread this flag
     is already set but if the signal is directly send (internally or
     from another process) is has to be done here.  */
  int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK;

  if (oldval == newval || (oldval & EXITING_BITMASK) != 0)
    /* Already canceled or exiting.  */
    break;

So, second cancel or cancel after exit will be a noop for dead thread.

The exiting_bitmask is set by __do_cancel:

/* Called when a thread reacts on a cancellation request.  */
static inline void
__attribute ((noreturn, always_inline))
__do_cancel (void)
{
  struct pthread *self = THREAD_SELF;

  /* Make sure we get no more cancellations.  */
  THREAD_ATOMIC_BIT_SET (self, cancelhandling, EXITING_BIT);

__do_cancel is both reaction to async cancel and to pthread_exit

void
__pthread_exit (value)
     void *value;
{
  THREAD_SETMEM (THREAD_SELF, result, value);

  __do_cancel ();
osgx
  • 90,338
  • 53
  • 357
  • 513
  • 2
    Yes, but even if the thread terminated, `pthread_join` might be called, so **when a thread ends its ID remains valid**. – cnicutar Aug 29 '11 at 20:31
  • I think the question is how the mentioned lifetime is defined. Does it specifiy the time until the thread terminates or does it mean the time until pthread_join() is called? – domachine Aug 29 '11 at 20:40
  • 2
    domachine, the link from [this answer](http://stackoverflow.com/questions/6371844/calling-pthread-cancel-on-a-joined-thread-causes-segfault-under-linux/6373108#6373108) is to [standard IEEE Std 1003.1-2008](http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_02) which says: 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. – osgx Aug 29 '11 at 20:51
  • Also it says: "A conforming implementation is free to reuse a thread ID after its lifetime has ended. If an application attempts to use a thread ID whose lifetime has ended, the behavior is undefined.". So you can't call cancel after join. But you can send 100 cancel signals before join. – osgx Aug 29 '11 at 20:52
  • And the cancellation state after pthread_exit is PTHREAD_CANCEL_DISABLE: "When a cancellation request is acted upon, or when a thread calls pthread_exit(), the thread first disables cancellation by setting its cancelability state to PTHREAD_CANCEL_DISABLE and its cancelability type to PTHREAD_CANCEL_DEFERRED. " – osgx Aug 29 '11 at 20:54
  • You can't call cancel after join but you probably would call join after cancel as that's the only way to tell if the thread was actually canceled. – celavek Aug 29 '11 at 21:06
  • @celavek: Yes, but that's not the problem. The problem is if it is secure to call pthread_cancel on a thread that has not been terminated via pthread_cancel but has terminated by for example returning from the thread function. – domachine Aug 29 '11 at 21:10
  • 2
    @domachine yes, I know that. I was just making a remark. It's a good question with very informative answers. – celavek Aug 29 '11 at 21:22
  • 2
    Despite whatever the documentation may say about lifetimes and valid thread ids, on my plain Debian Wheezy system, calling `pthread_cancel` on a terminated thread results in a return value of -1 and `errno` set to `ESRCH`. – Craig M. Brandenburg Oct 22 '14 at 17:06
2

No, it isn't safe to call pthread_cancel() on a thread that has been terminated.

The reason is that the implementation may reuse the thread ID of the terminated thread for another thread and calling "pthread_cancel()" on that TID may trigger the termination on threads that shouldn't be terminated or undefined behaviour.

Kariddi
  • 62
  • 1
  • 2
1

Yes, it is safe to call it before pthread_detach or pthread_join has been called. However, according to my experiments, it can return ESRCH (=3) if the thread has already returned from its thread function. (gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0)

#include "gtest/gtest.h"

static void *S_ThreadEntry1(void* pvarg)
{
     return 0;
}

TEST(ThreadTest, PthreadCancelAfterExit)
{
     pthread_t threadid;
     ASSERT_EQ(pthread_create(&threadid, 0, &S_ThreadEntry1, nullptr), 0);
     sleep(1);

     int cancel_ret = pthread_cancel(threadid);
     std::cout << "pthread_cancel returned " << cancel_ret << std::endl;
     if (cancel_ret != 0)
          EXPECT_EQ(cancel_ret, ESRCH);
     void* res;
     EXPECT_EQ(pthread_join(threadid, &res), 0);
     std::cout << "res=" << res << std::endl;
}

Output:

[ RUN      ] ThreadTest.PthreadCancelAfterExit
pthread_cancel returned 3
res=0
[       OK ] ThreadTest.PthreadCancelAfterExit (1000 ms)