* 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>
This commit is contained in:
Ulrich Drepper 2000-06-08 19:54:27 +00:00
parent 47bc7a9be1
commit 2715f28ad4
11 changed files with 1289 additions and 0 deletions

View File

@ -1,3 +1,20 @@
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

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View 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