diff options
Diffstat (limited to 'mit-pthreads/pthreads/pthread_cancel.c')
-rw-r--r-- | mit-pthreads/pthreads/pthread_cancel.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/mit-pthreads/pthreads/pthread_cancel.c b/mit-pthreads/pthreads/pthread_cancel.c new file mode 100644 index 00000000000..4191a269027 --- /dev/null +++ b/mit-pthreads/pthreads/pthread_cancel.c @@ -0,0 +1,258 @@ +/* ==== pthread_cancel.c ==================================================== + * Copyright (c) 1996 by Larry V. Streepy, Jr. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Larry V. Streepy, Jr. + * 4. The name of Larry V. Streepy, Jr. may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY Larry V. Streepy, Jr. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Larry V. Streepy, Jr. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Description : pthread_cancel operations + * + * 27 Sep 1996 - Larry V. Streepy, Jr. + * - Initial coding + */ +#ifndef lint +static const char rcsid[] = "$Id:"; +#endif +#include <pthread.h> +#include <errno.h> +static void possiblyMakeRunnable( pthread_t pthread ); + +/*---------------------------------------------------------------------- + * Function: pthread_cancel + * Purpose: Allows a thread to request that it or another thread + * terminate execution + * Args: + * thread = thread to mark as cancelled + * Returns: + * int 0 = ok, -1 = some error (see errno) + * Notes: + * The thread is simply marked as CANCELLED, it is up to the cancelled + * thread to decide how to handle it. + *----------------------------------------------------------------------*/ int +pthread_cancel( pthread_t pthread ) +{ + int rtn = 0; /* Assume all ok */ + pthread_sched_prevent(); + /* Ensure they gave us a legal pthread pointer */ + if( ! __pthread_is_valid( pthread ) ) { + rtn = ESRCH; /* No such thread */ + } else if( pthread->state == PS_UNALLOCED || pthread->state == PS_DEAD ) { + /* The standard doesn't call these out as errors, so return 0 */ + rtn = 0; + } else { + SET_PF_CANCELLED(pthread); /* Set the flag */ + /* If the thread is in the right state, then stick it on the + * run queue so it will get a chance to process the cancel. + */ + if( pthread != pthread_run ) { + possiblyMakeRunnable( pthread ); + } + } + pthread_sched_resume(); + if( rtn == 0 ) + pthread_testcancel(); /* See if we cancelled ourself */ + return rtn; +} + +/*---------------------------------------------------------------------- + * Function: pthread_setcancelstate + * Purpose: Set the current thread's cancellability state + * Args: + * state = PTHREAD_CANCEL_DISABLE or PTHREAD_CANCEL_ENABLE + * oldstate= pointer to holder for old state or NULL (*MODIFIED*) + * Returns: + * int 0 = ok + * EINVAL = state is neither of the legal states + * Notes: + * This has to be async-cancel safe, so we prevent scheduling in + * here + *----------------------------------------------------------------------*/ + +int +pthread_setcancelstate( int newstate, int *oldstate ) +{ + int ostate = TEST_PF_CANCEL_STATE(pthread_run); + int rtn = 0; + pthread_sched_prevent(); + if( newstate == PTHREAD_CANCEL_ENABLE || + newstate == PTHREAD_CANCEL_DISABLE ) { + SET_PF_CANCEL_STATE(pthread_run, newstate); + if( oldstate != NULL ) + *oldstate = ostate; + } else { /* Invalid new state */ + rtn = EINVAL; + } + pthread_sched_resume(); + if( rtn == 0 ) { + /* Test to see if we have a pending cancel to handle */ + pthread_testcancel(); + } + return rtn; +} + +/*---------------------------------------------------------------------- + * Function: pthread_setcanceltype + * Purpose: Set the current thread's cancellability type + * Args: + * type = PTHREAD_CANCEL_DEFERRED or PTHREAD_CANCEL_ASYNCHRONOUS + * oldtype = pointer to holder for old type or NULL (*MODIFIED*) + * Returns: + * int 0 = ok + * EINVAL = type is neither of the legal states + * Notes: + * This has to be async-cancel safe, so we prevent scheduling in + * here + *----------------------------------------------------------------------*/ + +int +pthread_setcanceltype( int newtype, int *oldtype ) +{ + int otype = TEST_PF_CANCEL_TYPE(pthread_run); + int rtn = 0; + pthread_sched_prevent(); + if( newtype == PTHREAD_CANCEL_DEFERRED || + newtype == PTHREAD_CANCEL_ASYNCHRONOUS) { + SET_PF_CANCEL_TYPE(pthread_run, newtype); + if( oldtype != NULL ) + *oldtype = otype; + } else { /* Invalid new type */ + rtn = EINVAL; + } + pthread_sched_resume(); + if( rtn == 0 ) { + /* Test to see if we have a pending cancel to handle */ + pthread_testcancel(); + } + return rtn; +} + +/*---------------------------------------------------------------------- + * Function: pthread_testcancel + * Purpose: Requests delivery of a pending cancel to the current thread + * Args: void + * Returns: void + * Notes: + * If the current thread has been cancelled, this function will not + * return and the threads exit processing will be initiated. + *----------------------------------------------------------------------*/ + +void +pthread_testcancel( void ) +{ + if( TEST_PF_CANCEL_STATE(pthread_run) == PTHREAD_CANCEL_DISABLE ) { + return; /* Can't be cancelled */ + } + /* Ensure that we aren't in the process of exiting already */ + if( TEST_PF_RUNNING_TO_CANCEL(pthread_run) ) + return; + + /* See if we have been cancelled */ + if( TEST_PF_CANCELLED(pthread_run) ) { + /* Set this flag to avoid recursively calling pthread_exit */ + SET_PF_RUNNING_TO_CANCEL(pthread_run); + pthread_exit( PTHREAD_CANCELLED ); /* Easy - just call pthread_exit */ + } + return; /* Not cancelled */ +} + +/*---------------------------------------------------------------------- + * Function: pthread_cancel_internal + * Purpose: An internal routine to begin the cancel processing + * Args: freelocks = do we need to free locks before exiting + * Returns: void + * Notes: + * This routine is called from pthread_resched_resume + * prior to a context switch, and after a thread has resumed. + * + * The kernel must *NOT* be locked on entry here + *----------------------------------------------------------------------*/ + +void +pthread_cancel_internal( int freelocks ) +{ + pthread_sched_prevent(); /* gotta stay focused */ + /* Since we can be called from pthread_resched_resume(), our + * state is currently not PS_RUNNING. Since we side stepped + * the actually blocking, we need to be removed from the queue + * and marked as running. + */ + if( pthread_run->state != PS_RUNNING ) { + if( pthread_run->queue == NULL ) { + PANIC(); /* Must be on a queue */ + } + /* We MUST NOT put the thread on the prio_queue here. It + * is already running (although it's state has changed) and if we + * put it on the run queue, it will get resumed after it is dead + * and we end up with a nice panic. + */ + pthread_queue_remove(pthread_run->queue, pthread_run); + pthread_run->state = PS_RUNNING; /* we are running */ + } + /* Set this flag to avoid recursively calling pthread_exit */ + SET_PF_RUNNING_TO_CANCEL(pthread_run); + /* Free up any locks we hold if told to. */ + if( freelocks ) { + fd_unlock_for_cancel(); + } + pthread_sched_resume(); + pthread_exit( PTHREAD_CANCELLED ); /* Easy - just call pthread_exit */ +} + +/*---------------------------------------------------------------------- + * Function: possiblyMakeRunnable + * Purpose: Make a thread runnable so it can be cancelled if state allows + * Args: + * pthread = thread to process + * Returns: + * Notes: + *----------------------------------------------------------------------*/ + +static void +possiblyMakeRunnable( pthread_t pthread ) +{ + if( ! TEST_PTHREAD_IS_CANCELLABLE(pthread) ) + return; /* Not currently cancellable */ + /* If the thread is currently runnable, then we just let things + * take their course when it is next resumed. + */ + if( pthread->state == PS_RUNNING ) + return; /* will happen at context switch */ + /* If the thread is sleeping, the it isn't on a queue. */ + if( pthread->state == PS_SLEEP_WAIT ) { + sleep_cancel( pthread ); /* Remove from sleep list */ + } else { + /* Otherwise, we need to take it off the queue and make it runnable */ + if( pthread->queue == NULL ) { + PANIC(); /* Must be on a queue */ + } + pthread_queue_remove(pthread->queue, pthread); + } + /* And make it runnable */ + pthread_prio_queue_enq(pthread_current_prio_queue, pthread); + pthread->old_state = pthread->state; + pthread->state = PS_RUNNING; +} |