2000-06-08 Ulrich Drepper <drepper@redhat.com>
+ * sysdeps/pthread/Makefile: New file. Add rules to build timer
+ functionality.
+ * sysdeps/unix/sysv/linux/bits/local_lim.h: Add TIMER_MAX.
+
+2000-06-04 Kaz Kylheku <kaz@ashi.footprints.net>
+
+ * sysdeps/pthread/posix-timer.h: New file.
+ * sysdeps/pthread/timer_create.c: New file.
+ * sysdeps/pthread/timer_delete.c: New file.
+ * sysdeps/pthread/timer_getoverr.c: New file.
+ * sysdeps/pthread/timer_gettime.c: New file.
+ * sysdeps/pthread/timer_routines.c: New file.
+ * sysdeps/pthread/timer_settime.c: New file.
+ * sysdeps/pthread/tst-timer.c: New file.
+
+2000-06-08 Ulrich Drepper <drepper@redhat.com>
+
* sysdeps/unix/sysv/linux/bits/local_lim.h: Remove OPEN_MAX and
LINK_MAX definitions if necessary.
--- /dev/null
+ifeq ($(subdir),rt)
+librt-routines += timer_routines
+
+ifeq ($(have-thread-library),yes)
+tests += tst-timer
+endif
+
+ifeq (yes,$(build-shared))
+$(objpfx)tst-timer: $(objpfx)librt.so $(shared-thread-library)
+else
+$(objpfx)tst-timer: $(objpfx)librt.a $(static-thread-library)
+endif
+endif
--- /dev/null
+/* Definitions for POSIX timer implementation on top of LinuxThreads.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Kaz Kylheku <kaz@ashi.footprints.net>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <limits.h>
+#include <signal.h>
+
+/* Double linked list. */
+struct list_links
+{
+ struct list_links *next;
+ struct list_links *prev;
+};
+
+
+/* Forward declaration. */
+struct timer_node;
+
+
+/* Definitions for an internal thread of the POSIX timer implementation. */
+struct thread_node
+{
+ struct list_links links;
+ pthread_attr_t attr;
+ pthread_t id;
+ unsigned int exists;
+ struct list_links timer_queue;
+ pthread_cond_t cond;
+ struct timer_node *current_timer;
+ pthread_t captured;
+};
+
+
+/* Internal representation of a timer. */
+struct timer_node
+{
+ struct list_links links;
+ struct sigevent event;
+ clockid_t clock;
+ struct itimerspec value;
+ struct timespec expirytime;
+ pthread_attr_t attr;
+ unsigned int abstime;
+ unsigned int armed;
+ unsigned int inuse;
+ struct thread_node *thread;
+};
+
+
+/* Static array with the structures for all the timers. */
+extern struct timer_node __timer_array[TIMER_MAX];
+
+/* Global lock to protect operation on the lists. */
+extern pthread_mutex_t __timer_mutex;
+
+/* Variable to protext initialization. */
+extern pthread_once_t __timer_init_once_control;
+
+/* Nonzero if initialization of timer implementation failed. */
+extern int __timer_init_failed;
+
+/* Node for the thread used to deliver signals. */
+extern struct thread_node __timer_signal_thread;
+
+
+/* Return pointer to timer structure corresponding to ID. */
+static inline struct timer_node *
+timer_id2ptr (timer_t timerid)
+{
+ if (timerid >= 0 && timerid < TIMER_MAX && __timer_array[timerid].inuse)
+ return &__timer_array[timerid];
+
+ return NULL;
+}
+
+/* Return ID of TIMER. */
+static inline int
+timer_ptr2id (struct timer_node *timer)
+{
+ return __timer_array - timer;
+}
+
+
+/* Timespec helper routines. */
+static inline int
+timespec_compare (const struct timespec *left, const struct timespec *right)
+{
+ if (left->tv_sec < right->tv_sec)
+ return -1;
+ if (left->tv_sec > right->tv_sec)
+ return 1;
+
+ if (left->tv_nsec < right->tv_nsec)
+ return -1;
+ if (left->tv_nsec > right->tv_nsec)
+ return 1;
+
+ return 0;
+}
+
+static inline void
+timespec_add (struct timespec *sum, const struct timespec *left,
+ const struct timespec *right)
+{
+ sum->tv_sec = left->tv_sec + right->tv_sec;
+ sum->tv_nsec = left->tv_nsec + right->tv_nsec;
+
+ if (sum->tv_nsec >= 1000000000)
+ {
+ ++sum->tv_sec;
+ sum->tv_nsec -= 1000000000;
+ }
+}
+
+static inline void
+timespec_sub (struct timespec *diff, const struct timespec *left,
+ const struct timespec *right)
+{
+ diff->tv_sec = left->tv_sec - right->tv_sec;
+ diff->tv_nsec = left->tv_nsec - right->tv_nsec;
+
+ if (diff->tv_nsec < 0)
+ {
+ --diff->tv_sec;
+ diff->tv_nsec += 1000000000;
+ }
+}
+
+
+/* We need one of the list functions in the other modules. */
+static inline void
+list_unlink (struct list_links *list)
+{
+ list->next->prev = list->prev;
+ list->prev->next = list->next;
+ list->next = list;
+ list->prev = list;
+}
+
+
+/* Functions in the helper file. */
+extern void __timer_mutex_cancel_handler (void *arg);
+extern void __timer_init_once (void);
+extern struct timer_node *__timer_alloc (void);
+extern int __timer_thread_start (struct thread_node *thread);
+extern struct thread_node *__timer_thread_find_matching (const pthread_attr_t *desired_attr);
+extern struct thread_node *__timer_thread_alloc (const pthread_attr_t *desired_attr);
+extern void __timer_dealloc (struct timer_node *timer);
+extern void __timer_thread_dealloc (struct thread_node *thread);
+extern int __timer_thread_queue_timer (struct thread_node *thread,
+ struct timer_node *insert);
+extern void __timer_thread_wakeup (struct thread_node *thread);
--- /dev/null
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Kaz Kylheku <kaz@ashi.footprints.net>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <errno.h>
+#include <signal.h>
+#include <pthread.h>
+#include <time.h>
+
+#include "posix-timer.h"
+
+
+/* Create new per-process timer using CLOCK. */
+int
+timer_create (clock_id, evp, timerid)
+ clockid_t clock_id;
+ struct sigevent *evp;
+ timer_t *timerid;
+{
+ int retval = -1;
+ struct timer_node *newtimer = NULL;
+ struct thread_node *thread = NULL;
+
+ if (clock_id != CLOCK_REALTIME)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ pthread_once (&__timer_init_once_control, __timer_init_once);
+
+ if (__timer_init_failed)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ pthread_mutex_lock (&__timer_mutex);
+
+ newtimer = __timer_alloc ();
+ if (__builtin_expect (newtimer == NULL, 0))
+ {
+ errno = EAGAIN;
+ goto unlock_bail;
+ }
+
+ if (evp != NULL)
+ newtimer->event = *evp;
+ else
+ {
+ newtimer->event.sigev_notify = SIGEV_SIGNAL;
+ newtimer->event.sigev_signo = SIGALRM;
+ newtimer->event.sigev_value.sival_int = timer_ptr2id (newtimer);
+ newtimer->event.sigev_notify_function = 0;
+ }
+
+ newtimer->event.sigev_notify_attributes = &newtimer->attr;
+
+ switch (__builtin_expect (newtimer->event.sigev_notify, SIGEV_SIGNAL))
+ {
+ case SIGEV_NONE:
+ /* This is a strange choice! */
+ break;
+
+ case SIGEV_SIGNAL:
+ /* We have a global thread for delivering timed signals.
+ If it is not running, try to start it up. */
+ if (! __timer_signal_thread.exists)
+ {
+ if (__builtin_expect (__timer_thread_start (&__timer_signal_thread),
+ 1) < 0)
+ {
+ errno = EAGAIN;
+ goto unlock_bail;
+ }
+ }
+ thread = &__timer_signal_thread;
+ break;
+
+ case SIGEV_THREAD:
+ /* Copy over thread attributes or set up default ones. */
+ if (evp->sigev_notify_attributes)
+ newtimer->attr = *(pthread_attr_t *) evp->sigev_notify_attributes;
+ else
+ pthread_attr_init (&newtimer->attr);
+
+ /* Ensure thread attributes call for deatched thread. */
+ pthread_attr_setdetachstate (&newtimer->attr, PTHREAD_CREATE_DETACHED);
+
+ /* Try to find existing thread having the right attributes. */
+ thread = __timer_thread_find_matching (&newtimer->attr);
+
+ /* If no existing thread has these attributes, try to allocate one. */
+ if (thread == NULL)
+ thread = __timer_thread_alloc (&newtimer->attr);
+
+ /* Out of luck; no threads are available. */
+ if (__builtin_expect (thread == NULL, 0))
+ {
+ errno = EAGAIN;
+ goto unlock_bail;
+ }
+
+ /* If the thread is not running already, try to start it. */
+ if (! thread->exists
+ && __builtin_expect (! __timer_thread_start (thread), 0))
+ {
+ errno = EAGAIN;
+ goto unlock_bail;
+ }
+ break;
+
+ default:
+ errno = EINVAL;
+ goto unlock_bail;
+ }
+
+ newtimer->clock = clock_id;
+ newtimer->abstime = 0;
+ newtimer->armed = 0;
+ newtimer->thread = thread;
+
+ *timerid = timer_ptr2id (newtimer);
+ retval = 0;
+
+ if (__builtin_expect (retval, 0) == -1)
+ {
+ unlock_bail:
+ if (thread != NULL)
+ __timer_thread_dealloc (thread);
+ if (newtimer != NULL)
+ __timer_dealloc (newtimer);
+ }
+
+ pthread_mutex_unlock (&__timer_mutex);
+
+ return retval;
+}
--- /dev/null
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Kaz Kylheku <kaz@ashi.footprints.net>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+#include "posix-timer.h"
+
+
+/* Delete timer TIMERID. */
+int
+timer_delete (timerid)
+ timer_t timerid;
+{
+ struct timer_node *timer;
+ int retval = -1;
+
+ pthread_mutex_lock (&__timer_mutex);
+
+ timer = timer_id2ptr (timerid);
+ if (timer == NULL)
+ /* Invalid timer ID or the timer is not in use. */
+ errno = EINVAL;
+ else
+ {
+ if (timer->armed)
+ {
+ 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. */
+ list_unlink (&timer->links);
+ __timer_dealloc (timer);
+ retval = 0;
+ }
+
+ pthread_mutex_unlock (&__timer_mutex);
+
+ return retval;
+}
--- /dev/null
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Kaz Kylheku <kaz@ashi.footprints.net>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+#include "posix-timer.h"
+
+
+/* Get expiration overrun for timer TIMERID. */
+int
+timer_getoverrun (timerid)
+ timer_t timerid;
+{
+ int retval = -1;
+
+ pthread_mutex_lock (&__timer_mutex);
+
+ if (timer_id2ptr (timerid) == NULL)
+ errno = EINVAL;
+ else
+ {
+ retval = 0; /* TODO: overrun counting not supported */
+ }
+
+ pthread_mutex_lock (&__timer_mutex);
+
+ return retval;
+}
--- /dev/null
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Kaz Kylheku <kaz@ashi.footprints.net>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+#include "posix-timer.h"
+
+
+/* Get current value of timer TIMERID and store it in VLAUE. */
+int
+timer_gettime (timerid, value)
+ timer_t timerid;
+ struct itimerspec *value;
+{
+ struct timer_node *timer;
+ struct timespec now;
+ int retval = -1;
+
+ pthread_mutex_lock (&__timer_mutex);
+
+ timer = timer_id2ptr (timerid);
+ if (timer == NULL)
+ /* Invalid timer ID or the timer is not in use. */
+ errno = EINVAL;
+ else
+ {
+ value->it_interval = timer->value.it_interval;
+
+ if (timer->armed)
+ {
+ clock_gettime (timer->clock, &now);
+ timespec_sub (&value->it_value, &timer->expirytime, &now);
+ }
+ else
+ {
+ value->it_value.tv_sec = 0;
+ value->it_value.tv_nsec = 0;
+ }
+
+ retval = 0;
+ }
+
+ pthread_mutex_lock (&__timer_mutex);
+
+ return retval;
+}
--- /dev/null
+/* Helper code for POSIX timer implementation on LinuxThreads.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Kaz Kylheku <kaz@ashi.footprints.net>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <assert.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <time.h>
+
+#include "posix-timer.h"
+
+
+/* Number of threads used. */
+#define THREAD_MAXNODES 16
+
+/* Array containing the descriptors for the used threads. */
+static struct thread_node thread_array[THREAD_MAXNODES];
+
+/* Static array with the structures for all the timers. */
+struct timer_node __timer_array[TIMER_MAX];
+
+/* Global lock to protect operation on the lists. */
+pthread_mutex_t __timer_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Variable to protext initialization. */
+pthread_once_t __timer_init_once_control = PTHREAD_ONCE_INIT;
+
+/* Nonzero if initialization of timer implementation failed. */
+int __timer_init_failed;
+
+/* Node for the thread used to deliver signals. */
+struct thread_node __timer_signal_thread;
+
+/* Lists to keep free and used timers and threads. */
+struct list_links timer_free_list;
+struct list_links thread_free_list;
+struct list_links thread_active_list;
+
+
+/* List handling functions. */
+static inline void
+list_init (struct list_links *list)
+{
+ list->next = list->prev = list;
+}
+
+static inline void
+list_append (struct list_links *list, struct list_links *newp)
+{
+ newp->prev = list->prev;
+ newp->next = list;
+ list->prev->next = newp;
+ list->prev = list;
+}
+
+static inline void
+list_insbefore (struct list_links *list, struct list_links *newp)
+{
+ list_append (list, newp);
+}
+
+static inline struct list_links *
+list_first (struct list_links *list)
+{
+ return list->next;
+}
+
+static inline struct list_links *
+list_null (struct list_links *list)
+{
+ return list;
+}
+
+static inline struct list_links *
+list_next (struct list_links *list)
+{
+ return list->next;
+}
+
+static inline int
+list_isempty (struct list_links *list)
+{
+ return list->next == list;
+}
+
+
+/* Functions build on top of the list functions. */
+static inline struct thread_node *
+thread_links2ptr (struct list_links *list)
+{
+ return (struct thread_node *) ((char *) list
+ - offsetof (struct thread_node, links));
+}
+
+static inline struct timer_node *
+timer_links2ptr (struct list_links *list)
+{
+ return (struct timer_node *) ((char *) list
+ - offsetof (struct timer_node, links));
+}
+
+
+/* Initialize a newly allocated thread structure. */
+static void
+thread_init (struct thread_node *thread, const pthread_attr_t *attr)
+{
+ if (attr != NULL)
+ thread->attr = *attr;
+ else
+ {
+ pthread_attr_init (&thread->attr);
+ pthread_attr_setdetachstate (&thread->attr, PTHREAD_CREATE_DETACHED);
+ }
+
+ thread->exists = 0;
+ list_init (&thread->timer_queue);
+ pthread_cond_init (&thread->cond, 0);
+ thread->current_timer = 0;
+ thread->captured = pthread_self ();
+}
+
+
+/* Initialize the global lists, and acquire global resources. Error
+ reporting is done by storing a non-zero value to the global variable
+ timer_init_failed. */
+static void
+init_module (void)
+{
+ int i;
+
+ list_init (&timer_free_list);
+ list_init (&thread_free_list);
+ list_init (&thread_active_list);
+
+ for (i = 0; i < TIMER_MAX; ++i)
+ {
+ list_append (&timer_free_list, &__timer_array[i].links);
+ __timer_array[i].inuse = 0;
+ }
+
+ for (i = 0; i < THREAD_MAXNODES - 1; ++i)
+ list_append (&thread_free_list, &thread_array[i].links);
+
+ thread_init (&__timer_signal_thread, 0);
+}
+
+
+/* This is a handler executed in a child process after a fork()
+ occurs. It reinitializes the module, resetting all of the data
+ structures to their initial state. The mutex is initialized in
+ case it was locked in the parent process. */
+static void
+reinit_after_fork (void)
+{
+ init_module ();
+ pthread_mutex_init (&__timer_mutex, 0);
+}
+
+
+/* Called once form pthread_once in timer_init. This initializes the
+ module and ensures that reinit_after_fork will be executed in any
+ child process. */
+void
+__timer_init_once (void)
+{
+ init_module ();
+ pthread_atfork (0, 0, reinit_after_fork);
+}
+
+
+/* Deinitialize a thread that is about to be deallocated. */
+static void
+thread_deinit (struct thread_node *thread)
+{
+ assert (list_isempty (&thread->timer_queue));
+ pthread_cond_destroy (&thread->cond);
+}
+
+
+/* Allocate a thread structure from the global free list. Global
+ mutex lock must be held by caller. */
+struct thread_node *
+__timer_thread_alloc (const pthread_attr_t *desired_attr)
+{
+ struct list_links *node = list_first (&thread_free_list);
+
+ if (node != list_null (&thread_free_list))
+ {
+ struct thread_node *thread = thread_links2ptr (node);
+ list_unlink (node);
+ thread_init (thread, desired_attr);
+ return thread;
+ }
+
+ return 0;
+}
+
+
+/* Return a thread structure to the global free list. Global lock
+ must be held by caller. */
+void
+__timer_thread_dealloc (struct thread_node *thread)
+{
+ thread_deinit (thread);
+ list_append (&thread_free_list, &thread->links);
+}
+
+
+/*
+ * Each of our threads which terminates executes this cleanup handler. We never
+ * terminate threads ourselves; if a thread gets here it means that the evil
+ * application has killed it. If the thread has timers, these require
+ * servicing and so we must hire a replacement thread right away.
+ * We must also unblock another thread that may have been waiting for
+ * this thread to finish servicing a timer (see timer_delete()).
+ */
+
+static void
+thread_cleanup (void *val)
+{
+ if (val != NULL)
+ {
+ struct thread_node *thread = val;
+
+ /* How did the signal thread get killed? */
+ assert (thread != &__timer_signal_thread);
+
+ pthread_mutex_lock (&__timer_mutex);
+
+ thread->exists = 0;
+
+ /* We are no longer processing a timer event. */
+ thread->current_timer = 0;
+
+ if (list_isempty (&thread->timer_queue))
+ {
+ list_unlink (&thread->links);
+ __timer_thread_dealloc (thread);
+ }
+ else
+ (void) __timer_thread_start (thread);
+
+ pthread_mutex_unlock (&__timer_mutex);
+
+ /* Unblock potentially blocked timer_delete(). */
+ pthread_cond_broadcast (&thread->cond);
+ }
+}
+
+
+/* Handle a timer which is supposed to go off now. */
+static void
+thread_expire_timer (struct thread_node *self, struct timer_node *timer)
+{
+ self->current_timer = timer;
+
+ pthread_mutex_unlock (&__timer_mutex);
+
+ switch (timer->event.sigev_notify)
+ {
+ case SIGEV_NONE:
+ assert (! "timer_create should never have created such a timer");
+ break;
+
+ case SIGEV_SIGNAL:
+ if (pthread_kill (self->captured, timer->event.sigev_signo) != 0)
+ {
+ if (pthread_kill (self->id, timer->event.sigev_signo) != 0)
+ abort ();
+ }
+ break;
+
+ case SIGEV_THREAD:
+ timer->event.sigev_notify_function (timer->event.sigev_value);
+ break;
+
+ default:
+ assert (! "unknown event");
+ break;
+ }
+
+ pthread_mutex_lock (&__timer_mutex);
+
+ self->current_timer = 0;
+
+ pthread_cond_broadcast (&self->cond);
+}
+
+
+/* Thread function; executed by each timer thread. The job of this
+ function is to wait on the thread's timer queue and expire the
+ timers in chronological order as close to their scheduled time as
+ possible. */
+static void *
+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 (first);
+
+ if (timer->value.it_interval.tv_sec
+ || timer->value.it_interval.tv_nsec)
+ {
+ timespec_add (&timer->expirytime, &now,
+ &timer->value.it_interval);
+ (void) __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);
+ }
+
+ pthread_mutex_unlock (&__timer_mutex);
+ pthread_cleanup_pop (1);
+
+ return NULL;
+}
+
+
+/* Enqueue a timer in wakeup order in the thread's timer queue. */
+int
+__timer_thread_queue_timer (struct thread_node *thread,
+ struct timer_node *insert)
+{
+ struct list_links *iter;
+ struct timer_node *matching = NULL;
+ struct timer_node *timer = NULL;
+
+ for (iter = list_first (&thread->timer_queue);
+ iter != list_null (&thread->timer_queue);
+ iter = list_next (iter))
+ {
+ timer = timer_links2ptr (iter);
+
+ if (insert->clock == timer->clock)
+ {
+ matching = timer;
+ if (timespec_compare (&insert->expirytime, &timer->expirytime) < 0)
+ break;
+ }
+ }
+
+ if (insert->clock != timer->clock)
+ {
+ if (matching == NULL)
+ /* We cannot queue this timer. */
+ return -1;
+
+ timer = matching;
+ }
+
+ list_insbefore (iter, &insert->links);
+
+ return 0;
+}
+
+
+/* Start a thread and associate it with the given thread node. Global
+ lock must be held by caller. */
+int
+__timer_thread_start (struct thread_node *thread)
+{
+ int retval = 1;
+
+ assert (!thread->exists);
+
+ thread->exists = 1;
+
+ if (pthread_create (&thread->id, &thread->attr, thread_func, thread) != 0)
+ {
+ thread->exists = 0;
+ retval = -1;
+ }
+
+ return retval;
+}
+
+
+void
+__timer_thread_wakeup (struct thread_node *thread)
+{
+ pthread_cond_broadcast (&thread->cond);
+}
+
+
+/* Compare two pthread_attr_t thread attributes for exact equality.
+ Returns 1 if they are equal, otherwise zero if they are not equal or
+ contain illegal values. This version is LinuxThreads-specific for
+ performance reason. One could use the access functions to get the
+ values of all the fields of the attribute structure. */
+static int
+thread_attr_compare (const pthread_attr_t *left, const pthread_attr_t *right)
+{
+ return (left->__detachstate == right->__detachstate
+ && left->__schedpolicy == right->__schedpolicy
+ && (left->__schedparam.sched_priority
+ == right->__schedparam.sched_priority)
+ && left->__inheritsched == right->__inheritsched
+ && left->__scope == right->__scope);
+}
+
+
+/* Search the list of active threads and find one which has matching
+ attributes. Global mutex lock must be held by caller. */
+struct thread_node *
+__timer_thread_find_matching (const pthread_attr_t *desired_attr)
+{
+ struct list_links *iter = list_first (&thread_active_list);
+
+ while (iter != list_null (&thread_active_list))
+ {
+ struct thread_node *candidate = thread_links2ptr (iter);
+
+ if (thread_attr_compare (desired_attr, &candidate->attr))
+ {
+ list_unlink (iter);
+ return candidate;
+ }
+
+ iter = list_next (iter);
+ }
+
+ return NULL;
+}
+
+
+/* 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 (node);
+ timer->inuse = 1;
+ return timer;
+ }
+
+ return NULL;
+}
+
+
+/* Return a timer structure to the global free list. The global lock
+ must be held by the caller. */
+void
+__timer_dealloc (struct timer_node *timer)
+{
+ timer->thread = NULL; /* Break association between timer and thread. */
+ timer->inuse = 0;
+ list_append (&timer_free_list, &timer->links);
+}
+
+
+/* Thread cancellation handler which unlocks a mutex. */
+void
+__timer_mutex_cancel_handler (void *arg)
+{
+ pthread_mutex_unlock (arg);
+}
--- /dev/null
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Kaz Kylheku <kaz@ashi.footprints.net>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+#include "posix-timer.h"
+
+
+/* Set timer TIMERID to VALUE, returning old value in OVLAUE. */
+int
+timer_settime (timerid, flags, value, ovalue)
+ 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;
+ int retval = -1;
+
+ pthread_mutex_lock (&__timer_mutex);
+
+ timer = timer_id2ptr (timerid);
+ if (timer == NULL)
+ {
+ 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)
+ {
+ errno = EINVAL;
+ goto bail;
+ }
+
+ if (ovalue != NULL)
+ {
+ ovalue->it_interval = timer->value.it_interval;
+
+ if (timer->armed)
+ {
+ clock_gettime (timer->clock, &now);
+ have_now = 1;
+ 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 (&timer->links);
+ timer->armed = 0;
+
+ thread = timer->thread;
+
+ 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
+ {
+ if (! have_now)
+ clock_gettime (timer->clock, &now);
+
+ timespec_add (&timer->expirytime, &now, &value->it_value);
+ }
+
+ __timer_thread_queue_timer (thread, timer);
+ timer->armed = 1;
+ }
+
+ retval = 0;
+
+bail:
+ pthread_mutex_unlock (&__timer_mutex);
+
+ /* TODO: optimize this. Only need to wake up the thread if inserting
+ a new timer at the head of the queue. */
+ if (thread != NULL)
+ __timer_thread_wakeup (thread);
+
+ return retval;
+}
--- /dev/null
+/* Tests for POSIX timer implementation.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Kaz Kylheku <kaz@ashi.footprints.net>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+
+static void
+notify_func (union sigval sigval)
+{
+ puts ("notify_func");
+}
+
+
+static void
+signal_func (int sig)
+{
+ static const char text[] = "signal_func\n";
+ signal (sig, signal_func);
+ write (STDOUT_FILENO, text, sizeof text - 1);
+}
+
+static void
+intr_sleep (int sec)
+{
+ struct timespec ts;
+
+ ts.tv_sec = sec;
+ ts.tv_nsec = 0;
+
+ while (nanosleep (&ts, &ts) == -1 && errno == EINTR)
+ ;
+}
+
+#define ZSIGALRM 14
+
+
+int
+main (void)
+{
+ struct timespec ts;
+ timer_t timer_sig, timer_thr1, timer_thr2;
+ int retval;
+ struct sigevent sigev1 = { SIGEV_SIGNAL, ZSIGALRM, { 0 }, notify_func, 0 };
+ struct sigevent sigev2 = { SIGEV_THREAD, 0, { 0 }, notify_func, 0 };
+ struct itimerspec itimer1 = { { 0, 200000000 }, { 0, 200000000 } };
+ struct itimerspec itimer2 = { { 0, 100000000 }, { 0, 500000000 } };
+ struct itimerspec itimer3 = { { 0, 150000000 }, { 0, 300000000 } };
+ struct itimerspec old;
+
+ retval = clock_gettime (CLOCK_REALTIME, &ts);
+
+ setvbuf (stdout, 0, _IOLBF, 0);
+
+ printf ("clock_gettime returned %d, timespec = { %ld, %ld }\n",
+ retval, ts.tv_sec, ts.tv_nsec);
+
+ retval = clock_getres (CLOCK_REALTIME, &ts);
+
+ printf ("clock_getres returned %d, timespec = { %ld, %ld }\n",
+ retval, ts.tv_sec, ts.tv_nsec);
+
+#if 1
+ timer_create (CLOCK_REALTIME, &sigev1, &timer_sig);
+#else
+ timer_create (CLOCK_REALTIME, 0, &timer_sig);
+#endif
+
+ timer_create (CLOCK_REALTIME, &sigev2, &timer_thr1);
+ timer_create (CLOCK_REALTIME, &sigev2, &timer_thr2);
+
+#if 0
+ if (fork ())
+ exit (1);
+#endif
+
+ timer_settime (timer_thr1, 0, &itimer2, &old);
+ timer_settime (timer_thr2, 0, &itimer3, &old);
+
+ signal (ZSIGALRM, signal_func);
+
+ timer_settime (timer_sig, 0, &itimer1, &old);
+
+ timer_delete (-1);
+
+ intr_sleep (3);
+
+ timer_delete (timer_sig);
+ timer_delete (timer_thr1);
+
+ intr_sleep (3);
+
+ timer_delete (timer_thr2);
+
+ return 0;
+}
/* Minimum size for a thread. We are free to choose a reasonable value. */
#define PTHREAD_STACK_MIN 16384
+
+/* Maximum number of POSIX timers available. */
+#define TIMER_MAX 256