thread_func (void *arg)
{
  struct thread_node *self = arg;

  /* Register cleanup handler, in case rogue application terminates
     this thread.  (This cannot happen to __timer_signal_thread, which
     doesn't invoke application callbacks). */

  pthread_cleanup_push (thread_cleanup, self);

  pthread_mutex_lock (&__timer_mutex);

  while (1)
    {
      struct list_links *first;
      struct timer_node *timer = NULL;

      /* While the timer queue is not empty, inspect the first node.  */
      first = list_first (&self->timer_queue);
      if (first != list_null (&self->timer_queue))
	{
	  struct timespec now;

	  timer = timer_links2ptr (first);

	  /* This assumes that the elements of the list of one thread
	     are all for the same clock.  */
	  clock_gettime (timer->clock, &now);

	  while (1)
	    {
	      /* If the timer is due or overdue, remove it from the queue.
		 If it's a periodic timer, re-compute its new time and
		 requeue it.  Either way, perform the timer expiry. */
	      if (timespec_compare (&now, &timer->expirytime) < 0)
		break;

	      list_unlink_ip (first);

	      if (__builtin_expect (timer->value.it_interval.tv_sec, 0) != 0
		  || timer->value.it_interval.tv_nsec != 0)
		{
		  timespec_add (&timer->expirytime, &now,
				&timer->value.it_interval);
		  __timer_thread_queue_timer (self, timer);
		}

	      thread_expire_timer (self, timer);

	      first = list_first (&self->timer_queue);
	      if (first == list_null (&self->timer_queue))
		break;

	      timer = timer_links2ptr (first);
	    }
	}

      /* If the queue is not empty, wait until the expiry time of the
	 first node.  Otherwise wait indefinitely.  Insertions at the
	 head of the queue must wake up the thread by broadcasting
	 this condition variable.  */
      if (timer != NULL)
	pthread_cond_timedwait (&self->cond, &__timer_mutex,
				&timer->expirytime);
      else
	pthread_cond_wait (&self->cond, &__timer_mutex);
    }
  /* This macro will never be executed since the while loop loops
     forever - but we have to add it for proper nesting.  */
  pthread_cleanup_pop (1);

}
Exemple #2
0
/* Set timer TIMERID to VALUE, returning old value in OVLAUE.  */
int
timer_settime (timer_t timerid, int flags, const struct itimerspec *value,
	       struct itimerspec *ovalue)
{
  struct timer_node *timer;
  struct thread_node *thread = NULL;
  struct timespec now;
  int have_now = 0, need_wakeup = 0;
  int retval = -1;

  timer = timer_id2ptr (timerid);
  if (timer == NULL)
    {
      __set_errno (EINVAL);
      goto bail;
    }

  if (value->it_interval.tv_nsec < 0
      || value->it_interval.tv_nsec >= 1000000000
      || value->it_value.tv_nsec < 0
      || value->it_value.tv_nsec >= 1000000000)
    {
      __set_errno (EINVAL);
      goto bail;
    }

  /* Will need to know current time since this is a relative timer;
     might as well make the system call outside of the lock now! */

  if ((flags & TIMER_ABSTIME) == 0)
    {
      clock_gettime (timer->clock, &now);
      have_now = 1;
    }

  pthread_mutex_lock (&__timer_mutex);
  timer_addref (timer);

  /* One final check of timer validity; this one is possible only
     until we have the mutex, because it accesses the inuse flag. */

  if (! timer_valid(timer))
    {
      __set_errno (EINVAL);
      goto unlock_bail;
    }

  if (ovalue != NULL)
    {
      ovalue->it_interval = timer->value.it_interval;

      if (timer->armed)
	{
	  if (! have_now)
	    {
	      pthread_mutex_unlock (&__timer_mutex);
	      clock_gettime (timer->clock, &now);
	      have_now = 1;
	      pthread_mutex_lock (&__timer_mutex);
	      timer_addref (timer);
	    }

	  timespec_sub (&ovalue->it_value, &timer->expirytime, &now);
	}
      else
	{
	  ovalue->it_value.tv_sec = 0;
	  ovalue->it_value.tv_nsec = 0;
	}
    }

  timer->value = *value;

  list_unlink_ip (&timer->links);
  timer->armed = 0;

  thread = timer->thread;

  /* A value of { 0, 0 } causes the timer to be stopped. */
  if (value->it_value.tv_sec != 0
      || __builtin_expect (value->it_value.tv_nsec != 0, 1))
    {
      if ((flags & TIMER_ABSTIME) != 0)
	/* The user specified the expiration time.  */
	timer->expirytime = value->it_value;
      else
	timespec_add (&timer->expirytime, &now, &value->it_value);

      /* Only need to wake up the thread if timer is inserted
	 at the head of the queue. */
      if (thread != NULL)
	need_wakeup = __timer_thread_queue_timer (thread, timer);
      timer->armed = 1;
    }

  retval = 0;

unlock_bail:
  timer_delref (timer);
  pthread_mutex_unlock (&__timer_mutex);

bail:
  if (thread != NULL && need_wakeup)
    __timer_thread_wakeup (thread);

  return retval;
}