Allocate and free backing store for event mutices
[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 static char waitV;
20
21
22 void
23 afs_osi_InitWaitHandle(struct afs_osi_WaitHandle *achandle)
24 {
25     AFS_STATCNT(osi_InitWaitHandle);
26     achandle->proc = (caddr_t) 0;
27 }
28
29 /* cancel osi_Wait */
30 /* XXX
31  * I can't tell -- is this supposed to be cv_signal() or cv_waitq_remove()?
32  * Or perhaps cv_broadcast()?
33  * Assuming cv_signal() is the desired meaning.  -GAW
34  */
35 void
36 afs_osi_CancelWait(struct afs_osi_WaitHandle *achandle)
37 {
38     caddr_t proc;
39
40     AFS_STATCNT(osi_CancelWait);
41
42     proc = achandle->proc;
43     if (proc == 0)
44         return;
45     achandle->proc = NULL;      /* so dude can figure out he was signalled */
46     afs_osi_Wakeup(&waitV);
47 }
48
49 /* afs_osi_Wait
50  * Waits for data on ahandle, or ams ms later.  ahandle may be null.
51  * Returns 0 if timeout and EINTR if signalled.
52  */
53 int
54 afs_osi_Wait(afs_int32 ams, struct afs_osi_WaitHandle *ahandle, int aintok)
55 {
56     int code;
57     afs_int32 endTime;
58
59     AFS_STATCNT(osi_Wait);
60
61     endTime = osi_Time() + (ams / 1000);
62     if (ahandle)
63         ahandle->proc = (caddr_t) curproc;
64     do {
65         AFS_ASSERT_GLOCK();
66         code = afs_osi_TimedSleep(&waitV, ams, aintok);
67         if (code)
68             break;              /* if something happened, quit now */
69         /* if we we're cancelled, quit now */
70         if (ahandle && (ahandle->proc == (caddr_t) 0)) {
71             /* we've been signalled */
72             break;
73         }
74     } while (osi_Time() < endTime);
75
76     return code;
77 }
78
79 /*
80  * All this gluck should probably also be replaced with CVs.
81  */
82 typedef struct afs_event {
83     struct afs_event *next;     /* next in hash chain */
84     char *event;                /* lwp event: an address */
85     int refcount;               /* Is it in use? */
86     int seq;                    /* Sequence number: this is incremented
87                                  * by wakeup calls; wait will not return until
88                                  * it changes */
89     struct mtx *lck;
90     struct thread *owner;
91 } afs_event_t;
92
93 #define HASHSIZE 128
94 afs_event_t *afs_evhasht[HASHSIZE];     /* Hash table for events */
95 #define afs_evhash(event)       (afs_uint32) ((((long)event)>>2) & (HASHSIZE-1));
96 int afs_evhashcnt = 0;
97
98 #define EVTLOCK_INIT(e) \
99     do { \
100         mtx_init((e)->lck, "event lock", NULL, MTX_DEF); \
101         (e)->owner = 0; \
102     } while (0)
103 #define EVTLOCK_LOCK(e) \
104     do { \
105         osi_Assert((e)->owner != curthread); \
106         mtx_lock((e)->lck);                  \
107         osi_Assert((e)->owner == 0); \
108         (e)->owner = curthread; \
109     } while (0)
110 #define EVTLOCK_UNLOCK(e) \
111     do { \
112         osi_Assert((e)->owner == curthread); \
113         (e)->owner = 0; \
114         mtx_unlock((e)->lck); \
115     } while (0)
116 #define EVTLOCK_DESTROY(e) mtx_destroy((e)->lck)
117
118 /* Get and initialize event structure corresponding to lwp event (i.e. address)
119  * */
120 static afs_event_t *
121 afs_getevent(char *event)
122 {
123     afs_event_t *evp, *newp = 0;
124     int hashcode;
125
126     AFS_ASSERT_GLOCK();
127     hashcode = afs_evhash(event);
128     evp = afs_evhasht[hashcode];
129     while (evp) {
130         EVTLOCK_LOCK(evp);
131         if (evp->event == event) {
132             evp->refcount++;
133             return evp;
134         }
135         if (evp->refcount == 0)
136             newp = evp;
137         EVTLOCK_UNLOCK(evp);
138         evp = evp->next;
139     }
140     if (!newp) {
141         newp = osi_AllocSmallSpace(sizeof(afs_event_t));
142         newp->lck = osi_AllocSmallSpace(sizeof(struct mtx));
143         memset(newp->lck, 0, sizeof(struct mtx));
144         afs_evhashcnt++;
145         newp->next = afs_evhasht[hashcode];
146         afs_evhasht[hashcode] = newp;
147         newp->seq = 0;
148         EVTLOCK_INIT(newp);
149     }
150     EVTLOCK_LOCK(newp);
151     newp->event = event;
152     newp->refcount = 1;
153     return newp;
154 }
155
156 /* Release the specified event */
157 #define relevent(evp) \
158     do { \
159         osi_Assert((evp)->owner == curthread); \
160         (evp)->refcount--; \
161         (evp)->owner = 0; \
162         mtx_unlock((evp)->lck); \
163     } while (0)
164
165 void
166 afs_osi_Sleep(void *event)
167 {
168     struct afs_event *evp;
169     int seq;
170
171     evp = afs_getevent(event);
172     seq = evp->seq;
173     AFS_GUNLOCK();
174     while (seq == evp->seq) {
175         evp->owner = 0;
176         msleep(event, evp->lck, PVFS, "afsslp", 0);
177         evp->owner = curthread;
178     }
179     relevent(evp);
180     AFS_GLOCK();
181 }
182
183 int
184 afs_osi_SleepSig(void *event)
185 {
186     afs_osi_Sleep(event);
187     return 0;
188 }
189
190 /* afs_osi_TimedSleep
191  * 
192  * Arguments:
193  * event - event to sleep on
194  * ams --- max sleep time in milliseconds
195  * aintok - 1 if should sleep interruptibly
196  *
197  * Returns 0 if timeout and EINTR if signalled.
198  */
199 int
200 afs_osi_TimedSleep(void *event, afs_int32 ams, int aintok)
201 {
202     int code = 0;
203     struct afs_event *evp;
204     int seq, prio;
205
206     evp = afs_getevent(event);
207     seq = evp->seq;
208     AFS_GUNLOCK();
209     if (aintok)
210         prio = PCATCH | PPAUSE;
211     else
212         prio = PVFS;
213     evp->owner = 0;
214     code = msleep(event, evp->lck, prio, "afsslp", (ams * hz) / 1000);
215     evp->owner = curthread;
216     if (seq == evp->seq)
217         code = EINTR;
218     relevent(evp);
219     AFS_GLOCK();
220     return code;
221 }
222
223 int
224 afs_osi_Wakeup(void *event)
225 {
226     int ret = 1;
227     struct afs_event *evp;
228
229     evp = afs_getevent(event);
230     if (evp->refcount > 1) {
231         evp->seq++;
232         wakeup(event);
233         ret = 0;
234     }
235     relevent(evp);
236     return ret;
237 }
238
239 void
240 shutdown_osisleep(void) {
241     struct afs_event *evp, *nevp, **pevpp;
242     int i;
243     for (i=0; i < HASHSIZE; i++) {
244         evp = afs_evhasht[i];
245         pevpp = &afs_evhasht[i];
246         while (evp) {
247             EVTLOCK_LOCK(evp);
248             nevp = evp->next;
249             if (evp->refcount == 0) {
250                 EVTLOCK_DESTROY(evp);
251                 *pevpp = evp->next;
252                 osi_FreeSmallSpace(evp->lck);
253                 osi_FreeSmallSpace(evp);
254                 afs_evhashcnt--;
255             } else {
256                 EVTLOCK_UNLOCK(evp);
257                 pevpp = &evp->next;
258             }
259             evp = nevp;
260         }
261     }
262 }