public inbox for pthreads-win32@sourceware.org
 help / color / mirror / Atom feed
From: Ross Johnson <rpj@callisto.canberra.edu.au>
To: vc <vcotirlea1@hotmail.com>
Cc: pthreads-win32@sources.redhat.com
Subject: Re: problem using pthread_cancel and pthread_mutex_lock
Date: Thu, 04 Mar 2004 23:13:00 -0000	[thread overview]
Message-ID: <4047B840.5000509@callisto.canberra.edu.au> (raw)
In-Reply-To: <BAY2-DAV61fMHTs2FZQ000129c8@hotmail.com>

Hi VC,

I saw you're original post and the response that you got advising 
against async cancelation, etc, which I would urge you to consider 
further, even if you need to redesign your application.

There is another reason to avoid async cancelation that is specific to 
pthreads-win32: this implementation only approximates async cancelation 
because it relies on the thread actually running at some point after 
cancelation. So if your thread is blocked on a resource at the time that 
it's async canceled, it won't actually exit until it's unblocked in some 
way to resume execution (at which point it will exit immediately) - and 
if you can do that then you don't need async cancelation anyway. 
Unfortunately, the time you're most likely to really need an async 
cancel - to kill a thread blocked on a system resource that you can't 
unblock - is the very time it won't work in pthreads-win32, and if it 
did work, as in does in other implementations, then you'd probably be 
creating a resource leak. So it's hard to find a good argument for async 
cancel.

If you were to list the situations where your threads could possibly 
hang, then you'd probably find that there's a solution for each instance.

Re switching cancel state within the library:-
There are places in the library that temporarily suspend cancelability 
for cancel safety, usually because the standard requires it, but mutexes 
are not one of them, for the simple reason that, for the vast majority 
of cases, it isn't needed, while speed is, and for those rare cases that 
do need it, programmers can employ solutions similar to the one you've 
chosen.

A few more suggestions:
If you're using mutexes to control access to resources that could hang 
your application then maybe semaphores would be more appropriate - they 
are not owned by any thread and sem_wait() is a defined [deferred] 
cancelation point. There is also pthread_testcancel(), which you can use 
to create your own [deferred] cancelation points.

There are also timed versions of all of the synchronisation objects: 
pthread_mutex_timedlock(), sem_timedwait(), 
pthread_rwlock_timedrdlock(), pthread_rwlock_timedwrlock(), and 
pthread_cond_timedwait(); that you can perhaps exploit in your attempts 
to avoid canceling threads at all.

Hope that helps.

Regards.
Ross

vc wrote:

