/* Single-thread optimization definitions.  Linux version.
   Copyright (C) 2017-2026 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
   <https://www.gnu.org/licenses/>.  */

#ifndef _SYSDEP_CANCEL_H
#define _SYSDEP_CANCEL_H

#include <sysdep.h>
#include "pthreadP.h"

/* Called by the INTERNAL_SYSCALL_CANCEL macro, check for cancellation and
   returns the syscall value or its negative error code.  */
static __always_inline 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.  */
static __always_inline 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)
{
  long 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;
}

#endif
