Mercurial > flash_v2
view packages/compat/posix/current/src/signal.cxx @ 130:eb9fd8c04db3 ecos-sw-2000-10-23
Merge from eCos master repository on 2000-10-23-17:01:37-BST
| author | jlarmour |
|---|---|
| date | Mon, 23 Oct 2000 17:10:57 +0000 |
| parents | 6ed91473a1cd |
| children | d2f49caf9e38 |
line wrap: on
line source
//========================================================================== // // signal.cxx // // POSIX signal functions implementation // //========================================================================== //####COPYRIGHTBEGIN#### // // ------------------------------------------- // The contents of this file are subject to the Red Hat eCos Public License // Version 1.1 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://www.redhat.com/ // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations under // the License. // // The Original Code is eCos - Embedded Configurable Operating System, // released September 30, 1998. // // The Initial Developer of the Original Code is Red Hat. // Portions created by Red Hat are // Copyright (C) 1998, 1999, 2000 Red Hat, Inc. // All Rights Reserved. // ------------------------------------------- // //####COPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): nickg // Contributors: nickg // Date: 2000-03-27 // Purpose: POSIX signal functions implementation // Description: This file contains the implementation of the POSIX signal // functions. // // // //####DESCRIPTIONEND#### // //========================================================================== #include <pkgconf/hal.h> #include <pkgconf/kernel.h> #include <pkgconf/posix.h> #include <cyg/kernel/ktypes.h> // base kernel types #include <cyg/infra/cyg_trac.h> // tracing macros #include <cyg/infra/cyg_ass.h> // assertion macros #include "pprivate.h" // POSIX private header #include <signal.h> // our header #include <setjmp.h> #include <cyg/kernel/clock.hxx> #include <cyg/kernel/clock.inl> // ------------------------------------------------------------------------- // Internal definitions // Handle entry to a signal package function. #define SIGNAL_ENTRY() CYG_REPORT_FUNCTYPE( "returning %d" ); // Do a signal package defined return. This requires the error code // to be placed in errno, and if it is non-zero, -1 returned as the // result of the function. This also gives us a place to put any // generic tidyup handling needed for things like signal delivery and // cancellation. #define SIGNAL_RETURN(err) \ CYG_MACRO_START \ int __retval = 0; \ if( err != 0 ) __retval = -1, errno = err; \ CYG_REPORT_RETVAL( __retval ); \ return __retval; \ CYG_MACRO_END // Range check on a signal value. #define SIGNAL_VALID(_sig_) (((_sig_) > 0) && ((_sig_) < ((int)sizeof(sigset_t)*8))) //========================================================================== // Signal management structures typedef struct signal_info { struct signal_info *next; // link in list of pending signals siginfo_t si; // siginfo to pass to handler } signal_info; typedef struct { struct sigaction sa; // Sigaction defining what to do signal_info *pending; // List of pending signals - this is // a circular list with pending pointing // to the tail element (or NULL if empty). } signal_state; //========================================================================== // Signal management variables // Lock used to protect signal management structures Cyg_Mutex signal_mutex CYGBLD_POSIX_INIT; // Condition variable for all threads in sigsuspend() and sigwait() // to wait on. Cyg_Condition_Variable signal_sigwait( signal_mutex ) CYGBLD_POSIX_INIT; // Global pending signal set sigset_t sig_pending; // Array controlling signal states static signal_state sigstate[sizeof(sigset_t)*8]; // Array of available signal_info objects for queueing signals static signal_info siginfo[SIGQUEUE_MAX]; // List of free signal_info objects static signal_info *siginfo_next = NULL; //========================================================================== // Variables used to support alarm() // Forward def of action function static void sigalrm_action( Cyg_Alarm *alarm, CYG_ADDRWORD data ); // Kernel alarm object static Cyg_Alarm sigalrm_alarm( Cyg_Clock::real_time_clock, sigalrm_action, 0 ) CYGBLD_POSIX_INIT; // Set true when alarm is armed volatile cyg_bool sigalrm_armed = false; // Set true when alarm has fired and is waiting to be delivered volatile cyg_bool sigalrm_pending = false; //========================================================================== // Implementation functions. // These are where the real work of the signal mechanism gets done. externC void cyg_posix_signal_start() { // Chain all free signal_info objects together for( int i = 0; i < SIGQUEUE_MAX; i++ ) { siginfo[i].next = siginfo_next; siginfo_next = &siginfo[i]; } // Clear the pending signal set sigemptyset( &sig_pending ); } // ------------------------------------------------------------------------- // Generate a signal cyg_bool cyg_sigqueue( const struct sigevent *sev, int code, pthread_info *thread ) { if( sev->sigev_notify == SIGEV_NONE ) { // Do nothing return true; } if( sev->sigev_notify == SIGEV_THREAD ) { // create a thread to run the notification // function. // FIXME: implement SIGEV_THREAD return true; } // Otherwise we must have a SIGEV_SIGNAL notification // Find out whether the current thread already has the mutex // locked. This is a distinct possibility if this function is // called from the ASR while exiting the signal_sigwait condvar in // pause() and sigtimedwait(). pthread_info *self = pthread_self_info(); cyg_bool locked = (self != NULL) && (signal_mutex.get_owner() == self->thread); // Lock the mutex only if we do not already own it if( !locked ) signal_mutex.lock(); int signo = sev->sigev_signo; signal_state *ss = &sigstate[signo]; if( ss->sa.sa_flags & SA_SIGINFO ) { // We have a queuable signal, allocate a signal_info // object and add it to the queue. if( siginfo_next == NULL ) { if( !locked ) signal_mutex.unlock(); return false; } signal_info *si = siginfo_next; siginfo_next = si->next; si->si.si_signo = signo; si->si.si_code = code; si->si.si_value = sev->sigev_value; if( ss->pending == NULL ) { si->next = si; } else { si->next = ss->pending->next; ss->pending->next = si; } ss->pending = si; } // else A non-queuable signal, just set it pending if( thread != NULL ) sigaddset( &thread->sigpending, signo ); else sigaddset( &sig_pending, signo ); // Wake up any threads in sigsuspend() and sigwait(). signal_sigwait.broadcast(); if( !locked ) signal_mutex.unlock(); return true; } // ------------------------------------------------------------------------- // Deliver any pending unblocked signals to the current thread // Returns true if a signal handler was called. cyg_bool cyg_deliver_signals() { cyg_bool res = false; pthread_info *self = pthread_self_info(); // If there is no pthread_info pointer for this thread then // it is not a POSIX thread and cannot have signals delivered // to it. if( self == NULL ) return false; // If there are no pending signals our work is done if( sig_pending == 0 && self->sigpending == 0 ) return false; // If there are no unmasked pending signals our // work is also done if( ((sig_pending | self->sigpending) & ~self->sigmask) == 0 ) return false; // As with cyg_sigqueue(), this function can get called from an // ASR where the signal_mutex is already locked. Check here to // avoid relocking... cyg_bool locked = signal_mutex.get_owner() == self->thread; if( !locked ) signal_mutex.lock(); sigset_t todo; // Since a signal handler may raise another signal, or unmask an existing // signal, we loop here while there are no more unblocked signals pending. while( (todo = ((sig_pending | self->sigpending) & ~self->sigmask)) != 0 ) { // Here todo is a mask of the signals available for delivery int signo = 0; // This prioritizes low numbered signals HAL_LSBIT_INDEX( signo, todo ); signal_state *ss = &sigstate[signo]; sigset_t sigbit = 1L<<signo; if( ss->sa.sa_handler != SIG_DFL && ss->sa.sa_handler != SIG_IGN ) { sigset_t oldmask = self->sigmask; siginfo_t lsi; if(ss->pending != NULL) { // There is a queued signal. Dequeue it and copy the // siginfo object to a local copy. signal_info *si = ss->pending->next; // Make a local copy of the siginfo object lsi = si->si; // Remove the head signal_info object from the // circular list. if( ss->pending == si ) ss->pending = NULL; else ss->pending->next = si->next; // Return it to the free list si->next = siginfo_next; siginfo_next = si; } else { // There are no signals queued. Set up the local siginfo_t // object with default values. lsi.si_signo = signo; lsi.si_code = SI_USER; lsi.si_value.sival_int = 0; } // Clear the bit from the pending masks. If the pending // queue is not empty, leave the bits set, otherwise clear // them. Do this now so that if the signal handler longjumps // out, the signal subsystem is clean. if( ss->pending == NULL ) { // Clear the bit in both masks regardless of which // one it actually came from. This is cheaper than // trying to find out. sig_pending &= ~sigbit; self->sigpending &= ~sigbit; } // Add the mask set and the signal itself to the // mask while we call the signal handler self->sigmask = oldmask | ss->sa.sa_mask | sigbit; // Unlock now so that a longjmp out of the handler // does the right thing. We do this even if we did not // lock the mutex since it will only recently have been // relocked and thus all data is still consistent. signal_mutex.unlock(); if( ss->sa.sa_flags & SA_SIGINFO ) { // A sigaction delivery ss->sa.sa_sigaction( signo, &lsi, NULL ); } else { // This is a standard signal delivery. ss->sa.sa_handler( signo ); } // Relock the mutex signal_mutex.lock(); // Restore original signal mask self->sigmask = oldmask; // return that we have handled a signal res = true; } } if( !locked ) signal_mutex.unlock(); return res; } // ------------------------------------------------------------------------- // Action routine called from kernel alarm to deliver the SIGALRM signal. // We cannot call any signal delivery functions directly here, so we simply // set a flag and schedule an ASR to be called. static void sigalrm_action( Cyg_Alarm *alarm, CYG_ADDRWORD data ) { sigset_t mask; sigalrm_armed = false; sigalrm_pending = true; sigemptyset( &mask ); sigaddset( &mask, SIGALRM ); cyg_posix_pthread_release_thread( &mask ); } // ------------------------------------------------------------------------- // signal ASR function. This is called from the general POSIX ASR to // deal with any signal related issues. externC void cyg_posix_signal_asr(pthread_info *self) { // If there is a pending SIGALRM, generate it if( sigalrm_pending ) { sigalrm_pending = false; struct sigevent sev; sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGALRM; sev.sigev_value.sival_int = 0; // generate the signal cyg_sigqueue( &sev, SI_USER ); } // Now call cyg_deliver_signals() to see if we can // handle any signals now. cyg_deliver_signals(); } //========================================================================== // Per-thread initialization and destruction externC void cyg_posix_thread_siginit( pthread_info *thread ) { // Clear out signal masks sigemptyset( &thread->sigpending ); sigemptyset( &thread->sigmask ); cyg_pthread_exception_init( thread ); } externC void cyg_posix_thread_sigdestroy( pthread_info *thread ) { cyg_pthread_exception_destroy( thread ); } //========================================================================== // Functions to generate signals // ------------------------------------------------------------------------- // Deliver sig to a process. // eCos only supports the value 0 for pid. externC int kill (pid_t pid, int sig) { SIGNAL_ENTRY(); if( !SIGNAL_VALID(sig) ) SIGNAL_RETURN(EINVAL); if( pid != 0 ) SIGNAL_RETURN(ESRCH); struct sigevent sev; sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = sig; sev.sigev_value.sival_int = 0; cyg_sigqueue( &sev, SI_USER ); cyg_deliver_signals(); SIGNAL_RETURN(0); } // ------------------------------------------------------------------------- externC int pthread_kill (pthread_t threadid, int sig) { SIGNAL_ENTRY(); if( !SIGNAL_VALID(sig) ) SIGNAL_RETURN(EINVAL); struct sigevent sev; pthread_info *thread = pthread_info_id(threadid); if( thread == NULL ) SIGNAL_RETURN(ESRCH); sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = sig; sev.sigev_value.sival_int = 0; cyg_sigqueue( &sev, SI_USER, thread ); cyg_deliver_signals(); SIGNAL_RETURN(0); } //========================================================================== // Functions to catch signals // ------------------------------------------------------------------------- // Install signal handler for sig. externC int sigaction (int sig, const struct sigaction *act, struct sigaction *oact) { SIGNAL_ENTRY(); if( !SIGNAL_VALID(sig) ) SIGNAL_RETURN(EINVAL); signal_state *ss = &sigstate[sig]; signal_mutex.lock(); if( oact != NULL ) *oact = ss->sa; ss->sa = *act; if( ss->sa.sa_handler == SIG_IGN ) { // Setting the handler to SIG_IGN causes any pending // signals to be discarded and any queued values to also // be removed. pthread_info *self = pthread_self_info(); sigset_t sigbit = 1<<sig; if( (sig_pending | self->sigpending) & sigbit ) { // This signal is pending, clear it sig_pending &= ~sigbit; self->sigpending &= ~sigbit; // Clean out any queued signal_info objects while( ss->pending != NULL ) { signal_info *si = ss->pending->next; // Remove the head signal_info object from the // circular list. if( ss->pending == si ) ss->pending = NULL; else ss->pending->next = si->next; // Return it to the free list si->next = siginfo_next; siginfo_next = si; } } } cyg_deliver_signals(); signal_mutex.unlock(); SIGNAL_RETURN(0); } // ------------------------------------------------------------------------- // Queue signal to process with value. externC int sigqueue (pid_t pid, int sig, const union sigval value) { SIGNAL_ENTRY(); if( !SIGNAL_VALID(sig) ) SIGNAL_RETURN(EINVAL); struct sigevent sev; sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = sig; sev.sigev_value = value; cyg_sigqueue( &sev, SI_QUEUE ); cyg_deliver_signals(); SIGNAL_RETURN(0); } //========================================================================== // Functions to deal with current blocked and pending masks // ------------------------------------------------------------------------- // Set process blocked signal mask // Map this onto pthread_sigmask(). externC int sigprocmask (int how, const sigset_t *set, sigset_t *oset) { return pthread_sigmask( how, set, oset); } // ------------------------------------------------------------------------- // Set calling thread's blocked signal mask externC int pthread_sigmask (int how, const sigset_t *set, sigset_t *oset) { int err = 0; SIGNAL_ENTRY(); pthread_info *self = pthread_self_info(); // Save old set if( oset != NULL ) *oset = self->sigmask; if( set != NULL ) { switch( how ) { case SIG_BLOCK: self->sigmask |= *set; break; case SIG_UNBLOCK: self->sigmask &= ~*set; break; case SIG_SETMASK: self->sigmask = *set; break; default: err = EINVAL; break; } } // Deliver any newly unblocked signals cyg_deliver_signals(); SIGNAL_RETURN(err); } // ------------------------------------------------------------------------- // Get set of pending signals for this process externC int sigpending (sigset_t *set) { SIGNAL_ENTRY(); if( set == NULL ) SIGNAL_RETURN(EINVAL); pthread_info *self = pthread_self_info(); *set = self->sigpending | sig_pending; SIGNAL_RETURN(0); } //========================================================================== // Wait for or accept signals // ------------------------------------------------------------------------- // Block signals in set and wait for a signal externC int sigsuspend (const sigset_t *set) { SIGNAL_ENTRY(); pthread_info *self = pthread_self_info(); signal_mutex.lock(); // Save the old mask and set the current mask to // the one supplied. sigset_t old = self->sigmask; self->sigmask = *set; // Loop until a signal gets delivered while( !cyg_deliver_signals() ) signal_sigwait.wait(); self->sigmask = old; signal_mutex.unlock(); SIGNAL_RETURN(EINTR); } // ------------------------------------------------------------------------- // Wait for a signal in set to arrive // Implement this as a variant on sigtimedwait(). externC int sigwait (const sigset_t *set, int *sig) { SIGNAL_ENTRY(); siginfo_t info; int ret = sigtimedwait( set, &info, NULL ); if( ret == -1 ) SIGNAL_RETURN(errno); *sig = ret; SIGNAL_RETURN(0); } // ------------------------------------------------------------------------- // Do the same as sigwait() except return a siginfo_t object too. // Implement this as a variant on sigtimedwait(). externC int sigwaitinfo (const sigset_t *set, siginfo_t *info) { SIGNAL_ENTRY(); int ret = sigtimedwait( set, info, NULL ); SIGNAL_RETURN(ret); } // ------------------------------------------------------------------------- // Wait either for a signal in the given set to become pending, or // for the timeout to expire. If timeout is NULL, wait for ever. externC int sigtimedwait (const sigset_t *set, siginfo_t *info, const struct timespec *timeout) { SIGNAL_ENTRY(); int err = 0; cyg_tick_count ticks; if( timeout == NULL ) ticks = 0; else ticks = cyg_timespec_to_ticks( timeout ) + Cyg_Clock::real_time_clock->current_value(); pthread_info *self = pthread_self_info(); signal_mutex.lock(); sigset_t todo; // Wait for a signal in the set to become pending while( (todo = (*set & (sig_pending | self->sigpending))) == 0 ) { // If timeout is not NULL, do a timed wait on the // sigwait condition variable. If it is NULL - wait // until we are woken. if( timeout ) { if( ticks == 0 || !signal_sigwait.wait(ticks) ) { // If the timeout is actually zero, or we have waited and // timed out, then we must quit with an error. err = EAGAIN; break; } } else signal_sigwait.wait(); } if( err == 0 ) { // There is a signal in the set that is pending: deliver // it. todo contains a mask of all the signals that could be // delivered now, but we only want to deliver one of them. int signo = 0; // Select the lowest numbered signal from the todo mask HAL_LSBIT_INDEX( signo, todo ); signal_state *ss = &sigstate[signo]; sigset_t sigbit = 1L<<signo; if( (ss->sa.sa_flags & SA_SIGINFO) && (ss->pending != NULL) ) { // If the SA_SIGINFO bit is set, then there // will be a signal_info object queued on the // pending field. signal_info *si = ss->pending->next; *info = si->si; // Remove the head signal_info object from the // circular list. if( ss->pending == si ) ss->pending = NULL; else ss->pending->next = si->next; si->next = siginfo_next; siginfo_next = si; } else { // Not a queued signal, or there is no signal_info object // on the pending queue: fill in info structure with // default values. info->si_signo = signo; info->si_code = SI_USER; info->si_value.sival_int = 0; } // Clear the bit from the pending masks. If the pending // queue is not empty, leave the bits set, otherwise clear // them. if( ss->pending == NULL ) { // Clear the bit in both masks regardless of which // one it actually came from. This is cheaper than // trying to find out. sig_pending &= ~sigbit; self->sigpending &= ~sigbit; } // all done } signal_mutex.unlock(); SIGNAL_RETURN(err); } //========================================================================== // alarm, pause and sleep // ------------------------------------------------------------------------- // Generate SIGALRM after some number of seconds externC unsigned int alarm( unsigned int seconds ) { int res = 0; struct timespec tv; cyg_tick_count trigger, interval; SIGNAL_ENTRY(); signal_mutex.lock(); if( sigalrm_armed ) { sigalrm_alarm.disable(); sigalrm_alarm.get_times( &trigger, &interval ); // Convert trigger time back to interval trigger -= Cyg_Clock::real_time_clock->current_value(); cyg_ticks_to_timespec( trigger, &tv ); res = tv.tv_sec; sigalrm_armed = false; } if( seconds != 0 ) { // Here we know that the sigalrm_alarm is unarmed, set it up // to trigger in the required number of seconds. tv.tv_sec = seconds; tv.tv_nsec = 0; trigger = cyg_timespec_to_ticks( &tv ); // Convert trigger interval to absolute time trigger += Cyg_Clock::real_time_clock->current_value(); sigalrm_alarm.initialize( trigger, 0 ); sigalrm_armed = true; } signal_mutex.unlock(); CYG_REPORT_RETVAL(res); return res; } // ------------------------------------------------------------------------- // Wait for a signal to be delivered. externC int pause( void ) { SIGNAL_ENTRY(); signal_mutex.lock(); // Check for any pending signals that can be delivered and // if there are none, wait for a signal to be generated if( !cyg_deliver_signals() ) signal_sigwait.wait(); // Now check again for some signals to deliver cyg_deliver_signals(); signal_mutex.unlock(); SIGNAL_RETURN(EINTR); } // ------------------------------------------------------------------------- // Wait for a signal, or the given number of seconds externC unsigned int sleep( unsigned int seconds ) { SIGNAL_ENTRY(); struct timespec timeout; timeout.tv_sec = seconds; timeout.tv_nsec = 0; if( nanosleep( &timeout, &timeout ) != 0 ) { CYG_REPORT_RETVAL(timeout.tv_sec); return timeout.tv_sec; } CYG_REPORT_RETVAL(0); return 0; } //========================================================================== // Signal sets // ------------------------------------------------------------------------- // Clear all signals from set. externC int sigemptyset (sigset_t *set) { SIGNAL_ENTRY(); *set = 0; SIGNAL_RETURN(0); } // ------------------------------------------------------------------------- // Set all signals in set. externC int sigfillset (sigset_t *set) { SIGNAL_ENTRY(); *set = ~0; SIGNAL_RETURN(0); } // ------------------------------------------------------------------------- // Add signo to set. externC int sigaddset (sigset_t *set, int signo) { SIGNAL_ENTRY(); int err = 0; if( !SIGNAL_VALID(signo) ) err = EINVAL; else *set |= 1<<signo; SIGNAL_RETURN(err); } // ------------------------------------------------------------------------- // Remove signo from set. externC int sigdelset (sigset_t *set, int signo) { SIGNAL_ENTRY(); int err = 0; if( !SIGNAL_VALID(signo) ) err = EINVAL; else *set &= ~(1<<signo); SIGNAL_RETURN(err); } // ------------------------------------------------------------------------- // Test whether signo is in set externC int sigismember (const sigset_t *set, int signo) { SIGNAL_ENTRY(); int ret = 0; if( !SIGNAL_VALID(signo) ) SIGNAL_RETURN(EINVAL); if( *set & (1<<signo) ) ret = 1; CYG_REPORT_RETVAL( ret ); return ret; } //========================================================================== // ISO C compatibility functions // ------------------------------------------------------------------------- // Installs a new signal handler for the specified signal, and returns // the old handler externC sa_sighandler_t signal(int sig, sa_sighandler_t handler) { SIGNAL_ENTRY(); int err; sa_sighandler_t ret; struct sigaction new_action; struct sigaction old_action; sigemptyset( &new_action.sa_mask ); new_action.sa_flags = 0; new_action.sa_handler = handler; err = sigaction( sig, &new_action, &old_action ); if( err < 0 ) ret = SIG_ERR; else ret = old_action.sa_handler; CYG_REPORT_RETVAL( ret ); return ret; } // ------------------------------------------------------------------------- // raise() - ISO C 7.7.2 // // // Raises the signal, which will cause the current signal handler for // that signal to be called externC int raise(int sig) { return kill( 0, sig ); } // ------------------------------------------------------------------------- // siglongjmp() // Restores signal mask and longjumps. __externC void siglongjmp( sigjmp_buf env, int val ) { CYG_REPORT_FUNCNAME( "siglongjmp" ); CYG_REPORT_FUNCARG2( "&env=%08x, val=%d", &env, val ); // ISO C says that if we are passed val == 0, then we change it to 1 if( val == 0 ) val = 1; if( *(int *)&env[sizeof(hal_jmp_buf)] ) pthread_sigmask( SIG_SETMASK, (sigset_t *)&(env)[sizeof(hal_jmp_buf)+sizeof(int)], NULL ); HAL_REORDER_BARRIER(); // prevent any chance of optimisation re-ordering hal_longjmp( (hal_jmp_buf)env, val ); HAL_REORDER_BARRIER(); // prevent any chance of optimisation re-ordering #ifdef CYGDBG_USE_ASSERTS CYG_ASSERT( 0, "siglongjmp should not have reached this point!" ); #else for (;;) CYG_EMPTY_STATEMENT; #endif } // ------------------------------------------------------------------------- // EOF signal.cxx
