diff options
Diffstat (limited to 'cpukit/posix/src/killinfo.c')
-rw-r--r-- | cpukit/posix/src/killinfo.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/cpukit/posix/src/killinfo.c b/cpukit/posix/src/killinfo.c new file mode 100644 index 0000000000..b1569e791b --- /dev/null +++ b/cpukit/posix/src/killinfo.c @@ -0,0 +1,351 @@ +/* + * kill() support routine + * + * COPYRIGHT (c) 1989-2009. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <pthread.h> +#include <signal.h> +#include <errno.h> + +#include <rtems/system.h> +#include <rtems/posix/pthread.h> +#include <rtems/posix/psignal.h> +#include <rtems/seterr.h> +#include <rtems/score/isr.h> + +/* + * If you enable this, then you get printk() feedback on each path + * and the input to the decision that lead to the decision. Hopefully + * this will help in debugging the algorithm that distributes process + * signals to individual threads. + */ + +/* #define DEBUG_SIGNAL_PROCESSING */ +#if defined(DEBUG_SIGNAL_PROCESSING) + #include <rtems/bspIo.h> + #define DEBUG_STEP(_x) printk(_x) +#else + #define DEBUG_STEP(_x) +#endif + +/* + * 3.3.2 Send a Signal to a Process, P1003.1b-1993, p. 68 + * + * NOTE: Behavior of kill() depends on _POSIX_SAVED_IDS. + */ + +#define _POSIX_signals_Is_interested( _api, _mask ) \ + ( ~(_api)->signals_blocked & (_mask) ) + +int killinfo( + pid_t pid, + int sig, + const union sigval *value +) +{ + sigset_t mask; + POSIX_API_Control *api; + uint32_t the_api; + uint32_t index; + uint32_t maximum; + Objects_Information *the_info; + Objects_Control **object_table; + Thread_Control *the_thread; + Thread_Control *interested; + Priority_Control interested_priority; + Chain_Control *the_chain; + Chain_Node *the_node; + siginfo_t siginfo_struct; + siginfo_t *siginfo; + POSIX_signals_Siginfo_node *psiginfo; + + /* + * Only supported for the "calling process" (i.e. this node). + */ + if ( pid != getpid() ) + rtems_set_errno_and_return_minus_one( ESRCH ); + + /* + * Validate the signal passed. + */ + if ( !sig ) + rtems_set_errno_and_return_minus_one( EINVAL ); + + if ( !is_valid_signo(sig) ) + rtems_set_errno_and_return_minus_one( EINVAL ); + + /* + * If the signal is being ignored, then we are out of here. + */ + if ( _POSIX_signals_Vectors[ sig ].sa_handler == SIG_IGN ) + return 0; + + /* + * P1003.1c/Draft 10, p. 33 says that certain signals should always + * be directed to the executing thread such as those caused by hardware + * faults. + */ + if ( (sig == SIGFPE) || (sig == SIGILL) || (sig == SIGSEGV ) ) + return pthread_kill( pthread_self(), sig ); + + mask = signo_to_mask( sig ); + + /* + * Build up a siginfo structure + */ + siginfo = &siginfo_struct; + siginfo->si_signo = sig; + siginfo->si_code = SI_USER; + if ( !value ) { + siginfo->si_value.sival_int = 0; + } else { + siginfo->si_value = *value; + } + + _Thread_Disable_dispatch(); + + /* + * Is the currently executing thread interested? If so then it will + * get it an execute it as soon as the dispatcher executes. + */ + the_thread = _Thread_Executing; + + api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + if ( _POSIX_signals_Is_interested( api, mask ) ) { + goto process_it; + } + + /* + * Is an interested thread waiting for this signal (sigwait())? + * + * There is no requirement on the order of threads pending on a sigwait(). + */ + + /* XXX violation of visibility -- need to define thread queue support */ + + the_chain = &_POSIX_signals_Wait_queue.Queues.Fifo; + + for ( the_node = _Chain_First( the_chain ); + !_Chain_Is_tail( the_chain, the_node ) ; + the_node = the_node->next ) { + + the_thread = (Thread_Control *)the_node; + api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + + #if defined(DEBUG_SIGNAL_PROCESSING) + printk( "Waiting Thread=%p option=0x%08x mask=0x%08x blocked=0x%08x\n", + the_thread, the_thread->Wait.option, mask, api->signals_blocked); + #endif + + /* + * Is this thread is actually blocked waiting for the signal? + */ + if (the_thread->Wait.option & mask) + goto process_it; + + /* + * Is this thread is blocked waiting for another signal but has + * not blocked this one? + */ + if (~api->signals_blocked & mask) + goto process_it; + } + + /* + * Is any other thread interested? The highest priority interested + * thread is selected. In the event of a tie, then the following + * additional criteria is used: + * + * + ready thread over blocked + * + blocked on call interruptible by signal (can return EINTR) + * + blocked on call not interruptible by signal + * + * This looks at every thread in the system regardless of the creating API. + * + * NOTES: + * + * + rtems internal threads do not receive signals. + */ + interested = NULL; + interested_priority = PRIORITY_MAXIMUM + 1; + + for (the_api = OBJECTS_CLASSIC_API; the_api <= OBJECTS_APIS_LAST; the_api++) { + + /* + * This can occur when no one is interested and an API is not configured. + */ + if ( !_Objects_Information_table[ the_api ] ) + continue; + + the_info = _Objects_Information_table[ the_api ][ 1 ]; + + #if defined(RTEMS_DEBUG) + /* + * This cannot happen in the current (as of June 2009) implementation + * of initialization but at some point, the object information + * structure for a particular manager may not be installed. + */ + if ( !the_info ) + continue; + #endif + + maximum = the_info->maximum; + object_table = the_info->local_table; + + for ( index = 1 ; index <= maximum ; index++ ) { + the_thread = (Thread_Control *) object_table[ index ]; + + if ( !the_thread ) + continue; + + #if defined(DEBUG_SIGNAL_PROCESSING) + printk("\n 0x%08x/0x%08x %d/%d 0x%08x 1", + the_thread->Object.id, + ((interested) ? interested->Object.id : 0), + the_thread->current_priority, interested_priority, + the_thread->current_state + ); + #endif + + /* + * If this thread is of lower priority than the interested thread, + * go on to the next thread. + */ + if ( the_thread->current_priority > interested_priority ) + continue; + DEBUG_STEP("2"); + + /* + * If this thread is not interested, then go on to the next thread. + */ + api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + + #if defined(RTEMS_DEBUG) + if ( !api ) + continue; + #endif + + if ( !_POSIX_signals_Is_interested( api, mask ) ) + continue; + DEBUG_STEP("3"); + + /* + * Now we know the thread under consideration is interested. + * If the thread under consideration is of higher priority, then + * it becomes the interested thread. + * + * NOTE: We initialized interested_priority to PRIORITY_MAXIMUM + 1 + * so we never have to worry about deferencing a NULL + * interested thread. + */ + if ( the_thread->current_priority < interested_priority ) { + interested = the_thread; + interested_priority = the_thread->current_priority; + continue; + } + DEBUG_STEP("4"); + + /* + * Now the thread and the interested thread have the same priority. + * We have to sort through the combinations of blocked/not blocked + * and blocking interruptibutable by signal. + * + * If the interested thread is ready, don't think about changing. + */ + + if ( interested && !_States_Is_ready( interested->current_state ) ) { + /* preferred ready over blocked */ + DEBUG_STEP("5"); + if ( _States_Is_ready( the_thread->current_state ) ) { + interested = the_thread; + interested_priority = the_thread->current_priority; + continue; + } + + DEBUG_STEP("6"); + /* prefer blocked/interruptible over blocked/not interruptible */ + if ( !_States_Is_interruptible_by_signal(interested->current_state) ) { + DEBUG_STEP("7"); + if ( _States_Is_interruptible_by_signal(the_thread->current_state) ) { + DEBUG_STEP("8"); + interested = the_thread; + interested_priority = the_thread->current_priority; + continue; + } + } + } + } + } + + if ( interested ) { + the_thread = interested; + goto process_it; + } + + /* + * OK so no threads were interested right now. It will be left on the + * global pending until a thread receives it. The global set of threads + * can change interest in this signal in one of the following ways: + * + * + a thread is created with the signal unblocked, + * + pthread_sigmask() unblocks the signal, + * + sigprocmask() unblocks the signal, OR + * + sigaction() which changes the handler to SIG_IGN. + */ + the_thread = NULL; + goto post_process_signal; + + /* + * We found a thread which was interested, so now we mark that this + * thread needs to do the post context switch extension so it can + * evaluate the signals pending. + */ +process_it: + + /* + * Returns true if the signal was synchronously given to a thread + * blocked waiting for the signal. + */ + if ( _POSIX_signals_Unblock_thread( the_thread, sig, siginfo ) ) { + _Thread_Enable_dispatch(); + return 0; + } + +post_process_signal: + + /* + * We may have woken up a thread but we definitely need to post the + * signal to the process wide information set. + */ + _POSIX_signals_Set_process_signals( mask ); + + if ( _POSIX_signals_Vectors[ sig ].sa_flags == SA_SIGINFO ) { + + psiginfo = (POSIX_signals_Siginfo_node *) + _Chain_Get( &_POSIX_signals_Inactive_siginfo ); + if ( !psiginfo ) { + _Thread_Enable_dispatch(); + rtems_set_errno_and_return_minus_one( EAGAIN ); + } + + psiginfo->Info = *siginfo; + + _Chain_Append( &_POSIX_signals_Siginfo[ sig ], &psiginfo->Node ); + } + + DEBUG_STEP("\n"); + _Thread_Enable_dispatch(); + return 0; +} |