1  /* Copyright (C) 2003-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public License as
       6     published by the Free Software Foundation; either version 2.1 of the
       7     License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; see the file COPYING.LIB.  If
      16     not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <pthread.h>
      20  #include <signal.h>
      21  #include <stdlib.h>
      22  #include <string.h>
      23  #include <time.h>
      24  #include <sysdep.h>
      25  #include <internaltypes.h>
      26  #include <pthreadP.h>
      27  #include "kernel-posix-timers.h"
      28  #include "kernel-posix-cpu-timers.h"
      29  #include <shlib-compat.h>
      30  
      31  int
      32  ___timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
      33  {
      34    {
      35      clockid_t syscall_clockid = (clock_id == CLOCK_PROCESS_CPUTIME_ID
      36  				 ? PROCESS_CLOCK
      37  				 : clock_id == CLOCK_THREAD_CPUTIME_ID
      38  				 ? THREAD_CLOCK
      39  				 : clock_id);
      40  
      41      /* If the user wants notification via a thread we need to handle
      42         this special.  */
      43      if (evp == NULL
      44  	|| __builtin_expect (evp->sigev_notify != SIGEV_THREAD, 1))
      45        {
      46  	struct sigevent local_evp;
      47  
      48  	if (evp == NULL)
      49  	  {
      50  	    /* The kernel has to pass up the timer ID which is a
      51  	       userlevel object.  Therefore we cannot leave it up to
      52  	       the kernel to determine it.  */
      53  	    local_evp.sigev_notify = SIGEV_SIGNAL;
      54  	    local_evp.sigev_signo = SIGALRM;
      55  	    local_evp.sigev_value.sival_ptr = NULL;
      56  
      57  	    evp = &local_evp;
      58  	  }
      59  
      60  	kernel_timer_t ktimerid;
      61  	if (INLINE_SYSCALL_CALL (timer_create, syscall_clockid, evp,
      62  				 &ktimerid) == -1)
      63  	  return -1;
      64  
      65  	*timerid = kernel_timer_to_timerid (ktimerid);
      66        }
      67      else
      68        {
      69  	/* Create the helper thread.  */
      70  	__pthread_once (&__timer_helper_once, __timer_start_helper_thread);
      71  	if (__timer_helper_tid == 0)
      72  	  {
      73  	    /* No resources to start the helper thread.  */
      74  	    __set_errno (EAGAIN);
      75  	    return -1;
      76  	  }
      77  
      78  	struct timer *newp = malloc (sizeof (struct timer));
      79  	if (newp == NULL)
      80  	  return -1;
      81  
      82  	/* Copy the thread parameters the user provided.  */
      83  	newp->sival = evp->sigev_value;
      84  	newp->thrfunc = evp->sigev_notify_function;
      85  
      86  	/* We cannot simply copy the thread attributes since the
      87  	   implementation might keep internal information for
      88  	   each instance.  */
      89  	__pthread_attr_init (&newp->attr);
      90  	if (evp->sigev_notify_attributes != NULL)
      91  	  {
      92  	    struct pthread_attr *nattr;
      93  	    struct pthread_attr *oattr;
      94  
      95  	    nattr = (struct pthread_attr *) &newp->attr;
      96  	    oattr = (struct pthread_attr *) evp->sigev_notify_attributes;
      97  
      98  	    nattr->schedparam = oattr->schedparam;
      99  	    nattr->schedpolicy = oattr->schedpolicy;
     100  	    nattr->flags = oattr->flags;
     101  	    nattr->guardsize = oattr->guardsize;
     102  	    nattr->stackaddr = oattr->stackaddr;
     103  	    nattr->stacksize = oattr->stacksize;
     104  	  }
     105  
     106  	/* In any case set the detach flag.  */
     107  	__pthread_attr_setdetachstate (&newp->attr, PTHREAD_CREATE_DETACHED);
     108  
     109  	/* Create the event structure for the kernel timer.  */
     110  	struct sigevent sev =
     111  	  { .sigev_value.sival_ptr = newp,
     112  	    .sigev_signo = SIGTIMER,
     113  	    .sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID,
     114  	    ._sigev_un = { ._pad = { [0] = __timer_helper_tid } } };
     115  
     116  	/* Create the timer.  */
     117  	int res;
     118  	res = INTERNAL_SYSCALL_CALL (timer_create, syscall_clockid, &sev,
     119  				     &newp->ktimerid);
     120  	if (INTERNAL_SYSCALL_ERROR_P (res))
     121  	  {
     122  	    free (newp);
     123  	    __set_errno (INTERNAL_SYSCALL_ERRNO (res));
     124  	    return -1;
     125  	  }
     126  
     127  	/* Add to the queue of active timers with thread delivery.  */
     128  	__pthread_mutex_lock (&__timer_active_sigev_thread_lock);
     129  	newp->next = __timer_active_sigev_thread;
     130  	__timer_active_sigev_thread = newp;
     131  	__pthread_mutex_unlock (&__timer_active_sigev_thread_lock);
     132  
     133  	*timerid = timer_to_timerid (newp);
     134        }
     135    }
     136  
     137    return 0;
     138  }
     139  versioned_symbol (libc, ___timer_create, timer_create, GLIBC_2_34);
     140  libc_hidden_ver (___timer_create, __timer_create)
     141  
     142  #if TIMER_T_WAS_INT_COMPAT
     143  # if OTHER_SHLIB_COMPAT (librt, GLIBC_2_3_3, GLIBC_2_34)
     144  compat_symbol (librt, ___timer_create, timer_create, GLIBC_2_3_3);
     145  # endif
     146  
     147  # if OTHER_SHLIB_COMPAT (librt, GLIBC_2_2, GLIBC_2_3_3)
     148  timer_t __timer_compat_list[OLD_TIMER_MAX];
     149  
     150  int
     151  __timer_create_old (clockid_t clock_id, struct sigevent *evp, int *timerid)
     152  {
     153    timer_t newp;
     154  
     155    int res = __timer_create (clock_id, evp, &newp);
     156    if (res == 0)
     157      {
     158        int i;
     159        for (i = 0; i < OLD_TIMER_MAX; ++i)
     160  	if (__timer_compat_list[i] == NULL
     161  	    && ! atomic_compare_and_exchange_bool_acq (&__timer_compat_list[i],
     162  						       newp, NULL))
     163  	  {
     164  	    *timerid = i;
     165  	    break;
     166  	  }
     167  
     168        if (__glibc_unlikely (i == OLD_TIMER_MAX))
     169  	{
     170  	  /* No free slot.  */
     171  	  __timer_delete (newp);
     172  	  __set_errno (EINVAL);
     173  	  res = -1;
     174  	}
     175      }
     176  
     177    return res;
     178  }
     179  compat_symbol (librt, __timer_create_old, timer_create, GLIBC_2_2);
     180  # endif /* OTHER_SHLIB_COMPAT */
     181  
     182  #else /* !TIMER_T_WAS_INT_COMPAT */
     183  # if OTHER_SHLIB_COMPAT (librt, GLIBC_2_2, GLIBC_2_34)
     184  compat_symbol (librt, ___timer_create, timer_create, GLIBC_2_2);
     185  # endif
     186  #endif /* !TIMER_T_WAS_INT_COMPAT */