Return to paction.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / mpd / src / contrib / libpdel / util |
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: