diff options
author | Roland McGrath <roland@gnu.org> | 1995-02-18 01:27:10 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 1995-02-18 01:27:10 +0000 |
commit | 28f540f45bbacd939bfd07f213bcad2bf730b1bf (patch) | |
tree | 15f07c4c43d635959c6afee96bde71fb1b3614ee /sysdeps/mach/hurd/setitimer.c | |
download | glibc-28f540f45bbacd939bfd07f213bcad2bf730b1bf.tar.gz glibc-28f540f45bbacd939bfd07f213bcad2bf730b1bf.tar.xz glibc-28f540f45bbacd939bfd07f213bcad2bf730b1bf.zip |
initial import
Diffstat (limited to 'sysdeps/mach/hurd/setitimer.c')
-rw-r--r-- | sysdeps/mach/hurd/setitimer.c | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/sysdeps/mach/hurd/setitimer.c b/sysdeps/mach/hurd/setitimer.c new file mode 100644 index 0000000000..4351c5ea99 --- /dev/null +++ b/sysdeps/mach/hurd/setitimer.c @@ -0,0 +1,332 @@ +/* Copyright (C) 1994, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +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., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stddef.h> +#include <errno.h> +#include <sys/time.h> +#include <hurd.h> +#include <hurd/signal.h> +#include <hurd/msg_request.h> +#include <mach/message.h> + +/* XXX Temporary cheezoid implementation of ITIMER_REAL/SIGALRM. */ + +spin_lock_t _hurd_itimer_lock = SPIN_LOCK_INITIALIZER; +struct itimerval _hurd_itimerval; /* Current state of the timer. */ +mach_port_t _hurd_itimer_port; /* Port the timer thread blocks on. */ +thread_t _hurd_itimer_thread; /* Thread waiting for timeout. */ +int _hurd_itimer_thread_suspended; /* Nonzero if that thread is suspended. */ +vm_address_t _hurd_itimer_thread_stack_base; /* Base of its stack. */ +vm_address_t _hurd_itimer_thread_stack_size; /* Size of its stack. */ +struct timeval _hurd_itimer_started; /* Time the thread started waiting. */ + +static inline void +subtract_timeval (struct timeval *from, const struct timeval *subtract) +{ + from->tv_usec -= subtract->tv_usec; + from->tv_sec -= subtract->tv_sec; + while (from->tv_usec < 0) + { + --from->tv_sec; + from->tv_usec += 1000000; + } +} + +/* Function run by the itimer thread. + This code must be very careful not ever to require a MiG reply port. */ + +static void +timer_thread (void) +{ + while (1) + { + error_t err; + /* The only message we ever expect to receive is the reply from the + signal thread to a sig_post call we did. We never examine the + contents. */ + struct + { + mach_msg_header_t header; + error_t return_code; + } msg; + + /* Wait for a message on a port that noone sends to. The purpose is + the receive timeout. Notice interrupts so that if we are + thread_abort'd, we will loop around and fetch new values from + _hurd_itimerval. */ + err = __mach_msg (&msg.header, + MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_INTERRUPT, + 0, 0, _hurd_itimer_port, + _hurd_itimerval.it_value.tv_sec * 1000 + + _hurd_itimerval.it_value.tv_usec / 1000, + MACH_PORT_NULL); + switch (err) + { + case MACH_RCV_TIMED_OUT: + /* We got the expected timeout. Send a message to the signal + thread to tell it to post a SIGALRM signal. We use + _hurd_itimer_port as the reply port just so we will block until + the signal thread has frobnicated things to reload the itimer or + has terminated this thread. */ + __msg_sig_post_request (_hurd_msgport, + _hurd_itimer_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + SIGALRM, __mach_task_self ()); + break; + + case MACH_RCV_INTERRUPTED: + /* We were thread_abort'd. This is to tell us that + _hurd_itimerval has changed and we need to reexamine it + and start waiting with the new timeout value. */ + break; + + case MACH_MSG_SUCCESS: + /* We got the reply message from the sig_post_request above. + Ignore it and reexamine the timer value. */ + __mach_msg_destroy (&msg.header); /* Just in case. */ + break; + + default: + /* Unexpected lossage. Oh well, keep trying. */ + break; + } + } +} + +/* Forward declaration. */ +static sighandler_t preempt_sigalrm (thread_t thread, int signo, + long int sigcode, int sigerror); + +/* Called before any normal SIGALRM signal is delivered. + Reload the itimer, or disable the itimer. */ + +static int +setitimer_locked (const struct itimerval *new, struct itimerval *old, + void *crit) +{ + struct itimerval newval = *new; + struct timeval now, remaining, elapsed; + struct timeval old_interval; + error_t err; + + inline void kill_itimer_thread (void) + { + __thread_terminate (_hurd_itimer_thread); + __vm_deallocate (__mach_task_self (), + _hurd_itimer_thread_stack_base, + _hurd_itimer_thread_stack_size); + _hurd_itimer_thread = MACH_PORT_NULL; + } + + if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0) + { + /* Make sure the itimer thread is set up. */ + + if (_hurd_signal_preempt[SIGALRM] == NULL) + { + static struct hurd_signal_preempt preempt = + { preempt_sigalrm, 0, 0, NULL }; + _hurd_signal_preempt[SIGALRM] = &preempt; + } + + if (_hurd_itimer_port == MACH_PORT_NULL) + { + /* Allocate a receive right that the itimer thread will + block waiting for a message on. */ + if (err = __mach_port_allocate (__mach_task_self (), + MACH_PORT_RIGHT_RECEIVE, + &_hurd_itimer_port)) + goto out; + } + + if (_hurd_itimer_thread == MACH_PORT_NULL) + { + /* Start up the itimer thread running `timer_thread' (below). */ + if (err = __thread_create (__mach_task_self (), + &_hurd_itimer_thread)) + return __hurd_fail (err); + _hurd_itimer_thread_stack_base = 0; /* Anywhere. */ + _hurd_itimer_thread_stack_size = __vm_page_size; /* Small stack. */ + if (err = __mach_setup_thread (__mach_task_self (), + _hurd_itimer_thread, + &timer_thread, + &_hurd_itimer_thread_stack_base, + &_hurd_itimer_thread_stack_size)) + { + __thread_terminate (_hurd_itimer_thread); + _hurd_itimer_thread = MACH_PORT_NULL; + goto out; + } + _hurd_itimer_thread_suspended = 1; + } + } + + if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0 || old != NULL) + { + /* Calculate how much time is remaining for the pending alarm. */ + if (__gettimeofday (&now, NULL) < 0) + { + __spin_unlock (&_hurd_itimer_lock); + _hurd_critical_section_unlock (crit); + return -1; + } + elapsed = now; + subtract_timeval (&elapsed, &_hurd_itimer_started); + remaining = _hurd_itimerval.it_value; + if (timercmp (&remaining, &elapsed, <)) + { + /* Hmm. The timer should have just gone off, but has not been reset. + This is a possible timing glitch. The alarm will signal soon. */ + /* XXX wrong */ + remaining.tv_sec = 0; + remaining.tv_usec = 0; + } + else + subtract_timeval (&remaining, &elapsed); + + /* Remember the old reload interval before changing it. */ + old_interval = _hurd_itimerval.it_interval; + + /* Record the starting time that the timer interval relates to. */ + _hurd_itimer_started = now; + } + + /* Load the new itimer value. */ + _hurd_itimerval = newval; + + if ((newval.it_value.tv_sec | newval.it_value.tv_usec) == 0) + { + /* Disable the itimer. */ + if (_hurd_itimer_thread && !_hurd_itimer_thread_suspended) + { + /* Suspend the itimer thread so it does nothing. Then abort its + kernel context so that when the thread is resumed, mach_msg + will return to timer_thread (below) and it will fetch new + values from _hurd_itimerval. */ + if ((err = __thread_suspend (_hurd_itimer_thread)) || + (err = __thread_abort (_hurd_itimer_thread))) + /* If we can't save it for later, nuke it. */ + kill_itimer_thread (); + else + _hurd_itimer_thread_suspended = 1; + } + } + /* See if the timeout changed. If so, we must alert the itimer thread. */ + else if (remaining.tv_sec != new->it_value.tv_sec || + remaining.tv_usec != new->it_value.tv_usec) + { + /* The timeout value is changing. Tell the itimer thread to + reexamine it and start counting down. If the itimer thread is + marked as suspended, either we just created it, or it was + suspended and thread_abort'd last time the itimer was disabled; + either way it will wake up and start waiting for the new timeout + value when we resume it. If it is not suspended, the itimer + thread is waiting to deliver a pending alarm that we will override + (since it would come later than the new alarm being set); + thread_abort will make mach_msg return MACH_RCV_INTERRUPTED, so it + will loop around and use the new timeout value. */ + if (err = (_hurd_itimer_thread_suspended + ? __thread_resume : __thread_abort) (_hurd_itimer_thread)) + { + kill_itimer_thread (); + goto out; + } + _hurd_itimer_thread_suspended = 0; + } + + __spin_unlock (&_hurd_itimer_lock); + _hurd_critical_section_unlock (crit); + + if (old != NULL) + { + old->it_value = remaining; + old->it_interval = old_interval; + } + return 0; + + out: + __spin_unlock (&_hurd_itimer_lock); + _hurd_critical_section_unlock (crit); + return __hurd_fail (err); +} + +/* Set the timer WHICH to *NEW. If OLD is not NULL, + set *OLD to the old value of timer WHICH. + Returns 0 on success, -1 on errors. */ +int +DEFUN(__setitimer, (which, new, old), + enum __itimer_which which AND + struct itimerval *new AND struct itimerval *old) +{ + void *crit; + + switch (which) + { + default: + return __hurd_fail (EINVAL); + + case ITIMER_VIRTUAL: + case ITIMER_PROF: + return __hurd_fail (ENOSYS); + + case ITIMER_REAL: + break; + } + + crit = _hurd_critical_section_lock (); + __spin_lock (&_hurd_itimer_lock); + return setitimer_locked (new, old, crit); +} + +static sighandler_t +preempt_sigalrm (thread_t thread, int signo, long int sigcode, int sigerror) +{ + struct itimerval it; + + if (thread != _hurd_sigthread || signo != SIGALRM || sigcode != 0) + /* Too much monkey business. */ + return SIG_DFL; + + /* Either reload or disable the itimer. */ + __spin_lock (&_hurd_itimer_lock); + it = _hurd_itimerval; + it.it_value = it.it_interval; + setitimer_locked (&it, NULL, NULL); + + /* Continue with normal delivery of SIGALRM. */ + return SIG_DFL; +} + +static void +fork_itimer (void) +{ + /* We must restart the itimer in the child. */ + + struct itimerval it; + + __spin_lock (&_hurd_itimer_lock); + _hurd_itimer_thread = MACH_PORT_NULL; + it = _hurd_itimerval; + it.it_value = it.it_interval; + + setitimer_locked (&it, NULL, NULL); +} +text_set_element (_hurd_fork_child_hook, fork_itimer); + +weak_alias (__setitimer, setitimer) |