Blame sysdeps/unix/sysv/linux/timer_routines.c

Packit 6c4009
/* Copyright (C) 2003-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
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 <setjmp.h>
Packit 6c4009
#include <signal.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <sysdep-cancel.h>
Packit 6c4009
#include <nptl/pthreadP.h>
Packit 6c4009
#include "kernel-posix-timers.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* List of active SIGEV_THREAD timers.  */
Packit 6c4009
struct timer *__active_timer_sigev_thread;
Packit 6c4009
/* Lock for the __active_timer_sigev_thread.  */
Packit 6c4009
pthread_mutex_t __active_timer_sigev_thread_lock = PTHREAD_MUTEX_INITIALIZER;
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct thread_start_data
Packit 6c4009
{
Packit 6c4009
  void (*thrfunc) (sigval_t);
Packit 6c4009
  sigval_t sival;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Helper thread to call the user-provided function.  */
Packit 6c4009
static void *
Packit 6c4009
timer_sigev_thread (void *arg)
Packit 6c4009
{
Packit 6c4009
  /* The parent thread has all signals blocked.  This is a bit
Packit 6c4009
     surprising for user code, although valid.  We unblock all
Packit 6c4009
     signals.  */
Packit 6c4009
  sigset_t ss;
Packit 6c4009
  sigemptyset (&ss);
Packit 6c4009
  INTERNAL_SYSCALL_DECL (err);
Packit 6c4009
  INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_SETMASK, &ss, NULL, _NSIG / 8);
Packit 6c4009
Packit 6c4009
  struct thread_start_data *td = (struct thread_start_data *) arg;
Packit 6c4009
Packit 6c4009
  void (*thrfunc) (sigval_t) = td->thrfunc;
Packit 6c4009
  sigval_t sival = td->sival;
Packit 6c4009
Packit 6c4009
  /* The TD object was allocated in timer_helper_thread.  */
Packit 6c4009
  free (td);
Packit 6c4009
Packit 6c4009
  /* Call the user-provided function.  */
Packit 6c4009
  thrfunc (sival);
Packit 6c4009
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Helper function to support starting threads for SIGEV_THREAD.  */
Packit 6c4009
static void *
Packit 6c4009
timer_helper_thread (void *arg)
Packit 6c4009
{
Packit 6c4009
  /* Wait for the SIGTIMER signal, allowing the setXid signal, and
Packit 6c4009
     none else.  */
Packit 6c4009
  sigset_t ss;
Packit 6c4009
  sigemptyset (&ss);
Packit 6c4009
  __sigaddset (&ss, SIGTIMER);
Packit 6c4009
Packit 6c4009
  /* Endless loop of waiting for signals.  The loop is only ended when
Packit 6c4009
     the thread is canceled.  */
Packit 6c4009
  while (1)
Packit 6c4009
    {
Packit 6c4009
      siginfo_t si;
Packit 6c4009
Packit 6c4009
      /* sigwaitinfo cannot be used here, since it deletes
Packit 6c4009
	 SIGCANCEL == SIGTIMER from the set.  */
Packit 6c4009
Packit 6c4009
      /* XXX The size argument hopefully will have to be changed to the
Packit 6c4009
	 real size of the user-level sigset_t.  */
Packit 6c4009
      int result = SYSCALL_CANCEL (rt_sigtimedwait, &ss, &si, NULL, _NSIG / 8);
Packit 6c4009
Packit 6c4009
      if (result > 0)
Packit 6c4009
	{
Packit 6c4009
	  if (si.si_code == SI_TIMER)
Packit 6c4009
	    {
Packit 6c4009
	      struct timer *tk = (struct timer *) si.si_ptr;
Packit 6c4009
Packit 6c4009
	      /* Check the timer is still used and will not go away
Packit 6c4009
		 while we are reading the values here.  */
Packit 6c4009
	      pthread_mutex_lock (&__active_timer_sigev_thread_lock);
Packit 6c4009
Packit 6c4009
	      struct timer *runp = __active_timer_sigev_thread;
Packit 6c4009
	      while (runp != NULL)
Packit 6c4009
		if (runp == tk)
Packit 6c4009
		  break;
Packit 6c4009
		else
Packit 6c4009
		  runp = runp->next;
Packit 6c4009
Packit 6c4009
	      if (runp != NULL)
Packit 6c4009
		{
Packit 6c4009
		  struct thread_start_data *td = malloc (sizeof (*td));
Packit 6c4009
Packit 6c4009
		  /* There is not much we can do if the allocation fails.  */
Packit 6c4009
		  if (td != NULL)
Packit 6c4009
		    {
Packit 6c4009
		      /* This is the signal we are waiting for.  */
Packit 6c4009
		      td->thrfunc = tk->thrfunc;
Packit 6c4009
		      td->sival = tk->sival;
Packit 6c4009
Packit 6c4009
		      pthread_t th;
Packit 6c4009
		      (void) pthread_create (&th, &tk->attr,
Packit 6c4009
					     timer_sigev_thread, td);
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      pthread_mutex_unlock (&__active_timer_sigev_thread_lock);
Packit 6c4009
	    }
Packit 6c4009
	  else if (si.si_code == SI_TKILL)
Packit 6c4009
	    /* The thread is canceled.  */
Packit 6c4009
	    pthread_exit (NULL);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Control variable for helper thread creation.  */
Packit 6c4009
pthread_once_t __helper_once attribute_hidden;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* TID of the helper thread.  */
Packit 6c4009
pid_t __helper_tid attribute_hidden;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Reset variables so that after a fork a new helper thread gets started.  */
Packit 6c4009
static void
Packit 6c4009
reset_helper_control (void)
Packit 6c4009
{
Packit 6c4009
  __helper_once = PTHREAD_ONCE_INIT;
Packit 6c4009
  __helper_tid = 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
attribute_hidden
Packit 6c4009
__start_helper_thread (void)
Packit 6c4009
{
Packit 6c4009
  /* The helper thread needs only very little resources
Packit 6c4009
     and should go away automatically when canceled.  */
Packit 6c4009
  pthread_attr_t attr;
Packit 6c4009
  (void) pthread_attr_init (&attr);
Packit 6c4009
  (void) pthread_attr_setstacksize (&attr, __pthread_get_minstack (&attr));
Packit 6c4009
Packit 6c4009
  /* Block all signals in the helper thread but SIGSETXID.  To do this
Packit 6c4009
     thoroughly we temporarily have to block all signals here.  The
Packit 6c4009
     helper can lose wakeups if SIGCANCEL is not blocked throughout,
Packit 6c4009
     but sigfillset omits it SIGSETXID.  So, we add SIGCANCEL back
Packit 6c4009
     explicitly here.  */
Packit 6c4009
  sigset_t ss;
Packit 6c4009
  sigset_t oss;
Packit 6c4009
  sigfillset (&ss);
Packit 6c4009
  __sigaddset (&ss, SIGCANCEL);
Packit 6c4009
  INTERNAL_SYSCALL_DECL (err);
Packit 6c4009
  INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_SETMASK, &ss, &oss, _NSIG / 8);
Packit 6c4009
Packit 6c4009
  /* Create the helper thread for this timer.  */
Packit 6c4009
  pthread_t th;
Packit 6c4009
  int res = pthread_create (&th, &attr, timer_helper_thread, NULL);
Packit 6c4009
  if (res == 0)
Packit 6c4009
    /* We managed to start the helper thread.  */
Packit 6c4009
    __helper_tid = ((struct pthread *) th)->tid;
Packit 6c4009
Packit 6c4009
  /* Restore the signal mask.  */
Packit 6c4009
  INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_SETMASK, &oss, NULL,
Packit 6c4009
		    _NSIG / 8);
Packit 6c4009
Packit 6c4009
  /* No need for the attribute anymore.  */
Packit 6c4009
  (void) pthread_attr_destroy (&attr);
Packit 6c4009
Packit 6c4009
  /* We have to make sure that after fork()ing a new helper thread can
Packit 6c4009
     be created.  */
Packit 6c4009
  pthread_atfork (NULL, NULL, reset_helper_control);
Packit 6c4009
}