From mboxrd@z Thu Jan 1 00:00:00 1970 X-Msuck: nntp://news.gmane.org/gmane.linux.lib.musl.general/14150 Path: news.gmane.org!.POSTED.blaine.gmane.org!not-for-mail From: pengyuanhong Newsgroups: gmane.linux.lib.musl.general Subject: thread which is blocked by mutex cannot be canceled Date: Mon, 27 May 2019 13:17:41 +0000 Message-ID: <4CF320752F2B99449115298D4A06B22F34E2F8DF@dggemm509-mbx.china.huawei.com> Reply-To: musl@lists.openwall.com Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="_000_4CF320752F2B99449115298D4A06B22F34E2F8DFdggemm509mbxchi_" Injection-Info: blaine.gmane.org; posting-host="blaine.gmane.org:195.159.176.226"; logging-data="58383"; mail-complaints-to="usenet@blaine.gmane.org" Cc: leijitang , "Huangqiang (H)" To: "musl@lists.openwall.com" Original-X-From: musl-return-14166-gllmg-musl=m.gmane.org@lists.openwall.com Mon May 27 15:35:52 2019 Return-path: Envelope-to: gllmg-musl@m.gmane.org Original-Received: from mother.openwall.net ([195.42.179.200]) by blaine.gmane.org with smtp (Exim 4.89) (envelope-from ) id 1hVFn4-000F0W-C0 for gllmg-musl@m.gmane.org; Mon, 27 May 2019 15:35:50 +0200 Original-Received: (qmail 28514 invoked by uid 550); 27 May 2019 13:35:47 -0000 Mailing-List: contact musl-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Original-Received: (qmail 12093 invoked from network); 27 May 2019 13:18:03 -0000 Thread-Topic: thread which is blocked by mutex cannot be canceled Thread-Index: AdUUi3dd1w0furxvTcqXxUMwgw+aPQ== Accept-Language: zh-CN, en-US Content-Language: zh-CN x-originating-ip: [10.177.32.146] X-CFilter-Loop: Reflected Xref: news.gmane.org gmane.linux.lib.musl.general:14150 Archived-At: --_000_4CF320752F2B99449115298D4A06B22F34E2F8DFdggemm509mbxchi_ Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Hello, I am doing test for pthread_cancel(), and the following test can pass when compiled with gcc while fail compiled with musl-gcc: ``` $ cat test_pthread_cancel.c #include #include #include #include # define INTHREAD 0 /* Control going to or is already for Thread */ # define INMAIN 1 /* Control going to or is already for Main */ # define TIMEOUT 10 /* Time out time in seconds */ int sem1; /* Manual semaphore */ int cleanup_flag; /* Flag to indicate the thread's cleanup handler wa= s called */ pthread_mutex_t mutex =3D PTHREAD_MUTEX_INITIALIZER; /* Mutex */ /* Cleanup function that the thread executes when it is canceled. So if * cleanup_flag is 1, it means that the thread was canceled. */ static void a_cleanup_func(void *args) { cleanup_flag =3D 1; return; } /* Function that the thread executes upon its creation */ static void *a_thread_func(void) { pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); pthread_cleanup_push(a_cleanup_func, NULL); /* Indicate to main() that the thread has been created. */ sem1 =3D INMAIN; /* Lock the mutex. It should have already been locked in main, so t= he thread * should block. */ if (pthread_mutex_lock(&mutex) !=3D 0) { printf("Error in pthread_mutex_lock()\n"); pthread_exit((void *) -1); return (void *) -1; } /* Shouldn't get here if the cancel request was honored immediately * like it should have been. */ cleanup_flag =3D -1; pthread_cleanup_pop(0); pthread_exit(0); return NULL; } int main(void) { pthread_t new_th; int i =3D 0; /* Initializing values */ sem1 =3D INTHREAD; cleanup_flag =3D 0; /* Lock the mutex */ if (pthread_mutex_lock(&mutex) !=3D 0) { printf("Error in pthread_mutex_lock()\n"); return -1; } /* Create a new thread. */ if (pthread_create(&new_th, NULL, (void *)a_thread_func, NULL) !=3D= 0) { printf("Error creating thread\n"); return -1; } /* Make sure thread is created before we cancel it. (wait for * a_thread_func() to set sem1=3DINMAIN.) */ while (sem1 =3D=3D INTHREAD) sleep(1); /* Send cancel request to the thread. */ if (pthread_cancel(new_th) !=3D 0) { printf("Test FAILED: Error in pthread_cancel()\n"); return -1; } /* Wait for the thread to either cancel immediately (as it should d= o) and call it's * cleanup handler, or for TIMEOUT(10) seconds if the cancel reques= t was not honored * immediately. */ while ((cleanup_flag =3D=3D 0) && (i !=3D TIMEOUT)) { sleep(1); i++; } /* Unlock the mutex */ pthread_mutex_unlock(&mutex); pthread_join(new_th, NULL); /* This means that the cleanup function wasn't called, so the cance= l * request was not honord immediately like it should have been. */ if (cleanup_flag <=3D 0) { printf("Test FAILED: Cancel request timed out\n"); return -1; } printf("Test PASSED\n"); return 0; } $ $ gcc -pthread test_pthread_cancel.c $ ./a.out Test PASSED $ $ /usr/local/musl/bin/musl-gcc test_pthread_cancel.c $ ./a.out =3D=3D=3D=3Dnew->tid is 70450, self->tid is 70449, pid is 70449=3D=3D=3D=3D= =3D Test FAILED: Cancel request timed out ``` Digging into musl's codes, I find that in __timedwait(), PTHREAD_CANCEL_DIS= ABLE is set before thread is waiting for the futex. In the above test, when child thread is blocked by futex, it is canceled by main thread, then SIGCANCEL is signaled, and child thread is interrupted= by this signal. At this point, canceldisable is true in child, so the child ju= st return from cancel_handler() and is blocked again by mutex. So I wonder, 1. Why PTHREAD_CANCEL_DISABLE is set before thread is waiting for the= futex? 2. Is pthread_mutex_lock() a possible cancellation point? --_000_4CF320752F2B99449115298D4A06B22F34E2F8DFdggemm509mbxchi_ Content-Type: text/html; charset="us-ascii" Content-Transfer-Encoding: quoted-printable

