Blame nptl/pthread_cancel.c

Packit 6c4009
/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <signal.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include "pthreadP.h"
Packit 6c4009
#include <atomic.h>
Packit 6c4009
#include <sysdep.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__pthread_cancel (pthread_t th)
Packit 6c4009
{
Packit 6c4009
  volatile struct pthread *pd = (volatile struct pthread *) th;
Packit 6c4009
Packit 6c4009
  /* Make sure the descriptor is valid.  */
Packit 6c4009
  if (INVALID_TD_P (pd))
Packit 6c4009
    /* Not a valid thread handle.  */
Packit 6c4009
    return ESRCH;
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
  pthread_cancel_init ();
Packit 6c4009
#endif
Packit 6c4009
  int result = 0;
Packit 6c4009
  int oldval;
Packit 6c4009
  int newval;
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
    again:
Packit 6c4009
      oldval = pd->cancelhandling;
Packit 6c4009
      newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK;
Packit 6c4009
Packit 6c4009
      /* Avoid doing unnecessary work.  The atomic operation can
Packit 6c4009
	 potentially be expensive if the bug has to be locked and
Packit 6c4009
	 remote cache lines have to be invalidated.  */
Packit 6c4009
      if (oldval == newval)
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      /* If the cancellation is handled asynchronously just send a
Packit 6c4009
	 signal.  We avoid this if possible since it's more
Packit 6c4009
	 expensive.  */
Packit 6c4009
      if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval))
Packit 6c4009
	{
Packit 6c4009
	  /* Mark the cancellation as "in progress".  */
Packit 6c4009
	  if (atomic_compare_and_exchange_bool_acq (&pd->cancelhandling,
Packit 6c4009
						    oldval | CANCELING_BITMASK,
Packit 6c4009
						    oldval))
Packit 6c4009
	    goto again;
Packit 6c4009
Packit 6c4009
#ifdef SIGCANCEL
Packit 6c4009
	  /* The cancellation handler will take care of marking the
Packit 6c4009
	     thread as canceled.  */
Packit 6c4009
	  pid_t pid = __getpid ();
Packit 6c4009
Packit 6c4009
	  INTERNAL_SYSCALL_DECL (err);
Packit 6c4009
	  int val = INTERNAL_SYSCALL_CALL (tgkill, err, pid, pd->tid,
Packit 6c4009
					   SIGCANCEL);
Packit 6c4009
	  if (INTERNAL_SYSCALL_ERROR_P (val, err))
Packit 6c4009
	    result = INTERNAL_SYSCALL_ERRNO (val, err);
Packit 6c4009
#else
Packit 6c4009
          /* It should be impossible to get here at all, since
Packit 6c4009
             pthread_setcanceltype should never have allowed
Packit 6c4009
             PTHREAD_CANCEL_ASYNCHRONOUS to be set.  */
Packit 6c4009
          abort ();
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
	  break;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	/* A single-threaded process should be able to kill itself, since
Packit 6c4009
	   there is nothing in the POSIX specification that says that it
Packit 6c4009
	   cannot.  So we set multiple_threads to true so that cancellation
Packit 6c4009
	   points get executed.  */
Packit 6c4009
	THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1);
Packit 6c4009
#ifndef TLS_MULTIPLE_THREADS_IN_TCB
Packit 6c4009
	__pthread_multiple_threads = *__libc_multiple_threads_ptr = 1;
Packit 6c4009
#endif
Packit 6c4009
    }
Packit 6c4009
  /* Mark the thread as canceled.  This has to be done
Packit 6c4009
     atomically since other bits could be modified as well.  */
Packit 6c4009
  while (atomic_compare_and_exchange_bool_acq (&pd->cancelhandling, newval,
Packit 6c4009
					       oldval));
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
weak_alias (__pthread_cancel, pthread_cancel)
Packit 6c4009
Packit 6c4009
PTHREAD_STATIC_FN_REQUIRE (__pthread_create)