Initial revision

This commit is contained in:
rsc
2003-09-30 17:47:42 +00:00
parent ed7c8e8d02
commit 76193d7cb0
223 changed files with 32479 additions and 0 deletions

21
src/libthread/386.c Normal file
View File

@@ -0,0 +1,21 @@
#include "threadimpl.h"
static void
launcher386(void (*f)(void *arg), void *arg)
{
(*f)(arg);
threadexits(nil);
}
void
_threadinitstack(Thread *t, void (*f)(void*), void *arg)
{
ulong *tos;
tos = (ulong*)&t->stk[t->stksize&~7];
*--tos = (ulong)arg;
*--tos = (ulong)f;
t->sched.pc = (ulong)launcher386;
t->sched.sp = (ulong)tos - 8; /* old PC and new PC */
}

View File

@@ -0,0 +1,18 @@
.globl _xinc
_xinc:
movl 4(%esp), %eax
lock incl 0(%eax)
ret
.globl _xdec
_xdec:
movl 4(%esp), %eax
lock decl 0(%eax)
jz iszero
movl %eax, 1
ret
iszero:
movl %eax, 0
ret

258
src/libthread/LICENSE Normal file
View File

@@ -0,0 +1,258 @@
The Plan 9 software is provided under the terms of the
Lucent Public License, Version 1.02, reproduced below,
with the following exceptions:
1. No right is granted to create derivative works of or
to redistribute (other than with the Plan 9 Operating System)
the screen imprinter fonts identified in subdirectory
/lib/font/bit/lucida and printer fonts (Lucida Sans Unicode, Lucida
Sans Italic, Lucida Sans Demibold, Lucida Typewriter, Lucida Sans
Typewriter83), identified in subdirectory /sys/lib/postscript/font.
These directories contain material copyrights by B&H Inc. and Y&Y Inc.
2. The printer fonts identified in subdirectory /sys/lib/ghostscript/font
are subject to the GNU GPL, reproduced in the file /LICENSE.gpl.
3. The ghostscript program in the subdirectory /sys/src/cmd/gs is
covered by the Aladdin Free Public License, reproduced in the file
/LICENSE.afpl.
===================================================================
Lucent Public License Version 1.02
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE
PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original
Program, and
b. in the case of each Contributor,
i. changes to the Program, and
ii. additions to the Program;
where such changes and/or additions to the Program were added to the
Program by such Contributor itself or anyone acting on such
Contributor's behalf, and the Contributor explicitly consents, in
accordance with Section 3C, to characterization of the changes and/or
additions as Contributions.
"Contributor" means LUCENT and any other entity that has Contributed a
Contribution to the Program.
"Distributor" means a Recipient that distributes the Program,
modifications to the Program, or any part thereof.
"Licensed Patents" mean patent claims licensable by a Contributor
which are necessarily infringed by the use or sale of its Contribution
alone or when combined with the Program.
"Original Program" means the original version of the software
accompanying this Agreement as released by LUCENT, including source
code, object code and documentation, if any.
"Program" means the Original Program and Contributions or any part
thereof
"Recipient" means anyone who receives the Program under this
Agreement, including all Contributors.
2. GRANT OF RIGHTS
a. Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free copyright
license to reproduce, prepare derivative works of, publicly display,
publicly perform, distribute and sublicense the Contribution of such
Contributor, if any, and such derivative works, in source code and
object code form.
b. Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free patent
license under Licensed Patents to make, use, sell, offer to sell,
import and otherwise transfer the Contribution of such Contributor, if
any, in source code and object code form. The patent license granted
by a Contributor shall also apply to the combination of the
Contribution of that Contributor and the Program if, at the time the
Contribution is added by the Contributor, such addition of the
Contribution causes such combination to be covered by the Licensed
Patents. The patent license granted by a Contributor shall not apply
to (i) any other combinations which include the Contribution, nor to
(ii) Contributions of other Contributors. No hardware per se is
licensed hereunder.
c. Recipient understands that although each Contributor grants the
licenses to its Contributions set forth herein, no assurances are
provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity. Each
Contributor disclaims any liability to Recipient for claims brought by
any other entity based on infringement of intellectual property rights
or otherwise. As a condition to exercising the rights and licenses
granted hereunder, each Recipient hereby assumes sole responsibility
to secure any other intellectual property rights needed, if any. For
example, if a third party patent license is required to allow
Recipient to distribute the Program, it is Recipient's responsibility
to acquire that license before distributing the Program.
d. Each Contributor represents that to its knowledge it has sufficient
copyright rights in its Contribution, if any, to grant the copyright
license set forth in this Agreement.
3. REQUIREMENTS
A. Distributor may choose to distribute the Program in any form under
this Agreement or under its own license agreement, provided that:
a. it complies with the terms and conditions of this Agreement;
b. if the Program is distributed in source code or other tangible
form, a copy of this Agreement or Distributor's own license agreement
is included with each copy of the Program; and
c. if distributed under Distributor's own license agreement, such
license agreement:
i. effectively disclaims on behalf of all Contributors all warranties
and conditions, express and implied, including warranties or
conditions of title and non-infringement, and implied warranties or
conditions of merchantability and fitness for a particular purpose;
ii. effectively excludes on behalf of all Contributors all liability
for damages, including direct, indirect, special, incidental and
consequential damages, such as lost profits; and
iii. states that any provisions which differ from this Agreement are
offered by that Contributor alone and not by any other party.
B. Each Distributor must include the following in a conspicuous
location in the Program:
Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights
Reserved.
C. In addition, each Contributor must identify itself as the
originator of its Contribution in a manner that reasonably allows
subsequent Recipients to identify the originator of the Contribution.
Also, each Contributor must agree that the additions and/or changes
are intended to be a Contribution. Once a Contribution is contributed,
it may not thereafter be revoked.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain
responsibilities with respect to end users, business partners and the
like. While this license is intended to facilitate the commercial use
of the Program, the Distributor who includes the Program in a
commercial product offering should do so in a manner which does not
create potential liability for Contributors. Therefore, if a
Distributor includes the Program in a commercial product offering,
such Distributor ("Commercial Distributor") hereby agrees to defend
and indemnify every Contributor ("Indemnified Contributor") against
any losses, damages and costs (collectively"Losses") arising from
claims, lawsuits and other legal actions brought by a third party
against the Indemnified Contributor to the extent caused by the acts
or omissions of such Commercial Distributor in connection with its
distribution of the Program in a commercial product offering. The
obligations in this section do not apply to any claims or Losses
relating to any actual or alleged intellectual property infringement.
In order to qualify, an Indemnified Contributor must: a) promptly
notify the Commercial Distributor in writing of such claim, and b)
allow the Commercial Distributor to control, and cooperate with the
Commercial Distributor in, the defense and any related settlement
negotiations. The Indemnified Contributor may participate in any such
claim at its own expense.
For example, a Distributor might include the Program in a commercial
product offering, Product X. That Distributor is then a Commercial
Distributor. If that Commercial Distributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Distributor's responsibility
alone. Under this section, the Commercial Distributor would have to
defend claims against the Contributors related to those performance
claims and warranties, and if a court requires any Contributor to pay
any damages as a result, the Commercial Distributor must pay those
damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement, including but not limited to
the risks and costs of program errors, compliance with applicable
laws, damage to or loss of data, programs or equipment, and
unavailability or interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. EXPORT CONTROL
Recipient agrees that Recipient alone is responsible for compliance
with the United States export administration regulations (and the
export control laws and regulation of any other countries).
8. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further
action by the parties hereto, such provision shall be reformed to the
minimum extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against a Contributor with
respect to a patent applicable to software (including a cross-claim or
counterclaim in a lawsuit), then any patent licenses granted by that
Contributor to such Recipient under this Agreement shall terminate as
of the date such litigation is filed. In addition, if Recipient
institutes patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Program
itself (excluding combinations of the Program with other software or
hardware) infringes such Recipient's patent(s), then such Recipient's
rights granted under Section 2(b) shall terminate as of the date such
litigation is filed.
All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of
time after becoming aware of such noncompliance. If all Recipient's
rights under this Agreement terminate, Recipient agrees to cease use
and distribution of the Program as soon as reasonably practicable.
However, Recipient's obligations under this Agreement and any licenses
granted by Recipient relating to the Program shall continue and
survive.
LUCENT may publish new versions (including revisions) of this
Agreement from time to time. Each new version of the Agreement will be
given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new
version of the Agreement is published, Contributor may elect to
distribute the Program (including its Contributions) under the new
version. No one other than LUCENT has the right to modify this
Agreement. Except as expressly stated in Sections 2(a) and 2(b) above,
Recipient receives no rights or licenses to the intellectual property
of any Contributor under this Agreement, whether expressly, by
implication, estoppel or otherwise. All rights in the Program not
expressly granted under this Agreement are reserved.
This Agreement is governed by the laws of the State of New York and
the intellectual property laws of the United States of America. No
party to this Agreement will bring a legal action under this Agreement
more than one year after the cause of action arose. Each party waives
its rights to a jury trial in any resulting litigation.

View File

@@ -0,0 +1,6 @@
CC=gcc
CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I${PREFIX}/include
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

View File

@@ -0,0 +1,7 @@
CC=gcc
CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
NAN=nan64.$O

View File

@@ -0,0 +1,6 @@
CC=cc
CFLAGS=-O -c -Ae -I.
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

View File

@@ -0,0 +1,7 @@
CC=gcc
CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I.
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
NAN=nan64.$O

View File

@@ -0,0 +1,7 @@
CC=gcc
CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
NAN=nan64.$O

View File

@@ -0,0 +1,6 @@
CC=cc
CFLAGS+=-g -c -I.
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

View File

@@ -0,0 +1,2 @@
include Make.SunOS-sun4u-$(CC)
NAN=nan64.$O

View File

@@ -0,0 +1,6 @@
CC=cc
CFLAGS+=-g -c -I. -O
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

View File

@@ -0,0 +1,6 @@
CC=gcc
CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

125
src/libthread/Makefile Normal file
View File

