/* Grab a free timer structure from the global free list. The global lock must be held by the caller. */ struct timer_node * __timer_alloc (void) { struct list_links *node = list_first (&timer_free_list); if (node != list_null (&timer_free_list)) { struct timer_node *timer = timer_links2ptr (node); list_unlink_ip (node); timer->inuse = TIMER_INUSE; timer->refcount = 1; return timer; } return NULL; }
/* Delete timer TIMERID. */ int timer_delete ( timer_t timerid) { struct timer_node *timer; int retval = -1; pthread_mutex_lock (&__timer_mutex); timer = timer_id2ptr (timerid); if (! timer_valid (timer)) /* Invalid timer ID or the timer is not in use. */ __set_errno (EINVAL); else { if (timer->armed && timer->thread != NULL) { struct thread_node *thread = timer->thread; assert (thread != NULL); /* If thread is cancelled while waiting for handler to terminate, the mutex is unlocked and timer_delete is aborted. */ pthread_cleanup_push (__timer_mutex_cancel_handler, &__timer_mutex); /* If timer is currently being serviced, wait for it to finish. */ while (thread->current_timer == timer) pthread_cond_wait (&thread->cond, &__timer_mutex); pthread_cleanup_pop (0); } /* Remove timer from whatever queue it may be on and deallocate it. */ timer->inuse = TIMER_DELETED; list_unlink_ip (&timer->links); timer_delref (timer); retval = 0; } pthread_mutex_unlock (&__timer_mutex); return retval; }
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); }
/* 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; }