views:

211

answers:

4

I have an application that runs multiple threads which are sometimes cancelled. These threads may call into another object that internally accesses a resources (socket). To prevent the resource to be accessed simultaneously, there is a critical section to get some order in the execution.

Now, when cancelling the thread, it (sometimes) happens that the thread is just within that code that is blocked by the critical section. The critical section is locked using an object and I was hoping that upon cancellation of the thread this object would be destructed and consequently release the lock. However this does not seem to be the case, so that at thread destruction this resource object is permanently locked.

Changing the resource object is probably not an option (3rd party delivered), plus it makes sense to prevent simultaneous access to a resource that can not be used in parallel.

I have experimented with preventing the thread to be cancelled using pthread_setcancelstate when the section is locked/unlocked, however this does feel a bit dirty and would not be a final solution for other situations (e.g. aquired mutexes, etc).

I know that a prefered solution would be to not use pthread_cancel but instead set a flag in the thread and it would cancel itself when it is ready (in a clean way). However as I want to cancel the thread asap, I was wondering (also out of academic interest) if there would be other options to do that.

A: 

Hi,

if the lock which controls the critical section is not exposed to you directly, there is not much you can do. When you cancel a thread, all the cleanup handlers for the thread are executed in the normal reverse order, but of course these handlers could only release mutexes which you have access to. So you really can't do much more than disable canceling during your visit to the 3rd party component.

I think your best solution is to both use a flag and the pthread_cancel functionality. WHen you are entering the 3rd party component, disable cancel processing (PTHREAD_CANCEL_DISABLE); when you get back out of it, re-enable it. After re-enabling it, check for the flag:

/* In thread which you want to be able to be canceled: */
int oldstate;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
... call 3rd party component ...
pthread setcancelstate(oldstate, NULL);
if (cancelled_flag) pthread_exit(PTHREAD_CANCELED);

/* In the thread canceling the other one. Note the order of operations
   to avoid race condition: */
cancelled_flag = true;
pthread_cancel(thread_id);
antti.huima
+1  A: 

Thread cancellation without the help from the application (the mentioned flag) is a bad idea. Just google.

Actually cancellation is so hard that it has been omitted from the latest C++0x draft. You can search http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2497.html and won't find any mention of cancellation at all. Here's the definition of the proposed thread class (you won't find cancel there):

class thread
{
public:
    // types:
    class id;
    typedef implementation-defined native_handle_type; // See [thread.native]

    // construct/copy/destroy:
    thread();
    template <class F> explicit thread(F f);
    template <class F, class ...Args> thread(F&& f, Args&&... args);
    ~thread();
    thread(const thread&) = delete;
    thread(thread&&);
    thread& operator=(const thread&) = delete;
    thread& operator=(thread&&);

    // members:
    void swap(thread&&);
    bool joinable() const;
    void join();
    void detach();
    id get_id() const;
    native_handle_type native_handle(); // See [thread.native]

    // static members:
    static unsigned hardware_concurrency();
};
lothar
A: 

The idea of aborting threads without using a well defined control method (ie, flags) is just so evil that you simply shouldn't do it.

If you have third party code that you have no option except to do this, I might go as far as suggest to abstract the horrible code inside a process, and then interact with the process instead, separating each such component nicely.

Now, such a design would be even worse on windows, because windows is not good at running multiple processes, however this is not such a bad idea on linux.

Of course, having a sensible design for your threaded modules would be even better...

(Personally, I prefer not using threads at all, and always using processes, or non-blocking designs)

Arafangion
A: 

You could use pthread_cleanup_push() to push a cancellation cleanup handler onto the threads cancellation cleanup stack. This handler would be responsible for unlocking the critical section.

Once you leave the critical section you should call pthread_cleanup_pop(0) to remove it.

i.e.

CRIITICAL_SECTION g_section;

void clean_crit_sec( void * )
{
    LeaveCriticalSection( &g_section )
}

void *thrfunc( void * )
{
    EnterCriticalSection( &g_section );
    pthread_cleanup_push( clean_crit_sec, NULL );

    // Do something that may be cancellable

    LeaveCriticalSection( &g_section );
    pthread_cleanup_pop( 0 );
}

This would still leave a small race condition where the critcial section has been unlocked but the cleanup handler could still be executed if the thread was canceled between the Leave.. and the cleanup_pop.

You could call pthread_cleanup_pop with 1 which would execute your cleanup code and not levae the critical section yourself. i.e

CRIITICAL_SECTION g_section;

void clean_crit_sec( void * )
{
    LeaveCriticalSection( &g_section )
}

void *thrfunc( void * )
{
    EnterCriticalSection( &g_section );
    pthread_cleanup_push( clean_crit_sec, NULL );

    // Do something that may be cancellable

    pthread_cleanup_pop( 1 );  // this will pop the handler and execute it.
}
ScaryAardvark