Annotation of embedaddon/libpdel/util/paction.c, revision 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>