void __pthread_exit (void *value) { THREAD_SETMEM (THREAD_SELF, result, value); __do_cancel (); }
/* The next two functions are similar to pthread_setcanceltype() but more specialized for the use in the cancelable functions like write(). They do not need to check parameters etc. */ int attribute_hidden __pthread_enable_asynccancel (void) { struct pthread *self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { int newval = oldval | CANCELTYPE_BITMASK; if (newval == oldval) break; int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (__builtin_expect (curval == oldval, 1)) { if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval)) { THREAD_SETMEM (self, result, PTHREAD_CANCELED); __do_cancel (); } break; } /* Prepare the next round. */ oldval = curval; } return oldval; }
void attribute_protected __pthread_exit (void* value) { THREAD_SETMEM (THREAD_SELF, result, value); __do_cancel (); }
/* For asynchronous cancellation we use a signal. This is the handler. */ static void sigcancel_handler (int sig, siginfo_t *si, void *ctx) { #ifdef __ASSUME_CORRECT_SI_PID /* Determine the process ID. It might be negative if the thread is in the middle of a fork() call. */ pid_t pid = THREAD_GETMEM (THREAD_SELF, pid); if (__builtin_expect (pid < 0, 0)) pid = -pid; #endif /* Safety check. It would be possible to call this function for other signals and send a signal from another process. This is not correct and might even be a security problem. Try to catch as many incorrect invocations as possible. */ if (sig != SIGCANCEL #ifdef __ASSUME_CORRECT_SI_PID /* Kernels before 2.5.75 stored the thread ID and not the process ID in si_pid so we skip this test. */ || si->si_pid != pid #endif || si->si_code != SI_TKILL) return; struct pthread *self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { /* We are canceled now. When canceled by another thread this flag is already set but if the signal is directly send (internally or from another process) is has to be done here. */ int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK; if (oldval == newval || (oldval & EXITING_BITMASK) != 0) /* Already canceled or exiting. */ break; int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (curval == oldval) { /* Set the return value. */ THREAD_SETMEM (self, result, PTHREAD_CANCELED); /* Make sure asynchronous cancellation is still enabled. */ if ((newval & CANCELTYPE_BITMASK) != 0) /* Run the registered destructors and terminate the thread. */ __do_cancel (); break; } oldval = curval; } }
int attribute_hidden __pthread_enable_asynccancel (void) { struct pthread *self = THREAD_SELF; int oldval; if (is_recording()) { pthread_log_record (0, PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling, 1); oldval = THREAD_GETMEM (self, cancelhandling); pthread_log_record (oldval, PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling, 0); } else if (is_replaying()) { pthread_log_replay (PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling); oldval = pthread_log_replay (PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling); } else { oldval = THREAD_GETMEM (self, cancelhandling); } while (1) { int newval = oldval | CANCELTYPE_BITMASK; if (newval == oldval) break; int curval; if (is_recording()) { pthread_log_record (0, PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling, 1); curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); pthread_log_record (curval, PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling, 0); } else if (is_replaying()) { pthread_log_replay (PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling); curval = pthread_log_replay (PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling); } else { curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); } if (__builtin_expect (curval == oldval, 1)) { if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval)) { THREAD_SETMEM (self, result, PTHREAD_CANCELED); __do_cancel (); } break; } /* Prepare the next round. */ oldval = curval; } return oldval; }
int __pthread_setcanceltype (int type, int *oldtype) { if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS) return EINVAL; #ifndef SIGCANCEL if (type == PTHREAD_CANCEL_ASYNCHRONOUS) return ENOTSUP; #endif volatile struct pthread *self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { int newval = (type == PTHREAD_CANCEL_ASYNCHRONOUS ? oldval | CANCELTYPE_BITMASK : oldval & ~CANCELTYPE_BITMASK); /* Store the old value. */ if (oldtype != NULL) *oldtype = ((oldval & CANCELTYPE_BITMASK) ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED); /* 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)) { THREAD_SETMEM (self, result, PTHREAD_CANCELED); __do_cancel (); } break; } /* Prepare for the next round. */ oldval = curval; } return 0; }
int attribute_protected __pthread_setcancelstate ( int state, int *oldstate) { volatile struct pthread *self; if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE) return EINVAL; 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 (__builtin_expect (curval == oldval, 1)) { if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval)) __do_cancel (); break; } /* Prepare for the next round. */ oldval = curval; } return 0; }
/* The next two functions are similar to pthread_setcanceltype() but more specialized for the use in the cancelable functions like write(). They do not need to check parameters etc. */ int attribute_hidden __libc_enable_asynccancel (void) { struct pthread *self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { int newval = oldval | CANCELTYPE_BITMASK; if (__builtin_expect ((oldval & CANCELED_BITMASK) != 0, 0)) { /* If we are already exiting or if PTHREAD_CANCEL_DISABLED, stop right here. */ if ((oldval & (EXITING_BITMASK | CANCELSTATE_BITMASK)) != 0) break; int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (__builtin_expect (curval != oldval, 0)) { /* Somebody else modified the word, try again. */ oldval = curval; continue; } THREAD_SETMEM (self, result, PTHREAD_CANCELED); __do_cancel (); /* NOTREACHED */ } int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (__builtin_expect (curval == oldval, 1)) break; /* Prepare the next round. */ oldval = curval; } return oldval; }
/* For asynchronous cancellation we use a signal. This is the handler. */ static void sigcancel_handler (int sig, siginfo_t *si, void *ctx) { /* Safety check. It would be possible to call this function for other signals and send a signal from another process. This is not correct and might even be a security problem. Try to catch as many incorrect invocations as possible. */ if (sig != SIGCANCEL || si->si_pid != __getpid() || si->si_code != SI_TKILL) return; struct pthread *self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { /* We are canceled now. When canceled by another thread this flag is already set but if the signal is directly send (internally or from another process) is has to be done here. */ int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK; if (oldval == newval || (oldval & EXITING_BITMASK) != 0) /* Already canceled or exiting. */ break; int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (curval == oldval) { /* Set the return value. */ THREAD_SETMEM (self, result, PTHREAD_CANCELED); /* Make sure asynchronous cancellation is still enabled. */ if ((newval & CANCELTYPE_BITMASK) != 0) /* Run the registered destructors and terminate the thread. */ __do_cancel (); break; } oldval = curval; } }