tholm@uvicctr.UUCP (Terrence W. Holm) (10/26/88)
EFTH MINIX report #51 - October 1988 - POSIX sleep(3) The MINIX 1.3 sleep(3) does not handle alarm(2) pending when sleep(3) is called. Nor does it return a correct value. The following corrects these problems. ---------------------------------------------------------- echo x - sleep.3 gres '^X' '' > sleep.3 << '/' XSUBROUTINES X sleep(3) - suspend execution for awhile X XINVOCATION X unsigned sleep( secs ) X unsigned secs; X XEXPLANATION X The process halts execution for <secs> seconds. It X will wake up early if any non-ignored signal occurs. X X If an alarm(2) was already set then sleep(3) uses X the minimum of the time remaining and <secs> as the X sleep time. If the alarm(2) signal expires first, then X its handler is called and sleep(3) returns. X X If an alarm(2) is still pending after sleep(3) completes, X then its time is adjusted by how long the process X really slept. X XRESULTS X Returns the number of seconds remaining until <secs> X should occur. This is non-zero when another signal woke X up sleep(3) early. X XREFERENCES X alarm(2), kill(2), pause(2), signal(2) / echo x - sleep.c gres '^X' '' > sleep.c << '/' X/* sleep(3) X * X * Sleep(n) pauses for 'n' seconds by scheduling an alarm interrupt. X * X * Changed to conform with POSIX Terrence W. Holm Oct. 1988 X */ X X#include <signal.h> X X Xstatic _alfun() /* Used with sleep() below */ X { X } X X Xunsigned sleep( secs ) X unsigned secs; X X { X unsigned current_secs; X unsigned remaining_secs; X void (*old_signal)(); X X if ( secs == 0 ) X return( 0 ); X X current_secs = alarm( 0 ); /* Is there currently an alarm? */ X X if ( current_secs == 0 || current_secs > secs ) X { X old_signal = signal( SIGALRM, _alfun ); X X alarm( secs ); X pause(); X remaining_secs = alarm( 0 ); X X signal( SIGALRM, old_signal ); X X if ( current_secs > secs ) X alarm( current_secs - ( secs - remaining_secs ) ); X X return( remaining_secs ); X } X X X /* current_secs <= secs, ie. alarm should occur before secs */ X X alarm( current_secs ); X pause(); X remaining_secs = alarm( 0 ); X X alarm( remaining_secs ); X X return( secs - ( current_secs - remaining_secs ) ); X } / ---------------------------------------------------------- Terrence W. Holm uw-beaver!uvicctr!tholm
n62@nikhefh.nikhef.nl (Klamer Schutte) (11/16/89)
Here is a POSIX implementation of sleep(3). Also are provided the POSIX signal routines. There are (minimal :-)) two bugs in the code: - When implementing the posix-style signal routines in user code it is not possible to overcome the problem of two interrupts quickly after each other. When there is not enough time for another signal() call the default action will be taken. - With the implementation of sleep is there a problem of a bad resolution of alarm(2). Please look in the file sleep.c for the complete problem. Please all try this code and report bugs. I have not tried to implement the signal routines as kernel routines as that has to wait for V2.0; this code is intended for V1.4?. Included are test routines for the code. Klamer Schutte (.signature at end). ------------------------------------- cut here ----------------------------- echo x - Makefile sed '/^X/s///' > Makefile << 'Shar_Of_Makefile_Eof' X# Makefile for signal functions X# X# Klamer Schutte, 14/11/89 X XSRC = signal.c pos_signal.c sleep.c X XOBJ = signal.o pos_signal.o sleep.o X XCFLAGS = -DATARI_ST -DACK -I. -v X X Xall: test1 test2 test3 test4 X Xtest1: ${OBJ} test1.o X cc -v -o $@ $< X Xtest2: ${OBJ} test2.o X cc -v -o $@ $< X Xtest3: ${OBJ} test3.o X cc -v -o $@ $< X Xtest4: ${OBJ} test4.o X cc -v -o $@ $< X X${OBJ}: signal.h X Shar_Of_Makefile_Eof echo x - lib.h sed '/^X/s///' > lib.h << 'Shar_Of_lib.h_Eof' X#include <minix/const.h> X#include <minix/type.h> X#include <minix/callnr.h> X#include <errno.h> X Xextern message M; X X#define MM 0 X#define FS 1 X Xextern int callm1(), callm3(), callx(), len(); Xextern int errno; Xextern void begsig(); /* interrupts all vector here */ Shar_Of_lib.h_Eof echo x - pos_signal.c sed '/^X/s///' > pos_signal.c << 'Shar_Of_pos_signal.c_Eof' X/* X * pos_signal.c X * X * Fake the POSIX 1003.1 1988 signal routines using the old V7 signal X * interface. X * The old signal() must be renamed to _signal(). Also must the function X * called by a signal have the signal number as an argument. X * A new signal() interface is also provided. X * X * Bugs: X * Two signal send shortly after each other will still result in X * execution of the action associated with SIG_DFL. X * X * Klamer Schutte, 14/11/89 X */ X X#ifndef PRIVATE X#define PRIVATE static X#endif X X#include <signal.h> X#include <setjmp.h> X#include <errno.h> X Xextern void (* _signal())(); X X#ifndef NULL X#define NULL 0 X#endif X X#define DIRTY_OK /* define only if sigset_t is an integral type */ X XPRIVATE jmp_buf jmp_suspended; X XPRIVATE sigset_t sig_blocked, sig_pending, sig_special; X XPRIVATE struct sigaction sig_action[NR_SIGS+1]; X XPRIVATE int sig_special_case = 0; X XPRIVATE void sys_called(), exec_func(), set_mask(); X Xextern int X errno; X XPRIVATE void sys_called( nr ) Xint nr; X{ X _signal( nr, sys_called ); X X if (sig_special_case) X { if (!sigismember( &sig_special, nr )) X { sig_special_case = 0; X exec_func( nr ); X longjmp( jmp_suspended, 1 ); X } else X { sigaddset( &sig_pending, nr ); X return; X } X } X X if (sigismember( &sig_blocked, nr)) /* signal is blocked */ X sigaddset( &sig_pending, nr ); X else X exec_func( nr ); X} X XPRIVATE void exec_func( nr ) Xint nr; X{ X sigset_t old_set; X void (* handler)(); X X sigprocmask( SIG_BLOCK, &sig_blocked, &old_set ); X handler = sig_action[nr].sa_handler; X switch( (long) handler ) X { case (long) SIG_IGN: /* should only happen after init */ X break; X case (long) SIG_DFL: X _signal( nr, SIG_DFL ); X kill( getpid(), nr ); X /* are there signals who's default action is ignore? */ X _signal( nr, sys_called ); X break; X default: X (* handler)(nr); X } X sigprocmask( SIG_SETMASK, &old_set, (sigset_t *) NULL ); X} X Xsigset_t sig_bits[] = { 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, X 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000 }; X Xint sigismember( sig_set, nr ) Xsigset_t *sig_set; Xint nr; X{ X if ((nr<=0) || (nr>NR_SIGS)) X { errno = EINVAL; X return -1; X } X X return (*sig_set & sig_bits[nr] ? 1 : 0); X} X Xint sigdelset( sig_set, nr ) Xsigset_t *sig_set; Xint nr; X{ X if ((nr<=0) || (nr>NR_SIGS)) X { errno = EINVAL; X return -1; X } X X *sig_set &= ~(sig_bits[nr]); X X return 0; X} X Xint sigaddset( sig_set, nr ) Xsigset_t *sig_set; Xint nr; X{ X if ((nr<=0) || (nr>NR_SIGS)) X { errno = EINVAL; X return -1; X } X X *sig_set |= sig_bits[nr]; X X return 0; X} X Xint sigemptyset( set ) Xsigset_t *set; X{ X *set = 0; X X return 0; X} X Xint sigfillset( set ) Xsigset_t *set; X{ X *set = ~0; X X return 0; X} X XPRIVATE int sigs_inited = 0; X XPRIVATE init_sigs() X{ X int i; X X sigemptyset( &sig_pending ); X sig_blocked = sig_pending; X for(i=1;i<=NR_SIGS;i++) X { sigemptyset( &(sig_action[i].sa_mask) ); X sig_action[i].sa_flags = 0; X sig_action[i].sa_handler = _signal( i, sys_called ); X if (sig_action[i].sa_handler == SIG_IGN) X _signal( i, SIG_IGN ); X } X sigs_inited = 1; X} X Xint sigaction( sig, act, oact ) Xint sig; Xstruct sigaction *act, *oact; X{ X /* before the first use the whole set must be inited */ X if (!sigs_inited) X init_sigs(); X X if ((sig<=0) || (sig>NR_SIGS)) X { errno = EINVAL; X return -1; X } X X if (oact != (struct sigaction *) NULL) X *oact = sig_action[sig]; X X if (act != (struct sigaction *) NULL) X { if (act->sa_handler == SIG_IGN) X _signal( sig, SIG_IGN ); X else X if (sig_action[sig].sa_handler == SIG_IGN) X _signal( sig, sys_called ); X sig_action[sig] = *act; X } X X return 0; X} X Xint sigprocmask( how, set, oset ) Xint how; Xsigset_t *set, *oset; X{ X sigset_t tmp_set; X#ifndef DIRTY_OK X int i; X#endif X X /* before the first use the whole set must be inited */ X if (!sigs_inited) X init_sigs(); X X X if (oset != (sigset_t *) NULL) X *oset = sig_blocked; X X if (set != (sigset_t *) NULL) X switch( how ) X { case SIG_BLOCK: X#ifdef DIRTY_OK X sig_blocked |= *set; X break; X case SIG_UNBLOCK: X tmp_set = sig_blocked & ~(*set); X set_mask( &tmp_set ); X#else X for(i=1;i<=NR_SIGS;i++) X if (sigismember( &(set), i ) == 1) X sigaddset( &sig_blocked, i ); X break; X case SIG_UNBLOCK: X for(i=1;i<=NR_SIGS;i++) X if (sigismember( &(set), i ) == 0) X { sigdelset( &sig_blocked, i ); X exec_func( i ); X } X#endif X break; X case SIG_SETMASK: X set_mask( set ); X break; X default: X errno = EINVAL; X return -1; X } X X return 0; X} X XPRIVATE void set_mask( set ) Xsigset_t *set; X{ X int i; X X for(i=1;i<=NR_SIGS;i++) X if ((!sigismember( set, i )) && sigismember( &sig_pending, i )) X { sigdelset( &sig_pending, i ); X exec_func( i ); X } X sig_blocked = *set; X} X Xint sigpending( set ) Xsigset_t *set; X{ X if (!sigs_inited) X init_sigs(); X X X *set = sig_pending; X X return 0; X} X Xint sigsuspend( sigmask ) Xsigset_t *sigmask; X{ X sigset_t old; X int res; X X if (!sigs_inited) X init_sigs(); X X old = sig_blocked; X if ( setjmp( jmp_suspended ) == 0) X { set_mask( sigmask ); X sig_special = *sigmask; X sig_special_case = 1; X while(1) X pause(); X } X set_mask( &old ); X X errno = EINTR; X return -1; X} X X/* provide old V7 interface to signal function */ X XPRIVATE void (*old_funcs[NR_SIGS+1])(); X XPRIVATE void old_sig( nr ) Xint nr; X{ X void (*func)(); X X func = signal( nr, SIG_DFL ); X switch((long)func) X { case (long) SIG_IGN: /* should not happen after init */ X break; X case (long) SIG_DFL: X _signal( nr, SIG_DFL ); X kill( getpid(), nr ); X /* are there signals who's default action is ignore? */ X _signal( nr, sys_called ); X break; X default: X (* func)(nr); X } X} X Xvoid (*signal( sig, func))() Xint sig; Xvoid (*func)(); X{ X struct sigaction new; X void (*old)(); X X new.sa_handler = old_sig; X sigemptyset( &(new.sa_mask) ); X new.sa_flags = 0; X old = old_funcs[sig]; X old_funcs[sig] = func; X sigaction( sig, &new, (struct sigaction *) NULL); X return old; X} X X Shar_Of_pos_signal.c_Eof echo x - signal.c sed '/^X/s///' > signal.c << 'Shar_Of_signal.c_Eof' X#include "lib.h" X#include <signal.h> X X/* ATTENTION: changed code but not comments to handler type (*void)() -- KS */ X Xvoid (*vectab[NSIG])(); /* array of functions to catch signals */ X X/* The definition of signal really should be X * PUBLIC int (*signal(signr, func))() X * but some compilers refuse to accept this, even though it is correct. X * The only thing to do if you are stuck with such a defective compiler is X * change it to X * PUBLIC int *signal(signr, func) X * and change ../h/signal.h accordingly. X */ X XPUBLIC void (*_signal(signr, func))() Xint signr; /* which signal is being set */ Xvoid (*func)(); /* pointer to function that catches signal */ X{ X int r; X void (*old)(), begsig(); X X old = vectab[signr - 1]; X vectab[signr - 1] = func; X M.m6_i1 = signr; X M.m6_f1 = ( (func == SIG_IGN || func == SIG_DFL) ? func : begsig); X r = callx(MM, SIGNAL); X if (r == 1) X old = SIG_IGN; X return( (r < 0 ? (void (*)()) r : old) ); X} Shar_Of_signal.c_Eof echo x - signal.h sed '/^X/s///' > signal.h << 'Shar_Of_signal.h_Eof' X/* X * signal.h X * X * original: minix-st distribution. X * X * Changed signal handler type to (*void)() X * Added sigset_t X * struct sigaction X * SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK X * SIGABRT, SIGUSR1, SIGUSR2 X * for POSIX conformance. X * Added SIG_FL_DFL KS 14/11/89 X */ X X#define NSIG 16 /* number of signals used */ X X#define SIGHUP 1 /* hangup */ X#define SIGINT 2 /* interrupt (DEL) */ X#define SIGQUIT 3 /* quit (ASCII FS) */ X#define SIGILL 4 /* illegal instruction (not reset when caught)*/ X#define SIGTRAP 5 /* trace trap (not reset when caught) */ X#define SIGIOT 6 /* IOT instruction */ X#define SIGEMT 7 /* EMT instruction */ X#define SIGFPE 8 /* floating point exception */ X#define SIGKILL 9 /* kill (cannot be caught or ignored) */ X#define SIGBUS 10 /* bus error */ X#define SIGSEGV 11 /* segmentation violation */ X#define SIGSYS 12 /* bad argument to system call */ X#define SIGPIPE 13 /* write on a pipe with no one to read it */ X#define SIGALRM 14 /* alarm clock */ X#define SIGTERM 15 /* software termination signal from kill */ X X#define SIGABRT SIGIOT /* Not normally used */ X#define SIGUSR1 SIGEMT /* Not normally used */ X#define SIGUSR2 SIGSYS /* Only used in very rare conditions */ X X#define STACK_FAULT 16 /* used by kernel to signal stack fault */ X#define NR_SIGS NSIG X Xvoid (*signal())(); X Xtypedef int sigset_t; X Xstruct sigaction X{ void (* sa_handler)(); X sigset_t sa_mask; X int sa_flags; X}; X X/* Values for calling sigprocmask */ X#define SIG_BLOCK 1 X#define SIG_UNBLOCK 2 X#define SIG_SETMASK 4 X X#define SIG_DFL (void (*)())0 X#define SIG_IGN (void (*)())1 Shar_Of_signal.h_Eof echo x - sleep.c sed '/^X/s///' > sleep.c << 'Shar_Of_sleep.c_Eof' X/* X * sleep() X * X * POSIX compatible implementation Klamer Schutte, 24/10/89 X * X * Using new (IEEE 1003.1 1988) signal routines KS 15/11/89 X * X * Bugs: X * The minix kernel doesn't give a precise info about the time the X * next alarm is scheduled. When the time to go is less than 1 second X * but bigger than 0 (i.e. there is a alarm) than alarm returnes 0. X * This means small alarms get lost. X * Fix: replace line 3160 in the book by: X * mc.SECONDS_LEFT = (rp->p_alarm == 0L ? 0 : X * (rp->p_alarm - realtime + HZ - 1) / HZ; X * This will round the time up instead of down, thus avoiding the bug. X * (This kernel fix is not (yet) tested -- KS). X */ X X#include "lib.h" X#include <signal.h> X#include <sys/types.h> X X#ifndef NULL X#define NULL (char *)0L X#endif X XPRIVATE void alfun(nr) /* used with sleep() below */ Xint nr; X{ X} X XPUBLIC int sleep(n) Xunsigned int n; X{ X/* sleep(n) pauses for 'n' seconds by scheduling an alarm interrupt. */ X long old_alarm; /* save old alarm signal */ X int rest; X time_t start; /* time at start */ X X int pending = 0, blocked = 0; X sigset_t mask_alrm, no_mask, old_mask, pend; X struct sigaction alfun_wrap, old_action; X X if (n == 0) X return 0; /* as in UNIX */ X start = time( NULL ); X X /* get old signal state and block SIGALRM */ X old_alarm = alarm( 0 ); X sigemptyset( &mask_alrm ); X sigaddset( &mask_alrm, SIGALRM ); X sigprocmask( SIG_BLOCK, &mask_alrm, &old_mask ); X if (sigismember( &old_mask, SIGALRM )) X blocked = 1; X sigpending( &pend ); X if (sigismember( &pend, SIGALRM )) X pending = 1; X X /* set up alfun */ X alfun_wrap.sa_handler = alfun; X alfun_wrap.sa_flags = 0; X sigemptyset( &(alfun_wrap.sa_mask ) ); X sigaction( SIGALRM, &alfun_wrap, &old_action ); X X if (pending) /* if pending make unpending */ X { sigprocmask( SIG_UNBLOCK, &mask_alrm, (sigset_t *) NULL ); X sigprocmask( SIG_BLOCK, &mask_alrm, (sigset_t *) NULL ); X } X X no_mask = old_mask; X sigdelset( &no_mask, SIGALRM ); X if ((!blocked) && old_alarm && (old_alarm <= n)) X { X alarm((unsigned int) old_alarm); X sigaction( SIGALRM, &old_action, (struct sigaction *) NULL ); X sigsuspend( &no_mask ); /* wait for alarm; call old handler */ X sigprocmask( SIG_SETMASK, &old_mask, (sigset_t *) NULL ); X rest = n - (unsigned int)(time(NULL) - start); X return (rest < 0 ? 0 : rest); X } X X alarm( n ); X sigsuspend( &no_mask ); X alarm( 0 ); /* reset alarm if still set */ X X /* reset signal state to original state */ X sigaction( SIGALRM, &old_action, (struct sigaction *) NULL ); X X rest = (int)(time(NULL) - start); X if (old_alarm) X { old_alarm -= rest; X /* old_alarm should be >= 0; when no big scheduler delays occur */ X if (old_alarm > 0) X { X alarm((unsigned int)old_alarm); X } else X kill(getpid(),SIGALRM); X } X if (pending) X kill(getpid(), SIGALRM); X sigprocmask( SIG_SETMASK, &old_mask, (sigset_t *) NULL ); X rest = n - rest; X return (rest < 0 ? 0 : rest); X} Shar_Of_sleep.c_Eof echo x - test1.c sed '/^X/s///' > test1.c << 'Shar_Of_test1.c_Eof' X#include <signal.h> X#include <stdio.h> X#include <setjmp.h> X Xjmp_buf save; X Xvoid catch( nr ) X{ X printf("Caught signal %d\n", nr ); X signal( nr, catch ); X longjmp( save, 1 ); X} X Xmain() X{ X signal( SIGALRM, catch ); X signal( SIGINT, catch ); X signal( SIGUSR1, catch ); X X while( 1 ) X { X alarm( 1 ); X if (setjmp( save )) X { printf("Escaped by setjmp\n"); X continue; X } X printf("Going to sleep\n"); X pause(); X printf("Awake again!\n"); X } X} Shar_Of_test1.c_Eof echo x - test2.c sed '/^X/s///' > test2.c << 'Shar_Of_test2.c_Eof' X#include <signal.h> X#include <stdio.h> X Xvoid catch( nr ) X{ X char buf[80]; X X sprintf(buf,"Caught signal %d\n", nr ); X write(1, buf, strlen(buf) ); X} X Xmain() X{ X struct sigaction new; X X sigemptyset(&new.sa_mask); X new.sa_flags = 0; X new.sa_handler = catch; X sigaction( SIGALRM, &new, (struct sigaction *)NULL ); X sigaction( SIGINT, &new, (struct sigaction *)NULL ); X sigaction( SIGUSR1, &new, (struct sigaction *)NULL ); X X while( 1 ) X { X alarm( 1 ); X printf("%d Going to sleep\n", getpid()); X pause(); X printf("%d Awake again!\n", getpid()); X } X} Shar_Of_test2.c_Eof echo x - test3.c sed '/^X/s///' > test3.c << 'Shar_Of_test3.c_Eof' X#include <signal.h> X#include <stdio.h> X Xvoid catch( nr ) X{ X char buf[80]; X sigset_t pend; X X sigpending( &pend ); X sprintf(buf,"Caught signal %d pending %x\n", nr, pend ); X write(1, buf, strlen(buf) ); X} X Xmain() X{ X struct sigaction new; X sigset_t sleep_mask; X int i=10; X X sigemptyset(&new.sa_mask); X new.sa_flags = 0; X new.sa_handler = catch; X sigaction( SIGALRM, &new, (struct sigaction *)NULL ); X sigaction( SIGINT, &new, (struct sigaction *)NULL ); X sigaction( SIGUSR1, &new, (struct sigaction *)NULL ); X X sigfillset( &sleep_mask ); X sigdelset( &sleep_mask, SIGALRM ); X while( i-- ) X { X alarm( 1 ); X printf("%d Going to sleep\n", getpid()); X pause(); X printf("%d Awake again!\n", getpid()); X alarm( 1 ); X printf("Going to sigsuspend\n" ); X sigsuspend( &sleep_mask ); X printf("Returned from sigsuspend\n"); X } X} Shar_Of_test3.c_Eof echo x - test4.c sed '/^X/s///' > test4.c << 'Shar_Of_test4.c_Eof' X/* test for sleep */ X#include <stdio.h> X#include <signal.h> X Xvoid catch(nr) Xint nr; X{ X char buf[80]; X X sprintf(buf, "Caught signal %d\n", nr ); X write( 1, buf, strlen(buf) ); X} X Xint r; Xstatic sigset_t blocked; Xstatic struct sigaction state; X X#define Sleep(xxx) printf("Going to sleep %d sec.\n", xxx ); \ X r = sleep( xxx ); \ X printf("Result of sleep(%d) = %d\n", xxx, r ); \ X /* sigprocmask(SIG_SETMASK,(sigset_t *)NULL,&blocked); \ X printf("Left blocked to be %x\n", blocked ); \ X sigaction(SIGALRM, (struct sigaction *)NULL, &state ); \ X printf("handler %lx\n", state.sa_handler ); */ X X#define Alarm(x) printf("Setting alarm to %d sec.\n", x ); \ X alarm( x ); X Xmain() X{ X struct sigaction act; X X Sleep(1); X Sleep(0); X X act.sa_handler = catch; X sigemptyset( &(act.sa_mask)); X act.sa_flags = 0; X sigaction(SIGALRM, &act, (struct sigaction *)NULL ); X sigaction(SIGINT, &act, (struct sigaction *)NULL ); X X Sleep( 1 ); X X Alarm( 2 ); X Sleep( 4 ); X X Alarm( 6 ); X Sleep( 2 ); X Sleep( 10 ); X X Alarm( 5 ); X Sleep( 2 ); X Sleep( 10 ); X X Alarm( 4 ); X Sleep( 2 ); X Sleep( 10 ); X X Alarm( 3 ); X Sleep( 2 ); X Sleep( 10 ); X X Sleep( 10 ); X} Shar_Of_test4.c_Eof -- _____________________Yes, mail address changed again :-(________________________ Klamer Schutte mcvax!nikhefh!{n62,Schutte} {Schutte,n62}@nikhef.nl