github.com/afumu/libc@v0.0.6/musl/src/thread/pthread_cancel.c (about)

     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  }