@@ -0,0 +1,125 @@
# this works in gnu make
SYSNAME:=${shell uname}
OBJTYPE:=${shell uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'}
# this works in bsd make
SYSNAME!=uname
OBJTYPE!=uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'
# the gnu rules will mess up bsd but not vice versa,
# hence the gnu rules come first.
include Make.$(SYSNAME)-$(OBJTYPE)
PREFIX=/usr/local
NUKEFILES=
TGZFILES=
LIB=libthread.a
VERSION=2.0
PORTPLACE=devel/libthread
NAME=libthread
OFILES=\
$(OBJTYPE).$O\
asm-$(SYSNAME)-$(OBJTYPE).$O\
channel.$O\
chanprint.$O\
create.$O\
debug.$O\
exec-unix.$O\
exit.$O\
getpid.$O\
id.$O\
iocall.$O\
ioclose.$O\
ioopen.$O\
ioproc.$O\
ioread.$O\
ioreadn.$O\
iowrite.$O\
kill.$O\
lib.$O\
main.$O\
memset.$O\
memsetd.$O\
note.$O\
proctab.$O\
ref.$O\
rendez.$O\
sched.$O\
HFILES=\
thread.h\
label.h\
threadimpl.h\
all: $(LIB)
install: $(LIB)
test -d $(PREFIX)/man/man3 || mkdir $(PREFIX)/man/man3
install -m 0644 thread.3 $(PREFIX)/man/man3/thread.3
install -m 0644 ioproc.3 $(PREFIX)/man/man3/ioproc.3
install -m 0644 thread.h $(PREFIX)/include/thread.h
install -m 0644 $(LIB) $(PREFIX)/lib/$(LIB)
tprimes: $(LIB) tprimes.$O
$(CC) -o tprimes tprimes.$O $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf
texec: $(LIB) texec.$O
$(CC) -o texec texec.$O $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf
$(LIB): $(OFILES)
$(AR) $(ARFLAGS) $(LIB) $(OFILES)
NUKEFILES+=$(LIB)
.c.$O:
$(CC) $(CFLAGS) -I/usr/X11R6/include -I../sam -I$(PREFIX)/include $*.c
%.$O: %.c
$(CC) $(CFLAGS) -I/usr/X11R6/include -I../sam -I$(PREFIX)/include $*.c
$(OFILES): $(HFILES)
tgz:
rm -rf $(NAME)-$(VERSION)
mkdir $(NAME)-$(VERSION)
cp Makefile Make.* README LICENSE NOTICE *.[ch137] rpm.spec bundle.ports $(TGZFILES) $(NAME)-$(VERSION)
tar cf - $(NAME)-$(VERSION) | gzip >$(NAME)-$(VERSION).tgz
rm -rf $(NAME)-$(VERSION)
clean:
rm -f $(OFILES) $(LIB)
nuke:
rm -f $(OFILES) *.tgz *.rpm $(NUKEFILES)
rpm:
make tgz
cp $(NAME)-$(VERSION).tgz /usr/src/RPM/SOURCES
rpm -ba rpm.spec
cp /usr/src/RPM/SRPMS/$(NAME)-$(VERSION)-1.src.rpm .
cp /usr/src/RPM/RPMS/i586/$(NAME)-$(VERSION)-1.i586.rpm .
scp *.rpm rsc@amsterdam.lcs.mit.edu:public_html/software
PORTDIR=/usr/ports/$(PORTPLACE)
ports:
make tgz
rm -rf $(PORTDIR)
mkdir $(PORTDIR)
cp $(NAME)-$(VERSION).tgz /usr/ports/distfiles
cat bundle.ports | (cd $(PORTDIR) && awk '$$1=="---" && $$3=="---" { ofile=$$2; next} {if(ofile) print >ofile}')
(cd $(PORTDIR); make makesum)
(cd $(PORTDIR); make)
(cd $(PORTDIR); /usr/local/bin/portlint)
rm -rf $(PORTDIR)/work
shar `find $(PORTDIR)` > ports.shar
(cd $(PORTDIR); tar cf - *) | gzip >$(NAME)-$(VERSION)-ports.tgz
scp *.tgz rsc@amsterdam.lcs.mit.edu:public_html/software
.phony: all clean nuke install tgz rpm ports

View File

@@ -0,0 +1,54 @@
LIB=libthread.a
VERSION=2.0
PORTPLACE=devel/libthread
NAME=libthread
OFILES=\
$(OBJTYPE).$O\
asm-$(SYSNAME)-$(OBJTYPE).$O\
channel.$O\
chanprint.$O\
create.$O\
debug.$O\
exec-unix.$O\
exit.$O\
getpid.$O\
id.$O\
iocall.$O\
ioclose.$O\
ioopen.$O\
ioproc.$O\
ioread.$O\
ioreadn.$O\
iowrite.$O\
kill.$O\
lib.$O\
main.$O\
memset.$O\
memsetd.$O\
note.$O\
proctab.$O\
ref.$O\
rendez.$O\
sched.$O\
HFILES=\
thread.h\
label.h\
threadimpl.h\
all: $(LIB)
install: $(LIB)
test -d $(PREFIX)/man/man3 || mkdir $(PREFIX)/man/man3
install -m 0644 thread.3 $(PREFIX)/man/man3/thread.3
install -m 0644 ioproc.3 $(PREFIX)/man/man3/ioproc.3
install -m 0644 thread.h $(PREFIX)/include/thread.h
install -m 0644 $(LIB) $(PREFIX)/lib/$(LIB)
tprimes: $(LIB) tprimes.$O
$(CC) -o tprimes tprimes.$O $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf
texec: $(LIB) texec.$O
$(CC) -o texec texec.$O $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf

19
src/libthread/NOTICE Normal file
View File

@@ -0,0 +1,19 @@
/*
* The authors of this software are Russ Cox, Sape Mullender, and Rob Pike.
* Copyright (c) 2003 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
This is a Unix port of the Plan 9 thread library.
Please send comments about the packaging
to Russ Cox <rsc@post.harvard.edu>.

19
src/libthread/README Normal file
View File

@@ -0,0 +1,19 @@
/*
* The authors of this software are Russ Cox, Sape Mullender, and Rob Pike.
* Copyright (c) 2003 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
This is a Unix port of the Plan 9 thread library.
Please send comments about the packaging
to Russ Cox <rsc@post.harvard.edu>.

View File

@@ -0,0 +1,49 @@
.globl _setlabel
.type _setlabel,@function
_setlabel:
movl 4(%esp), %eax
movl 0(%esp), %edx
movl %edx, 0(%eax)
movl %ebx, 4(%eax)
movl %esp, 8(%eax)
movl %ebp, 12(%eax)
movl %esi, 16(%eax)
movl %edi, 20(%eax)
xorl %eax, %eax
ret
.globl _gotolabel
.type _gotolabel,@function
_gotolabel:
movl 4(%esp), %edx
movl 0(%edx), %ecx
movl 4(%edx), %ebx
movl 8(%edx), %esp
movl 12(%edx), %ebp
movl 16(%edx), %esi
movl 20(%edx), %edi
xorl %eax, %eax
incl %eax
movl %ecx, 0(%esp)
ret
.globl _xinc
_xinc:
movl 4(%esp), %eax
lock incl 0(%eax)
ret
.globl _xdec
_xdec:
movl 4(%esp), %eax
lock decl 0(%eax)
jz iszero
movl %eax, 1
ret
iszero:
movl %eax, 0
ret

View File

@@ -0,0 +1 @@
.include "asm-FreeBSD-386.s"

View File

@@ -0,0 +1,42 @@
--- Makefile ---
# New ports collection makefile for: libthread
# Date Created: 11 Feb 2003
# Whom: rsc
#
PORTNAME= libthread
PORTVERSION= 1.0
CATEGORIES= devel
MASTER_SITES= http://pdos.lcs.mit.edu/~rsc/software/
DISTNAME= libthread
EXTRACT_SUFX= .tgz
MAINTAINER= rsc@post.harvard.edu
MAN3= print.3 fmtinstall.3
MLINKS= XXX
USE_REINPLACE= XXX (wkj says yes)
.include <bsd.port.pre.mk>
post-patch:
${REINPLACE_CMD} -e 's,@@LOCAL@@,${PREFIX},g' ${WRKSRC}/Makefile
.include <bsd.port.post.mk>
--- pkg-comment ---
Plan 9 thread library
--- pkg-descr ---
Libthread is a port of Plan 9's thread library.
WWW: http://pdos.lcs.mit.edu/~rsc/software/
WWW: http://plan9.bell-labs.com/magic/man2html/2/thread
Russ Cox
rsc@post.harvard.edu
--- pkg-plist ---
lib/libthread.a
include/thread.h
--- /dev/null ---
This is just a way to make sure blank lines don't
creep into pkg-plist.

485
src/libthread/channel.c Normal file
View File

@@ -0,0 +1,485 @@
#include "threadimpl.h"
static Lock chanlock; /* central channel access lock */
static void enqueue(Alt*, Channel**);
static void dequeue(Alt*);
static int altexec(Alt*, int);
int _threadhighnentry;
int _threadnalt;
static int
canexec(Alt *a)
{
int i, otherop;
Channel *c;
c = a->c;
/* are there senders or receivers blocked? */
otherop = (CHANSND+CHANRCV) - a->op;
for(i=0; i<c->nentry; i++)
if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]->tag==nil){
_threaddebug(DBGCHAN, "can rendez alt %p chan %p", a, c);
return 1;
}
/* is there room in the channel? */
if((a->op==CHANSND && c->n < c->s)
|| (a->op==CHANRCV && c->n > 0)){
_threaddebug(DBGCHAN, "can buffer alt %p chan %p", a, c);
return 1;
}
return 0;
}
static void
_chanfree(Channel *c)
{
int i, inuse;
inuse = 0;
for(i = 0; i < c->nentry; i++)
if(c->qentry[i])
inuse = 1;
if(inuse)
c->freed = 1;
else{
if(c->qentry)
free(c->qentry);
free(c);
}
}
void
chanfree(Channel *c)
{
lock(&chanlock);
_chanfree(c);
unlock(&chanlock);
}
int
chaninit(Channel *c, int elemsize, int elemcnt)
{
if(elemcnt < 0 || elemsize <= 0 || c == nil)
return -1;
c->f = 0;
c->n = 0;
c->freed = 0;
c->e = elemsize;
c->s = elemcnt;
_threaddebug(DBGCHAN, "chaninit %p", c);
return 1;
}
Channel*
chancreate(int elemsize, int elemcnt)
{
Channel *c;
if(elemcnt < 0 || elemsize <= 0)
return nil;
c = _threadmalloc(sizeof(Channel)+elemsize*elemcnt, 1);
c->e = elemsize;
c->s = elemcnt;
_threaddebug(DBGCHAN, "chancreate %p", c);
return c;
}
int
alt(Alt *alts)
{
Alt *a, *xa;
Channel *volatile c;
int n, s;
ulong r;
Thread *t;
/*
* The point of going splhi here is that note handlers
* might reasonably want to use channel operations,
* but that will hang if the note comes while we hold the
* chanlock. Instead, we delay the note until we've dropped
* the lock.
*/
t = _threadgetproc()->thread;
if(t->moribund || _threadexitsallstatus)
yield(); /* won't return */
s = _procsplhi();
lock(&chanlock);
t->alt = alts;
t->chan = Chanalt;
/* test whether any channels can proceed */
n = 0;
a = nil;
for(xa=alts; xa->op!=CHANEND && xa->op!=CHANNOBLK; xa++){
xa->entryno = -1;
if(xa->op == CHANNOP)
continue;
c = xa->c;
if(c==nil){
unlock(&chanlock);
_procsplx(s);
t->chan = Channone;
return -1;
}
if(canexec(xa))
if(nrand(++n) == 0)
a = xa;
}
if(a==nil){
/* nothing can proceed */
if(xa->op == CHANNOBLK){
unlock(&chanlock);
_procsplx(s);
t->chan = Channone;
_threadnalt++;
return xa - alts;
}
/* enqueue on all channels. */
c = nil;
for(xa=alts; xa->op!=CHANEND; xa++){
if(xa->op==CHANNOP)
continue;
enqueue(xa, (Channel**)&c);
}
/*
* wait for successful rendezvous.
* we can't just give up if the rendezvous
* is interrupted -- someone else might come
* along and try to rendezvous with us, so
* we need to be here.
*/
Again:
unlock(&chanlock);
_procsplx(s);
r = _threadrendezvous((ulong)&c, 0);
s = _procsplhi();
lock(&chanlock);
if(r==~0){ /* interrupted */
if(c!=nil) /* someone will meet us; go back */
goto Again;
c = (Channel*)~0; /* so no one tries to meet us */
}
/* dequeue from channels, find selected one */
a = nil;
for(xa=alts; xa->op!=CHANEND; xa++){
if(xa->op==CHANNOP)
continue;
if(xa->c == c)
a = xa;
dequeue(xa);
}
unlock(&chanlock);
_procsplx(s);
if(a == nil){ /* we were interrupted */
assert(c==(Channel*)~0);
return -1;
}
}else{
altexec(a, s); /* unlocks chanlock, does splx */
}
_sched();
t->chan = Channone;
_threadnalt++;
return a - alts;
}
static int
runop(int op, Channel *c, void *v, int nb)
{
int r;
Alt a[2];
/*
* we could do this without calling alt,
* but the only reason would be performance,
* and i'm not convinced it matters.
*/
a[0].op = op;
a[0].c = c;
a[0].v = v;
a[1].op = CHANEND;
if(nb)
a[1].op = CHANNOBLK;
switch(r=alt(a)){
case -1: /* interrupted */
return -1;
case 1: /* nonblocking, didn't accomplish anything */
assert(nb);
return 0;
case 0:
return 1;
default:
fprint(2, "ERROR: channel alt returned %d\n", r);
abort();
return -1;
}
}
int
recv(Channel *c, void *v)
{
return runop(CHANRCV, c, v, 0);
}
int
nbrecv(Channel *c, void *v)
{
return runop(CHANRCV, c, v, 1);
}
int
send(Channel *c, void *v)
{
return runop(CHANSND, c, v, 0);
}
int
nbsend(Channel *c, void *v)
{
return runop(CHANSND, c, v, 1);
}
static void
channelsize(Channel *c, int sz)
{
if(c->e != sz){
fprint(2, "expected channel with elements of size %d, got size %d",
sz, c->e);
abort();
}
}
int
sendul(Channel *c, ulong v)
{
channelsize(c, sizeof(ulong));
return send(c, &v);
}
ulong
recvul(Channel *c)
{
ulong v;
channelsize(c, sizeof(ulong));
if(recv(c, &v) < 0)
return ~0;
return v;
}
int
sendp(Channel *c, void *v)
{
channelsize(c, sizeof(void*));
return send(c, &v);
}
void*
recvp(Channel *c)
{
void *v;
channelsize(c, sizeof(void*));
if(recv(c, &v) < 0)
return nil;
return v;
}
int
nbsendul(Channel *c, ulong v)
{
channelsize(c, sizeof(ulong));
return nbsend(c, &v);
}
ulong
nbrecvul(Channel *c)
{
ulong v;
channelsize(c, sizeof(ulong));
if(nbrecv(c, &v) == 0)
return 0;
return v;
}
int
nbsendp(Channel *c, void *v)
{
channelsize(c, sizeof(void*));
return nbsend(c, &v);
}
void*
nbrecvp(Channel *c)
{
void *v;
channelsize(c, sizeof(void*));
if(nbrecv(c, &v) == 0)
return nil;
return v;
}
static int
emptyentry(Channel *c)
{
int i, extra;
assert((c->nentry==0 && c->qentry==nil) || (c->nentry && c->qentry));
for(i=0; i<c->nentry; i++)
if(c->qentry[i]==nil)
return i;
extra = 16;
c->nentry += extra;
if(c->nentry > _threadhighnentry) _threadhighnentry = c->nentry;
c->qentry = realloc((void*)c->qentry, c->nentry*sizeof(c->qentry[0]));
if(c->qentry == nil)
sysfatal("realloc channel entries: %r");
_threadmemset(&c->qentry[i], 0, extra*sizeof(c->qentry[0]));
return i;
}
static void
enqueue(Alt *a, Channel **c)
{
int i;
_threaddebug(DBGCHAN, "Queuing alt %p on channel %p", a, a->c);
a->tag = c;
i = emptyentry(a->c);
a->c->qentry[i] = a;
}
static void
dequeue(Alt *a)
{
int i;
Channel *c;
c = a->c;
for(i=0; i<c->nentry; i++)
if(c->qentry[i]==a){
_threaddebug(DBGCHAN, "Dequeuing alt %p from channel %p", a, a->c);
c->qentry[i] = nil;
if(c->freed)
_chanfree(c);
return;
}
}
static void*
altexecbuffered(Alt *a, int willreplace)
{
uchar *v;
Channel *c;
c = a->c;
/* use buffered channel queue */
if(a->op==CHANRCV && c->n > 0){
_threaddebug(DBGCHAN, "buffer recv alt %p chan %p", a, c);
v = c->v + c->e*(c->f%c->s);
if(!willreplace)
c->n--;
c->f++;
return v;
}
if(a->op==CHANSND && c->n < c->s){
_threaddebug(DBGCHAN, "buffer send alt %p chan %p", a, c);
v = c->v + c->e*((c->f+c->n)%c->s);
if(!willreplace)
c->n++;
return v;
}
abort();
return nil;
}
static void
altcopy(void *dst, void *src, int sz)
{
if(dst){
if(src)
memmove(dst, src, sz);
else
_threadmemset(dst, 0, sz);
}
}
static int
altexec(Alt *a, int spl)
{
volatile Alt *b;
int i, n, otherop;
Channel *c;
void *me, *waiter, *buf;
c = a->c;
/* rendezvous with others */
otherop = (CHANSND+CHANRCV) - a->op;
n = 0;
b = nil;
me = a->v;
for(i=0; i<c->nentry; i++)
if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]->tag==nil)
if(nrand(++n) == 0)
b = c->qentry[i];
if(b != nil){
_threaddebug(DBGCHAN, "rendez %s alt %p chan %p alt %p", a->op==CHANRCV?"recv":"send", a, c, b);
waiter = b->v;
if(c->s && c->n){
/*
* if buffer is full and there are waiters
* and we're meeting a waiter,
* we must be receiving.
*
* we use the value in the channel buffer,
* copy the waiter's value into the channel buffer
* on behalf of the waiter, and then wake the waiter.
*/
if(a->op!=CHANRCV)
abort();
buf = altexecbuffered(a, 1);
altcopy(me, buf, c->e);
altcopy(buf, waiter, c->e);
}else{
if(a->op==CHANRCV)
altcopy(me, waiter, c->e);
else
altcopy(waiter, me, c->e);
}
*b->tag = c; /* commits us to rendezvous */
_threaddebug(DBGCHAN, "unlocking the chanlock");
unlock(&chanlock);
_procsplx(spl);
_threaddebug(DBGCHAN, "chanlock is %lud", *(ulong*)&chanlock);
while(_threadrendezvous((ulong)b->tag, 0) == ~0)
;
return 1;
}
buf = altexecbuffered(a, 0);
if(a->op==CHANRCV)
altcopy(me, buf, c->e);
else
altcopy(buf, me, c->e);
unlock(&chanlock);
_procsplx(spl);
return 1;
}

