Blame sysdeps/pthread/timer_routines.c

Packit 6c4009
/* Helper code for POSIX timer implementation on NPTL.
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 <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <pthread.h>
Packit 6c4009
#include <stddef.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <sysdep.h>
Packit 6c4009
#include <time.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/syscall.h>
Packit 6c4009
Packit 6c4009
#include "posix-timer.h"
Packit 6c4009
#include <timer_routines.h>
Packit 6c4009
Packit 6c4009
#ifndef DELAYTIMER_MAX
Packit 6c4009
# define DELAYTIMER_MAX INT_MAX
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Number of threads used.  */
Packit 6c4009
#define THREAD_MAXNODES	16
Packit 6c4009
Packit 6c4009
/* Array containing the descriptors for the used threads.  */
Packit 6c4009
static struct thread_node thread_array[THREAD_MAXNODES];
Packit 6c4009
Packit 6c4009
/* Static array with the structures for all the timers.  */
Packit 6c4009
struct timer_node __timer_array[TIMER_MAX];
Packit 6c4009
Packit 6c4009
/* Global lock to protect operation on the lists.  */
Packit 6c4009
pthread_mutex_t __timer_mutex = PTHREAD_MUTEX_INITIALIZER;
Packit 6c4009
Packit 6c4009
/* Variable to protext initialization.  */
Packit 6c4009
pthread_once_t __timer_init_once_control = PTHREAD_ONCE_INIT;
Packit 6c4009
Packit 6c4009
/* Nonzero if initialization of timer implementation failed.  */
Packit 6c4009
int __timer_init_failed;
Packit 6c4009
Packit 6c4009
/* Node for the thread used to deliver signals.  */
Packit 6c4009
struct thread_node __timer_signal_thread_rclk;
Packit 6c4009
Packit 6c4009
/* Lists to keep free and used timers and threads.  */
Packit 6c4009
static struct list_head timer_free_list;
Packit 6c4009
static struct list_head thread_free_list;
Packit 6c4009
static struct list_head thread_active_list;
Packit 6c4009
Packit 6c4009
Packit 6c4009
#ifdef __NR_rt_sigqueueinfo
Packit 6c4009
extern int __syscall_rt_sigqueueinfo (int, int, siginfo_t *);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* List handling functions.  */
Packit 6c4009
static inline void
Packit 6c4009
list_append (struct list_head *list, struct list_head *newp)
Packit 6c4009
{
Packit 6c4009
  newp->prev = list->prev;
Packit 6c4009
  newp->next = list;
Packit 6c4009
  list->prev->next = newp;
Packit 6c4009
  list->prev = newp;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline void
Packit 6c4009
list_insbefore (struct list_head *list, struct list_head *newp)
Packit 6c4009
{
Packit 6c4009
  list_append (list, newp);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Like list_unlink_ip, except that calling it on a node that
Packit 6c4009
 * is already unlinked is disastrous rather than a noop.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
static inline void
Packit 6c4009
list_unlink (struct list_head *list)
Packit 6c4009
{
Packit 6c4009
  struct list_head *lnext = list->next, *lprev = list->prev;
Packit 6c4009
Packit 6c4009
  lnext->prev = lprev;
Packit 6c4009
  lprev->next = lnext;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline struct list_head *
Packit 6c4009
list_first (struct list_head *list)
Packit 6c4009
{
Packit 6c4009
  return list->next;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline struct list_head *
Packit 6c4009
list_null (struct list_head *list)
Packit 6c4009
{
Packit 6c4009
  return list;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline struct list_head *
Packit 6c4009
list_next (struct list_head *list)
Packit 6c4009
{
Packit 6c4009
  return list->next;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline int
Packit 6c4009
list_isempty (struct list_head *list)
Packit 6c4009
{
Packit 6c4009
  return list->next == list;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Functions build on top of the list functions.  */
Packit 6c4009
static inline struct thread_node *
Packit 6c4009
thread_links2ptr (struct list_head *list)
Packit 6c4009
{
Packit 6c4009
  return (struct thread_node *) ((char *) list
Packit 6c4009
				 - offsetof (struct thread_node, links));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline struct timer_node *
Packit 6c4009
timer_links2ptr (struct list_head *list)
Packit 6c4009
{
Packit 6c4009
  return (struct timer_node *) ((char *) list
Packit 6c4009
				- offsetof (struct timer_node, links));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Initialize a newly allocated thread structure.  */
Packit 6c4009
static void
Packit 6c4009
thread_init (struct thread_node *thread, const pthread_attr_t *attr, clockid_t clock_id)
Packit 6c4009
{
Packit 6c4009
  if (attr != NULL)
Packit 6c4009
    thread->attr = *attr;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      pthread_attr_init (&thread->attr);
Packit 6c4009
      pthread_attr_setdetachstate (&thread->attr, PTHREAD_CREATE_DETACHED);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  thread->exists = 0;
Packit 6c4009
  INIT_LIST_HEAD (&thread->timer_queue);
Packit 6c4009
  pthread_cond_init (&thread->cond, 0);
Packit 6c4009
  thread->current_timer = 0;
Packit 6c4009
  thread->captured = pthread_self ();
Packit 6c4009
  thread->clock_id = clock_id;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Initialize the global lists, and acquire global resources.  Error
Packit 6c4009
   reporting is done by storing a non-zero value to the global variable
Packit 6c4009
   timer_init_failed.  */
Packit 6c4009
static void
Packit 6c4009
init_module (void)
Packit 6c4009
{
Packit 6c4009
  int i;
Packit 6c4009
Packit 6c4009
  INIT_LIST_HEAD (&timer_free_list);
Packit 6c4009
  INIT_LIST_HEAD (&thread_free_list);
Packit 6c4009
  INIT_LIST_HEAD (&thread_active_list);
Packit 6c4009
Packit 6c4009
  for (i = 0; i < TIMER_MAX; ++i)
Packit 6c4009
    {
Packit 6c4009
      list_append (&timer_free_list, &__timer_array[i].links);
Packit 6c4009
      __timer_array[i].inuse = TIMER_FREE;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  for (i = 0; i < THREAD_MAXNODES; ++i)
Packit 6c4009
    list_append (&thread_free_list, &thread_array[i].links);
Packit 6c4009
Packit 6c4009
  thread_init (&__timer_signal_thread_rclk, 0, CLOCK_REALTIME);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This is a handler executed in a child process after a fork()
Packit 6c4009
   occurs.  It reinitializes the module, resetting all of the data
Packit 6c4009
   structures to their initial state.  The mutex is initialized in
Packit 6c4009
   case it was locked in the parent process.  */
Packit 6c4009
static void
Packit 6c4009
reinit_after_fork (void)
Packit 6c4009
{
Packit 6c4009
  init_module ();
Packit 6c4009
  pthread_mutex_init (&__timer_mutex, 0);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Called once form pthread_once in timer_init. This initializes the
Packit 6c4009
   module and ensures that reinit_after_fork will be executed in any
Packit 6c4009
   child process.  */
Packit 6c4009
void
Packit 6c4009
__timer_init_once (void)
Packit 6c4009
{
Packit 6c4009
  init_module ();
Packit 6c4009
  pthread_atfork (0, 0, reinit_after_fork);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Deinitialize a thread that is about to be deallocated.  */
Packit 6c4009
static void
Packit 6c4009
thread_deinit (struct thread_node *thread)
Packit 6c4009
{
Packit 6c4009
  assert (list_isempty (&thread->timer_queue));
Packit 6c4009
  pthread_cond_destroy (&thread->cond);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Allocate a thread structure from the global free list.  Global
Packit 6c4009
   mutex lock must be held by caller.  The thread is moved to
Packit 6c4009
   the active list. */
Packit 6c4009
struct thread_node *
Packit 6c4009
__timer_thread_alloc (const pthread_attr_t *desired_attr, clockid_t clock_id)
Packit 6c4009
{
Packit 6c4009
  struct list_head *node = list_first (&thread_free_list);
Packit 6c4009
Packit 6c4009
  if (node != list_null (&thread_free_list))
Packit 6c4009
    {
Packit 6c4009
      struct thread_node *thread = thread_links2ptr (node);
Packit 6c4009
      list_unlink (node);
Packit 6c4009
      thread_init (thread, desired_attr, clock_id);
Packit 6c4009
      list_append (&thread_active_list, node);
Packit 6c4009
      return thread;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Return a thread structure to the global free list.  Global lock
Packit 6c4009
   must be held by caller.  */
Packit 6c4009
void
Packit 6c4009
__timer_thread_dealloc (struct thread_node *thread)
Packit 6c4009
{
Packit 6c4009
  thread_deinit (thread);
Packit 6c4009
  list_unlink (&thread->links);
Packit 6c4009
  list_append (&thread_free_list, &thread->links);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Each of our threads which terminates executes this cleanup
Packit 6c4009
   handler. We never terminate threads ourselves; if a thread gets here
Packit 6c4009
   it means that the evil application has killed it.  If the thread has
Packit 6c4009
   timers, these require servicing and so we must hire a replacement
Packit 6c4009
   thread right away.  We must also unblock another thread that may
Packit 6c4009
   have been waiting for this thread to finish servicing a timer (see
Packit 6c4009
   timer_delete()).  */
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
thread_cleanup (void *val)
Packit 6c4009
{
Packit 6c4009
  if (val != NULL)
Packit 6c4009
    {
Packit 6c4009
      struct thread_node *thread = val;
Packit 6c4009
Packit 6c4009
      /* How did the signal thread get killed?  */
Packit 6c4009
      assert (thread != &__timer_signal_thread_rclk);
Packit 6c4009
Packit 6c4009
      pthread_mutex_lock (&__timer_mutex);
Packit 6c4009
Packit 6c4009
      thread->exists = 0;
Packit 6c4009
Packit 6c4009
      /* We are no longer processing a timer event.  */
Packit 6c4009
      thread->current_timer = 0;
Packit 6c4009
Packit 6c4009
      if (list_isempty (&thread->timer_queue))
Packit 6c4009
	__timer_thread_dealloc (thread);
Packit 6c4009
      else
Packit 6c4009
	(void) __timer_thread_start (thread);
Packit 6c4009
Packit 6c4009
      pthread_mutex_unlock (&__timer_mutex);
Packit 6c4009
Packit 6c4009
      /* Unblock potentially blocked timer_delete().  */
Packit 6c4009
      pthread_cond_broadcast (&thread->cond);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Handle a timer which is supposed to go off now.  */
Packit 6c4009
static void
Packit 6c4009
thread_expire_timer (struct thread_node *self, struct timer_node *timer)
Packit 6c4009
{
Packit 6c4009
  self->current_timer = timer; /* Lets timer_delete know timer is running. */
Packit 6c4009
Packit 6c4009
  pthread_mutex_unlock (&__timer_mutex);
Packit 6c4009
Packit 6c4009
  switch (__builtin_expect (timer->event.sigev_notify, SIGEV_SIGNAL))
Packit 6c4009
    {
Packit 6c4009
    case SIGEV_NONE:
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case SIGEV_SIGNAL:
Packit 6c4009
#ifdef __NR_rt_sigqueueinfo
Packit 6c4009
      {
Packit 6c4009
	siginfo_t info;
Packit 6c4009
Packit 6c4009
	/* First, clear the siginfo_t structure, so that we don't pass our
Packit 6c4009
	   stack content to other tasks.  */
Packit 6c4009
	memset (&info, 0, sizeof (siginfo_t));
Packit 6c4009
	/* We must pass the information about the data in a siginfo_t
Packit 6c4009
           value.  */
Packit 6c4009
	info.si_signo = timer->event.sigev_signo;
Packit 6c4009
	info.si_code = SI_TIMER;
Packit 6c4009
	info.si_pid = timer->creator_pid;
Packit 6c4009
	info.si_uid = getuid ();
Packit 6c4009
	info.si_value = timer->event.sigev_value;
Packit 6c4009
Packit 6c4009
	INLINE_SYSCALL (rt_sigqueueinfo, 3, info.si_pid, info.si_signo, &info;;
Packit 6c4009
      }
Packit 6c4009
#else
Packit 6c4009
      if (pthread_kill (self->captured, timer->event.sigev_signo) != 0)
Packit 6c4009
	{
Packit 6c4009
	  if (pthread_kill (self->id, timer->event.sigev_signo) != 0)
Packit 6c4009
	    abort ();
Packit 6c4009
        }
Packit 6c4009
#endif
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case SIGEV_THREAD:
Packit 6c4009
      timer->event.sigev_notify_function (timer->event.sigev_value);
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    default:
Packit 6c4009
      assert (! "unknown event");
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  pthread_mutex_lock (&__timer_mutex);
Packit 6c4009
Packit 6c4009
  self->current_timer = 0;
Packit 6c4009
Packit 6c4009
  pthread_cond_broadcast (&self->cond);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Thread function; executed by each timer thread. The job of this
Packit 6c4009
   function is to wait on the thread's timer queue and expire the
Packit 6c4009
   timers in chronological order as close to their scheduled time as
Packit 6c4009
   possible.  */
Packit 6c4009
static void
Packit 6c4009
__attribute__ ((noreturn))
Packit 6c4009
thread_func (void *arg)
Packit 6c4009
{
Packit 6c4009
  struct thread_node *self = arg;
Packit 6c4009
Packit 6c4009
  /* Register cleanup handler, in case rogue application terminates
Packit 6c4009
     this thread.  (This cannot happen to __timer_signal_thread, which
Packit 6c4009
     doesn't invoke application callbacks). */
Packit 6c4009
Packit 6c4009
  pthread_cleanup_push (thread_cleanup, self);
Packit 6c4009
Packit 6c4009
  pthread_mutex_lock (&__timer_mutex);
Packit 6c4009
Packit 6c4009
  while (1)
Packit 6c4009
    {
Packit 6c4009
      struct list_head *first;
Packit 6c4009
      struct timer_node *timer = NULL;
Packit 6c4009
Packit 6c4009
      /* While the timer queue is not empty, inspect the first node.  */
Packit 6c4009
      first = list_first (&self->timer_queue);
Packit 6c4009
      if (first != list_null (&self->timer_queue))
Packit 6c4009
	{
Packit 6c4009
	  struct timespec now;
Packit 6c4009
Packit 6c4009
	  timer = timer_links2ptr (first);
Packit 6c4009
Packit 6c4009
	  /* This assumes that the elements of the list of one thread
Packit 6c4009
	     are all for the same clock.  */
Packit 6c4009
	  __clock_gettime (timer->clock, &now;;
Packit 6c4009
Packit 6c4009
	  while (1)
Packit 6c4009
	    {
Packit 6c4009
	      /* If the timer is due or overdue, remove it from the queue.
Packit 6c4009
		 If it's a periodic timer, re-compute its new time and
Packit 6c4009
		 requeue it.  Either way, perform the timer expiry. */
Packit 6c4009
	      if (timespec_compare (&now, &timer->expirytime) < 0)
Packit 6c4009
		break;
Packit 6c4009
Packit 6c4009
	      list_unlink_ip (first);
Packit 6c4009
Packit 6c4009
	      if (__builtin_expect (timer->value.it_interval.tv_sec, 0) != 0
Packit 6c4009
		  || timer->value.it_interval.tv_nsec != 0)
Packit 6c4009
		{
Packit 6c4009
		  timer->overrun_count = 0;
Packit 6c4009
		  timespec_add (&timer->expirytime, &timer->expirytime,
Packit 6c4009
				&timer->value.it_interval);
Packit 6c4009
		  while (timespec_compare (&timer->expirytime, &now) < 0)
Packit 6c4009
		    {
Packit 6c4009
		      timespec_add (&timer->expirytime, &timer->expirytime,
Packit 6c4009
				    &timer->value.it_interval);
Packit 6c4009
		      if (timer->overrun_count < DELAYTIMER_MAX)
Packit 6c4009
			++timer->overrun_count;
Packit 6c4009
		    }
Packit 6c4009
		  __timer_thread_queue_timer (self, timer);
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      thread_expire_timer (self, timer);
Packit 6c4009
Packit 6c4009
	      first = list_first (&self->timer_queue);
Packit 6c4009
	      if (first == list_null (&self->timer_queue))
Packit 6c4009
		break;
Packit 6c4009
Packit 6c4009
	      timer = timer_links2ptr (first);
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* If the queue is not empty, wait until the expiry time of the
Packit 6c4009
	 first node.  Otherwise wait indefinitely.  Insertions at the
Packit 6c4009
	 head of the queue must wake up the thread by broadcasting
Packit 6c4009
	 this condition variable.  */
Packit 6c4009
      if (timer != NULL)
Packit 6c4009
	pthread_cond_timedwait (&self->cond, &__timer_mutex,
Packit 6c4009
				&timer->expirytime);
Packit 6c4009
      else
Packit 6c4009
	pthread_cond_wait (&self->cond, &__timer_mutex);
Packit 6c4009
    }
Packit 6c4009
  /* This macro will never be executed since the while loop loops
Packit 6c4009
     forever - but we have to add it for proper nesting.  */
Packit 6c4009
  pthread_cleanup_pop (1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Enqueue a timer in wakeup order in the thread's timer queue.
Packit 6c4009
   Returns 1 if the timer was inserted at the head of the queue,
Packit 6c4009
   causing the queue's next wakeup time to change. */
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__timer_thread_queue_timer (struct thread_node *thread,
Packit 6c4009
			    struct timer_node *insert)
Packit 6c4009
{
Packit 6c4009
  struct list_head *iter;
Packit 6c4009
  int athead = 1;
Packit 6c4009
Packit 6c4009
  for (iter = list_first (&thread->timer_queue);
Packit 6c4009
       iter != list_null (&thread->timer_queue);
Packit 6c4009
        iter = list_next (iter))
Packit 6c4009
    {
Packit 6c4009
      struct timer_node *timer = timer_links2ptr (iter);
Packit 6c4009
Packit 6c4009
      if (timespec_compare (&insert->expirytime, &timer->expirytime) < 0)
Packit 6c4009
	  break;
Packit 6c4009
      athead = 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  list_insbefore (iter, &insert->links);
Packit 6c4009
  return athead;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Start a thread and associate it with the given thread node.  Global
Packit 6c4009
   lock must be held by caller.  */
Packit 6c4009
int
Packit 6c4009
__timer_thread_start (struct thread_node *thread)
Packit 6c4009
{
Packit 6c4009
  int retval = 1;
Packit 6c4009
  sigset_t set, oset;
Packit 6c4009
Packit 6c4009
  assert (!thread->exists);
Packit 6c4009
  thread->exists = 1;
Packit 6c4009
Packit 6c4009
  sigfillset (&set);
Packit 6c4009
  pthread_sigmask (SIG_SETMASK, &set, &oset);
Packit 6c4009
Packit 6c4009
  if (pthread_create (&thread->id, &thread->attr,
Packit 6c4009
		      (void *(*) (void *)) thread_func, thread) != 0)
Packit 6c4009
    {
Packit 6c4009
      thread->exists = 0;
Packit 6c4009
      retval = -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  pthread_sigmask (SIG_SETMASK, &oset, NULL);
Packit 6c4009
Packit 6c4009
  return retval;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__timer_thread_wakeup (struct thread_node *thread)
Packit 6c4009
{
Packit 6c4009
  pthread_cond_broadcast (&thread->cond);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Search the list of active threads and find one which has matching
Packit 6c4009
   attributes.  Global mutex lock must be held by caller.  */
Packit 6c4009
struct thread_node *
Packit 6c4009
__timer_thread_find_matching (const pthread_attr_t *desired_attr,
Packit 6c4009
			      clockid_t desired_clock_id)
Packit 6c4009
{
Packit 6c4009
  struct list_head *iter = list_first (&thread_active_list);
Packit 6c4009
Packit 6c4009
  while (iter != list_null (&thread_active_list))
Packit 6c4009
    {
Packit 6c4009
      struct thread_node *candidate = thread_links2ptr (iter);
Packit 6c4009
Packit 6c4009
      if (thread_attr_compare (desired_attr, &candidate->attr)
Packit 6c4009
	  && desired_clock_id == candidate->clock_id)
Packit 6c4009
	return candidate;
Packit 6c4009
Packit 6c4009
      iter = list_next (iter);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Grab a free timer structure from the global free list.  The global
Packit 6c4009
   lock must be held by the caller.  */
Packit 6c4009
struct timer_node *
Packit 6c4009
__timer_alloc (void)
Packit 6c4009
{
Packit 6c4009
  struct list_head *node = list_first (&timer_free_list);
Packit 6c4009
Packit 6c4009
  if (node != list_null (&timer_free_list))
Packit 6c4009
    {
Packit 6c4009
      struct timer_node *timer = timer_links2ptr (node);
Packit 6c4009
      list_unlink_ip (node);
Packit 6c4009
      timer->inuse = TIMER_INUSE;
Packit 6c4009
      timer->refcount = 1;
Packit 6c4009
      return timer;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Return a timer structure to the global free list.  The global lock
Packit 6c4009
   must be held by the caller.  */
Packit 6c4009
void
Packit 6c4009
__timer_dealloc (struct timer_node *timer)
Packit 6c4009
{
Packit 6c4009
  assert (timer->refcount == 0);
Packit 6c4009
  timer->thread = NULL;	/* Break association between timer and thread.  */
Packit 6c4009
  timer->inuse = TIMER_FREE;
Packit 6c4009
  list_append (&timer_free_list, &timer->links);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Thread cancellation handler which unlocks a mutex.  */
Packit 6c4009
void
Packit 6c4009
__timer_mutex_cancel_handler (void *arg)
Packit 6c4009
{
Packit 6c4009
  pthread_mutex_unlock (arg);
Packit 6c4009
}