provide afs_osi_TimedSleep
[openafs.git] / src / afs / FBSD / osi_sleep.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10
11 #include <afsconfig.h>
12 #include "afs/param.h"
13
14
15 #include "afs/sysincludes.h"    /* Standard vendor system headers */
16 #include "afsincludes.h"        /* Afs-based standard headers */
17 #include "afs/afs_stats.h"      /* afs statistics */
18
19 #ifndef AFS_FBSD50_ENV
20 static char waitV;
21 #endif
22
23
24 void
25 afs_osi_InitWaitHandle(struct afs_osi_WaitHandle *achandle)
26 {
27     AFS_STATCNT(osi_InitWaitHandle);
28 #ifdef AFS_FBSD50_ENV
29     cv_init(&achandle->wh_condvar, "afscondvar");
30     achandle->wh_inited = 1;
31 #else
32     achandle->proc = NULL;
33 #endif
34 }
35
36 /* cancel osi_Wait */
37 /* XXX
38  * I can't tell -- is this supposed to be cv_signal() or cv_waitq_remove()?
39  * Or perhaps cv_broadcast()?
40  * Assuming cv_signal() is the desired meaning.  -GAW
41  */
42 void
43 afs_osi_CancelWait(struct afs_osi_WaitHandle *achandle)
44 {
45 #ifndef AFS_FBSD50_ENV
46     caddr_t proc;
47 #endif
48
49     AFS_STATCNT(osi_CancelWait);
50
51 #ifdef AFS_FBSD50_ENV
52     /* XXX should not be necessary */
53     if (!achandle->wh_inited)
54         return;
55     AFS_ASSERT_GLOCK();
56     cv_signal(&achandle->wh_condvar);
57 #else
58     proc = achandle->proc;
59     if (proc == 0)
60         return;
61     achandle->proc = NULL;      /* so dude can figure out he was signalled */
62     afs_osi_Wakeup(&waitV);
63 #endif
64 }
65
66 /* afs_osi_Wait
67  * Waits for data on ahandle, or ams ms later.  ahandle may be null.
68  * Returns 0 if timeout and EINTR if signalled.
69  */
70 int
71 afs_osi_Wait(afs_int32 ams, struct afs_osi_WaitHandle *ahandle, int aintok)
72 {
73     int code;
74 #ifdef AFS_FBSD50_ENV
75     struct timeval tv;
76     int ticks;
77 #else
78     afs_int32 endTime;
79 #endif
80
81     AFS_STATCNT(osi_Wait);
82 #ifdef AFS_FBSD50_ENV
83     tv.tv_sec = ams / 1000;
84     tv.tv_usec = (ams % 1000) * 1000;
85     ticks = tvtohz(&tv);
86
87     AFS_ASSERT_GLOCK();
88     if (ahandle == NULL) {
89         /* This is nasty and evil and rude. */
90         afs_global_owner = 0;
91         code = msleep(&tv, &afs_global_mtx, (aintok ? PPAUSE|PCATCH : PVFS),
92             "afswait", ticks);
93         afs_global_owner = curthread;
94     } else {
95         if (!ahandle->wh_inited)
96             afs_osi_InitWaitHandle(ahandle);    /* XXX should not be needed */
97
98         if (aintok)
99             code = cv_timedwait_sig(&ahandle->wh_condvar, &afs_global_mtx,
100                 ticks);
101         else
102             code = cv_timedwait(&ahandle->wh_condvar, &afs_global_mtx, ticks);
103     }
104 #else
105     endTime = osi_Time() + (ams / 1000);
106     if (ahandle)
107         ahandle->proc = (caddr_t) curproc;
108     do {
109         AFS_ASSERT_GLOCK();
110         code = afs_osi_TimedSleep(&waitV, ams, aintok);
111         if (code)
112             break;              /* if something happened, quit now */
113         /* if we we're cancelled, quit now */
114         if (ahandle && (ahandle->proc == NULL)) {
115             /* we've been signalled */
116             break;
117         }
118     } while (osi_Time() < endTime);
119 #endif
120     return code;
121 }
122
123 /*
124  * All this gluck should probably also be replaced with CVs.
125  */
126 typedef struct afs_event {
127     struct afs_event *next;     /* next in hash chain */
128     char *event;                /* lwp event: an address */
129     int refcount;               /* Is it in use? */
130     int seq;                    /* Sequence number: this is incremented
131                                  * by wakeup calls; wait will not return until
132                                  * it changes */
133 #ifdef AFS_FBSD50_ENV
134     struct mtx *lck;
135     struct thread *owner;
136 #else
137     int cond;
138 #endif
139 } afs_event_t;
140
141 #define HASHSIZE 128
142 afs_event_t *afs_evhasht[HASHSIZE];     /* Hash table for events */
143 #define afs_evhash(event)       (afs_uint32) ((((long)event)>>2) & (HASHSIZE-1));
144 int afs_evhashcnt = 0;
145
146 #ifdef AFS_FBSD50_ENV
147 #define EVTLOCK_INIT(e) \
148     do { \
149         mtx_init((e)->lck, "event lock", NULL, MTX_DEF); \
150         (e)->owner = 0; \
151     } while (0)
152 #define EVTLOCK_LOCK(e) \
153     do { \
154         osi_Assert((e)->owner != curthread); \
155         mtx_lock((e)->lck);                  \
156         osi_Assert((e)->owner == 0); \
157         (e)->owner = curthread; \
158     } while (0)
159 #define EVTLOCK_UNLOCK(e) \
160     do { \
161         osi_Assert((e)->owner == curthread); \
162         (e)->owner = 0; \
163         mtx_unlock((e)->lck); \
164     } while (0)
165 #define EVTLOCK_DESTROY(e) mtx_destroy((e)->lck)
166 #else
167 #define EVTLOCK_INIT(e)
168 #define EVTLOCK_LOCK(e)
169 #define EVTLOCK_UNLOCK(e)
170 #define EVTLOCK_DESTROY(e)
171 #endif
172 #endif
173
174 /* Get and initialize event structure corresponding to lwp event (i.e. address)
175  * */
176 static afs_event_t *
177 afs_getevent(char *event)
178 {
179     afs_event_t *evp, *newp = 0;
180     int hashcode;
181
182     AFS_ASSERT_GLOCK();
183     hashcode = afs_evhash(event);
184     evp = afs_evhasht[hashcode];
185     while (evp) {
186         EVTLOCK_LOCK(evp);
187         if (evp->event == event) {
188             evp->refcount++;
189             return evp;
190         }
191         if (evp->refcount == 0)
192             newp = evp;
193         EVTLOCK_UNLOCK(evp);
194         evp = evp->next;
195     }
196     if (!newp) {
197         newp = (afs_event_t *) osi_AllocSmallSpace(sizeof(afs_event_t));
198         afs_evhashcnt++;
199         newp->next = afs_evhasht[hashcode];
200         afs_evhasht[hashcode] = newp;
201         newp->seq = 0;
202         EVTLOCK_INIT(newp);
203     }
204     EVTLOCK_LOCK(newp);
205     newp->event = event;
206     newp->refcount = 1;
207     return newp;
208 }
209
210 /* Release the specified event */
211 #ifdef AFS_FBSD50_ENV
212 #define relevent(evp) \
213     do { \
214         osi_Assert((evp)->owner == curthread); \
215         (evp)->refcount--; \
216         (evp)->owner = 0; \
217         mtx_unlock((evp)->lck); \
218     } while (0)
219 #else
220 #define relevent(evp) ((evp)->refcount--)
221 #endif
222
223 void
224 afs_osi_Sleep(void *event)
225 {
226     struct afs_event *evp;
227     int seq;
228
229     evp = afs_getevent(event);
230     seq = evp->seq;
231     while (seq == evp->seq) {
232         AFS_ASSERT_GLOCK();
233 #ifdef AFS_FBSD50_ENV
234         evp->owner = 0;
235         msleep(event, &afs_global_mtx, PVFS, "afsslp", 0);
236         evp->owner = curthread;
237 #else
238         AFS_GUNLOCK();
239         tsleep(event, PVFS, "afs_osi_Sleep", 0);
240         AFS_GLOCK();
241 #endif
242     }
243     relevent(evp);
244 }
245
246 int
247 afs_osi_SleepSig(void *event)
248 {
249     afs_osi_Sleep(event);
250     return 0;
251 }
252
253 /* afs_osi_TimedSleep
254  * 
255  * Arguments:
256  * event - event to sleep on
257  * ams --- max sleep time in milliseconds
258  * aintok - 1 if should sleep interruptibly
259  *
260  * Returns 0 if timeout and EINTR if signalled.
261  */
262 int
263 afs_osi_TimedSleep(void *event, afs_int32 ams, int aintok)
264 {
265     int code = 0;
266     struct afs_event *evp;
267     int seq, prio;
268     struct timespec ts;
269
270     evp = afs_getevent(event);
271     seq = evp->seq;
272     AFS_GUNLOCK();
273     if (aintok)
274         prio = PCATCH | PPAUSE;
275     else
276         prio = PVFS;
277     ts.tv_sec = ams / 1000;
278     ts.tv_nsec = (ams % 1000) * 1000000;
279     evp->owner = 0;
280     code = msleep(event, evp->lck, prio, "afsslp", &ts);
281     evp->owner = curthread;
282     if (seq == evp->seq)
283         code = EINTR;
284     relevent(evp);
285     AFS_GLOCK();
286     return code;
287 }
288
289 int
290 afs_osi_Wakeup(void *event)
291 {
292     int ret = 1;
293     struct afs_event *evp;
294
295     evp = afs_getevent(event);
296     if (evp->refcount > 1) {
297         evp->seq++;
298         wakeup(event);
299         ret = 0;
300     }
301     relevent(evp);
302     return ret;
303 }
304
305 void
306 shutdown_osisleep(void) {
307     struct afs_event *evp, *nevp, **pevpp;
308     int i;
309     for (i=0; i < HASHSIZE; i++) {
310         evp = afs_evhasht[i];
311         pevpp = &afs_evhasht[i];
312         while (evp) {
313             EVTLOCK_LOCK(evp);
314             nevp = evp->next;
315             if (evp->refcount == 0) {
316                 EVTLOCK_DESTROY(evp);
317                 *pevpp = evp->next;
318                 osi_FreeSmallSpace(evp);
319                 afs_evhashcnt--;
320             } else {
321                 EVTLOCK_UNLOCK(evp);
322                 pevpp = &evp->next;
323             }
324             evp = nevp;
325         }
326     }
327 }