29

I have a C++ program on Linux (CentOS 5.3) spawning multiple threads which are in an infinite loop to perform a job and sleep for certain minutes. Now I have to cancel the running threads in case a new configuration notification comes in and freshly start new set of threads, for which i have used pthread_cancel. What I observed was, the threads were not getting stopped even after receiving cancel indication,even some sleeping threads were coming up after the sleep was completed.

As the behavior was not desired, usage of pthread_cancel in the mentioned scenario raises question about being good or bad practice.

Please comment on the pthread_cancel usage in above mentioned scenario.

Mandar
  • 693
  • 2
  • 9
  • 19

4 Answers4

48

In general thread cancellation is not a really good idea. It is better, whenever possible, to have a shared flag, that is used by the threads to break out of the loop. That way, you will let the threads perform any cleanup they might need to do before actually exiting.

On the issue of the threads not actually cancelling, the POSIX specification determines a set of cancellation points ( man 7 pthreads ). Threads can be cancelled only at those points. If your infinite loop does not contain a cancellation point you can add one by calling pthread_testcancel. If pthread_cancel has been called, then it will be acted upon at this point.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 7
    +1 for avoiding cancellation, it's the fastest way toward memory leaks, and worse. Ask politely instead :) – Matthieu M. Jan 21 '11 at 18:56
  • 2
    Seems like I have to change the logic for thread termination using a shared flag. But on other note my program were having threads with cancel state set to ASYNCHRONOUS which, I believe, is related to immediate termination of threads after calling the respective cleanup handlers. – Mandar Jan 23 '11 at 14:09
  • @user584631: The pthreads manual page says that when the mode is set to asynchronous, the cancellation can be immediate, but the system is not required to do so. – David Rodríguez - dribeas Jan 23 '11 at 23:39
  • 1
    the system is not required to do the immediate cancellation of thread means till then the thread execution remains unaware of cancellation, is it so ? – Mandar Jan 28 '11 at 08:40
  • 12
    Can't agree. `pthread_cancel()` may be easier to use. And resources leak can be avoided if you call `pthread_cleanup_push()` and `pthread_cleanup_pop()` to set up the cleanup handlers. – zeekvfu Oct 22 '13 at 05:09
  • @David Rodríguez - dribeas, please answer http://stackoverflow.com/questions/28534081/pthread-cancel-is-successful-but-failing-to-create-thread-after-few-100s-of-thr – Renuka Feb 16 '15 at 03:54
  • Setting a flag will not interrupt f.ex. a TTY read. A signal or pthread cancel will. Async cancellation is basically garbage as it's impossible to get right unless every single called is designed for it. – curiousguy Dec 04 '19 at 16:55
10

If you are writing exception safe C++ code (see http://www.boost.org/community/exception_safety.html) than your code is naturally ready for thread cancellation. glibs throws C++ exception on thread cancel, so that your destructors can do the appropriate clean-up.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 3
    If the OP knows that they'll never need to run on another pthreads implementation, that's OK, but I recommend NOT relying on thread cancellation being implemented through exceptions. Using a flag moves thread exit out of the realm of 'hidden stuff' and into the code you can see, making it easier for later maintainers. I've had experience with this particular issue for years and I've concluded that the best thing to is to avoid cancellation, because there's always SOMETHING that trips you up when you've got cancellation in the mix. – Michael Kohne Jun 06 '12 at 14:47
  • 2
    This blog post describes why exactly RAII is not composable with cancellation: https://skaark.wordpress.com/2010/08/26/pthread_cancel-considered-harmful/ – erenon Mar 29 '15 at 21:33
  • 1
    @erenon Nice article, but it is only half the story. One can disable thread cancellation in destructors. – Maxim Egorushkin Mar 30 '15 at 07:28
  • @MaximEgorushkin VERY late comment to a 5 years old question, but I liked your idea. Can I specify with gcc that all C++ distructors should automatically (temporarily) disable pthread_cancel? – Erik Alapää Aug 26 '16 at 14:07
  • 1
    @ErikAlapää Probably not. – Maxim Egorushkin Aug 26 '16 at 15:56
  • I guess the only way to work then is to never use pthread_cancel with C++. I usually use other design patterns when stopping threads. – Erik Alapää Aug 27 '16 at 10:05
  • In my opinion this is huge flaw of C++. All blocking system calls are cancellation points and you can't cancel thread executing such blocking system calls without pthread_cancel. Yes you can switch to non-blocking mode with many system calls, but when you do real projects you use many 3rd party libraries which may not use non-blocking calls. Additionally it makes no sense to use non-blocking calls all the time. Therefore stay with C or use Java which has well defined memory model. @ErikAlapää can you just list all those patterns you use when stopping threads? – Juraj Michalak Dec 28 '18 at 10:29
  • @Juraj Just don't use those low quality libraries that do such blocking calls. One doesn't switch the language because of a stupid library. I have never encountered this issue in practice, so I do not think it is a "huge flaw". More like marginal. – Maxim Egorushkin Dec 28 '18 at 10:55
  • @MaximEgorushkin I understand what you want to say, but I would like to achieve big things with existing libraries, programming is a tool for me, we should use existing libraries. Generally SW reliability is very low. We as programmers have failed, everything is so fragmented. Mature and clever libraries use blocking system calls. If you want to create application which is multi-threaded, uses various libraries and you want to be able to cancel running sub-tasks in your app, you just need pthread_cancel(). You don't want to spend your life with creating ways how to avoid this flaw. – Juraj Michalak Dec 28 '18 at 12:59
1

You can do the equivalent of the code below.

#include <pthread.h>
#include <cxxabi.h>
#include <unistd.h>
...
void *Control(void* pparam)
{
    try
    {
        // do your work here, maybe long loop
    }   
    catch (abi::__forced_unwind&)
    {  // handle pthread_cancel stack unwinding exception
        throw;
    }
    catch (exception &ex) 
    {
        throw ex;
    }
}

int main()
{
    pthread_t tid;
    int rtn;
    rtn = pthread_create( &tid, NULL, Control, NULL );

    usleep(500);
    // some other work here

    rtn = pthtead_cancel( tid );
}
Jack
  • 21
  • 3
0

I'd use boost::asio.

Something like:

struct Wait {
  Wait() : timer_(io_service_), run_(true) {}

  boost::asio::io_service io_service_;
  mutable boost::asio::deadline_timer timer_;
  bool run_;
};

void Wait::doWwork() {
  while (run) {
    boost::system::error_code ec;
    timer_.wait(ec);
    io_service_.run();
    if (ec) {
      if (ec == boost::asio::error::operation_aborted) {
        // cleanup
      } else {
        // Something else, possibly nasty, happened
      }
    }
  }
}

void Wait::halt() {
  run_ = false;
  timer_.cancel();
}

Once you've got your head round it, asio is a wonderful tool.

Nicole
  • 699
  • 1
  • 5
  • 15