18
src/libthread/chanprint.c Normal file
View File

@@ -0,0 +1,18 @@
#include "threadimpl.h"
int
chanprint(Channel *c, char *fmt, ...)
{
va_list arg;
char *p;
int n;
va_start(arg, fmt);
p = vsmprint(fmt, arg);
va_end(arg);
if(p == nil)
sysfatal("vsmprint failed: %r");
n = sendp(c, p);
yield(); /* let recipient handle message immediately */
return n;
}

182
src/libthread/create.c Normal file
View File

@@ -0,0 +1,182 @@
#include "threadimpl.h"
#define free
Pqueue _threadpq;
static int nextID(void);
/*
* Create and initialize a new Thread structure attached to a given proc.
*/
typedef struct Stack Stack;
struct Stack {
ulong magic;
Thread *thr;
Stack *next;
uchar buf[STKSIZE-12];
};
static Stack *stkfree;
static Lock stklock;
void
_stackfree(void *v)
{
Stack *s;
s = v;
lock(&stklock);
s->thr = nil;
s->magic = 0;
s->next = stkfree;
stkfree = s;
unlock(&stklock);
}
static Stack*
stackalloc(void)
{
char *buf;
Stack *s;
int i;
lock(&stklock);
while(stkfree == nil){
unlock(&stklock);
assert(STKSIZE == sizeof(Stack));
buf = malloc(STKSIZE+128*STKSIZE);
s = (Stack*)(((ulong)buf+STKSIZE)&~(STKSIZE-1));
for(i=0; i<128; i++)
_stackfree(&s[i]);
lock(&stklock);
}
s = stkfree;
stkfree = stkfree->next;
unlock(&stklock);
s->magic = STKMAGIC;
return s;
}
static int
newthread(Proc *p, void (*f)(void *arg), void *arg, uint stacksize, char *name, int grp)
{
int id;
Thread *t;
Stack *s;
if(stacksize < 32)
sysfatal("bad stacksize %d", stacksize);
t = _threadmalloc(sizeof(Thread), 1);
s = stackalloc();
s->thr = t;
t->stk = (char*)s;
t->stksize = STKSIZE;
_threaddebugmemset(s->buf, 0xFE, sizeof s->buf);
_threadinitstack(t, f, arg);
t->proc = p;
t->grp = grp;
if(name)
t->cmdname = strdup(name);
t->id = nextID();
id = t->id;
t->next = (Thread*)~0;
_threaddebug(DBGSCHED, "create thread %d.%d name %s", p->pid, t->id, name);
lock(&p->lock);
p->nthreads++;
if(p->threads.head == nil)
p->threads.head = t;
else{
t->prevt = p->threads.tail;
t->prevt->nextt = t;
}
p->threads.tail = t;
t->state = Ready;
_threadready(t);
unlock(&p->lock);
return id;
}
static int
nextID(void)
{
static Lock l;
static int id;
int i;
lock(&l);
i = ++id;
unlock(&l);
return i;
}
int
procrfork(void (*f)(void *), void *arg, uint stacksize, int rforkflag)
{
Proc *p;
int id;
p = _threadgetproc();
assert(p->newproc == nil);
p->newproc = _newproc(f, arg, stacksize, nil, p->thread->grp, rforkflag);
id = p->newproc->threads.head->id;
_sched();
return id;
}
int
proccreate(void (*f)(void*), void *arg, uint stacksize)
{
return procrfork(f, arg, stacksize, 0);
}
void
_freeproc(Proc *p)
{
Thread *t, *nextt;
for(t = p->threads.head; t; t = nextt){
if(t->cmdname)
free(t->cmdname);
assert(t->stk != nil);
_stackfree((Stack*)t->stk);
nextt = t->nextt;
free(t);
}
free(p);
}
/*
* Create a new thread and schedule it to run.
* The thread grp is inherited from the currently running thread.
*/
int
threadcreate(void (*f)(void *arg), void *arg, uint stacksize)
{
return newthread(_threadgetproc(), f, arg, stacksize, nil, threadgetgrp());
}
/*
* Create and initialize a new Proc structure with a single Thread
* running inside it. Add the Proc to the global process list.
*/
Proc*
_newproc(void (*f)(void *arg), void *arg, uint stacksize, char *name, int grp, int rforkflag)
{
Proc *p;
p = _threadmalloc(sizeof *p, 1);
p->pid = -1;
p->rforkflag = rforkflag;
newthread(p, f, arg, stacksize, name, grp);
lock(&_threadpq.lock);
if(_threadpq.head == nil)
_threadpq.head = p;
else
*_threadpq.tail = p;
_threadpq.tail = &p->next;
unlock(&_threadpq.lock);
return p;
}

48
src/libthread/debug.c Normal file
View File

@@ -0,0 +1,48 @@
#include "threadimpl.h"
int _threaddebuglevel;
void
__threaddebug(ulong flag, char *fmt, ...)
{
char buf[128];
va_list arg;
Fmt f;
Proc *p;
if((_threaddebuglevel&flag) == 0)
return;
fmtfdinit(&f, 2, buf, sizeof buf);
p = _threadgetproc();
if(p==nil)
fmtprint(&f, "noproc ");
else if(p->thread)
fmtprint(&f, "%d.%d ", p->pid, p->thread->id);
else
fmtprint(&f, "%d._ ", p->pid);
va_start(arg, fmt);
fmtvprint(&f, fmt, arg);
va_end(arg);
fmtprint(&f, "\n");
fmtfdflush(&f);
}
void
_threadassert(char *s)
{
char buf[256];
int n;
Proc *p;
p = _threadgetproc();
if(p && p->thread)
n = sprint(buf, "%d.%d ", p->pid, p->thread->id);
else
n = 0;
snprint(buf+n, sizeof(buf)-n, "%s: assertion failed\n", s);
write(2, buf, strlen(buf));
abort();
}

124
src/libthread/exec-unix.c Normal file
View File

