Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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 *
70
threadLookup(void)
71
{
72
	return *vtRock;
73
}
74
 
75
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
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 *
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*
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
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*
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
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*
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
211
vtRendezFree(VtRendez *q)
212
{
213
	if(q == nil)
214
		return;
215
	assert(q->wfirst == nil);
216
	vtMemFree(q);
217
}
218
 
219
int
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
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
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
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
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
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
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
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
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
461
threadSleep(Thread *t)
462
{
463
	if(rendezvous(t, (void*)0x22bbdfd6) != (void*)0x44391f14)
464
		sysfatal("threadSleep: rendezvous failed: %r");
465
}
466
 
467
static void
468
threadWakeup(Thread *t)
469
{
470
	if(rendezvous(t, (void*)0x44391f14) != (void*)0x22bbdfd6)
471
		sysfatal("threadWakeup: rendezvous failed: %r");
472
}