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