@@ -0,0 +1,124 @@
#include <fcntl.h>
#include <unistd.h>
#include "threadimpl.h"
void
procexec(Channel *pidc, char *prog, char *args[])
{
int n;
Proc *p;
Thread *t;
_threaddebug(DBGEXEC, "procexec %s", prog);
/* must be only thread in proc */
p = _threadgetproc();
t = p->thread;
if(p->threads.head != t || p->threads.head->nextt != nil){
werrstr("not only thread in proc");
Bad:
if(pidc)
sendul(pidc, ~0);
return;
}
/*
* We want procexec to behave like exec; if exec succeeds,
* never return, and if it fails, return with errstr set.
* Unfortunately, the exec happens in another proc since
* we have to wait for the exec'ed process to finish.
* To provide the semantics, we open a pipe with the
* write end close-on-exec and hand it to the proc that
* is doing the exec. If the exec succeeds, the pipe will
* close so that our read below fails. If the exec fails,
* then the proc doing the exec sends the errstr down the
* pipe to us.
*/
if(pipe(p->exec.fd) < 0)
goto Bad;
if(fcntl(p->exec.fd[1], F_SETFD, 1) < 0)
goto Bad;
/* exec in parallel via the scheduler */
assert(p->needexec==0);
p->exec.prog = prog;
p->exec.args = args;
p->needexec = 1;
_sched();
close(p->exec.fd[1]);
if((n = read(p->exec.fd[0], p->exitstr, ERRMAX-1)) > 0){ /* exec failed */
p->exitstr[n] = '\0';
errstr(p->exitstr, ERRMAX);
close(p->exec.fd[0]);
goto Bad;
}
close(p->exec.fd[0]);
if(pidc)
sendul(pidc, t->ret);
/* wait for exec'ed program, then exit */
_schedexecwait();
}
void
procexecl(Channel *pidc, char *f, ...)
{
procexec(pidc, f, &f+1);
}
void
_schedexecwait(void)
{
int pid;
Channel *c;
Proc *p;
Thread *t;
Waitmsg *w;
p = _threadgetproc();
t = p->thread;
pid = t->ret;
_threaddebug(DBGEXEC, "_schedexecwait %d", t->ret);
for(;;){
w = wait();
if(w == nil)
break;
if(w->pid == pid)
break;
free(w);
}
if(w != nil){
if((c = _threadwaitchan) != nil)
sendp(c, w);
else
free(w);
}
threadexits("procexec");
}
static void
efork(void *ve)
{
char buf[ERRMAX];
Execargs *e;
e = ve;
_threaddebug(DBGEXEC, "_schedexec %s", e->prog);
close(e->fd[0]);
execv(e->prog, e->args);
_threaddebug(DBGEXEC, "_schedexec failed: %r");
rerrstr(buf, sizeof buf);
if(buf[0]=='\0')
strcpy(buf, "exec failed");
write(e->fd[1], buf, strlen(buf));
close(e->fd[1]);
_exits(buf);
}
int
_schedexec(Execargs *e)
{
return ffork(RFFDG|RFPROC|RFMEM, efork, e);
}

77
src/libthread/exec.c Normal file
View File

@@ -0,0 +1,77 @@
#include "threadimpl.h"
#define PIPEMNT "/mnt/temp"
void
procexec(Channel *pidc, char *prog, char *args[])
{
int n;
Proc *p;
Thread *t;
_threaddebug(DBGEXEC, "procexec %s", prog);
/* must be only thread in proc */
p = _threadgetproc();
t = p->thread;
if(p->threads.head != t || p->threads.head->nextt != nil){
werrstr("not only thread in proc");
Bad:
if(pidc)
sendul(pidc, ~0);
return;
}
/*
* We want procexec to behave like exec; if exec succeeds,
* never return, and if it fails, return with errstr set.
* Unfortunately, the exec happens in another proc since
* we have to wait for the exec'ed process to finish.
* To provide the semantics, we open a pipe with the
* write end close-on-exec and hand it to the proc that
* is doing the exec. If the exec succeeds, the pipe will
* close so that our read below fails. If the exec fails,
* then the proc doing the exec sends the errstr down the
* pipe to us.
*/
if(bind("#|", PIPEMNT, MREPL) < 0)
goto Bad;
if((p->exec.fd[0] = open(PIPEMNT "/data", OREAD)) < 0){
unmount(nil, PIPEMNT);
goto Bad;
}
if((p->exec.fd[1] = open(PIPEMNT "/data1", OWRITE|OCEXEC)) < 0){
close(p->exec.fd[0]);
unmount(nil, PIPEMNT);
goto Bad;
}
unmount(nil, PIPEMNT);
/* exec in parallel via the scheduler */
assert(p->needexec==0);
p->exec.prog = prog;
p->exec.args = args;
p->needexec = 1;
_sched();
close(p->exec.fd[1]);
if((n = read(p->exec.fd[0], p->exitstr, ERRMAX-1)) > 0){ /* exec failed */
p->exitstr[n] = '\0';
errstr(p->exitstr, ERRMAX);
close(p->exec.fd[0]);
goto Bad;
}
close(p->exec.fd[0]);
if(pidc)
sendul(pidc, t->ret);
/* wait for exec'ed program, then exit */
_schedexecwait();
}
void
procexecl(Channel *pidc, char *f, ...)
{
procexec(pidc, f, &f+1);
}

63
src/libthread/exit.c Normal file
View File

@@ -0,0 +1,63 @@
#include "threadimpl.h"
#include <signal.h>
char *_threadexitsallstatus;
Channel *_threadwaitchan;
void
threadexits(char *exitstr)
{
Proc *p;
Thread *t;
p = _threadgetproc();
t = p->thread;
t->moribund = 1;
if(exitstr==nil)
exitstr="";
utfecpy(p->exitstr, p->exitstr+ERRMAX, exitstr);
_sched();
}
void
threadexitsall(char *exitstr)
{
Proc *p;
int *pid;
int i, npid, mypid;
if(exitstr == nil)
exitstr = "";
_threadexitsallstatus = exitstr;
_threaddebug(DBGSCHED, "_threadexitsallstatus set to %p", _threadexitsallstatus);
mypid = _threadgetpid();
/*
* signal others.
* copying all the pids first avoids other threads
* teardown procedures getting in the way.
*/
lock(&_threadpq.lock);
npid = 0;
for(p=_threadpq.head; p; p=p->next)
npid++;
pid = _threadmalloc(npid*sizeof(pid[0]), 0);
npid = 0;
for(p = _threadpq.head; p; p=p->next)
pid[npid++] = p->pid;
unlock(&_threadpq.lock);
for(i=0; i<npid; i++)
if(pid[i] != mypid)
kill(pid[i], SIGTERM);
/* leave */
exit(0);
}
Channel*
threadwaitchan(void)
{
if(_threadwaitchan==nil)
_threadwaitchan = chancreate(sizeof(Waitmsg*), 16);
return _threadwaitchan;
}

8
src/libthread/getpid.c Normal file
View File

@@ -0,0 +1,8 @@
#include "threadimpl.h"
#include <unistd.h>
int
_threadgetpid(void)
{
return getpid();
}

135
src/libthread/id.c Normal file
View File

@@ -0,0 +1,135 @@
#include "threadimpl.h"
int
threadid(void)
{
return _threadgetproc()->thread->id;
}
int
threadpid(int id)
{
int pid;
Proc *p;
Thread *t;
if (id < 0)
return -1;
if (id == 0)
return _threadgetproc()->pid;
lock(&_threadpq.lock);
for (p = _threadpq.head; p->next; p = p->next){
lock(&p->lock);
for (t = p->threads.head; t; t = t->nextt)
if (t->id == id){
pid = p->pid;
unlock(&p->lock);
unlock(&_threadpq.lock);
return pid;
}
unlock(&p->lock);
}
unlock(&_threadpq.lock);
return -1;
}
int
threadsetgrp(int ng)
{
int og;
Thread *t;
t = _threadgetproc()->thread;
og = t->grp;
t->grp = ng;
return og;
}
int
threadgetgrp(void)
{
return _threadgetproc()->thread->grp;
}
void
threadsetname(char *name)
{
/*
int fd, n;
char buf[128], *s;
*/
Proc *p;
Thread *t;
p = _threadgetproc();
t = p->thread;
if (t->cmdname)
free(t->cmdname);
t->cmdname = strdup(name);
/* Plan 9 only
if(p->nthreads == 1){
snprint(buf, sizeof buf, "#p/%d/args", getpid());
if((fd = open(buf, OWRITE)) >= 0){
snprint(buf, sizeof buf, "%s [%s]", argv0, name);
n = strlen(buf)+1;
s = strchr(buf, ' ');
if(s)
*s = '\0';
write(fd, buf, n);
close(fd);
}
}
*/
}
char*
threadgetname(void)
{
return _threadgetproc()->thread->cmdname;
}
void**
threaddata(void)
{
return &_threadgetproc()->thread->udata[0];
}
void**
procdata(void)
{
return &_threadgetproc()->udata;
}
static Lock privlock;
static int privmask = 1;
int
tprivalloc(void)
{
int i;
lock(&privlock);
for(i=0; i<NPRIV; i++)
if(!(privmask&(1<<i))){
privmask |= 1<<i;
unlock(&privlock);
return i;
}
unlock(&privlock);
return -1;
}
void
tprivfree(int i)
{
if(i < 0 || i >= NPRIV)
abort();
lock(&privlock);
privmask &= ~(1<<i);
}
void**
tprivaddr(int i)
{
return &_threadgetproc()->thread->udata[i];
}

49
src/libthread/iocall.c Normal file
View File

@@ -0,0 +1,49 @@
#include "threadimpl.h"
long
iocall(Ioproc *io, long (*op)(va_list*), ...)
{
int ret, inted;
Ioproc *msg;
if(send(io->c, &io) == -1){
werrstr("interrupted");
return -1;
}
assert(!io->inuse);
io->inuse = 1;
io->op = op;
va_start(io->arg, op);
msg = io;
inted = 0;
while(send(io->creply, &msg) == -1){
msg = nil;
inted = 1;
}
if(inted){
werrstr("interrupted");
return -1;
}
/*
* If we get interrupted, we have stick around so that
* the IO proc has someone to talk to. Send it an interrupt
* and try again.
*/
inted = 0;
while(recv(io->creply, nil) == -1){
inted = 1;
iointerrupt(io);
}
USED(inted);
va_end(io->arg);
ret = io->ret;
if(ret < 0)
errstr(io->err, sizeof io->err);
io->inuse = 0;
/* release resources */
while(send(io->creply, &io) == -1)
;
return ret;
}

16
src/libthread/ioclose.c Normal file
View File

@@ -0,0 +1,16 @@
#include "threadimpl.h"
static long
_ioclose(va_list *arg)
{
int fd;
fd = va_arg(*arg, int);
return close(fd);
}
int
ioclose(Ioproc *io, int fd)
{
return iocall(io, _ioclose, fd);
}

21
src/libthread/iodial.c Normal file
View File

@@ -0,0 +1,21 @@
#include "threadimpl.h"
static long
_iodial(va_list *arg)
{
char *addr, *local, *dir;
int *cdfp;
addr = va_arg(*arg, char*);
local = va_arg(*arg, char*);
dir = va_arg(*arg, char*);
cdfp = va_arg(*arg, int*);
return dial(addr, local, dir, cdfp);
}
int
iodial(Ioproc *io, char *addr, char *local, char *dir, int *cdfp)
{
return iocall(io, _iodial, addr, local, dir, cdfp);
}

20
src/libthread/ioopen.c Normal file
View File

@@ -0,0 +1,20 @@
#include <unistd.h>
#include <fcntl.h>
#include "threadimpl.h"
static long
_ioopen(va_list *arg)
{
char *path;
int mode;
path = va_arg(*arg, char*);
mode = va_arg(*arg, int);
return open(path, mode);
}
int
ioopen(Ioproc *io, char *path, int mode)
{
return iocall(io, _ioopen, path, mode);
}

179
src/libthread/ioproc.3 Normal file
View File

