Update.
authorUlrich Drepper <drepper@redhat.com>
Thu, 8 Jun 2000 19:54:27 +0000 (19:54 +0000)
committerUlrich Drepper <drepper@redhat.com>
Thu, 8 Jun 2000 19:54:27 +0000 (19:54 +0000)
* 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>

linuxthreads/ChangeLog
linuxthreads/sysdeps/pthread/Makefile [new file with mode: 0644]
linuxthreads/sysdeps/pthread/posix-timer.h [new file with mode: 0644]
linuxthreads/sysdeps/pthread/timer_create.c [new file with mode: 0644]
linuxthreads/sysdeps/pthread/timer_delete.c [new file with mode: 0644]
linuxthreads/sysdeps/pthread/timer_getoverr.c [new file with mode: 0644]
linuxthreads/sysdeps/pthread/timer_gettime.c [new file with mode: 0644]
linuxthreads/sysdeps/pthread/timer_routines.c [new file with mode: 0644]
linuxthreads/sysdeps/pthread/timer_settime.c [new file with mode: 0644]
linuxthreads/sysdeps/pthread/tst-timer.c [new file with mode: 0644]
linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h

index 4b6e585..32a0e9f 100644 (file)
@@ -1,5 +1,22 @@
 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.
 
diff --git a/linuxthreads/sysdeps/pthread/Makefile b/linuxthreads/sysdeps/pthread/Makefile
new file mode 100644 (file)
index 0000000..e896cad
--- /dev/null
@@ -0,0 +1,13 @@
+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
diff --git a/linuxthreads/sysdeps/pthread/posix-timer.h b/linuxthreads/sysdeps/pthread/posix-timer.h
new file mode 100644 (file)
index 0000000..fcb61aa
--- /dev/null
@@ -0,0 +1,168 @@
+/* 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);
diff --git a/linuxthreads/sysdeps/pthread/timer_create.c b/linuxthreads/sysdeps/pthread/timer_create.c
new file mode 100644 (file)
index 0000000..36823e8
--- /dev/null
@@ -0,0 +1,153 @@
+/* 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;
+}
diff --git a/linuxthreads/sysdeps/pthread/timer_delete.c b/linuxthreads/sysdeps/pthread/timer_delete.c
new file mode 100644 (file)
index 0000000..9e83ae1
--- /dev/null
@@ -0,0 +1,69 @@
+/* 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;
+}
diff --git a/linuxthreads/sysdeps/pthread/timer_getoverr.c b/linuxthreads/sysdeps/pthread/timer_getoverr.c
new file mode 100644 (file)
index 0000000..f3aee45
--- /dev/null
@@ -0,0 +1,46 @@
+/* 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;
+}
diff --git a/linuxthreads/sysdeps/pthread/timer_gettime.c b/linuxthreads/sysdeps/pthread/timer_gettime.c
new file mode 100644 (file)
index 0000000..2db89a0
--- /dev/null
@@ -0,0 +1,64 @@
+/* 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;
+}
diff --git a/linuxthreads/sysdeps/pthread/timer_routines.c b/linuxthreads/sysdeps/pthread/timer_routines.c
new file mode 100644 (file)
index 0000000..eb14643
--- /dev/null
@@ -0,0 +1,528 @@
+/* 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);
+}
diff --git a/linuxthreads/sysdeps/pthread/timer_settime.c b/linuxthreads/sysdeps/pthread/timer_settime.c
new file mode 100644 (file)
index 0000000..63b117f
--- /dev/null
@@ -0,0 +1,112 @@
+/* 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;
+}
diff --git a/linuxthreads/sysdeps/pthread/tst-timer.c b/linuxthreads/sysdeps/pthread/tst-timer.c
new file mode 100644 (file)
index 0000000..9d80a7d
--- /dev/null
@@ -0,0 +1,116 @@
+/* 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;
+}
index d68384b..0a7c837 100644 (file)
@@ -72,3 +72,6 @@
 
 /* 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