completion.h revision 1.4.4.3 1 1.4.4.2 tls /* $NetBSD: completion.h,v 1.4.4.3 2017/12/03 11:37:45 jdolecek Exp $ */
2 1.4.4.2 tls
3 1.4.4.2 tls /*-
4 1.4.4.2 tls * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 1.4.4.2 tls * All rights reserved.
6 1.4.4.2 tls *
7 1.4.4.2 tls * This code is derived from software contributed to The NetBSD Foundation
8 1.4.4.2 tls * by Taylor R. Campbell.
9 1.4.4.2 tls *
10 1.4.4.2 tls * Redistribution and use in source and binary forms, with or without
11 1.4.4.2 tls * modification, are permitted provided that the following conditions
12 1.4.4.2 tls * are met:
13 1.4.4.2 tls * 1. Redistributions of source code must retain the above copyright
14 1.4.4.2 tls * notice, this list of conditions and the following disclaimer.
15 1.4.4.2 tls * 2. Redistributions in binary form must reproduce the above copyright
16 1.4.4.2 tls * notice, this list of conditions and the following disclaimer in the
17 1.4.4.2 tls * documentation and/or other materials provided with the distribution.
18 1.4.4.2 tls *
19 1.4.4.2 tls * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.4.4.2 tls * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.4.4.2 tls * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.4.4.2 tls * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.4.4.2 tls * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.4.4.2 tls * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.4.4.2 tls * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.4.4.2 tls * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.4.4.2 tls * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.4.4.2 tls * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.4.4.2 tls * POSSIBILITY OF SUCH DAMAGE.
30 1.4.4.2 tls */
31 1.4.4.2 tls
32 1.4.4.2 tls /*
33 1.4.4.2 tls * Notes on porting:
34 1.4.4.2 tls *
35 1.4.4.2 tls * - Linux does not have destroy_completion. You must add it yourself
36 1.4.4.2 tls * in the appropriate place.
37 1.4.4.2 tls *
38 1.4.4.2 tls * - Some Linux code does `completion->done++' or similar. Convert
39 1.4.4.2 tls * that to complete(completion) and suggest the same change upstream,
40 1.4.4.2 tls * unless it turns out there actually is a good reason to do that, in
41 1.4.4.2 tls * which case the Linux completion API should be extended with a
42 1.4.4.2 tls * sensible name for this that doesn't expose the guts of `struct
43 1.4.4.2 tls * completion'.
44 1.4.4.2 tls */
45 1.4.4.2 tls
46 1.4.4.2 tls #ifndef _LINUX_COMPLETION_H_
47 1.4.4.2 tls #define _LINUX_COMPLETION_H_
48 1.4.4.2 tls
49 1.4.4.2 tls #include <sys/types.h>
50 1.4.4.2 tls #include <sys/condvar.h>
51 1.4.4.2 tls #include <sys/mutex.h>
52 1.4.4.2 tls
53 1.4.4.2 tls #include <machine/limits.h>
54 1.4.4.2 tls
55 1.4.4.2 tls #include <linux/errno.h>
56 1.4.4.2 tls
57 1.4.4.2 tls struct completion {
58 1.4.4.2 tls kmutex_t c_lock;
59 1.4.4.2 tls kcondvar_t c_cv;
60 1.4.4.2 tls
61 1.4.4.2 tls /*
62 1.4.4.2 tls * c_done is either
63 1.4.4.2 tls *
64 1.4.4.2 tls * . -1, meaning it's open season and we're done for good and
65 1.4.4.2 tls * nobody need wait any more;
66 1.4.4.2 tls *
67 1.4.4.2 tls * . 0, meaning nothing is done, so waiters must block; or
68 1.4.4.2 tls *
69 1.4.4.2 tls * . a positive integer, meaning that many waiters can
70 1.4.4.2 tls * proceed before further waiters must block.
71 1.4.4.2 tls *
72 1.4.4.2 tls * Negative values other than -1 are not allowed.
73 1.4.4.2 tls */
74 1.4.4.2 tls int c_done;
75 1.4.4.2 tls };
76 1.4.4.2 tls
77 1.4.4.2 tls /*
78 1.4.4.2 tls * Initialize a new completion object.
79 1.4.4.2 tls */
80 1.4.4.2 tls static inline void
81 1.4.4.2 tls init_completion(struct completion *completion)
82 1.4.4.2 tls {
83 1.4.4.2 tls
84 1.4.4.3 jdolecek mutex_init(&completion->c_lock, MUTEX_DEFAULT, IPL_SCHED);
85 1.4.4.2 tls cv_init(&completion->c_cv, "lnxcmplt");
86 1.4.4.2 tls completion->c_done = 0;
87 1.4.4.2 tls }
88 1.4.4.2 tls
89 1.4.4.2 tls /*
90 1.4.4.3 jdolecek * re-initialize a completion object.
91 1.4.4.3 jdolecek */
92 1.4.4.3 jdolecek static inline void
93 1.4.4.3 jdolecek reinit_completion(struct completion *completion)
94 1.4.4.3 jdolecek {
95 1.4.4.3 jdolecek
96 1.4.4.3 jdolecek completion->c_done = 0;
97 1.4.4.3 jdolecek }
98 1.4.4.3 jdolecek
99 1.4.4.3 jdolecek /*
100 1.4.4.2 tls * Destroy a completion object.
101 1.4.4.2 tls */
102 1.4.4.2 tls static inline void
103 1.4.4.2 tls destroy_completion(struct completion *completion)
104 1.4.4.2 tls {
105 1.4.4.2 tls KASSERT(!cv_has_waiters(&completion->c_cv));
106 1.4.4.2 tls cv_destroy(&completion->c_cv);
107 1.4.4.2 tls mutex_destroy(&completion->c_lock);
108 1.4.4.2 tls }
109 1.4.4.2 tls
110 1.4.4.2 tls /*
111 1.4.4.2 tls * Notify one waiter of completion, but not any future ones.
112 1.4.4.2 tls */
113 1.4.4.2 tls static inline void
114 1.4.4.2 tls complete(struct completion *completion)
115 1.4.4.2 tls {
116 1.4.4.2 tls
117 1.4.4.2 tls mutex_enter(&completion->c_lock);
118 1.4.4.2 tls
119 1.4.4.2 tls /* If it's not open season, wake one waiter. */
120 1.4.4.2 tls if (completion->c_done >= 0) {
121 1.4.4.2 tls KASSERT(completion->c_done < INT_MAX); /* XXX check */
122 1.4.4.2 tls completion->c_done++;
123 1.4.4.2 tls cv_signal(&completion->c_cv);
124 1.4.4.2 tls } else {
125 1.4.4.2 tls KASSERT(completion->c_done == -1);
126 1.4.4.2 tls }
127 1.4.4.2 tls
128 1.4.4.2 tls mutex_exit(&completion->c_lock);
129 1.4.4.2 tls }
130 1.4.4.2 tls
131 1.4.4.2 tls /*
132 1.4.4.2 tls * Notify all waiters, present and future (until INIT_COMPLETION), of
133 1.4.4.2 tls * completion.
134 1.4.4.2 tls */
135 1.4.4.2 tls static inline void
136 1.4.4.2 tls complete_all(struct completion *completion)
137 1.4.4.2 tls {
138 1.4.4.2 tls
139 1.4.4.2 tls mutex_enter(&completion->c_lock);
140 1.4.4.2 tls
141 1.4.4.2 tls /* If it's not open season, make it open season and wake everyone. */
142 1.4.4.2 tls if (completion->c_done >= 0) {
143 1.4.4.2 tls completion->c_done = -1;
144 1.4.4.2 tls cv_broadcast(&completion->c_cv);
145 1.4.4.2 tls } else {
146 1.4.4.2 tls KASSERT(completion->c_done == -1);
147 1.4.4.2 tls }
148 1.4.4.2 tls
149 1.4.4.2 tls mutex_exit(&completion->c_lock);
150 1.4.4.2 tls }
151 1.4.4.2 tls
152 1.4.4.2 tls /*
153 1.4.4.2 tls * Reverse the effect of complete_all so that subsequent waiters block
154 1.4.4.2 tls * until someone calls complete or complete_all.
155 1.4.4.2 tls *
156 1.4.4.2 tls * This operation is very different from its lowercase counterpart.
157 1.4.4.2 tls *
158 1.4.4.2 tls * For some reason this works on the completion object itself, not on a
159 1.4.4.2 tls * pointer thereto, so it must be a macro.
160 1.4.4.2 tls */
161 1.4.4.2 tls #define INIT_COMPLETION(COMPLETION) INIT_COMPLETION_blorp(&(COMPLETION))
162 1.4.4.2 tls
163 1.4.4.2 tls static inline void
164 1.4.4.2 tls INIT_COMPLETION_blorp(struct completion *completion)
165 1.4.4.2 tls {
166 1.4.4.2 tls
167 1.4.4.2 tls mutex_enter(&completion->c_lock);
168 1.4.4.2 tls completion->c_done = 0;
169 1.4.4.2 tls /* No notify -- waiters are interested only in nonzero values. */
170 1.4.4.2 tls mutex_exit(&completion->c_lock);
171 1.4.4.2 tls }
172 1.4.4.2 tls
173 1.4.4.2 tls static inline void
174 1.4.4.2 tls _completion_claim(struct completion *completion)
175 1.4.4.2 tls {
176 1.4.4.2 tls
177 1.4.4.2 tls KASSERT(mutex_owned(&completion->c_lock));
178 1.4.4.2 tls KASSERT(completion->c_done != 0);
179 1.4.4.2 tls if (completion->c_done > 0)
180 1.4.4.2 tls completion->c_done--;
181 1.4.4.2 tls else
182 1.4.4.2 tls KASSERT(completion->c_done == -1);
183 1.4.4.2 tls }
184 1.4.4.2 tls
185 1.4.4.2 tls /*
186 1.4.4.2 tls * Wait interruptibly with a timeout for someone to call complete or
187 1.4.4.2 tls * complete_all.
188 1.4.4.2 tls */
189 1.4.4.2 tls static inline int
190 1.4.4.2 tls wait_for_completion_interruptible_timeout(struct completion *completion,
191 1.4.4.2 tls unsigned long ticks)
192 1.4.4.2 tls {
193 1.4.4.2 tls /* XXX Arithmetic overflow...? */
194 1.4.4.2 tls unsigned int start = hardclock_ticks, now;
195 1.4.4.2 tls int error;
196 1.4.4.2 tls
197 1.4.4.2 tls mutex_enter(&completion->c_lock);
198 1.4.4.2 tls
199 1.4.4.2 tls /* Wait until c_done is nonzero. */
200 1.4.4.2 tls while (completion->c_done == 0) {
201 1.4.4.2 tls error = cv_timedwait_sig(&completion->c_cv,
202 1.4.4.2 tls &completion->c_lock, ticks);
203 1.4.4.2 tls if (error)
204 1.4.4.2 tls goto out;
205 1.4.4.2 tls now = hardclock_ticks;
206 1.4.4.2 tls if (ticks < (now - start)) {
207 1.4.4.2 tls error = EWOULDBLOCK;
208 1.4.4.2 tls goto out;
209 1.4.4.2 tls }
210 1.4.4.2 tls ticks -= (now - start);
211 1.4.4.2 tls start = now;
212 1.4.4.2 tls }
213 1.4.4.2 tls
214 1.4.4.2 tls /* Success! */
215 1.4.4.2 tls _completion_claim(completion);
216 1.4.4.2 tls error = 0;
217 1.4.4.2 tls
218 1.4.4.2 tls out: mutex_exit(&completion->c_lock);
219 1.4.4.2 tls if (error == EWOULDBLOCK) {
220 1.4.4.2 tls return 0;
221 1.4.4.2 tls } else if ((error == EINTR) || (error == ERESTART)) {
222 1.4.4.2 tls return -ERESTARTSYS;
223 1.4.4.2 tls } else {
224 1.4.4.2 tls KASSERTMSG((error == 0), "error = %d", error);
225 1.4.4.2 tls return ticks;
226 1.4.4.2 tls }
227 1.4.4.2 tls }
228 1.4.4.2 tls
229 1.4.4.2 tls /*
230 1.4.4.2 tls * Wait interruptibly for someone to call complete or complete_all.
231 1.4.4.2 tls */
232 1.4.4.2 tls static inline int
233 1.4.4.2 tls wait_for_completion_interruptible(struct completion *completion)
234 1.4.4.2 tls {
235 1.4.4.2 tls int error;
236 1.4.4.2 tls
237 1.4.4.2 tls mutex_enter(&completion->c_lock);
238 1.4.4.2 tls
239 1.4.4.2 tls /* Wait until c_done is nonzero. */
240 1.4.4.2 tls while (completion->c_done == 0) {
241 1.4.4.2 tls error = cv_wait_sig(&completion->c_cv, &completion->c_lock);
242 1.4.4.2 tls if (error)
243 1.4.4.2 tls goto out;
244 1.4.4.2 tls }
245 1.4.4.2 tls
246 1.4.4.2 tls /* Success! */
247 1.4.4.2 tls _completion_claim(completion);
248 1.4.4.2 tls error = 0;
249 1.4.4.2 tls
250 1.4.4.2 tls out: mutex_exit(&completion->c_lock);
251 1.4.4.2 tls if ((error == EINTR) || (error == ERESTART))
252 1.4.4.2 tls error = -ERESTARTSYS;
253 1.4.4.2 tls return error;
254 1.4.4.2 tls }
255 1.4.4.2 tls
256 1.4.4.2 tls /*
257 1.4.4.2 tls * Wait uninterruptibly, except by SIGKILL, for someone to call
258 1.4.4.2 tls * complete or complete_all.
259 1.4.4.2 tls *
260 1.4.4.2 tls * XXX In this implementation, any signal will actually wake us, not
261 1.4.4.2 tls * just SIGKILL.
262 1.4.4.2 tls */
263 1.4.4.2 tls static inline int
264 1.4.4.2 tls wait_for_completion_killable(struct completion *completion)
265 1.4.4.2 tls {
266 1.4.4.2 tls
267 1.4.4.2 tls return wait_for_completion_interruptible(completion);
268 1.4.4.2 tls }
269 1.4.4.2 tls
270 1.4.4.2 tls /*
271 1.4.4.2 tls * Try to claim a completion immediately. Return true on success, false
272 1.4.4.2 tls * if it would block.
273 1.4.4.2 tls */
274 1.4.4.2 tls static inline bool
275 1.4.4.2 tls try_wait_for_completion(struct completion *completion)
276 1.4.4.2 tls {
277 1.4.4.2 tls bool ok;
278 1.4.4.2 tls
279 1.4.4.2 tls mutex_enter(&completion->c_lock);
280 1.4.4.2 tls if (completion->c_done == 0) {
281 1.4.4.2 tls ok = false;
282 1.4.4.2 tls } else {
283 1.4.4.2 tls _completion_claim(completion);
284 1.4.4.2 tls ok = true;
285 1.4.4.2 tls }
286 1.4.4.2 tls mutex_exit(&completion->c_lock);
287 1.4.4.2 tls
288 1.4.4.2 tls return ok;
289 1.4.4.2 tls }
290 1.4.4.2 tls
291 1.4.4.2 tls #endif /* _LINUX_COMPLETION_H_ */
292