summaryrefslogtreecommitdiff
path: root/sys/src/liboventi/plan9-thread.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/liboventi/plan9-thread.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/liboventi/plan9-thread.c')
-rwxr-xr-xsys/src/liboventi/plan9-thread.c468
1 files changed, 468 insertions, 0 deletions
diff --git a/sys/src/liboventi/plan9-thread.c b/sys/src/liboventi/plan9-thread.c
new file mode 100755
index 000000000..8b158d519
--- /dev/null
+++ b/sys/src/liboventi/plan9-thread.c
@@ -0,0 +1,468 @@
+#include <u.h>
+#include <libc.h>
+#include <oventi.h>
+
+enum
+{
+ QueuingW, /* queuing for write lock */
+ QueuingR, /* queuing for read lock */
+};
+
+
+typedef struct Thread Thread;
+
+struct Thread {
+ int pid;
+ int ref;
+ char *error;
+ int state;
+ Thread *next;
+};
+
+struct VtLock {
+ Lock lk;
+ Thread *writer; /* thread writering write lock */
+ int readers; /* number writering read lock */
+ Thread *qfirst;
+ Thread *qlast;
+};
+
+struct VtRendez {
+ VtLock *lk;
+ Thread *wfirst;
+ Thread *wlast;
+};
+
+enum {
+ ERROR = 0,
+};
+
+static Thread **vtRock;
+
+static void vtThreadInit(void);
+static void threadSleep(Thread*);
+static void threadWakeup(Thread*);
+
+int
+vtThread(void (*f)(void*), void *rock)
+{
+ int tid;
+
+ tid = rfork(RFNOWAIT|RFMEM|RFPROC);
+ switch(tid){
+ case -1:
+ vtOSError();
+ return -1;
+ case 0:
+ break;
+ default:
+ return tid;
+ }
+ vtAttach();
+ (*f)(rock);
+ vtDetach();
+ _exits(0);
+ return 0;
+}
+
+static Thread *
+threadLookup(void)
+{
+ return *vtRock;
+}
+
+void
+vtAttach(void)
+{
+ int pid;
+ Thread *p;
+ static int init;
+ static Lock lk;
+
+ lock(&lk);
+ if(!init) {
+ rfork(RFREND);
+ vtThreadInit();
+ init = 1;
+ }
+ unlock(&lk);
+
+ pid = getpid();
+ p = *vtRock;
+ if(p != nil && p->pid == pid) {
+ p->ref++;
+ return;
+ }
+ p = vtMemAllocZ(sizeof(Thread));
+ p->ref = 1;
+ p->pid = pid;
+ *vtRock = p;
+}
+
+void
+vtDetach(void)
+{
+ Thread *p;
+
+ p = *vtRock;
+ assert(p != nil);
+ p->ref--;
+ if(p->ref == 0) {
+ vtMemFree(p->error);
+ vtMemFree(p);
+ *vtRock = nil;
+ }
+}
+
+char *
+vtGetError(void)
+{
+ char *s;
+
+ if(ERROR)
+ fprint(2, "vtGetError: %s\n", threadLookup()->error);
+ s = threadLookup()->error;
+ if(s == nil)
+ return "unknown error";
+ return s;
+}
+
+char*
+vtSetError(char* fmt, ...)
+{
+ Thread *p;
+ char *s;
+ va_list args;
+
+ p = threadLookup();
+
+ va_start(args, fmt);
+ s = vsmprint(fmt, args);
+ vtMemFree(p->error);
+ p->error = s;
+ va_end(args);
+ if(ERROR)
+ fprint(2, "vtSetError: %s\n", p->error);
+ werrstr("%s", p->error);
+ return p->error;
+}
+
+static void
+vtThreadInit(void)
+{
+ static Lock lk;
+
+ lock(&lk);
+ if(vtRock != nil) {
+ unlock(&lk);
+ return;
+ }
+ vtRock = privalloc();
+ if(vtRock == nil)
+ vtFatal("can't allocate thread-private storage");
+ unlock(&lk);
+}
+
+VtLock*
+vtLockAlloc(void)
+{
+ return vtMemAllocZ(sizeof(VtLock));
+}
+
+/*
+ * RSC: I think the test is backward. Let's see who uses it.
+ *
+void
+vtLockInit(VtLock **p)
+{
+ static Lock lk;
+
+ lock(&lk);
+ if(*p != nil)
+ *p = vtLockAlloc();
+ unlock(&lk);
+}
+ */
+
+void
+vtLockFree(VtLock *p)
+{
+ if(p == nil)
+ return;
+ assert(p->writer == nil);
+ assert(p->readers == 0);
+ assert(p->qfirst == nil);
+ vtMemFree(p);
+}
+
+VtRendez*
+vtRendezAlloc(VtLock *p)
+{
+ VtRendez *q;
+
+ q = vtMemAllocZ(sizeof(VtRendez));
+ q->lk = p;
+ setmalloctag(q, getcallerpc(&p));
+ return q;
+}
+
+void
+vtRendezFree(VtRendez *q)
+{
+ if(q == nil)
+ return;
+ assert(q->wfirst == nil);
+ vtMemFree(q);
+}
+
+int
+vtCanLock(VtLock *p)
+{
+ Thread *t;
+
+ lock(&p->lk);
+ t = *vtRock;
+ if(p->writer == nil && p->readers == 0) {
+ p->writer = t;
+ unlock(&p->lk);
+ return 1;
+ }
+ unlock(&p->lk);
+ return 0;
+}
+
+
+void
+vtLock(VtLock *p)
+{
+ Thread *t;
+
+ lock(&p->lk);
+ t = *vtRock;
+ if(p->writer == nil && p->readers == 0) {
+ p->writer = t;
+ unlock(&p->lk);
+ return;
+ }
+
+ /*
+ * venti currently contains code that assume locks can be passed between threads :-(
+ * assert(p->writer != t);
+ */
+
+ if(p->qfirst == nil)
+ p->qfirst = t;
+ else
+ p->qlast->next = t;
+ p->qlast = t;
+ t->next = nil;
+ t->state = QueuingW;
+ unlock(&p->lk);
+
+ threadSleep(t);
+ assert(p->writer == t && p->readers == 0);
+}
+
+int
+vtCanRLock(VtLock *p)
+{
+ lock(&p->lk);
+ if(p->writer == nil && p->qfirst == nil) {
+ p->readers++;
+ unlock(&p->lk);
+ return 1;
+ }
+ unlock(&p->lk);
+ return 0;
+}
+
+void
+vtRLock(VtLock *p)
+{
+ Thread *t;
+
+ lock(&p->lk);
+ t = *vtRock;
+ if(p->writer == nil && p->qfirst == nil) {
+ p->readers++;
+ unlock(&p->lk);
+ return;
+ }
+
+ /*
+ * venti currently contains code that assumes locks can be passed between threads
+ * assert(p->writer != t);
+ */
+ if(p->qfirst == nil)
+ p->qfirst = t;
+ else
+ p->qlast->next = t;
+ p->qlast = t;
+ t->next = nil;
+ t->state = QueuingR;
+ unlock(&p->lk);
+
+ threadSleep(t);
+ assert(p->writer == nil && p->readers > 0);
+}
+
+void
+vtUnlock(VtLock *p)
+{
+ Thread *t, *tt;
+
+ lock(&p->lk);
+ /*
+ * venti currently has code that assumes lock can be passed between threads :-)
+ * assert(p->writer == *vtRock);
+ */
+ assert(p->writer != nil);
+ assert(p->readers == 0);
+ t = p->qfirst;
+ if(t == nil) {
+ p->writer = nil;
+ unlock(&p->lk);
+ return;
+ }
+ if(t->state == QueuingW) {
+ p->qfirst = t->next;
+ p->writer = t;
+ unlock(&p->lk);
+ threadWakeup(t);
+ return;
+ }
+
+ p->writer = nil;
+ while(t != nil && t->state == QueuingR) {
+ tt = t;
+ t = t->next;
+ p->readers++;
+ threadWakeup(tt);
+ }
+ p->qfirst = t;
+ unlock(&p->lk);
+}
+
+void
+vtRUnlock(VtLock *p)
+{
+ Thread *t;
+
+ lock(&p->lk);
+ assert(p->writer == nil && p->readers > 0);
+ p->readers--;
+ t = p->qfirst;
+ if(p->readers > 0 || t == nil) {
+ unlock(&p->lk);
+ return;
+ }
+ assert(t->state == QueuingW);
+
+ p->qfirst = t->next;
+ p->writer = t;
+ unlock(&p->lk);
+
+ threadWakeup(t);
+}
+
+int
+vtSleep(VtRendez *q)
+{
+ Thread *s, *t, *tt;
+ VtLock *p;
+
+ p = q->lk;
+ lock(&p->lk);
+ s = *vtRock;
+ /*
+ * venti currently contains code that assume locks can be passed between threads :-(
+ * assert(p->writer != s);
+ */
+ assert(p->writer != nil);
+ assert(p->readers == 0);
+ t = p->qfirst;
+ if(t == nil) {
+ p->writer = nil;
+ } else if(t->state == QueuingW) {
+ p->qfirst = t->next;
+ p->writer = t;
+ threadWakeup(t);
+ } else {
+ p->writer = nil;
+ while(t != nil && t->state == QueuingR) {
+ tt = t;
+ t = t->next;
+ p->readers++;
+ threadWakeup(tt);
+ }
+ }
+
+ if(q->wfirst == nil)
+ q->wfirst = s;
+ else
+ q->wlast->next = s;
+ q->wlast = s;
+ s->next = nil;
+ unlock(&p->lk);
+
+ threadSleep(s);
+ assert(p->writer == s);
+ return 1;
+}
+
+int
+vtWakeup(VtRendez *q)
+{
+ Thread *t;
+ VtLock *p;
+
+ /*
+ * take off wait and put on front of queue
+ * put on front so guys that have been waiting will not get starved
+ */
+ p = q->lk;
+ lock(&p->lk);
+ /*
+ * venti currently has code that assumes lock can be passed between threads :-)
+ * assert(p->writer == *vtRock);
+ */
+ assert(p->writer != nil);
+ t = q->wfirst;
+ if(t == nil) {
+ unlock(&p->lk);
+ return 0;
+ }
+ q->wfirst = t->next;
+ if(p->qfirst == nil)
+ p->qlast = t;
+ t->next = p->qfirst;
+ p->qfirst = t;
+ t->state = QueuingW;
+ unlock(&p->lk);
+
+ return 1;
+}
+
+int
+vtWakeupAll(VtRendez *q)
+{
+ int i;
+
+ for(i=0; vtWakeup(q); i++)
+ ;
+ return i;
+}
+
+static void
+threadSleep(Thread *t)
+{
+ if(rendezvous(t, (void*)0x22bbdfd6) != (void*)0x44391f14)
+ sysfatal("threadSleep: rendezvous failed: %r");
+}
+
+static void
+threadWakeup(Thread *t)
+{
+ if(rendezvous(t, (void*)0x44391f14) != (void*)0x22bbdfd6)
+ sysfatal("threadWakeup: rendezvous failed: %r");
+}