Hello,

 

I am doing test for pthread_can= cel(), and the following test can

pass when compiled with gcc whi= le fail compiled with musl-gcc:

 

```

$ cat test_pthread_cancel.c

#include <pthread.h>=

#include <stdio.h>

#include <errno.h>

#include <unistd.h><= /o:p>

# define INTHREAD 0  =    /* Control going to or is already for Thread */

# define INMAIN 1  &n= bsp;    /* Control going to or is already for Main */

# define TIMEOUT 10  =    /* Time out time in seconds */

 

int sem1;   &nbs= p;           /* Manual se= maphore */

int cleanup_flag;  &n= bsp;    /* Flag to indicate the thread's cleanup handler was= called */

pthread_mutex_t mutex =3D PTHRE= AD_MUTEX_INITIALIZER;      /* Mutex */<= /span>

 

 

/* Cleanup function that the th= read executes when it is canceled.  So if

* cleanup_flag is 1, it means t= hat the thread was canceled. */

static void a_cleanup_func(void= *args)

{

     &= nbsp;  cleanup_flag =3D 1;

     &= nbsp;  return;

}

 

/* Function that the thread exe= cutes upon its creation */

static void *a_thread_func(void= )

{

     &= nbsp;  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);=

     &= nbsp;  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);<= /o:p>

 

     &= nbsp;  pthread_cleanup_push(a_cleanup_func, NULL);

 

     &= nbsp;  /* Indicate to main() that the thread has been created. */=

     &= nbsp;  sem1 =3D INMAIN;

 

     &= nbsp;  /* Lock the mutex. It should have already been locked in main, = so the thread

     &= nbsp;   * should block. */

     &= nbsp;  if (pthread_mutex_lock(&mutex) !=3D 0) {<= /p>

     &= nbsp;          printf("Er= ror in pthread_mutex_lock()\n");

     &= nbsp;          pthread_exit((v= oid *) -1);

     &= nbsp;          return (void *)= -1;

     &= nbsp;  }

 

     &= nbsp;  /* Shouldn't get here if the cancel request was honored immedia= tely

     &= nbsp;   * like it should have been. */

     &= nbsp;  cleanup_flag =3D -1;

     &= nbsp;  pthread_cleanup_pop(0);

     &= nbsp;  pthread_exit(0);

     &= nbsp;  return NULL;

}

 

int main(void)

{

     &= nbsp;  pthread_t new_th;

     &= nbsp;  int i =3D 0;

 

     &= nbsp;  /* Initializing values */

     &= nbsp;  sem1 =3D INTHREAD;

     &= nbsp;  cleanup_flag =3D 0;

 

     &= nbsp;  /* Lock the mutex */

     &= nbsp;  if (pthread_mutex_lock(&mutex) !=3D 0) {<= /p>

     &= nbsp;          printf("Er= ror in pthread_mutex_lock()\n");

     &= nbsp;          return -1;=

     &= nbsp;  }

 

     &= nbsp;  /* Create a new thread. */

     &= nbsp;  if (pthread_create(&new_th, NULL, (void *)a_thread_func, NU= LL) !=3D 0) {

     &= nbsp;          printf("Er= ror creating thread\n");

     &= nbsp;          return -1;=

     &= nbsp;  }

 

     &= nbsp;  /* Make sure thread is created before we cancel it. (wait for

     &= nbsp;   * a_thread_func() to set sem1=3DINMAIN.) */

     &= nbsp;  while (sem1 =3D=3D INTHREAD)

     &= nbsp;          sleep(1);<= /o:p>

 

     &= nbsp;  /* Send cancel request to the thread.  */

     &= nbsp;  if (pthread_cancel(new_th) !=3D 0) {

     &= nbsp;          printf("Te= st FAILED: Error in pthread_cancel()\n");

     &= nbsp;          return -1;=

     &= nbsp;  }

     &= nbsp;  /* Wait for the thread to either cancel immediately (as it shou= ld do) and call it's

     &= nbsp;   * cleanup handler, or for TIMEOUT(10) seconds if the canc= el request was not honored

     &= nbsp;   * immediately. */

     &= nbsp;  while ((cleanup_flag =3D=3D 0) && (i !=3D TIMEOUT)) {

     &= nbsp;          sleep(1);<= /o:p>

     &= nbsp;          i++;

     &= nbsp;  }

 

     &= nbsp;  /* Unlock the mutex */

     &= nbsp;  pthread_mutex_unlock(&mutex);

     &= nbsp;  pthread_join(new_th, NULL);

 

     &= nbsp;  /* This means that the cleanup function wasn't called, so the c= ancel

      =    * request was not honord immediately like it should have = been. */

     &= nbsp;  if (cleanup_flag <=3D 0) {

     &= nbsp;          printf("Te= st FAILED: Cancel request timed out\n");

     &= nbsp;          return -1;=

     &= nbsp;  }

 

     &= nbsp;  printf("Test PASSED\n");

     &= nbsp;  return 0;

}

$

$ gcc -pthread test_pthread_can= cel.c

$ ./a.out

Test PASSED

$

$ /usr/local/musl/bin/musl-gcc = test_pthread_cancel.c

$ ./a.out

=3D=3D=3D=3Dnew->tid is 7045= 0, self->tid is 70449, pid is 70449=3D=3D=3D=3D=3D

Test FAILED: Cancel request tim= ed out

```

 

Digging into musl’s codes= , I find that in __timedwait(), PTHREAD_CANCEL_DISABLE

is set before thread is waiting= for the futex.

 

In the above test, when child t= hread is blocked by futex, it is canceled

by main thread, then SIGCANCEL = is signaled, and child thread is interrupted by

this signal. At this point, can= celdisable is true in child, so the child just return from

cancel_handler() and is blocked= again by mutex.

 

So I wonder,<= /p>

1= .   &= nbsp;   Why PTHREAD_CANCEL_DISA= BLE is set before thread is waiting for the futex?

2= .   &= nbsp;   Is pthread_mutex_lock()= a possible cancellation point?

--_000_4CF320752F2B99449115298D4A06B22F34E2F8DFdggemm509mbxchi_--