aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libc.h3
-rw-r--r--include/thread.h2
-rw-r--r--man/man3/thread.318
-rw-r--r--src/lib9/mkfile1
-rw-r--r--src/lib9/pin.c11
-rw-r--r--src/libthread/thread.c52
6 files changed, 87 insertions, 0 deletions
diff --git a/include/libc.h b/include/libc.h
index 96b3f0c2..6a04b7b0 100644
--- a/include/libc.h
+++ b/include/libc.h
@@ -417,6 +417,9 @@ extern long p9time(long*);
extern void needstack(int);
extern char* readcons(char*, char*, int);
+extern void (*_pin)(void);
+extern void (*_unpin)(void);
+
#ifndef NOPLAN9DEFINES
#define atexit p9atexit
#define atexitdont p9atexitdont
diff --git a/include/thread.h b/include/thread.h
index 2a84e619..2301191d 100644
--- a/include/thread.h
+++ b/include/thread.h
@@ -25,6 +25,8 @@ void _threadsleep(Rendez*);
_Thread *_threadwakeup(Rendez*);
#define yield threadyield
int threadid(void);
+void _threadpin(void);
+void _threadunpin(void);
/*
* I am tired of making this mistake.
diff --git a/man/man3/thread.3 b/man/man3/thread.3
index 92abb3f3..5145a543 100644
--- a/man/man3/thread.3
+++ b/man/man3/thread.3
@@ -37,6 +37,8 @@ threadmain,
threadnotify,
threadid,
threadpid,
+threadpin,
+threadunpin,
threadsetgrp,
threadsetname,
threadsetstate,
@@ -84,6 +86,8 @@ int threadcreate(void (*fn)(void*), void *arg, uint stacksize)
void threadexits(char *status)
void threadexitsall(char *status)
void yield(void)
+int threadpin(void)
+int threadunpin(void)
.XX
int threadid(void)
int threadgrp(void)
@@ -260,6 +264,20 @@ System calls such as
block the entire proc;
all threads in a proc block until the system call finishes.
.PP
+.I Threadpin
+disables scheduling inside a proc, `pinning' the current
+thread as the only runnable one in the current proc.
+.I Threadunpin
+reenables scheduling, allowing other procs to run once the current
+thread relinquishes the processor.
+.I Threadpin
+and
+.I threadunpin
+can lead to deadlock.
+Used carefully, they can make library routines that use
+.B qlocks
+appear atomic relative to the current proc, like a system call.
+.PP
As mentioned above, each thread has a unique integer thread id.
Thread ids are not reused; they are unique across the life of the program.
.I Threadid
diff --git a/src/lib9/mkfile b/src/lib9/mkfile
index 143ca7db..ca615435 100644
--- a/src/lib9/mkfile
+++ b/src/lib9/mkfile
@@ -128,6 +128,7 @@ LIB9OFILES=\
nulldir.$O\
open.$O\
opentemp.$O\
+ pin.$O\
pipe.$O\
post9p.$O\
postnote.$O\
diff --git a/src/lib9/pin.c b/src/lib9/pin.c
new file mode 100644
index 00000000..3b15d3b8
--- /dev/null
+++ b/src/lib9/pin.c
@@ -0,0 +1,11 @@
+#include <u.h>
+#include <libc.h>
+
+static void
+nop(void)
+{
+}
+
+void (*_pin)(void) = nop;
+void (*_unpin)(void) = nop;
+
diff --git a/src/libthread/thread.c b/src/libthread/thread.c
index acd56fa2..48dd3e18 100644
--- a/src/libthread/thread.c
+++ b/src/libthread/thread.c
@@ -12,6 +12,7 @@ static void addproc(Proc*);
static void delproc(Proc*);
static void addthread(_Threadlist*, _Thread*);
static void delthread(_Threadlist*, _Thread*);
+static int onlist(_Threadlist*, _Thread*);
static void addthreadinproc(Proc*, _Thread*);
static void delthreadinproc(Proc*, _Thread*);
static void contextswitch(Context *from, Context *to);
@@ -254,6 +255,32 @@ threadexits(char *msg)
_threadswitch();
}
+void
+threadpin(void)
+{
+ Proc *p;
+
+ p = proc();
+ if(p->pinthread){
+ fprint(2, "already pinning a thread - %p %p\n", p->pinthread, p->thread);
+ assert(0);
+ }
+ p->pinthread = p->thread;
+}
+
+void
+threadunpin(void)
+{
+ Proc *p;
+
+ p = proc();
+ if(p->pinthread != p->thread){
+ fprint(2, "wrong pinthread - %p %p\n", p->pinthread, p->thread);
+ assert(0);
+ }
+ p->pinthread = nil;
+}
+
static void
contextswitch(Context *from, Context *to)
{
@@ -273,6 +300,14 @@ procscheduler(Proc *p)
/* print("s %p\n", p); */
lock(&p->lock);
for(;;){
+ if((t = p->pinthread) != nil){
+ while(!onlist(&p->runqueue, t)){
+ p->runrend.l = &p->lock;
+ _threaddebug("scheduler sleep (pin)");
+ _procsleep(&p->runrend);
+ _threaddebug("scheduler wake (pin)");
+ }
+ }else
while((t = p->runqueue.head) == nil){
if(p->nthread == 0)
goto Out;
@@ -291,6 +326,9 @@ procscheduler(Proc *p)
_procsleep(&p->runrend);
_threaddebug("scheduler wake");
}
+ if(p->pinthread && p->pinthread != t)
+ fprint(2, "p->pinthread %p t %p\n", p->pinthread, t);
+ assert(p->pinthread == nil || p->pinthread == t);
delthread(&p->runqueue, t);
unlock(&p->lock);
p->thread = t;
@@ -652,6 +690,8 @@ main(int argc, char **argv)
_rsleep = threadrsleep;
_rwakeup = threadrwakeup;
_notejmpbuf = threadnotejmp;
+ _pin = threadpin;
+ _unpin = threadunpin;
_pthreadinit();
p = procalloc();
@@ -697,6 +737,18 @@ delthread(_Threadlist *l, _Thread *t)
l->tail = t->prev;
}
+/* inefficient but rarely used */
+static int
+onlist(_Threadlist *l, _Thread *t)
+{
+ _Thread *tt;
+
+ for(tt = l->head; tt; tt=tt->next)
+ if(tt == t)
+ return 1;
+ return 0;
+}
+
static void
addthreadinproc(Proc *p, _Thread *t)
{