]> git.lizzy.rs Git - plan9front.git/blob - sys/src/liboventi/plan9-thread.c
libsec: use tsmemcmp() when comparing hashes, use mpfield() for ecc, use mptober...
[plan9front.git] / sys / src / liboventi / plan9-thread.c
1 #include <u.h>
2 #include <libc.h>
3 #include <oventi.h>
4
5 enum
6 {
7         QueuingW,       /* queuing for write lock */
8         QueuingR,       /* queuing for read lock */
9 };
10
11
12 typedef struct Thread Thread;
13
14 struct Thread {
15         int pid;
16         int ref;
17         char *error;
18         int state;
19         Thread *next;
20 };
21
22 struct VtLock {
23         Lock lk;
24         Thread *writer;         /* thread writering write lock */
25         int readers;            /* number writering read lock */
26         Thread *qfirst;
27         Thread *qlast;
28 };
29
30 struct VtRendez {
31         VtLock *lk;
32         Thread *wfirst;
33         Thread *wlast;
34 };
35
36 enum {
37         ERROR = 0,
38 };
39
40 static Thread **vtRock;
41
42 static void     vtThreadInit(void);
43 static void     threadSleep(Thread*);
44 static void     threadWakeup(Thread*);
45
46 int
47 vtThread(void (*f)(void*), void *rock)
48 {
49         int tid;
50
51         tid = rfork(RFNOWAIT|RFMEM|RFPROC);
52         switch(tid){
53         case -1:
54                 vtOSError();
55                 return -1;
56         case 0:
57                 break;
58         default:
59                 return tid;
60         }
61         vtAttach();
62         (*f)(rock);
63         vtDetach();
64         _exits(0);
65         return 0;
66 }
67
68 static Thread *
69 threadLookup(void)
70 {
71         return *vtRock;
72 }
73
74 void
75 vtAttach(void)
76 {
77         int pid;
78         Thread *p;
79         static int init;
80         static Lock lk;
81
82         lock(&lk);
83         if(!init) {
84                 rfork(RFREND);
85                 vtThreadInit();
86                 init = 1;
87         }
88         unlock(&lk);
89
90         pid = getpid();
91         p = *vtRock;
92         if(p != nil && p->pid == pid) {
93                 p->ref++;
94                 return;
95         }
96         p = vtMemAllocZ(sizeof(Thread));
97         p->ref = 1;
98         p->pid = pid;
99         *vtRock = p;
100 }
101
102 void
103 vtDetach(void)
104 {
105         Thread *p;
106
107         p = *vtRock;
108         assert(p != nil);
109         p->ref--;
110         if(p->ref == 0) {
111                 vtMemFree(p->error);
112                 vtMemFree(p);
113                 *vtRock = nil;
114         }
115 }
116
117 char *
118 vtGetError(void)
119 {
120         char *s;
121
122         if(ERROR)
123                 fprint(2, "vtGetError: %s\n", threadLookup()->error);
124         s = threadLookup()->error;
125         if(s == nil)
126                 return "unknown error";
127         return s;
128 }
129
130 char*
131 vtSetError(char* fmt, ...)
132 {
133         Thread *p;
134         char *s;
135         va_list args;
136
137         p = threadLookup();
138
139         va_start(args, fmt);
140         s = vsmprint(fmt, args);
141         vtMemFree(p->error);
142         p->error = s;
143         va_end(args);
144         if(ERROR)
145                 fprint(2, "vtSetError: %s\n", p->error);
146         werrstr("%s", p->error);
147         return p->error;
148 }
149
150 static void
151 vtThreadInit(void)
152 {
153         static Lock lk;
154
155         lock(&lk);
156         if(vtRock != nil) {
157                 unlock(&lk);
158                 return;
159         }
160         vtRock = privalloc();
161         if(vtRock == nil)
162                 vtFatal("can't allocate thread-private storage");
163         unlock(&lk);
164 }
165
166 VtLock*
167 vtLockAlloc(void)
168 {
169         return vtMemAllocZ(sizeof(VtLock));
170 }
171
172 /*
173  * RSC: I think the test is backward.  Let's see who uses it. 
174  *
175 void
176 vtLockInit(VtLock **p)
177 {
178         static Lock lk;
179
180         lock(&lk);
181         if(*p != nil)
182                 *p = vtLockAlloc();
183         unlock(&lk);
184 }
185  */
186
187 void
188 vtLockFree(VtLock *p)
189 {
190         if(p == nil)
191                 return;
192         assert(p->writer == nil);
193         assert(p->readers == 0);
194         assert(p->qfirst == nil);
195         vtMemFree(p);
196 }
197
198 VtRendez*
199 vtRendezAlloc(VtLock *p)
200 {
201         VtRendez *q;
202
203         q = vtMemAllocZ(sizeof(VtRendez));
204         q->lk = p;
205         setmalloctag(q, getcallerpc(&p));
206         return q;
207 }
208
209 void
210 vtRendezFree(VtRendez *q)
211 {
212         if(q == nil)
213                 return;
214         assert(q->wfirst == nil);
215         vtMemFree(q);
216 }
217
218 int
219 vtCanLock(VtLock *p)
220 {
221         Thread *t;
222
223         lock(&p->lk);
224         t = *vtRock;
225         if(p->writer == nil && p->readers == 0) {
226                 p->writer = t;
227                 unlock(&p->lk);
228                 return 1;
229         }
230         unlock(&p->lk);
231         return 0;
232 }
233
234
235 void
236 vtLock(VtLock *p)
237 {
238         Thread *t;
239
240         lock(&p->lk);
241         t = *vtRock;
242         if(p->writer == nil && p->readers == 0) {
243                 p->writer = t;
244                 unlock(&p->lk);
245                 return;
246         }
247
248         /*
249          * venti currently contains code that assume locks can be passed between threads :-(
250          * assert(p->writer != t);
251          */
252
253         if(p->qfirst == nil)
254                 p->qfirst = t;
255         else
256                 p->qlast->next = t;
257         p->qlast = t;
258         t->next = nil;
259         t->state = QueuingW;
260         unlock(&p->lk);
261
262         threadSleep(t);
263         assert(p->writer == t && p->readers == 0);
264 }
265
266 int
267 vtCanRLock(VtLock *p)
268 {
269         lock(&p->lk);
270         if(p->writer == nil && p->qfirst == nil) {
271                 p->readers++;
272                 unlock(&p->lk);
273                 return 1;
274         }
275         unlock(&p->lk);
276         return 0;
277 }
278
279 void
280 vtRLock(VtLock *p)
281 {
282         Thread *t;
283
284         lock(&p->lk);
285         t = *vtRock;
286         if(p->writer == nil && p->qfirst == nil) {
287                 p->readers++;
288                 unlock(&p->lk);
289                 return;
290         }
291
292         /*
293          * venti currently contains code that assumes locks can be passed between threads
294          * assert(p->writer != t);
295          */
296         if(p->qfirst == nil)
297                 p->qfirst = t;
298         else
299                 p->qlast->next = t;
300         p->qlast = t;
301         t->next = nil;
302         t->state = QueuingR;
303         unlock(&p->lk);
304
305         threadSleep(t);
306         assert(p->writer == nil && p->readers > 0);
307 }
308
309 void
310 vtUnlock(VtLock *p)
311 {
312         Thread *t, *tt;
313
314         lock(&p->lk);
315         /*
316          * venti currently has code that assumes lock can be passed between threads :-)
317          * assert(p->writer == *vtRock);
318          */
319         assert(p->writer != nil);   
320         assert(p->readers == 0);
321         t = p->qfirst;
322         if(t == nil) {
323                 p->writer = nil;
324                 unlock(&p->lk);
325                 return;
326         }
327         if(t->state == QueuingW) {
328                 p->qfirst = t->next;
329                 p->writer = t;
330                 unlock(&p->lk);
331                 threadWakeup(t);
332                 return;
333         }
334
335         p->writer = nil;
336         while(t != nil && t->state == QueuingR) {
337                 tt = t;
338                 t = t->next;
339                 p->readers++;
340                 threadWakeup(tt);
341         }
342         p->qfirst = t;
343         unlock(&p->lk);
344 }
345
346 void
347 vtRUnlock(VtLock *p)
348 {
349         Thread *t;
350
351         lock(&p->lk);
352         assert(p->writer == nil && p->readers > 0);
353         p->readers--;
354         t = p->qfirst;
355         if(p->readers > 0 || t == nil) {
356                 unlock(&p->lk);
357                 return;
358         }
359         assert(t->state == QueuingW);
360         
361         p->qfirst = t->next;
362         p->writer = t;
363         unlock(&p->lk);
364
365         threadWakeup(t);
366 }
367
368 int
369 vtSleep(VtRendez *q)
370 {
371         Thread *s, *t, *tt;
372         VtLock *p;
373
374         p = q->lk;
375         lock(&p->lk);
376         s = *vtRock;
377         /*
378          * venti currently contains code that assume locks can be passed between threads :-(
379          * assert(p->writer != s);
380          */
381         assert(p->writer != nil);
382         assert(p->readers == 0);
383         t = p->qfirst;
384         if(t == nil) {
385                 p->writer = nil;
386         } else if(t->state == QueuingW) {
387                 p->qfirst = t->next;
388                 p->writer = t;
389                 threadWakeup(t);
390         } else {
391                 p->writer = nil;
392                 while(t != nil && t->state == QueuingR) {
393                         tt = t;
394                         t = t->next;
395                         p->readers++;
396                         threadWakeup(tt);
397                 }
398         }
399
400         if(q->wfirst == nil)
401                 q->wfirst = s;
402         else
403                 q->wlast->next = s;
404         q->wlast = s;
405         s->next = nil;
406         unlock(&p->lk);
407
408         threadSleep(s);
409         assert(p->writer == s);
410         return 1;
411 }
412
413 int
414 vtWakeup(VtRendez *q)
415 {
416         Thread *t;
417         VtLock *p;
418
419         /*
420          * take off wait and put on front of queue
421          * put on front so guys that have been waiting will not get starved
422          */
423         p = q->lk;
424         lock(&p->lk);   
425         /*
426          * venti currently has code that assumes lock can be passed between threads :-)
427          * assert(p->writer == *vtRock);
428          */
429         assert(p->writer != nil);
430         t = q->wfirst;
431         if(t == nil) {
432                 unlock(&p->lk);
433                 return 0;
434         }
435         q->wfirst = t->next;
436         if(p->qfirst == nil)
437                 p->qlast = t;
438         t->next = p->qfirst;
439         p->qfirst = t;
440         t->state = QueuingW;
441         unlock(&p->lk);
442
443         return 1;
444 }
445
446 int
447 vtWakeupAll(VtRendez *q)
448 {
449         int i;
450         
451         for(i=0; vtWakeup(q); i++)
452                 ;
453         return i;
454 }
455
456 static void
457 threadSleep(Thread *t)
458 {
459         if(rendezvous(t, (void*)0x22bbdfd6) != (void*)0x44391f14)
460                 sysfatal("threadSleep: rendezvous failed: %r");
461 }
462
463 static void
464 threadWakeup(Thread *t)
465 {
466         if(rendezvous(t, (void*)0x44391f14) != (void*)0x22bbdfd6)
467                 sysfatal("threadWakeup: rendezvous failed: %r");
468 }