0

I wanted to cancel a thread right before the process de-initialization is done, like the following.

rc2 = pthread_attr_init(&attr);
ERR_IF( rc2 != 0 );

rc2 = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
ERR_IF( rc2 != 0 );

rc2 = pthread_create(&destroy_thread, &attr, destroy_expired_sessions, NULL);
ERR_IF( rc2 != 0 );
...
rc2 = pthread_cancel(destroy_thread);
ERR_IF( rc2 != 0 );

usleep(10); // without the sleep here, the real cancellation will be postponed.

rc2 = authSessionListDeInit();
ERR_IF( rc2 != 0 );

...

static void *destroy_expired_sessions(void *t)
{
    int rc2 = 0;

    (void)t;

    pthread_cleanup_push(cleanup_handler, NULL);

    rc2 = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    if (rc2 != 0)
        AUTH_DEBUG5("pthread_setcancelstate(): rc2 == %d\n", rc2);

    rc2 = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    if (rc2 != 0)
        AUTH_DEBUG5("pthread_setcanceltype(): rc2 == %d\n", rc2);

    ... // real work of the thread is done here
}

The problem is, although PTHREAD_CANCEL_ASYNCHRONOUS is set here, the real thread cancellation always happens after authSessionListDeInit(), unless I force a usleep() in between.

My understanding is the cancellation should happen right after the cancel request is sent through pthread_cancel(), shouldn't it?

If my understanding is not correct, how to ensure the thread gets cancelled before authSessionListDeInit() is called?

Qiang Xu
  • 4,353
  • 8
  • 36
  • 45

2 Answers2

1

From the docs:

The cancellation processing in the target thread shall run asynchronously with respect to the calling thread returning from pthread_cancel().

So pthread_cancel can return before the cancellation happens, meaning your assumption is wrong.

Instead of using pthread_cancel you may consider the more popular and arguably safer method (risk of failure with pthread_cancel) of using a cancel flag and/or condition variables to signal when the function actually ends.

Michael Chinen
  • 17,737
  • 5
  • 33
  • 45
  • 1
    Or you can just wait on a synchronization object (like a semaphore) after calling `pthread_cancel` and have the cancelled thread signal (e.g. post the semaphore) when it's done cleaning up for exit. – R.. GitHub STOP HELPING ICE Mar 17 '12 at 23:20
  • So, it looks that I have no way of knowing when the real thread cancellation will happen if using pthread_cancel(), right? Could you give me an example of using the more popular and safer method to cancel a thread, Michael? – Qiang Xu Mar 17 '12 at 23:25
  • @R..: Yes, a sem_wait() can follow pthread_cancel() to wait till it is sem_post()'ed by the thread cancellation cleanup handler (being pushed into the thread's stack earlier). It would work. Just feel it is a bit overkill to use a semaphore here. :-) – Qiang Xu Mar 17 '12 at 23:30
  • 1
    Well you need *some kind* of synchronization object, and semaphores are the simplest/lowest overhead for wait/signal semantics. The only other remotely simple solution is for the thread to have a "lifetime mutex" it holds and attempt to lock that to wait for it to exit, but that has a race condition if the thread has not yet proceeded far enough into its start function to lock the mutex when you attempt to cancel it. Semaphores are better here because they don't have owners, and thus you can just initialize the sem value to 0 before creating the thread. – R.. GitHub STOP HELPING ICE Mar 17 '12 at 23:37
  • 1
    condition variables are also a viable option here. Along with R's other comments (that I agree with), semaphores are lighter weight in pthreads, but that doesn't seem to be a concern for what you are doing (assuming this deinit code is not looped code) You can see this SO question for discussion on semaphores vs condvars: http://stackoverflow.com/questions/70773/pthread-cond-wait-versus-semaphore – Michael Chinen Mar 17 '12 at 23:48
  • @R.. Quite good reasoning, R.. So, could I understand that pthread_cancel() is total crap if not used jointly with some kind of synchronization mechanism? – Qiang Xu Mar 17 '12 at 23:49
  • The link you provided is very informative. I'll give a careful look at it. Thanks a lot, Michael! – Qiang Xu Mar 17 '12 at 23:58
  • 1
    @QiangXu: Creating a thread that's doing a task but with no way of synchronizing its result is pretty much always useless. By the way, another solution if the thread is not detached and there's not already somebody trying to join it is to call `pthread_join` after `pthread_cancel`... – R.. GitHub STOP HELPING ICE Mar 18 '12 at 00:11
  • @R.. If I don't set this attribute, would a thread be created detached or non-detached? By the way, which one is better? This is a little new to me. Thanks! – Qiang Xu Mar 18 '12 at 00:35
  • @R.. So far, creating the thread as JOINABLE, and following pthread_cancel() with a pthread_join() seems to be the simplest solution. Thanks a lot, Mr. R! – Qiang Xu Mar 18 '12 at 00:59
  • 1
    Joinable is the default; you only need a special attribute for detached. And when you do want detached, often it's easier to just write `pthread_detach(pthread_self());` in the start function instead of making an attribute just for the detached flag. – R.. GitHub STOP HELPING ICE Mar 18 '12 at 01:00
  • @R.. Oops. so many things to learn in pthread. It is really a delicate area and easily mistaken. You seem to be a walking dictionary in this area, Mr. R! Thanks a lot for your help (and Michael's). – Qiang Xu Mar 18 '12 at 01:09
  • Writing an implementation of pthread sort of forces you to learn it inside out... :-) – R.. GitHub STOP HELPING ICE Mar 18 '12 at 01:13
  • @R.. Wow, it is hard enough for me to use pthread library, just can't imagine how you make it in writing it up. Awesome! – Qiang Xu Mar 18 '12 at 01:30
0

No, cancellation occures in cancellation points, eg. system calls, libc calls - look at the documentation of functions (e.g. accept(), close()) or you can look here.

Community
  • 1
  • 1
sirgeorge
  • 6,331
  • 1
  • 28
  • 33
  • What you said is true if PTHREAD_CANCEL_DEFERRED is set to the cancellation type, but here I am setting the type to be PTHREAD_CANCEL_ASYNCHRONOUS, which means the thread can be canceled at any time (typically, it will be canceled immediately upon receiving a cancellation request, but the system doesn't guarantee this). – Qiang Xu Mar 17 '12 at 23:34