mirror of
https://sourceware.org/git/glibc.git
synced 2025-08-07 06:43:00 +03:00
Update.
* 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:
@@ -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>
|
2000-06-08 Ulrich Drepper <drepper@redhat.com>
|
||||||
|
|
||||||
* sysdeps/unix/sysv/linux/bits/local_lim.h: Remove OPEN_MAX and
|
* sysdeps/unix/sysv/linux/bits/local_lim.h: Remove OPEN_MAX and
|
||||||
|
13
linuxthreads/sysdeps/pthread/Makefile
Normal file
13
linuxthreads/sysdeps/pthread/Makefile
Normal 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
|
168
linuxthreads/sysdeps/pthread/posix-timer.h
Normal file
168
linuxthreads/sysdeps/pthread/posix-timer.h
Normal 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);
|
153
linuxthreads/sysdeps/pthread/timer_create.c
Normal file
153
linuxthreads/sysdeps/pthread/timer_create.c
Normal 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;
|
||||||
|
}
|
69
linuxthreads/sysdeps/pthread/timer_delete.c
Normal file
69
linuxthreads/sysdeps/pthread/timer_delete.c
Normal 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;
|
||||||
|
}
|
46
linuxthreads/sysdeps/pthread/timer_getoverr.c
Normal file
46
linuxthreads/sysdeps/pthread/timer_getoverr.c
Normal 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;
|
||||||
|
}
|
64
linuxthreads/sysdeps/pthread/timer_gettime.c
Normal file
64
linuxthreads/sysdeps/pthread/timer_gettime.c
Normal 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;
|
||||||
|
}
|
528
linuxthreads/sysdeps/pthread/timer_routines.c
Normal file
528
linuxthreads/sysdeps/pthread/timer_routines.c
Normal 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);
|
||||||
|
}
|
112
linuxthreads/sysdeps/pthread/timer_settime.c
Normal file
112
linuxthreads/sysdeps/pthread/timer_settime.c
Normal 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;
|
||||||
|
}
|
116
linuxthreads/sysdeps/pthread/tst-timer.c
Normal file
116
linuxthreads/sysdeps/pthread/tst-timer.c
Normal 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;
|
||||||
|
}
|
@@ -72,3 +72,6 @@
|
|||||||
|
|
||||||
/* Minimum size for a thread. We are free to choose a reasonable value. */
|
/* Minimum size for a thread. We are free to choose a reasonable value. */
|
||||||
#define PTHREAD_STACK_MIN 16384
|
#define PTHREAD_STACK_MIN 16384
|
||||||
|
|
||||||
|
/* Maximum number of POSIX timers available. */
|
||||||
|
#define TIMER_MAX 256
|
||||||
|
Reference in New Issue
Block a user