[PATCH 2/3] nptl: Move cancel state out of cancelhandling

Adhemerval Zanella adhemerval.zanella@linaro.org
Wed May 20 14:51:41 GMT 2020



On 16/05/2020 15:38, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> The thread cancellation state is not accessed concurrently internally
>> neither the pthread interface allows changing the state of a different
>> thread than its own.
>>
>> The code is also simplified: the CANCELLATION_P is replaced with a
>> internal pthread_testcancel call and the CANCELSTATE_BIT{MASK} is
>> removed.
>>
>> Checked on x86_64-linux-gnu.
> 
> Please use the more elaborate commit message.  It is helpful.

Ack.

> 
>> diff --git a/nptl/descr.h b/nptl/descr.h
>> index 9dcf480bdf..61665bf859 100644
>> --- a/nptl/descr.h
>> +++ b/nptl/descr.h
> 
>> @@ -394,6 +388,9 @@ struct pthread
>>    /* Indicates whether is a C11 thread created by thrd_creat.  */
>>    bool c11;
>>  
>> +  /* Thread cancel state (enable, disable).  */
>> +  unsigned char cancelstate;
>> +
> 
> Please document the permitted values in the comment.

Ack.

> 
>> diff --git a/nptl/pthread_setcancelstate.c b/nptl/pthread_setcancelstate.c
>> index 4d7f413e19..aa1c8073a8 100644
>> --- a/nptl/pthread_setcancelstate.c
>> +++ b/nptl/pthread_setcancelstate.c
>> @@ -31,39 +31,9 @@ __pthread_setcancelstate (int state, int *oldstate)
>>  
>>    self = THREAD_SELF;
>>  
>> -  int oldval = THREAD_GETMEM (self, cancelhandling);
>> -  while (1)
>> -    {
>> -      int newval = (state == PTHREAD_CANCEL_DISABLE
>> -		    ? oldval | CANCELSTATE_BITMASK
>> -		    : oldval & ~CANCELSTATE_BITMASK);
>> -
>> -      /* Store the old value.  */
>> -      if (oldstate != NULL)
>> -	*oldstate = ((oldval & CANCELSTATE_BITMASK)
>> -		     ? PTHREAD_CANCEL_DISABLE : PTHREAD_CANCEL_ENABLE);
>> -
>> -      /* Avoid doing unnecessary work.  The atomic operation can
>> -	 potentially be expensive if the memory has to be locked and
>> -	 remote cache lines have to be invalidated.  */
>> -      if (oldval == newval)
>> -	break;
>> -
>> -      /* Update the cancel handling word.  This has to be done
>> -	 atomically since other bits could be modified as well.  */
>> -      int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval,
>> -					      oldval);
>> -      if (__glibc_likely (curval == oldval))
>> -	{
>> -	  if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval))
>> -	    __do_cancel ();
>> -
>> -	  break;
>> -	}
>> -
>> -      /* Prepare for the next round.  */
>> -      oldval = curval;
>> -    }
>> +  if (oldstate != NULL)
>> +    *oldstate = self->cancelstate;
>> +  self->cancelstate = state;
>>  
>>    return 0;
>>  }
> 
> I've re-read the old code and I think this checks for cancellation
> even in the absence of a race (i.e., if the CAS succeeds).  I still
> think we should preserve this behavior, also for symmetry with this
> code below:

The second part of this change keeps the pthread_setcanceltype as 
cancellation entrypoint by calling pthread_testcancel if the new type
is PTHREAD_CANCEL_ASYNCHRONOUS.

And with this behavior I don't see a clear rationale to keep
pthread_setcancelstate as a cancellation entrypoint since it should act
iff cancel type is PTHREAD_CANCEL_ASYNCHRONOUS (which is already handled
by pthread_setcanceltype). 

> 
>> diff --git a/nptl/pthread_setcanceltype.c b/nptl/pthread_setcanceltype.c
>> index fcaae8abc7..cc0507ae04 100644
>> --- a/nptl/pthread_setcanceltype.c
>> +++ b/nptl/pthread_setcanceltype.c
>> @@ -53,7 +53,8 @@ __pthread_setcanceltype (int type, int *oldtype)
>>  					      oldval);
>>        if (__glibc_likely (curval == oldval))
>>  	{
>> -	  if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval))
>> +	  if (self->cancelstate == PTHREAD_CANCEL_ENABLE
>> +	      && CANCEL_CANCELED_AND_ASYNCHRONOUS (newval))
>>  	    {
>>  	      THREAD_SETMEM (self, result, PTHREAD_CANCELED);
>>  	      __do_cancel ();
> 
>> diff --git a/nptl/pthread_testcancel.c b/nptl/pthread_testcancel.c
>> index 30408c2008..3ffff4ebef 100644
>> --- a/nptl/pthread_testcancel.c
>> +++ b/nptl/pthread_testcancel.c
>> @@ -23,7 +23,16 @@
>>  void
>>  __pthread_testcancel (void)
>>  {
>> -  CANCELLATION_P (THREAD_SELF);
>> +  struct pthread *self = THREAD_SELF;
>> +  int cancelhandling = THREAD_GETMEM (self, cancelhandling);
>> +  if (self->cancelstate == PTHREAD_CANCEL_ENABLE
>> +      && (cancelhandling & (CANCELED_BITMASK | EXITING_BITMASK
>> +			    | TERMINATED_BITMASK))
>> +	  == CANCELED_BITMASK)
>> +    {
>> +      THREAD_SETMEM (self, result, PTHREAD_CANCELED);
>> +      __do_cancel ();
>> +    }
>>  }
>>  strong_alias (__pthread_testcancel, pthread_testcancel)
>>  hidden_def (__pthread_testcancel)
> 
> I think you can write this as 
> 
>    self->cancelstate == PTHREAD_CANCEL_ENABLE
>    && (cancelhandling & CANCELED_BITMASK)
>    && !(cancelhandling & EXITING_BITMASK)
>    && !(cancelhandling & TERMINATED_BITMASK)
> 
> and GCC will do the right thing.  I find this variant easier to read,
> but I don't have a strong preference.
> 

Ack, I think it is slight better as well.


More information about the Libc-alpha mailing list