@@ -0,0 +1,179 @@
.TH IOPROC 2
.SH NAME
closeioproc,
iocall,
ioclose,
iointerrupt,
iodial,
ioopen,
ioproc,
ioread,
ioreadn,
iowrite \- slave I/O processes for threaded programs
.SH SYNOPSIS
.PP
.de XX
.ift .sp 0.5
.ifn .sp
..
.EX
.ta \w'Ioproc* 'u
#include <u.h>
#include <libc.h>
#include <thread.h>
.sp
typedef struct Ioproc Ioproc;
.sp
Ioproc* ioproc(void);
.XX
int ioopen(Ioproc *io, char *file, int omode);
int ioclose(Ioproc *io, int fd);
long ioread(Ioproc *io, int fd, void *a, long n);
long ioreadn(Ioproc *io, int fd, void *a, long n);
long iowrite(Ioproc *io, int fd, void *a, long n);
int iodial(Ioproc *io, char *addr, char *local, char *dir, char *cdfp);
.XX
void iointerrupt(Ioproc *io);
void closeioproc(Ioproc *io);
.XX
long iocall(Ioproc *io, long (*op)(va_list *arg), ...);
.EE
.SH DESCRIPTION
.PP
These routines provide access to I/O in slave procs.
Since the I/O itself is done in a slave proc, other threads
in the calling proc can run while the calling thread
waits for the I/O to complete.
.PP
.I Ioproc
forks a new slave proc and returns a pointer to the
.B Ioproc
associated with it.
.I Ioproc
uses
.I mallocz
and
.IR proccreate ;
if either fails, it calls
.I sysfatal
rather than return an error.
.PP
.IR Ioopen ,
.IR ioclose ,
.IR ioread ,
.IR ioreadn ,
.IR iowrite ,
and
.IR iodial
are execute the
similarly named library or system calls
(see
.IR open (2),
.IR read (2),
and
.IR dial (2))
in the slave process associated with
.IR io .
It is an error to execute more than one call
at a time in an I/O proc.
.PP
.I Iointerrupt
interrupts the call currently executing in the I/O proc.
If no call is executing,
.IR iointerrupt
is a no-op.
.PP
.I Closeioproc
terminates the I/O proc and frees the associated
.B Ioproc .
.PP
.I Iocall
is a primitive that may be used to implement
more slave I/O routines.
.I Iocall
arranges for
.I op
to be called in
.IR io 's
proc, with
.I arg
set to the variable parameter list,
returning the value that
.I op
returns.
.SH EXAMPLE
Relay messages between two file descriptors,
counting the total number of bytes seen:
.IP
.EX
.ta +\w'xxxx'u +\w'xxxx'u +\w'xxxx'u
int tot;
void
relaythread(void *v)
{
int *fd, n;
char buf[1024];
Ioproc *io;
fd = v;
io = ioproc();
while((n = ioread(io, fd[0], buf, sizeof buf)) > 0){
if(iowrite(io, fd[1], buf, n) != n)
sysfatal("iowrite: %r");
tot += n;
}
closeioproc(io);
}
void
relay(int fd0, int fd1)
{
int fd[4];
fd[0] = fd[3] = fd0;
fd[1] = fd[2] = fd1;
threadcreate(relaythread, fd, 8192);
threadcreate(relaythread, fd+2, 8192);
}
.EE
.LP
If the two
.I relaythread
instances were running in different procs, the
common access to
.I tot
would be unsafe.
.EE
.PP
Implement
.IR ioread :
.IP
.EX
static long
_ioread(va_list *arg)
{
int fd;
void *a;
long n;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, long);
return read(fd, a, n);
}
long
ioread(Ioproc *io, int fd, void *a, long n)
{
return iocall(io, _ioread, fd, a, n);
}
.EE
.SH SOURCE
.B /sys/src/libthread/io*.c
.SH SEE ALSO
.IR dial (2),
.IR open (2),
.IR read (2),
.IR thread (2)

74
src/libthread/ioproc.c Normal file
View File

@@ -0,0 +1,74 @@
#include "threadimpl.h"
enum
{
STACK = 8192,
};
void
iointerrupt(Ioproc *io)
{
if(!io->inuse)
return;
threadint(io->tid);
}
static void
xioproc(void *a)
{
Ioproc *io, *x;
io = a;
/*
* first recvp acquires the ioproc.
* second tells us that the data is ready.
*/
for(;;){
while(recv(io->c, &x) == -1)
;
if(x == 0) /* our cue to leave */
break;
assert(x == io);
/* caller is now committed -- even if interrupted he'll return */
while(recv(io->creply, &x) == -1)
;
if(x == 0) /* caller backed out */
continue;
assert(x == io);
io->ret = io->op(&io->arg);
if(io->ret < 0)
rerrstr(io->err, sizeof io->err);
while(send(io->creply, &io) == -1)
;
while(recv(io->creply, &x) == -1)
;
}
}
Ioproc*
ioproc(void)
{
Ioproc *io;
io = mallocz(sizeof(*io), 1);
if(io == nil)
sysfatal("ioproc malloc: %r");
io->c = chancreate(sizeof(void*), 0);
io->creply = chancreate(sizeof(void*), 0);
io->tid = proccreate(xioproc, io, STACK);
return io;
}
void
closeioproc(Ioproc *io)
{
if(io == nil)
return;
iointerrupt(io);
while(send(io->c, 0) == -1)
;
chanfree(io->c);
chanfree(io->creply);
free(io);
}

20
src/libthread/ioread.c Normal file
View File

@@ -0,0 +1,20 @@
#include "threadimpl.h"
static long
_ioread(va_list *arg)
{
int fd;
void *a;
long n;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, long);
return read(fd, a, n);
}
long
ioread(Ioproc *io, int fd, void *a, long n)
{
return iocall(io, _ioread, fd, a, n);
}

21
src/libthread/ioreadn.c Normal file
View File

@@ -0,0 +1,21 @@
#include "threadimpl.h"
static long
_ioreadn(va_list *arg)
{
int fd;
void *a;
long n;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, long);
n = readn(fd, a, n);
return n;
}
long
ioreadn(Ioproc *io, int fd, void *a, long n)
{
return iocall(io, _ioreadn, fd, a, n);
}

16
src/libthread/iosleep.c Normal file
View File

@@ -0,0 +1,16 @@
#include "threadimpl.h"
static long
_iosleep(va_list *arg)
{
long n;
n = va_arg(*arg, long);
return sleep(n);
}
int
iosleep(Ioproc *io, long n)
{
return iocall(io, _iosleep, n);
}

21
src/libthread/iowrite.c Normal file
View File

@@ -0,0 +1,21 @@
#include "threadimpl.h"
static long
_iowrite(va_list *arg)
{
int fd;
void *a;
long n;
fd = va_arg(*arg, int);
a = va_arg(*arg, void*);
n = va_arg(*arg, long);
n = write(fd, a, n);
return n;
}
long
iowrite(Ioproc *io, int fd, void *a, long n)
{
return iocall(io, _iowrite, fd, a, n);
}

89
src/libthread/kill.c Normal file
View File

@@ -0,0 +1,89 @@
#include "threadimpl.h"
#include <signal.h>
static void tinterrupt(Proc*, Thread*);
static void
threadxxxgrp(int grp, int dokill)
{
Proc *p;
Thread *t;
lock(&_threadpq.lock);
for(p=_threadpq.head; p; p=p->next){
lock(&p->lock);
for(t=p->threads.head; t; t=t->nextt)
if(t->grp == grp){
if(dokill)
t->moribund = 1;
tinterrupt(p, t);
}
unlock(&p->lock);
}
unlock(&_threadpq.lock);
_threadbreakrendez();
}
static void
threadxxx(int id, int dokill)
{
Proc *p;
Thread *t;
lock(&_threadpq.lock);
for(p=_threadpq.head; p; p=p->next){
lock(&p->lock);
for(t=p->threads.head; t; t=t->nextt)
if(t->id == id){
if(dokill)
t->moribund = 1;
tinterrupt(p, t);
unlock(&p->lock);
unlock(&_threadpq.lock);
_threadbreakrendez();
return;
}
unlock(&p->lock);
}
unlock(&_threadpq.lock);
_threaddebug(DBGNOTE, "Can't find thread to kill");
return;
}
void
threadkillgrp(int grp)
{
threadxxxgrp(grp, 1);
}
void
threadkill(int id)
{
threadxxx(id, 1);
}
void
threadintgrp(int grp)
{
threadxxxgrp(grp, 0);
}
void
threadint(int id)
{
threadxxx(id, 0);
}
static void
tinterrupt(Proc *p, Thread *t)
{
switch(t->state){
case Running:
kill(p->pid, SIGINT);
// postnote(PNPROC, p->pid, "threadint");
break;
case Rendezvous:
_threadflagrendez(t);
break;
}
}

24
src/libthread/label.h Normal file
View File

@@ -0,0 +1,24 @@
/*
* setjmp and longjmp, but our own because some (stupid) c libraries
* assume longjmp is only used to move up the stack, and error out
* if you do otherwise.
*/
typedef struct Label Label;
#define LABELDPC 0
#if defined (__i386__) && (defined(__FreeBSD__) || defined(__linux__))
struct Label
{
ulong pc;
ulong bx;
ulong sp;
ulong bp;
ulong si;
ulong di;
};
#else
#error "Unknown or unsupported architecture"
#endif

35
src/libthread/lib.c Normal file
View File

@@ -0,0 +1,35 @@
#include "threadimpl.h"
static long totalmalloc;
void*
_threadmalloc(long size, int z)
{
void *m;
m = malloc(size);
if (m == nil)
sysfatal("Malloc of size %ld failed: %r\n", size);
setmalloctag(m, getcallerpc(&size));
totalmalloc += size;
if (size > 1000000) {
fprint(2, "Malloc of size %ld, total %ld\n", size, totalmalloc);
abort();
}
if (z)
_threadmemset(m, 0, size);
return m;
}
void
_threadsysfatal(char *fmt, va_list arg)
{
char buf[1024]; /* size doesn't matter; we're about to exit */
vseprint(buf, buf+sizeof(buf), fmt, arg);
if(argv0)
fprint(2, "%s: %s\n", argv0, buf);
else
fprint(2, "%s\n", buf);
threadexitsall(buf);
}

124
src/libthread/main.c Normal file
View File

@@ -0,0 +1,124 @@
#include "threadimpl.h"
#include <signal.h>
typedef struct Mainarg Mainarg;
struct Mainarg
{
int argc;
char **argv;
};
int mainstacksize;
int _threadnotefd;
int _threadpasserpid;
static void mainlauncher(void*);
extern void (*_sysfatal)(char*, va_list);
void
_threaddie(int x)
{
extern char *_threadexitsallstatus;
USED(x);
if(_threadexitsallstatus)
exit(_threadexitsallstatus[0] ? 1 : 0);
}
int
main(int argc, char **argv)
{
Mainarg *a;
Proc *p;
signal(SIGTERM, _threaddie);
// rfork(RFREND);
//_threaddebuglevel = (DBGSCHED|DBGCHAN|DBGREND)^~0;
_systhreadinit();
_qlockinit(_threadrendezvous);
_sysfatal = _threadsysfatal;
// notify(_threadnote);
if(mainstacksize == 0)
mainstacksize = 32*1024;
a = _threadmalloc(sizeof *a, 1);
a->argc = argc;
a->argv = argv;
p = _newproc(mainlauncher, a, mainstacksize, "threadmain", 0, 0);
_schedinit(p);
abort(); /* not reached */
return 0;
}
static void
mainlauncher(void *arg)
{
Mainarg *a;
a = arg;
threadmain(a->argc, a->argv);
threadexits("threadmain");
}
void
_threadsignal(void)
{
}
void
_threadsignalpasser(void)
{
}
int
_schedfork(Proc *p)
{
return ffork(RFMEM|RFNOWAIT, _schedinit, p);
}
void
_schedexit(Proc *p)
{
char ex[ERRMAX];
Proc **l;
lock(&_threadpq.lock);
for(l=&_threadpq.head; *l; l=&(*l)->next){
if(*l == p){
*l = p->next;
if(*l == nil)
_threadpq.tail = l;
break;
}
}
unlock(&_threadpq.lock);
strncpy(ex, p->exitstr, sizeof ex);
ex[sizeof ex-1] = '\0';
free(p);
_exit(ex[0]);
}
int
nrand(int n)
{
return random()%n;
}
void
_systhreadinit(void)
{
}
void
threadstats(void)
{
extern int _threadnrendez, _threadhighnrendez,
_threadnalt, _threadhighnentry;
fprint(2, "*** THREAD LIBRARY STATS ***\n");
fprint(2, "nrendez %d high simultaneous %d\n",
_threadnrendez, _threadhighnrendez);
fprint(2, "nalt %d high simultaneous entry %d\n",
_threadnalt, _threadhighnentry);
}

8
src/libthread/memset.c Normal file
View File

@@ -0,0 +1,8 @@
#include "threadimpl.h"
#include <string.h>
void
_threadmemset(void *v, int c, int n)
{
memset(v, c, n);
}

8
src/libthread/memsetd.c Normal file
View File

@@ -0,0 +1,8 @@
#include "threadimpl.h"
#include <string.h>
void
_threaddebugmemset(void *v, int c, int n)
{
memset(v, c, n);
}

2
src/libthread/mkfile Normal file
View File

@@ -0,0 +1,2 @@
<../libutf/mkfile

143
src/libthread/note.c Normal file
View File

