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>