/* Copyright (C) 2002-2024 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #include #include #include "pthreadP.h" /* Called by the INTERNAL_SYSCALL_CANCEL macro, check for cancellation and returns the syscall value or its negative error code. */ long int __internal_syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2, __syscall_arg_t a3, __syscall_arg_t a4, __syscall_arg_t a5, __syscall_arg_t a6, __SYSCALL_CANCEL7_ARG_DEF __syscall_arg_t nr) { long int result; struct pthread *pd = THREAD_SELF; /* If cancellation is not enabled, call the syscall directly and also for thread terminatation to avoid call __syscall_do_cancel while executing cleanup handlers. */ int ch = atomic_load_relaxed (&pd->cancelhandling); if (SINGLE_THREAD_P || !cancel_enabled (ch) || cancel_exiting (ch)) { result = INTERNAL_SYSCALL_NCS_CALL (nr, a1, a2, a3, a4, a5, a6 __SYSCALL_CANCEL7_ARCH_ARG7); if (INTERNAL_SYSCALL_ERROR_P (result)) return -INTERNAL_SYSCALL_ERRNO (result); return result; } /* Call the arch-specific entry points that contains the globals markers to be checked by SIGCANCEL handler. */ result = __syscall_cancel_arch (&pd->cancelhandling, nr, a1, a2, a3, a4, a5, a6 __SYSCALL_CANCEL7_ARCH_ARG7); /* If the cancellable syscall was interrupted by SIGCANCEL and it has no side-effect, cancel the thread if cancellation is enabled. */ ch = atomic_load_relaxed (&pd->cancelhandling); /* The behaviour here assumes that EINTR is returned only if there are no visible side effects. POSIX Issue 7 has not yet provided any stronger language for close, and in theory the close syscall could return EINTR and leave the file descriptor open (conforming and leaks). It expects that no such kernel is used with glibc. */ if (result == -EINTR && cancel_enabled_and_canceled (ch)) __syscall_do_cancel (); return result; } /* Called by the SYSCALL_CANCEL macro, check for cancellation and return the syscall expected success value (usually 0) or, in case of failure, -1 and sets errno to syscall return value. */ long int __syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2, __syscall_arg_t a3, __syscall_arg_t a4, __syscall_arg_t a5, __syscall_arg_t a6, __SYSCALL_CANCEL7_ARG_DEF __syscall_arg_t nr) { int r = __internal_syscall_cancel (a1, a2, a3, a4, a5, a6, __SYSCALL_CANCEL7_ARG nr); return __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (r)) ? SYSCALL_ERROR_LABEL (INTERNAL_SYSCALL_ERRNO (r)) : r; } /* Called by __syscall_cancel_arch or function above start the thread cancellation. */ _Noreturn void __syscall_do_cancel (void) { struct pthread *self = THREAD_SELF; /* Disable thread cancellation to avoid cancellable entrypoints calling __syscall_do_cancel recursively. We atomic load relaxed to check the state of cancelhandling, there is no particular ordering requirement between the syscall call and the other thread setting our cancelhandling with a atomic store acquire. POSIX Issue 7 notes that the cancellation occurs asynchronously on the target thread, that implies there is no ordering requirements. It does not need a MO release store here. */ int oldval = atomic_load_relaxed (&self->cancelhandling); while (1) { int newval = oldval | CANCELSTATE_BITMASK; if (oldval == newval) break; if (atomic_compare_exchange_weak_acquire (&self->cancelhandling, &oldval, newval)) break; } __do_cancel (PTHREAD_CANCELED); }