@@ -0,0 +1,143 @@
#include "threadimpl.h"
int _threadnopasser;
#ifdef NOTDEF
#define NFN 33
#define ERRLEN 48
typedef struct Note Note;
struct Note
{
Lock inuse;
Proc *proc; /* recipient */
char s[ERRMAX]; /* arg2 */
};
static Note notes[128];
static Note *enotes = notes+nelem(notes);
static int (*onnote[NFN])(void*, char*);
static int onnotepid[NFN];
static Lock onnotelock;
int
threadnotify(int (*f)(void*, char*), int in)
{
int i, topid;
int (*from)(void*, char*), (*to)(void*, char*);
if(in){
from = nil;
to = f;
topid = _threadgetproc()->pid;
}else{
from = f;
to = nil;
topid = 0;
}
lock(&onnotelock);
for(i=0; i<NFN; i++)
if(onnote[i]==from){
onnote[i] = to;
onnotepid[i] = topid;
break;
}
unlock(&onnotelock);
return i<NFN;
}
static void
delayednotes(Proc *p, void *v)
{
int i;
Note *n;
int (*fn)(void*, char*);
if(!p->pending)
return;
p->pending = 0;
for(n=notes; n<enotes; n++){
if(n->proc == p){
for(i=0; i<NFN; i++){
if(onnotepid[i]!=p->pid || (fn = onnote[i])==nil)
continue;
if((*fn)(v, n->s))
break;
}
if(i==NFN){
_threaddebug(DBGNOTE, "Unhandled note %s, proc %p\n", n->s, p);
if(v != nil)
noted(NDFLT);
else if(strncmp(n->s, "sys:", 4)==0)
abort();
threadexitsall(n->s);
}
n->proc = nil;
unlock(&n->inuse);
}
}
}
void
_threadnote(void *v, char *s)
{
Proc *p;
Note *n;
_threaddebug(DBGNOTE, "Got note %s", s);
if(strncmp(s, "sys:", 4) == 0)
noted(NDFLT);
// if(_threadexitsallstatus){
// _threaddebug(DBGNOTE, "Threadexitsallstatus = '%s'\n", _threadexitsallstatus);
// _exits(_threadexitsallstatus);
// }
if(strcmp(s, "threadint")==0)
noted(NCONT);
p = _threadgetproc();
if(p == nil)
noted(NDFLT);
for(n=notes; n<enotes; n++)
if(canlock(&n->inuse))
break;
if(n==enotes)
sysfatal("libthread: too many delayed notes");
utfecpy(n->s, n->s+ERRMAX, s);
n->proc = p;
p->pending = 1;
if(!p->splhi)
delayednotes(p, v);
noted(NCONT);
}
#endif
int
_procsplhi(void)
{
int s;
Proc *p;
p = _threadgetproc();
s = p->splhi;
p->splhi = 1;
return s;
}
void
_procsplx(int s)
{
Proc *p;
p = _threadgetproc();
p->splhi = s;
if(s)
return;
/*
if(p->pending)
delayednotes(p, nil);
*/
}

64
src/libthread/proctab.c Normal file
View File

@@ -0,0 +1,64 @@
#include "threadimpl.h"
/* this will need work */
enum
{
PTABHASH = 257,
};
static Lock ptablock;
Proc *ptab[PTABHASH];
void
_threadsetproc(Proc *p)
{
int h;
lock(&ptablock);
h = ((unsigned)p->pid)%PTABHASH;
p->link = ptab[h];
unlock(&ptablock);
ptab[h] = p;
}
static Proc*
__threadgetproc(int rm)
{
Proc **l, *p;
int h, pid;
Thread *t;
ulong *s;
s = (ulong*)((ulong)&pid & ~(STKSIZE-1));
if(s[0] == STKMAGIC){
t = (Thread*)s[1];
return t->proc;
}
pid = _threadgetpid();
lock(&ptablock);
h = ((unsigned)pid)%PTABHASH;
for(l=&ptab[h]; p=*l; l=&p->link){
if(p->pid == pid){
if(rm)
*l = p->link;
unlock(&ptablock);
return p;
}
}
unlock(&ptablock);
return nil;
}
Proc*
_threadgetproc(void)
{
return __threadgetproc(0);
}
Proc*
_threaddelproc(void)
{
return __threadgetproc(1);
}

13
src/libthread/ref.c Normal file
View File

@@ -0,0 +1,13 @@
#include "threadimpl.h"
void
incref(Ref *r)
{
_xinc(&r->ref);
}
long
decref(Ref *r)
{
return _xdec(&r->ref);
}

104
src/libthread/rendez.c Normal file
View File

@@ -0,0 +1,104 @@
#include "threadimpl.h"
Rgrp _threadrgrp;
static int isdirty;
int _threadhighnrendez;
int _threadnrendez;
static int nrendez;
static ulong
finish(Thread *t, ulong val)
{
ulong ret;
ret = t->rendval;
t->rendval = val;
while(t->state == Running)
sleep(0);
lock(&t->proc->lock);
if(t->state == Rendezvous){ /* not always true: might be Dead */
t->state = Ready;
_threadready(t);
}
unlock(&t->proc->lock);
return ret;
}
ulong
_threadrendezvous(ulong tag, ulong val)
{
ulong ret;
Thread *t, **l;
lock(&_threadrgrp.lock);
_threadnrendez++;
l = &_threadrgrp.hash[tag%nelem(_threadrgrp.hash)];
for(t=*l; t; l=&t->rendhash, t=*l){
if(t->rendtag==tag){
_threaddebug(DBGREND, "Rendezvous with thread %d.%d", t->proc->pid, t->id);
*l = t->rendhash;
ret = finish(t, val);
--nrendez;
unlock(&_threadrgrp.lock);
return ret;
}
}
/* Going to sleep here. */
t = _threadgetproc()->thread;
t->rendbreak = 0;
t->inrendez = 1;
t->rendtag = tag;
t->rendval = val;
t->rendhash = *l;
*l = t;
t->nextstate = Rendezvous;
++nrendez;
if(nrendez > _threadhighnrendez)
_threadhighnrendez = nrendez;
_threaddebug(DBGREND, "Rendezvous for tag %lud", t->rendtag);
unlock(&_threadrgrp.lock);
_sched();
t->inrendez = 0;
_threaddebug(DBGREND, "Woke after rendezvous; val is %lud", t->rendval);
return t->rendval;
}
/*
* This is called while holding _threadpq.lock and p->lock,
* so we can't lock _threadrgrp.lock. Instead our caller has
* to call _threadbreakrendez after dropping those locks.
*/
void
_threadflagrendez(Thread *t)
{
t->rendbreak = 1;
isdirty = 1;
}
void
_threadbreakrendez(void)
{
int i;
Thread *t, **l;
if(isdirty == 0)
return;
lock(&_threadrgrp.lock);
if(isdirty == 0){
unlock(&_threadrgrp.lock);
return;
}
isdirty = 0;
for(i=0; i<nelem(_threadrgrp.hash); i++){
l = &_threadrgrp.hash[i];
for(t=*l; t; t=*l){
if(t->rendbreak){
*l = t->rendhash;
finish(t, ~0);
}else
l=&t->rendhash;
}
}
unlock(&_threadrgrp.lock);
}

26
src/libthread/rpm.spec Normal file
View File

@@ -0,0 +1,26 @@
Summary: Port of Plan 9's thread library
Name: libthread
Version: 2.0
Release: 1
Group: Development/C
Copyright: BSD-like
Packager: Russ Cox <rsc@post.harvard.edu>
Source: http://pdos.lcs.mit.edu/~rsc/software/libthread-2.0.tgz
URL: http://pdos.lcs.mit.edu/~rsc/software/#libthread
%description
Libthread is a port of Plan 9's thread library
%prep
%setup
%build
make
%install
make install
%files
/usr/local/include/thread.h
/usr/local/lib/libthread.a
/usr/local/man/man3/thread.3
/usr/local/man/man3/ioproc.3

192
src/libthread/sched.c Normal file
View File

@@ -0,0 +1,192 @@
#include "threadimpl.h"
#include <signal.h>
//static Thread *runthread(Proc*);
static char *_psstate[] = {
"Dead",
"Running",
"Ready",
"Rendezvous",
};
static char*
psstate(int s)
{
if(s < 0 || s >= nelem(_psstate))
return "unknown";
return _psstate[s];
}
void
_schedinit(void *arg)
{
Proc *p;
Thread *t;
extern void ignusr1(void), _threaddie(int);
ignusr1();
signal(SIGTERM, _threaddie);
p = arg;
p->pid = _threadgetpid();
_threadsetproc(p);
while(_setlabel(&p->sched))
;
_threaddebug(DBGSCHED, "top of schedinit, _threadexitsallstatus=%p", _threadexitsallstatus);
if(_threadexitsallstatus)
exits(_threadexitsallstatus);
lock(&p->lock);
if((t=p->thread) != nil){
p->thread = nil;
if(t->moribund){
assert(t->moribund == 1);
t->state = Dead;
if(t->prevt)
t->prevt->nextt = t->nextt;
else
p->threads.head = t->nextt;
if(t->nextt)
t->nextt->prevt = t->prevt;
else
p->threads.tail = t->prevt;
unlock(&p->lock);
if(t->inrendez){
_threadflagrendez(t);
_threadbreakrendez();
}
_stackfree(t->stk);
free(t->cmdname);
free(t); /* XXX how do we know there are no references? */
t = nil;
_sched();
}
if(p->needexec){
t->ret = _schedexec(&p->exec);
p->needexec = 0;
}
if(p->newproc){
t->ret = _schedfork(p->newproc);
if(t->ret < 0){
//fprint(2, "_schedfork: %r\n");
abort();
}
p->newproc = nil;
}
t->state = t->nextstate;
if(t->state == Ready)
_threadready(t);
}
unlock(&p->lock);
_sched();
}
static inline Thread*
runthread(Proc *p)
{
Thread *t;
Tqueue *q;
if(p->nthreads==0)
return nil;
q = &p->ready;
lock(&p->readylock);
if(q->head == nil){
q->asleep = 1;
_threaddebug(DBGSCHED, "sleeping for more work");
unlock(&p->readylock);
while(rendezvous((ulong)q, 0) == ~0){
if(_threadexitsallstatus)
exits(_threadexitsallstatus);
}
/* lock picked up from _threadready */
}
t = q->head;
q->head = t->next;
unlock(&p->readylock);
return t;
}
void
_sched(void)
{
Proc *p;
Thread *t;
Resched:
p = _threadgetproc();
//fprint(2, "p %p\n", p);
if((t = p->thread) != nil){
if((ulong)&p < (ulong)t->stk){ /* stack overflow */
fprint(2, "stack overflow %lux %lux\n", (ulong)&p, (ulong)t->stk);
abort();
}
// _threaddebug(DBGSCHED, "pausing, state=%s set %p goto %p",
// psstate(t->state), &t->sched, &p->sched);
if(_setlabel(&t->sched)==0)
_gotolabel(&p->sched);
return;
}else{
t = runthread(p);
if(t == nil){
_threaddebug(DBGSCHED, "all threads gone; exiting");
_threaddelproc();
_schedexit(p);
}
// _threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id);
p->thread = t;
if(t->moribund){
_threaddebug(DBGSCHED, "%d.%d marked to die");
goto Resched;
}
t->state = Running;
t->nextstate = Ready;
_gotolabel(&t->sched);
}
}
long
threadstack(void)
{
Proc *p;
Thread *t;
p = _threadgetproc();
t = p->thread;
return (ulong)&p - (ulong)t->stk;
}
void
_threadready(Thread *t)
{
Tqueue *q;
assert(t->state == Ready);
_threaddebug(DBGSCHED, "readying %d.%d", t->proc->pid, t->id);
q = &t->proc->ready;
lock(&t->proc->readylock);
t->next = nil;
if(q->head==nil)
q->head = t;
else
q->tail->next = t;
q->tail = t;
if(q->asleep){
q->asleep = 0;
/* lock passes to runthread */
_threaddebug(DBGSCHED, "waking process %d", t->proc->pid);
while(rendezvous((ulong)q, 0) == ~0){
if(_threadexitsallstatus)
exits(_threadexitsallstatus);
}
}else
unlock(&t->proc->readylock);
}
void
yield(void)
{
_sched();
}

34
src/libthread/texec.c Normal file
View File

