Commit Diff


commit - f936548b5e1dc6cff6e422867a4265c64ebbcfd1
commit + df970459f9b37386fbfd4b7f3646825c8029caa8
blob - 96b3f0c237c861a8c284ed808f591d44e682e63e
blob + 6a04b7b0b25673fd343594abe7a379d520386c65
--- include/libc.h
+++ 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
blob - 2a84e619461835702aaad40c00ce682aad8fc691
blob + 2301191d7738c27508f6cab69b9a3e7b02de0fcc
--- include/thread.h
+++ 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.
blob - 92abb3f3c4165af235fbb35dcf7c193ae9de8861
blob + 5145a5432a1f49d9cb7bf0ec3eb2be8d1aea12dd
--- man/man3/thread.3
+++ 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 st
 void	threadexits(char *status)
 void	threadexitsall(char *status)
 void	yield(void)
+int	threadpin(void)
+int	threadunpin(void)
 .XX
 int	threadid(void)
 int	threadgrp(void)
@@ -259,6 +263,20 @@ System calls such as
 .IR read (3)
 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.
blob - 143ca7dba06229e63e4f0ffa8a034efc6f3af54a
blob + ca615435012dae92e388b6597dfa0f7afbdf3385
--- src/lib9/mkfile
+++ src/lib9/mkfile
@@ -128,6 +128,7 @@ LIB9OFILES=\
 	nulldir.$O\
 	open.$O\
 	opentemp.$O\
+	pin.$O\
 	pipe.$O\
 	post9p.$O\
 	postnote.$O\
blob - /dev/null
blob + 3b15d3b8aa4fb259ed7e21e02f3eb4d4a9a77bdd (mode 644)
--- /dev/null
+++ 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;
+
blob - acd56fa2ef285dede473a9f06fced8be142aa41a
blob + 48dd3e18ea167d6072d9d6d425acb6f7f8257bca
--- src/libthread/thread.c
+++ 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);
@@ -252,6 +253,32 @@ threadexits(char *msg)
 	utfecpy(p->msg, p->msg+sizeof p->msg, msg);
 	proc()->thread->exiting = 1;
 	_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
@@ -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)
 {