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>