@@ -0,0 +1,34 @@
#include <lib9.h>
#include <thread.h>
extern int _threaddebuglevel;
void
doexec(void *v)
{
char **argv = v;
procexec(nil, argv[0], argv);
sendp(threadwaitchan(), nil);
}
void
threadmain(int argc, char **argv)
{
Channel *c;
Waitmsg *w;
ARGBEGIN{
case 'D':
_threaddebuglevel = ~0;
break;
}ARGEND
c = threadwaitchan();
proccreate(doexec, argv, 8192);
w = recvp(c);
if(w == nil)
print("exec failed\n");
else
print("%d %lu %lu %lu %s\n", w->pid, w->time[0], w->time[1], w->time[2], w->msg);
threadexits(nil);
}

576
src/libthread/thread.3 Normal file
View File

@@ -0,0 +1,576 @@
.TH THREAD 2
.SH NAME
alt,
chancreate,
chanfree,
chaninit,
chanprint,
mainstacksize,
proccreate,
procdata,
procexec,
procexecl,
procrfork,
recv,
recvp,
recvul,
send,
sendp,
sendul,
nbrecv,
nbrecvp,
nbrecvul,
nbsend,
nbsendp,
nbsendul,
threadcreate,
threaddata,
threadexits,
threadexitsall,
threadgetgrp,
threadgetname,
threadint,
threadintgrp,
threadkill,
threadkillgrp,
threadmain,
threadnotify,
threadid,
threadpid,
threadsetgrp,
threadsetname,
threadwaitchan,
yield \- thread and proc management
.SH SYNOPSIS
.PP
.EX
.ta 4n +4n +4n +4n +4n +4n +4n
#include <u.h>
#include <libc.h>
#include <thread.h>
.sp
#define CHANEND 0
#define CHANSND 1
#define CHANRCV 2
#define CHANNOP 3
#define CHANNOBLK 4
.sp
.ta \w' 'u +\w'Channel 'u
typedef struct Alt Alt;
struct Alt {
Channel *c;
void *v;
int op;
Channel **tag;
int entryno;
};
.fi
.de XX
.ift .sp 0.5
.ifn .sp
..
.PP
.nf
.ft L
.ta \w'\fLChannel* 'u +4n +4n +4n +4n
void threadmain(int argc, char *argv[])
int mainstacksize
int proccreate(void (*fn)(void*), void *arg, uint stacksize)
int procrfork(void (*fn)(void*), void *arg, uint stacksize,
int rforkflag)
int threadcreate(void (*fn)(void*), void *arg, uint stacksize)
void threadexits(char *status)
void threadexitsall(char *status)
void yield(void)
.XX
int threadid(void)
int threadgrp(void)
int threadsetgrp(int group)
int threadpid(int id)
.XX
int threadint(int id)
int threadintgrp(int group)
int threadkill(int id)
int threadkillgrp(int group)
.XX
void threadsetname(char *name)
char* threadgetname(void)
.XX
void** threaddata(void)
void** procdata(void)
.XX
int chaninit(Channel *c, int elsize, int nel)
Channel* chancreate(int elsize, int nel)
void chanfree(Channel *c)
.XX
int alt(Alt *alts)
int recv(Channel *c, void *v)
void* recvp(Channel *c)
ulong recvul(Channel *c)
int nbrecv(Channel *c, void *v)
void* nbrecvp(Channel *c)
ulong nbrecvul(Channel *c)
int send(Channel *c, void *v)
int sendp(Channel *c, void *v)
int sendul(Channel *c, ulong v)
int nbsend(Channel *c, void *v)
int nbsendp(Channel *c, void *v)
int nbsendul(Channel *c, ulong v)
int chanprint(Channel *c, char *fmt, ...)
.XX
int procexecl(Channel *cpid, char *file, ...)
int procexec(Channel *cpid, char *file, char *args[])
Channel* threadwaitchan(void)
.XX
int threadnotify(int (*f)(void*, char*), int in)
.EE
.SH DESCRIPTION
.PP
The thread library provides parallel programming support similar to that
of the languages
Alef and Newsqueak.
Threads
and
procs
occupy a shared address space,
communicating and synchronizing through
.I channels
and shared variables.
.PP
A
.I proc
is a Plan 9 process that contains one or more cooperatively scheduled
.IR threads .
Programs using threads must replace
.I main
by
.IR threadmain .
The thread library provides a
.I main
function that sets up a proc with a single thread executing
.I threadmain
on a stack of size
.I mainstacksize
(default eight kilobytes).
To set
.IR mainstacksize ,
declare a global variable
initialized to the desired value
.RI ( e.g. ,
.B int
.B mainstacksize
.B =
.BR 1024 ).
.PP
.I Threadcreate
creates a new thread in the calling proc, returning a unique integer
identifying the thread; the thread
executes
.I fn(arg)
on a stack of size
.IR stacksize .
Thread stacks are allocated in shared memory, making it valid to pass
pointers to stack variables between threads and procs.
.I Procrfork
creates a new proc, and inside that proc creates
a single thread as
.I threadcreate
would,
returning the id of the created thread.
.I Procrfork
creates the new proc by calling
.B rfork
(see
.IR fork (2))
with flags
.BR RFPROC|RFMEM|RFNOWAIT| \fIrforkflag\fR.
(The thread library depends on all its procs
running in the same rendezvous group.
Do not include
.B RFREND
in
.IR rforkflag .)
.I Proccreate
is identical to
.I procrfork
with
.I rforkflag
set to zero.
Be aware that the calling thread may continue
execution before
the newly created proc and thread
are scheduled.
Because of this,
.I arg
should not point to data on the stack of a function that could
return before the new process is scheduled.
.PP
.I Threadexits
terminates the calling thread.
If the thread is the last in its proc,
.I threadexits
also terminates the proc, using
.I status
as the exit status.
.I Threadexitsall
terminates all procs in the program,
using
.I status
as the exit status.
.PP
The threads in a proc are coroutines, scheduled nonpreemptively
in a round-robin fashion.
A thread must explicitly relinquish control of the processor
before another thread in the same proc is run.
Calls that do this are
.IR yield ,
.IR proccreate ,
.IR procexec ,
.IR procexecl ,
.IR threadexits ,
.IR alt ,
.IR send ,
and
.I recv
(and the calls related to
.I send
and
.IR recv \(emsee
their descriptions further on).
Procs are scheduled by the operating system.
Therefore, threads in different procs can preempt one another
in arbitrary ways and should synchronize their
actions using
.B qlocks
(see
.IR lock (2))
or channel communication.
System calls such as
.IR read (2)
block the entire proc;
all threads in a proc block until the system call finishes.
.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
returns the id for the current thread.
Each thread also has a thread group id.
The initial thread has a group id of zero.
Each new thread inherits the group id of
the thread that created it.
.I Threadgrp
returns the group id for the current thread;
.I threadsetgrp
sets it.
.I Threadpid
returns the pid of the Plan 9 process containing
the thread identified by
.IR id ,
or \-1
if no such thread is found.
.PP
.I Threadint
interrupts a thread that is blocked in a channel operation
or system call.
.I Threadintgrp
interrupts all threads with the given group id.
.I Threadkill
marks a thread to die when it next relinquishes the processor
(via one of the calls listed above).
If the thread is blocked in a channel operation or system call,
it is also interrupted.
.I Threadkillgrp
kills all threads with the given group id.
Note that
.I threadkill
and
.I threadkillgrp
will not terminate a thread that never relinquishes
the processor.
.PP
Primarily for debugging,
threads can have string names associated with them.
.I Threadgetname
returns the current thread's name;
.I threadsetname
sets it.
The pointer returned by
.I threadgetname
is only valid until the next call to
.IR threadsetname .
.PP
.I Threaddata
returns a pointer to a per-thread pointer
that may be modified by threaded programs for
per-thread storage.
Similarly,
.I procdata
returns a pointer to a per-proc pointer.
.PP
.I Procexecl
and
.I procexec
are threaded analogues of
.I exec
and
.I execl
(see
.IR exec (2));
on success,
they replace the calling thread (which must be the only thread in its proc)
and invoke the external program, never returning.
On error, they return \-1.
If
.I cpid
is not null, the pid of the invoked program
will be sent along
.I cpid
once the program has been started, or \-1 will be sent if an
error occurs.
.I Procexec
and
.I procexecl
will not access their arguments after sending a result
along
.IR cpid .
Thus, programs that malloc the
.I argv
passed to
.I procexec
can safely free it once they have
received the
.I cpid
response.
.I Threadwaitchan
returns a channel of pointers to
.B Waitmsg
structures (see
.IR wait (2)).
When an exec'ed process exits, a pointer to a
.B Waitmsg
is sent to this channel.
These
.B Waitmsg
structures have been allocated with
.IR malloc (2)
and should be freed after use.
.PP
A
.B Channel
is a buffered or unbuffered queue for fixed-size messages.
Procs and threads
.I send
messages into the channel and
.I recv
messages from the channel. If the channel is unbuffered, a
.I send
operation blocks until the corresponding
.I recv
operation occurs and
.IR "vice versa" .
.I Chaninit
initializes a
.B Channel
for messages of size
.I elsize
and with a buffer holding
.I nel
messages.
If
.I nel
is zero, the channel is unbuffered.
.IR Chancreate
allocates a new channel and initializes it.
.I Chanfree
frees a channel that is no longer used.
.I Chanfree
can be called by either sender or receiver after the last item has been
sent or received. Freeing the channel will be delayed if there is a thread
blocked on it until that thread unblocks (but
.I chanfree
returns immediately).
.PP
.I Send
sends the element pointed at by
.I v
to the channel
.IR c .
If
.I v
is null, zeros are sent.
.I Recv
receives an element from
.I c
and stores it in
.IR v .
If
.I v
is null,
the received value is discarded.
.I Send
and
.I recv
return 1 on success, \-1 if interrupted.
.I Nbsend
and
.I nbrecv
behave similarly, but return 0 rather than blocking.
.PP
.IR Sendp ,
.IR nbsendp ,
.IR sendul ,
and
.I nbsendul
send a pointer or an unsigned long; the channel must
have been initialized with the appropriate
.IR elsize .
.IR Recvp ,
.IR nbrecvp ,
.IR recvul ,
and
.I nbrecvul
receive a pointer or an unsigned long;
they return zero when a zero is received,
when interrupted, or
(for
.I nbrecvp
and
.IR nbrecvul )
when the operation would have blocked.
To distinguish between these three cases,
use
.I recv
or
.IR nbrecv .
.PP
.I Alt
can be used to recv from or send to one of a number of channels,
as directed by an array of
.B Alt
structures,
each of which describes a potential send or receive operation.
In an
.B Alt
structure,
.B c
is the channel;
.B v
the value pointer (which may be null); and
.B op
the operation:
.B CHANSND
for a send operation,
.B CHANRECV
for a recv operation;
.B CHANNOP
for no operation
(useful
when
.I alt
is called with a varying set of operations).
The array of
.B Alt
structures is terminated by an entry with
.I op
.B CHANEND
or
.BR CHANNOBLK .
If at least one
.B Alt
structure can proceed, one of them is
chosen at random to be executed.
.I Alt
returns the index of the chosen structure.
If no operations can proceed and the list is terminated with
.BR CHANNOBLK ,
.I alt
returns the index of the terminating
.B CHANNOBLK
structure.
Otherwise,
.I alt
blocks until one of the operations can proceed,
eventually returning the index of the structure executes.
.I Alt
returns \-1 when interrupted.
The
.B tag
and
.B entryno
fields in the
.B Alt
structure are used internally by
.I alt
and need not be initialized.
They are not used between
.I alt
calls.
.PP
.I Chanprint
formats its arguments in the manner of
.IR print (2)
and sends the result to the channel
.IR c.
The string delivered by
.I chanprint
is allocated with
.IR malloc (2)
and should be freed upon receipt.
.PP
Thread library functions do not return on failure;
if errors occur, the entire program is aborted.
.PP
Threaded programs should use
.I threadnotify
in place of
.I atnotify
(see
.IR notify (2)).
.PP
It is safe to use
.B sysfatal
(see
.IR perror (2))
in threaded programs.
.I Sysfatal
will print the error string and call
.IR threadexitsall .
.PP
It is safe to use
.IR rfork
(see
.IR fork (2))
to manage the namespace, file descriptors, note group, and environment of a
single process.
That is, it is safe to call
.I rfork
with the flags
.BR RFNAMEG ,
.BR RFFDG ,
.BR RFCFDG ,
.BR RFNOTEG ,
.BR RFENVG ,
and
.BR RFCENVG.
(To create new processes, use
.I proccreate
and
.IR procrfork .)
As mentioned above,
the thread library depends on all procs being in the
same rendezvous group; do not change the rendezvous
group with
.IR rfork .
.SH FILES
.B /sys/lib/acid/thread
contains useful
.IR acid (1)
functions for debugging threaded programs.
.PP
.B /sys/src/libthread/example.c
contains a full example program.
.SH SOURCE
.B /sys/src/libthread
.SH SEE ALSO
.IR intro (2),
.IR ioproc (2)

132
src/libthread/thread.h Normal file
View File

@@ -0,0 +1,132 @@
#ifndef _THREADH_
#define _THREADH_ 1
/* avoid conflicts with socket library */
#undef send
#define send _threadsend
#undef recv
#define recv _threadrecv
typedef struct Alt Alt;
typedef struct Channel Channel;
typedef struct Ref Ref;
/* Channel structure. S is the size of the buffer. For unbuffered channels
* s is zero. v is an array of s values. If s is zero, v is unused.
* f and n represent the state of the queue pointed to by v.
*/
enum {
Nqwds = 2,
Nqshift = 5, // 2log #of bits in long
Nqmask = - 1,
Nqbits = (1 << Nqshift) * 2,
};
struct Channel {
int s; // Size of the channel (may be zero)
unsigned int f; // Extraction point (insertion pt: (f + n) % s)
unsigned int n; // Number of values in the channel
int e; // Element size
int freed; // Set when channel is being deleted
volatile Alt **qentry; // Receivers/senders waiting (malloc)
volatile int nentry; // # of entries malloc-ed
unsigned char v[1]; // Array of s values in the channel
};
/* Channel operations for alt: */
typedef enum {
CHANEND,
CHANSND,
CHANRCV,
CHANNOP,
CHANNOBLK,
} ChanOp;
struct Alt {
Channel *c; /* channel */
void *v; /* pointer to value */
ChanOp op; /* operation */
/* the next variables are used internally to alt
* they need not be initialized
*/
Channel **tag; /* pointer to rendez-vous tag */
int entryno; /* entry number */
};
struct Ref {
long ref;
};
int alt(Alt alts[]);
Channel* chancreate(int elemsize, int bufsize);
int chaninit(Channel *c, int elemsize, int elemcnt);
void chanfree(Channel *c);
int chanprint(Channel *, char *, ...);
long decref(Ref *r); /* returns 0 iff value is now zero */
void incref(Ref *r);
int nbrecv(Channel *c, void *v);
void* nbrecvp(Channel *c);
unsigned long nbrecvul(Channel *c);
int nbsend(Channel *c, void *v);
int nbsendp(Channel *c, void *v);
int nbsendul(Channel *c, unsigned long v);
int proccreate(void (*f)(void *arg), void *arg, unsigned int stacksize);
int procrfork(void (*f)(void *arg), void *arg, unsigned int stacksize, int flag);
void** procdata(void);
void procexec(Channel *, char *, char *[]);
void procexecl(Channel *, char *, ...);
int recv(Channel *c, void *v);
void* recvp(Channel *c);
unsigned long recvul(Channel *c);
int send(Channel *c, void *v);
int sendp(Channel *c, void *v);
int sendul(Channel *c, unsigned long v);
int threadcreate(void (*f)(void *arg), void *arg, unsigned int stacksize);
void** threaddata(void);
void threadexits(char *);
void threadexitsall(char *);
int threadgetgrp(void); /* return thread group of current thread */
char* threadgetname(void);
void threadint(int); /* interrupt thread */
void threadintgrp(int); /* interrupt threads in grp */
void threadkill(int); /* kill thread */
void threadkillgrp(int); /* kill threads in group */
void threadmain(int argc, char *argv[]);
void threadnonotes(void);
int threadnotify(int (*f)(void*, char*), int in);
int threadid(void);
int threadpid(int);
int threadsetgrp(int); /* set thread group, return old */
void threadsetname(char *name);
Channel* threadwaitchan(void);
int tprivalloc(void);
void tprivfree(int);
void **tprivaddr(int);
void yield(void);
long threadstack(void);
extern int mainstacksize;
/* slave I/O processes */
typedef struct Ioproc Ioproc;
Ioproc* ioproc(void);
void closeioproc(Ioproc*);
void iointerrupt(Ioproc*);
int ioclose(Ioproc*, int);
int iodial(Ioproc*, char*, char*, char*, int*);
int ioopen(Ioproc*, char*, int);
long ioread(Ioproc*, int, void*, long);
long ioreadn(Ioproc*, int, void*, long);
long iowrite(Ioproc*, int, void*, long);
int iosleep(Ioproc*, long);
long iocall(Ioproc*, long (*)(va_list*), ...);
void ioret(Ioproc*, int);
#endif /* _THREADH_ */

219
src/libthread/threadimpl.h Normal file
View File

@@ -0,0 +1,219 @@
/*
* Some notes on locking:
*
* All the locking woes come from implementing
* threadinterrupt (and threadkill).
*
* _threadgetproc()->thread is always a live pointer.
* p->threads, p->ready, and _threadrgrp also contain
* live thread pointers. These may only be consulted
* while holding p->lock or _threadrgrp.lock; in procs
* other than p, the pointers are only guaranteed to be live
* while the lock is still being held.
*
* Thread structures can only be freed by the proc
* they belong to. Threads marked with t->inrendez
* need to be extracted from the _threadrgrp before
* being freed.
*
* _threadrgrp.lock cannot be acquired while holding p->lock.
*/
#include <assert.h>
#include <lib9.h>
#include <thread.h>
#include "label.h"
enum{
STKSIZE = 16384,
STKMAGIC = 0xCAFEBEEF
};
typedef struct Thread Thread;
typedef struct Proc Proc;
typedef struct Tqueue Tqueue;
typedef struct Pqueue Pqueue;
typedef struct Rgrp Rgrp;
typedef struct Execargs Execargs;
/* must match list in sched.c */
typedef enum
{
Dead,
Running,
Ready,
Rendezvous,
} State;
typedef enum
{
Channone,
Chanalt,
Chansend,
Chanrecv,
} Chanstate;
enum
{
RENDHASH = 10009,
Printsize = 2048,
NPRIV = 8,
};
struct Rgrp
{
Lock lock;
Thread *hash[RENDHASH];
};
struct Tqueue /* Thread queue */
{
int asleep;
Thread *head;
Thread *tail;
};
struct Thread
{
Lock lock; /* protects thread data structure */
Label sched; /* for context switches */
int id; /* thread id */
int grp; /* thread group */
int moribund; /* thread needs to die */
State state; /* run state */
State nextstate; /* next run state */
uchar *stk; /* top of stack (lowest address of stack) */
uint stksize; /* stack size */
Thread *next; /* next on ready queue */
Proc *proc; /* proc of this thread */
Thread *nextt; /* next on list of threads in this proc */
Thread *prevt; /* prev on list of threads in this proc */
int ret; /* return value for Exec, Fork */
char *cmdname; /* ptr to name of thread */
int inrendez;
Thread *rendhash; /* Trgrp linked list */
ulong rendtag; /* rendezvous tag */
ulong rendval; /* rendezvous value */
int rendbreak; /* rendezvous has been taken */
Chanstate chan; /* which channel operation is current */
Alt *alt; /* pointer to current alt structure (debugging) */
void* udata[NPRIV]; /* User per-thread data pointer */
};
struct Execargs
{
char *prog;
char **args;
int fd[2];
};
struct Proc
{
Lock lock;
Label sched; /* for context switches */
Proc *link; /* in proctab */
int pid; /* process id */
int splhi; /* delay notes */
Thread *thread; /* running thread */
int needexec;
Execargs exec; /* exec argument */
Proc *newproc; /* fork argument */
char exitstr[ERRMAX]; /* exit status */
int rforkflag;
int nthreads;
Tqueue threads; /* All threads of this proc */
Tqueue ready; /* Runnable threads */
Lock readylock;
char printbuf[Printsize];
int blocked; /* In a rendezvous */
int pending; /* delayed note pending */
int nonotes; /* delay notes */
uint nextID; /* ID of most recently created thread */
Proc *next; /* linked list of Procs */
void *arg; /* passed between shared and unshared stk */
char str[ERRMAX]; /* used by threadexits to avoid malloc */
char errbuf[ERRMAX]; /* errstr */
void* udata; /* User per-proc data pointer */
};
struct Pqueue { /* Proc queue */
Lock lock;
Proc *head;
Proc **tail;
};
struct Ioproc
{
int tid;
Channel *c, *creply;
int inuse;
long (*op)(va_list*);
va_list arg;
long ret;
char err[ERRMAX];
Ioproc *next;
};
void _gotolabel(Label*);
int _setlabel(Label*);
void _freeproc(Proc*);
Proc* _newproc(void(*)(void*), void*, uint, char*, int, int);
int _procsplhi(void);
void _procsplx(int);
void _sched(void);
int _schedexec(Execargs*);
void _schedexecwait(void);
void _schedexit(Proc*);
int _schedfork(Proc*);
void _schedinit(void*);
void _systhreadinit(void);
void _threadassert(char*);
void _threadbreakrendez(void);
void __threaddebug(ulong, char*, ...);
#define _threaddebug if(!_threaddebuglevel){}else __threaddebug
void _threadexitsall(char*);
void _threadflagrendez(Thread*);
Proc* _threadgetproc(void);
Proc* _threaddelproc(void);
void _threadsetproc(Proc*);
void _threadinitstack(Thread*, void(*)(void*), void*);
void* _threadmalloc(long, int);
void _threadnote(void*, char*);
void _threadready(Thread*);
ulong _threadrendezvous(ulong, ulong);
void _threadsignal(void);
void _threadsysfatal(char*, va_list);
long _xdec(long*);
void _xinc(long*);
void _threadremove(Proc*, Thread*);
extern int _threaddebuglevel;
extern char* _threadexitsallstatus;
extern Pqueue _threadpq;
extern Channel* _threadwaitchan;
extern Rgrp _threadrgrp;
extern void _stackfree(void*);
#define DBGAPPL (1 << 0)
#define DBGSCHED (1 << 16)
#define DBGCHAN (1 << 17)
#define DBGREND (1 << 18)
/* #define DBGKILL (1 << 19) */
#define DBGNOTE (1 << 20)
#define DBGEXEC (1 << 21)
#define ioproc_arg(io, type) (va_arg((io)->arg, type))
extern int _threadgetpid(void);
extern void _threadmemset(void*, int, int);
extern void _threaddebugmemset(void*, int, int);

BIN
src/libthread/tprimes Executable file

Binary file not shown.

62
src/libthread/tprimes.c Normal file
View File

@@ -0,0 +1,62 @@
#include <lib9.h>
#include <thread.h>
int quiet;
int goal;
int buffer;
int (*fn)(void(*)(void*), void*, uint) = threadcreate;
void
primethread(void *arg)
{
Channel *c, *nc;
int p, i;
c = arg;
p = recvul(c);
if(p > goal)
threadexitsall(nil);
if(!quiet)
print("%d\n", p);
nc = chancreate(sizeof(ulong), buffer);
(*fn)(primethread, nc, 8192);
for(;;){
i = recvul(c);
if(i%p)
sendul(nc, i);
}
}
extern int _threaddebuglevel;
void
threadmain(int argc, char **argv)
{
int i;
Channel *c;
ARGBEGIN{
case 'D':
_threaddebuglevel = atoi(ARGF());
break;
case 'q':
quiet = 1;
break;
case 'b':
buffer = atoi(ARGF());
break;
case 'p':
fn=proccreate;
break;
}ARGEND
if(argc>0)
goal = atoi(argv[0]);
else
goal = 100;
c = chancreate(sizeof(ulong), buffer);
(*fn)(primethread, c, 8192);
for(i=2;; i++)
sendul(c, i);
}