Blame sysdeps/pthread/timer_create.c

Packit 6c4009
/* Copyright (C) 2000-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Kaz Kylheku <kaz@ashi.footprints.net>.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public License as
Packit 6c4009
   published by the Free Software Foundation; either version 2.1 of the
Packit 6c4009
   License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; see the file COPYING.LIB.  If
Packit 6c4009
   not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <signal.h>
Packit 6c4009
#include <pthread.h>
Packit 6c4009
#include <time.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
Packit 6c4009
#include "posix-timer.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Create new per-process timer using CLOCK.  */
Packit 6c4009
int
Packit 6c4009
timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
Packit 6c4009
{
Packit 6c4009
  int retval = -1;
Packit 6c4009
  struct timer_node *newtimer = NULL;
Packit 6c4009
  struct thread_node *thread = NULL;
Packit 6c4009
Packit 6c4009
  if (0
Packit 6c4009
#if defined _POSIX_CPUTIME && _POSIX_CPUTIME >= 0
Packit 6c4009
      || clock_id == CLOCK_PROCESS_CPUTIME_ID
Packit 6c4009
#endif
Packit 6c4009
#if defined _POSIX_THREAD_CPUTIME && _POSIX_THREAD_CPUTIME >= 0
Packit 6c4009
      || clock_id == CLOCK_THREAD_CPUTIME_ID
Packit 6c4009
#endif
Packit 6c4009
      )
Packit 6c4009
    {
Packit 6c4009
      /* We don't allow timers for CPU clocks.  At least not in the
Packit 6c4009
	 moment.  */
Packit 6c4009
      __set_errno (ENOTSUP);
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (clock_id != CLOCK_REALTIME)
Packit 6c4009
    {
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  pthread_once (&__timer_init_once_control, __timer_init_once);
Packit 6c4009
Packit 6c4009
  if (__timer_init_failed)
Packit 6c4009
    {
Packit 6c4009
      __set_errno (ENOMEM);
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  pthread_mutex_lock (&__timer_mutex);
Packit 6c4009
Packit 6c4009
  newtimer = __timer_alloc ();
Packit 6c4009
  if (__glibc_unlikely (newtimer == NULL))
Packit 6c4009
    {
Packit 6c4009
      __set_errno (EAGAIN);
Packit 6c4009
      goto unlock_bail;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (evp != NULL)
Packit 6c4009
    newtimer->event = *evp;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      newtimer->event.sigev_notify = SIGEV_SIGNAL;
Packit 6c4009
      newtimer->event.sigev_signo = SIGALRM;
Packit 6c4009
      newtimer->event.sigev_value.sival_ptr = newtimer;
Packit 6c4009
      newtimer->event.sigev_notify_function = 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  newtimer->event.sigev_notify_attributes = &newtimer->attr;
Packit 6c4009
  newtimer->creator_pid = getpid ();
Packit 6c4009
Packit 6c4009
  switch (__builtin_expect (newtimer->event.sigev_notify, SIGEV_SIGNAL))
Packit 6c4009
    {
Packit 6c4009
    case SIGEV_NONE:
Packit 6c4009
    case SIGEV_SIGNAL:
Packit 6c4009
      /* We have a global thread for delivering timed signals.
Packit 6c4009
	 If it is not running, try to start it up.  */
Packit 6c4009
      thread = &__timer_signal_thread_rclk;
Packit 6c4009
      if (! thread->exists)
Packit 6c4009
	{
Packit 6c4009
	  if (__builtin_expect (__timer_thread_start (thread),
Packit 6c4009
				1) < 0)
Packit 6c4009
	    {
Packit 6c4009
	      __set_errno (EAGAIN);
Packit 6c4009
	      goto unlock_bail;
Packit 6c4009
            }
Packit 6c4009
        }
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case SIGEV_THREAD:
Packit 6c4009
      /* Copy over thread attributes or set up default ones.  */
Packit 6c4009
      if (evp->sigev_notify_attributes)
Packit 6c4009
	newtimer->attr = *(pthread_attr_t *) evp->sigev_notify_attributes;
Packit 6c4009
      else
Packit 6c4009
	pthread_attr_init (&newtimer->attr);
Packit 6c4009
Packit 6c4009
      /* Ensure thread attributes call for deatched thread.  */
Packit 6c4009
      pthread_attr_setdetachstate (&newtimer->attr, PTHREAD_CREATE_DETACHED);
Packit 6c4009
Packit 6c4009
      /* Try to find existing thread having the right attributes.  */
Packit 6c4009
      thread = __timer_thread_find_matching (&newtimer->attr, clock_id);
Packit 6c4009
Packit 6c4009
      /* If no existing thread has these attributes, try to allocate one.  */
Packit 6c4009
      if (thread == NULL)
Packit 6c4009
	thread = __timer_thread_alloc (&newtimer->attr, clock_id);
Packit 6c4009
Packit 6c4009
      /* Out of luck; no threads are available.  */
Packit 6c4009
      if (__glibc_unlikely (thread == NULL))
Packit 6c4009
	{
Packit 6c4009
	  __set_errno (EAGAIN);
Packit 6c4009
	  goto unlock_bail;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* If the thread is not running already, try to start it.  */
Packit 6c4009
      if (! thread->exists
Packit 6c4009
	  && __builtin_expect (! __timer_thread_start (thread), 0))
Packit 6c4009
	{
Packit 6c4009
	  __set_errno (EAGAIN);
Packit 6c4009
	  goto unlock_bail;
Packit 6c4009
	}
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    default:
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      goto unlock_bail;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  newtimer->clock = clock_id;
Packit 6c4009
  newtimer->abstime = 0;
Packit 6c4009
  newtimer->armed = 0;
Packit 6c4009
  newtimer->thread = thread;
Packit 6c4009
Packit 6c4009
  *timerid = timer_ptr2id (newtimer);
Packit 6c4009
  retval = 0;
Packit 6c4009
Packit 6c4009
  if (__builtin_expect (retval, 0) == -1)
Packit 6c4009
    {
Packit 6c4009
    unlock_bail:
Packit 6c4009
      if (thread != NULL)
Packit 6c4009
	__timer_thread_dealloc (thread);
Packit 6c4009
      if (newtimer != NULL)
Packit 6c4009
	{
Packit 6c4009
	  timer_delref (newtimer);
Packit 6c4009
	  __timer_dealloc (newtimer);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  pthread_mutex_unlock (&__timer_mutex);
Packit 6c4009
Packit 6c4009
  return retval;
Packit 6c4009
}