>Hi all,
>
>I found a solution to the problem I have described (see below my orig email)
>and I'm wondering if this is ok ...
>
>In my program I have to use asynchronous cancellation as I have something
>called a "thread monitor" and
>if one thread hangs I want after a while my thread monitor to kill it
>regardless of where that
>thread hanged. Using asynchronous cancellation makes problems (as I
>discovered until now)
>only when a thread is in a pthread_mutex_lock call, as in that case, by
>canceling the thread
>the mutex is in an unusable state.
>
>So what I have done is like this (see below): just before calling the
>pthread_mutex_lock
>I change the cancellation to deferred cancellation then call the
>pthread_mutex_lock and then set back
>the original cancellation mode:
>
>void reader_function (void *arg )
>{
>
> pthread_cleanup_push(cleanup_routine, (void *) &test_data);
>
> retval = pthread_detach (pthread_self());
>
> pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &state);
> pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &state);
>
> retval = protect_code_with_mutex_deferred();
> pthread_cleanup_pop(1);
>
> pthread_exit(NULL);
>}
>
>int protect_code_with_mutex_deferred(void)
>{
> int oldtype = 0;
>
> pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
> retval = pthread_mutex_lock(&my_mutex);
> pthread_setcanceltype(oldtype, NULL); //put back
> [...]
>}
>
>This seems to work just fine and seems to solve my problem. As I'm generaly
>using asynchronous cancellation
>my thread can be killed at any point and when a pthread_mutex_lock is done
>because I switch
>to deferred cancellation I can be sure that my thread will first go out from
>the pthread_mutex_lock
>call and then it will be canceled, so in my cleanup fction I can do an
>unlock of the mutex.
>
>But I am wondering why this way of solving the problem was not added to the
>pthread library? Am I missing something?
>Is something wrong here? Am I overseen something?
>
>If no, then in the pthread library in the pthread_mutex_lock at the
>beginning the:
> pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype); could be called
>and at the end the:
> pthread_setcanceltype(oldtype, NULL);
>could be called.
>Of course some other changes are needed as pthread_setcanceltype calls also
>pthread_mutex_lock, but for internal use, I mean within the library the
>pthread_mutex_lock
>could be used with one more param, so that when pthread_mutex_lock is called
>from within the lib these
>2 lines will never be executed.
>
>Any feedback would be appreciated.
>Thanks a lot,
>Viv
>
>
>----- Original Message -----
>From: "vc" <vcotirlea1@hotmail.com>
>To: <pthreads-win32@sources.redhat.com>
>Sent: Monday, February 23, 2004 6:45 PM
>Subject: problem using pthread_cancel and pthread_mutex_lock
>
>
>  
>
>>Hi all,
>>
>>I am using the pthread library and I'm having a problem while using
>>pthread_cancel and pthread_mutex_lock.
>>Problem description:
>>I start 2 threads: thread1 and thread2.
>>thread1 is doing a pthread_mutex_lock(&mutex), then sleeps for 5 secs and
>>then it is doing
>>a pthread_cancel for the thread2, then is doing a
>>pthread_mutex_unlock(&mutex)
>>Thread2 is doing a pthread_mutex_lock(&mutex), where it stays as the mutex
>>is owned
>>by the thread1, and at this point the cancel is called.
>>Even if in the cleanup procedure of the thread2 I'm doing an
>>pthread_mutex_unlock or
>>not, next time when the thread1 is trying a pthread_mutex_lock(&mutex) it
>>will block
>>and never gets the mutex.
>>Also the pthread_mutex_unlock(&mutex)  for the thread2 in the cleanup
>>function fails
>>(ret value is 1)
>>
>>So, my question is: how can a thread cleanly cancel another thread which
>>    
>>
>is
>  
>
>>waiting in a 'pthread_mutex_lock' call, so that this mutex is available
>>again ?
>>
>>Here is a sample program:
>>====================
>>
>>#include <windows.h>
>>#include <stdio.h>
>>#include <stdlib.h>
>>#include <pthread.h>
>>#include <errno.h>
>>
>>void cleanup_routine(void *arg);
>>void reader_function(void *arg);
>>void monitor(void *arg);
>>int global_counter=0;
>>
>>pthread_mutex_t my_mutex;
>>int id[2];
>>pthread_t reader[2];
>>int cancel_mode;
>>
>>
>>int main(int argc, char *argv[])
>>{
>>   int my_args;
>>   int err = 0;
>>   cancel_mode = 1;
>>
>>   printf("We'll try to cancel with mode ASYNCHRONOUS\n");
>>
>>   id[0] = 1;
>>   id[1] = 2;
>>   pthread_mutex_init(&my_mutex, NULL);
>>
>>   my_args = 1;
>>   pthread_create( &reader[0], NULL, (void*)&monitor, (void *) &my_args);
>>   Sleep(2000);
>>   my_args = 2;
>>   pthread_create( &reader[1], NULL, (void*)&reader_function, (void *)
>>&my_args);
>>
>>   while(1) {
>> Sleep(1000);
>>   }
>>}
>>
>>void monitor (void *arg )
>>{
>>   int retval;
>>
>>   printf("Monitor: Entering monitor routine\n\n");
>>
>>   printf("Monitor: monitor is locking thread...\n");
>>   pthread_mutex_lock(&my_mutex);
>>   printf("Monitor: monitor is locking thread - okay\n");
>>   Sleep (5000);
>>
>>   printf("Monitor: monitor kills pthread 0x%x:\n", (unsigned int)
>>reader[1]);
>>   retval = pthread_cancel (reader[1]);
>>   printf("Monitor: kill returns %d\n", retval);
>>
>>   printf("Monitor: monitor is unlocking thread...\n");
>>   pthread_mutex_unlock(&my_mutex);
>>   printf("Monitor: monitor is unlocking thread - okay\n");
>>
>>   printf("Monitor: monitor running\n");
>>   Sleep (3000);
>>   printf("Monitor: monitor is locking thread...\n");
>>   pthread_mutex_lock(&my_mutex); // HERE: it will never get the lock! It
>>will hang here!
>>   printf("Monitor: monitor is locking thread - okay\n");
>>
>>   Sleep(1000);
>>   printf("Monitor: monitor is unlocking thread...\n");
>>   pthread_mutex_unlock(&my_mutex);
>>   printf("Monitor: monitor is unlocking thread - okay\n");
>>}
>>
>>
>>int args;
>>
>>void reader_function (void *arg )
>>{
>>   int i=0;
>>   int id, state;
>>   int retval;
>>
>>   pthread_cleanup_push(cleanup_routine, NULL);
>>   retval = pthread_detach (pthread_self());
>>
>>   pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &state);
>>   printf("Thread: pthread_setcancelstate:   old state was %d\n", state);
>>
>>   if (cancel_mode == 1) {
>>       pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &state);
>>   }
>>
>>   id = *(int *) arg;
>>   printf("Thread: entered thread %d\n", id);
>>   printf("Thread: thread returns: 0x%x\n", (unsigned int)
>>    
>>
>pthread_self());
>  
>
>>   printf("Thread: testthread is locking thread...\n");
>>   pthread_mutex_lock(&my_mutex);
>>   printf("Thread: testthread is locking thread - okay\n");
>>
>>   // HERE: it shouldn't come here as the thread will be canceled by the
>>monitor thread
>>   printf("Thread: testthread is unlocking thread...\n");
>>   pthread_mutex_unlock(&my_mutex);
>>   printf("Thread: testthread is unlocking thread - okay\n");
>>
>>   printf("Thread: reader_function finished\n");
>>
>>   pthread_cleanup_pop(0);
>>}
>>
>>
>>void cleanup_routine(void *arg)
>>{
>>   int ret = 0;
>>   printf("ThreadCleanup: cleanup called\n");
>>   Sleep(5000);
>>
>>   ret = pthread_mutex_unlock(&my_mutex);
>>   printf("ThreadCleanup:Cleanup routine unlock ret = %d\n", ret);
>>   printf("ThreadCleanup:waitThread_cleanup done\n");
>>}
>>
>>
>>The output looks like:
>>=================
>>We'll try to cancel with mode ASYNCHRONOUS
>>Monitor: Entering monitor routine
>>
>>Monitor: monitor is locking thread...
>>Monitor: monitor is locking thread - okay
>>Thread: pthread_setcancelstate:   old state was 0
>>Thread: entered thread 2
>>Thread: thread returns: 0x312d80
>>Thread: testthread is locking thread...
>>Monitor: monitor kills pthread 0x312d80:
>>Monitor: kill returns 0
>>Monitor: monitor is unlocking thread...
>>ThreadCleanup: cleanup called
>>Monitor: monitor is unlocking thread - okay
>>Monitor: monitor running
>>Monitor: monitor is locking thread...
>>ThreadCleanup:Cleanup routine unlock ret = 1
>>ThreadCleanup:waitThread_cleanup done
>>
>>
>>So, from the output can be seen that the 1st thread (called monitor) will
>>never be able
>>to gain the mutex again.
>>
>>Sorry for the long post,
>>Any help will be appreciated,
>>Thanks a lot,
>>Viv
>>
>>    
>>

  reply	other threads:[~2004-03-04 23:13 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-02-23 17:43 vc
2004-03-01 16:27 ` vc
2004-03-04 23:13   ` Ross Johnson [this message]
2004-03-09 14:06     ` vc
2004-03-09 14:15     ` Brano Kemen
2004-03-09 15:09       ` Panagiotis E. Hadjidoukas
2004-03-09 15:42         ` Brano Kemen
2004-02-24  4:23 Simon Gerblich
2004-02-26 15:59 ` vc
2004-03-09 23:25 Simon Gerblich
2004-03-10  0:57 ` Will Bryant
2004-03-10 11:11 ` vc

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4047B840.5000509@callisto.canberra.edu.au \
    --to=rpj@callisto.canberra.edu.au \
    --cc=pthreads-win32@sources.redhat.com \
    --cc=vcotirlea1@hotmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).