pthread_cancel.c raw

   1  #define _GNU_SOURCE
   2  #include <string.h>
   3  #include "pthread_impl.h"
   4  #include "syscall.h"
   5  
   6  hidden long __cancel(), __syscall_cp_asm(), __syscall_cp_c();
   7  
   8  long __cancel()
   9  {
  10  	pthread_t self = __pthread_self();
  11  	if (self->canceldisable == PTHREAD_CANCEL_ENABLE || self->cancelasync)
  12  		pthread_exit(PTHREAD_CANCELED);
  13  	self->canceldisable = PTHREAD_CANCEL_DISABLE;
  14  	return -ECANCELED;
  15  }
  16  
  17  long __syscall_cp_asm(volatile void *, syscall_arg_t,
  18                        syscall_arg_t, syscall_arg_t, syscall_arg_t,
  19                        syscall_arg_t, syscall_arg_t, syscall_arg_t);
  20  
  21  long __syscall_cp_c(syscall_arg_t nr,
  22                      syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
  23                      syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
  24  {
  25  	pthread_t self;
  26  	long r;
  27  	int st;
  28  
  29  	if ((st=(self=__pthread_self())->canceldisable)
  30  	    && (st==PTHREAD_CANCEL_DISABLE || nr==SYS_close))
  31  		return __syscall(nr, u, v, w, x, y, z);
  32  
  33  	r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z);
  34  	if (r==-EINTR && nr!=SYS_close && self->cancel &&
  35  	    self->canceldisable != PTHREAD_CANCEL_DISABLE)
  36  		r = __cancel();
  37  	return r;
  38  }
  39  
  40  static void _sigaddset(sigset_t *set, int sig)
  41  {
  42  	unsigned s = sig-1;
  43  	set->__bits[s/8/sizeof *set->__bits] |= 1UL<<(s&8*sizeof *set->__bits-1);
  44  }
  45  
  46  extern hidden const char __cp_begin[1], __cp_end[1], __cp_cancel[1];
  47  
  48  static void cancel_handler(int sig, siginfo_t *si, void *ctx)
  49  {
  50  	pthread_t self = __pthread_self();
  51  	ucontext_t *uc = ctx;
  52  	uintptr_t pc = uc->uc_mcontext.MC_PC;
  53  
  54  	a_barrier();
  55  	if (!self->cancel || self->canceldisable == PTHREAD_CANCEL_DISABLE) return;
  56  
  57  	_sigaddset(&uc->uc_sigmask, SIGCANCEL);
  58  
  59  	if (self->cancelasync || pc >= (uintptr_t)__cp_begin && pc < (uintptr_t)__cp_end) {
  60  		uc->uc_mcontext.MC_PC = (uintptr_t)__cp_cancel;
  61  #ifdef CANCEL_GOT
  62  		uc->uc_mcontext.MC_GOT = CANCEL_GOT;
  63  #endif
  64  		return;
  65  	}
  66  
  67  	__syscall(SYS_tkill, self->tid, SIGCANCEL);
  68  }
  69  
  70  void __testcancel()
  71  {
  72  	pthread_t self = __pthread_self();
  73  	if (self->cancel && !self->canceldisable)
  74  		__cancel();
  75  }
  76  
  77  static void init_cancellation()
  78  {
  79  	struct sigaction sa = {
  80  		.sa_flags = SA_SIGINFO | SA_RESTART,
  81  		.sa_sigaction = cancel_handler
  82  	};
  83  	memset(&sa.sa_mask, -1, _NSIG/8);
  84  	__libc_sigaction(SIGCANCEL, &sa, 0);
  85  }
  86  
  87  int pthread_cancel(pthread_t t)
  88  {
  89  	static int init;
  90  	if (!init) {
  91  		init_cancellation();
  92  		init = 1;
  93  	}
  94  	a_store(&t->cancel, 1);
  95  	if (t == pthread_self()) {
  96  		if (t->canceldisable == PTHREAD_CANCEL_ENABLE && t->cancelasync)
  97  			pthread_exit(PTHREAD_CANCELED);
  98  		return 0;
  99  	}
 100  	return pthread_kill(t, SIGCANCEL);
 101  }
 102