Annotation of embedaddon/libpdel/util/paction.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (c) 2001-2002 Packet Design, LLC.
                      4:  * All rights reserved.
                      5:  * 
                      6:  * Subject to the following obligations and disclaimer of warranty,
                      7:  * use and redistribution of this software, in source or object code
                      8:  * forms, with or without modifications are expressly permitted by
                      9:  * Packet Design; provided, however, that:
                     10:  * 
                     11:  *    (i)  Any and all reproductions of the source or object code
                     12:  *         must include the copyright notice above and the following
                     13:  *         disclaimer of warranties; and
                     14:  *    (ii) No rights are granted, in any manner or form, to use
                     15:  *         Packet Design trademarks, including the mark "PACKET DESIGN"
                     16:  *         on advertising, endorsements, or otherwise except as such
                     17:  *         appears in the above copyright notice or in the software.
                     18:  * 
                     19:  * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
                     20:  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
                     21:  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
                     22:  * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
                     23:  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
                     24:  * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
                     25:  * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
                     26:  * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
                     27:  * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
                     28:  * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
                     29:  * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
                     30:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
                     31:  * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
                     32:  * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
                     33:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     34:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
                     35:  * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
                     36:  * THE POSSIBILITY OF SUCH DAMAGE.
                     37:  *
                     38:  * Author: Archie Cobbs <archie@freebsd.org>
                     39:  */
                     40: 
                     41: #include <sys/types.h>
                     42: 
                     43: #include <stdio.h>
                     44: #include <stdlib.h>
                     45: #include <stdarg.h>
                     46: #include <string.h>
                     47: #include <assert.h>
                     48: #include <pthread.h>
                     49: #include <unistd.h>
                     50: #include <errno.h>
                     51: 
                     52: #include "structs/structs.h"
                     53: #include "structs/type/array.h"
                     54: 
                     55: #include "util/paction.h"
                     56: #include "util/typed_mem.h"
                     57: 
                     58: #include "debug/debug.h"
                     59: 
                     60: #define        PACTION_MTYPE           "paction"
                     61: 
                     62: /* Action structure */
                     63: struct paction {
                     64:        pthread_t               tid;            /* action thread */
                     65:        struct paction          **actionp;      /* user action reference */
                     66:        pthread_mutex_t         mutex;          /* action mutex */
                     67:        pthread_mutex_t         *umutex;        /* user mutex */
                     68:        paction_handler_t       *handler;       /* action handler */
                     69: #if PDEL_DEBUG
                     70:        int                     mutex_count;
                     71:        int                     umutex_count;
                     72: #endif
                     73:        paction_finish_t        *finish;        /* action finisher */
                     74:        void                    *arg;           /* action argument */
                     75:        u_char                  may_cancel;     /* ok to cancel action thread */
                     76:        u_char                  canceled;       /* action was canceled */
                     77: };
                     78: 
                     79: /* Internal functions */
                     80: static void    *paction_main(void *arg);
                     81: static void    paction_cleanup(void *arg);
                     82: 
                     83: /*
                     84:  * Start an action.
                     85:  */
                     86: int
                     87: paction_start(struct paction **actionp, pthread_mutex_t *mutex,
                     88:        paction_handler_t *handler, paction_finish_t *finish, void *arg)
                     89: {
                     90:        struct paction *action;
                     91: 
                     92:        /* Check if action already in progress */
                     93:        if (*actionp != NULL) {
                     94:                errno = EBUSY;
                     95:                return (-1);
                     96:        }
                     97: 
                     98:        /* Create new action */
                     99:        if ((action = MALLOC(PACTION_MTYPE, sizeof(*action))) == NULL)
                    100:                return (-1);
                    101:        memset(action, 0, sizeof(*action));
                    102:        action->actionp = actionp;
                    103:        action->umutex = mutex;
                    104:        action->handler = handler;
                    105:        action->finish = finish;
                    106:        action->arg = arg;
                    107: 
                    108:        /* Create mutex */
                    109:        if ((errno = pthread_mutex_init(&action->mutex, NULL)) != 0) {
                    110:                FREE(PACTION_MTYPE, action);
                    111:                return (-1);
                    112:        }
                    113: 
                    114:        /* Spawn thread */
                    115:        if ((errno = pthread_create(&action->tid,
                    116:            NULL, paction_main, action)) != 0) {
                    117:                pthread_mutex_destroy(&action->mutex);
                    118:                FREE(PACTION_MTYPE, action);
                    119:                return (-1);
                    120:        }
                    121:        pthread_detach(action->tid);
                    122: 
                    123:        /* Done */
                    124:        *actionp = action;
                    125:        return (0);
                    126: }
                    127: 
                    128: /*
                    129:  * Cancel an action.
                    130:  */
                    131: void
                    132: paction_cancel(struct paction **actionp)
                    133: {
                    134:        struct paction *action = *actionp;
                    135: 
                    136:        /* Allow NULL action */
                    137:        if (action == NULL)
                    138:                return;
                    139: 
                    140:        /* Lock action */
                    141:        MUTEX_LOCK(&action->mutex, action->mutex_count);
                    142: 
                    143:        /* Mark action as canceled; this should only happen once */
                    144:        assert(!action->canceled);
                    145:        action->canceled = 1;
                    146: 
                    147:        /* Invalidate user's reference */
                    148:        assert(action->actionp == actionp);
                    149:        *action->actionp = NULL;
                    150:        action->actionp = NULL;
                    151: 
                    152:        /*
                    153:         * Don't cancel the thread before paction_main() starts, because
                    154:         * then paction_cleanup() would never get invoked. Also don't
                    155:         * pthread_cancel() the thread after the handler has completed,
                    156:         * because we might cancel in the middle of the cleanup.
                    157:         */
                    158:        if (action->may_cancel)
                    159:                pthread_cancel(action->tid);
                    160: 
                    161:        /* Unlock action */
                    162:        MUTEX_UNLOCK(&action->mutex, action->mutex_count);
                    163: }
                    164: 
                    165: /*
                    166:  * Action thread main entry point.
                    167:  */
                    168: static void *
                    169: paction_main(void *arg)
                    170: {
                    171:        struct paction *const action = arg;
                    172: 
                    173:        /* Cleanup when thread exits */
                    174:        pthread_cleanup_push(paction_cleanup, action);
                    175: 
                    176:        /* Begin allowing pthread_cancel()'s */
                    177:        assert(!action->may_cancel);
                    178:        action->may_cancel = 1;
                    179: 
                    180:        /* Handle race between paction_cancel() and paction_main() */
                    181:        if (action->canceled)                   /* race condition ok */
                    182:                goto done;
                    183: 
                    184:        /* Invoke handler */
                    185:        (*action->handler)(action->arg);
                    186: 
                    187: done:;
                    188:        /* Stop allowing pthread_cancel()'s */
                    189:        MUTEX_LOCK(&action->mutex, action->mutex_count);
                    190:        action->may_cancel = 0;
                    191:        MUTEX_UNLOCK(&action->mutex, action->mutex_count);
                    192: 
                    193:        /* Consume any last-minute pthread_cancel() still pending */
                    194:        pthread_testcancel();
                    195: 
                    196:        /* Done */
                    197:        pthread_cleanup_pop(1);
                    198:        return (NULL);
                    199: }
                    200: 
                    201: /*
                    202:  * Action thread cleanup
                    203:  */
                    204: static void
                    205: paction_cleanup(void *arg)
                    206: {
                    207:        struct paction *const action = arg;
                    208: 
                    209:        /*
                    210:         * Acquire the action mutex and then the user mutex. We must
                    211:         * do it in this order to avoid referencing the user mutex
                    212:         * after paction_cancel() has been called (because after that
                    213:         * the user mutex may have been destroyed).
                    214:         *
                    215:         * However, because paction_cancel() also acquires the action
                    216:         * mutex and it may be called with the user mutex already held,
                    217:         * there is a possibility for deadlock due to reverse lock ordering.
                    218:         * We avoid this by looping and yielding.
                    219:         */
                    220:        while (1) {
                    221: 
                    222:                /* Lock the action */
                    223:                MUTEX_LOCK(&action->mutex, action->mutex_count);
                    224: 
                    225:                /* Check for cancellation */
                    226:                if (action->canceled) {
                    227:                        action->umutex = NULL;
                    228:                        goto canceled;
                    229:                }
                    230: 
                    231:                /* Try to lock the user mutex */
                    232:                if (action->umutex == NULL)
                    233:                        break;
                    234:                MUTEX_TRYLOCK(action->umutex, action->umutex_count);
                    235:                if (errno == 0)
                    236:                        break;
                    237:                assert(errno == EBUSY);
                    238: 
                    239:                /* User mutex is busy, so unlock the action and try again */
                    240:                MUTEX_UNLOCK(&action->mutex, action->mutex_count);
                    241: 
                    242:                /* Let other threads progress */
                    243:                usleep(10 * 1000);                      /* 10 milliseconds */
                    244:        }
                    245: 
                    246:        /* Invalidate user reference */
                    247:        assert(action->actionp != NULL);
                    248:        *action->actionp = NULL;
                    249:        action->actionp = NULL;
                    250: 
                    251: canceled:
                    252:        /* Unlock action */
                    253:        MUTEX_UNLOCK(&action->mutex, action->mutex_count);
                    254: 
                    255:        /* Invoke finisher */
                    256:        (*action->finish)(action->arg, action->canceled);
                    257: 
                    258:        /* Release user mutex */
                    259:        if (action->umutex != NULL)
                    260:                MUTEX_UNLOCK(action->umutex, action->umutex_count);
                    261: 
                    262:        /* Destroy action */
                    263:        pthread_mutex_destroy(&action->mutex);
                    264:        FREE(PACTION_MTYPE, action);
                    265: }
                    266: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>