Initial revision
This commit is contained in:
21
src/libthread/386.c
Normal file
21
src/libthread/386.c
Normal 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 */
|
||||
}
|
||||
|
||||
18
src/libthread/FreeBSD-386.s
Normal file
18
src/libthread/FreeBSD-386.s
Normal 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
258
src/libthread/LICENSE
Normal 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.
|
||||
|
||||
6
src/libthread/Make.Darwin-PowerMacintosh
Normal file
6
src/libthread/Make.Darwin-PowerMacintosh
Normal 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
|
||||
7
src/libthread/Make.FreeBSD-386
Normal file
7
src/libthread/Make.FreeBSD-386
Normal 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
|
||||
6
src/libthread/Make.HP-UX-9000
Normal file
6
src/libthread/Make.HP-UX-9000
Normal file
@@ -0,0 +1,6 @@
|
||||
CC=cc
|
||||
CFLAGS=-O -c -Ae -I.
|
||||
O=o
|
||||
AR=ar
|
||||
ARFLAGS=rvc
|
||||
NAN=nan64.$O
|
||||
7
src/libthread/Make.Linux-386
Normal file
7
src/libthread/Make.Linux-386
Normal 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
|
||||
7
src/libthread/Make.NetBSD-386
Normal file
7
src/libthread/Make.NetBSD-386
Normal 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
|
||||
6
src/libthread/Make.OSF1-alpha
Normal file
6
src/libthread/Make.OSF1-alpha
Normal file
@@ -0,0 +1,6 @@
|
||||
CC=cc
|
||||
CFLAGS+=-g -c -I.
|
||||
O=o
|
||||
AR=ar
|
||||
ARFLAGS=rvc
|
||||
NAN=nan64.$O
|
||||
2
src/libthread/Make.SunOS-sun4u
Normal file
2
src/libthread/Make.SunOS-sun4u
Normal file
@@ -0,0 +1,2 @@
|
||||
include Make.SunOS-sun4u-$(CC)
|
||||
NAN=nan64.$O
|
||||
6
src/libthread/Make.SunOS-sun4u-cc
Normal file
6
src/libthread/Make.SunOS-sun4u-cc
Normal file
@@ -0,0 +1,6 @@
|
||||
CC=cc
|
||||
CFLAGS+=-g -c -I. -O
|
||||
O=o
|
||||
AR=ar
|
||||
ARFLAGS=rvc
|
||||
NAN=nan64.$O
|
||||
6
src/libthread/Make.SunOS-sun4u-gcc
Normal file
6
src/libthread/Make.SunOS-sun4u-gcc
Normal 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
125
src/libthread/Makefile
Normal 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
|
||||
54
src/libthread/Makefile.MID
Normal file
54
src/libthread/Makefile.MID
Normal 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
19
src/libthread/NOTICE
Normal 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
19
src/libthread/README
Normal 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>.
|
||||
|
||||
49
src/libthread/asm-FreeBSD-386.s
Normal file
49
src/libthread/asm-FreeBSD-386.s
Normal 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
|
||||
|
||||
1
src/libthread/asm-Linux-386.s
Normal file
1
src/libthread/asm-Linux-386.s
Normal file
@@ -0,0 +1 @@
|
||||
.include "asm-FreeBSD-386.s"
|
||||
42
src/libthread/bundle.ports
Normal file
42
src/libthread/bundle.ports
Normal 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
485
src/libthread/channel.c
Normal 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
18
src/libthread/chanprint.c
Normal 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
182
src/libthread/create.c
Normal 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
48
src/libthread/debug.c
Normal 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
124
src/libthread/exec-unix.c
Normal 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
77
src/libthread/exec.c
Normal 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
63
src/libthread/exit.c
Normal 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
8
src/libthread/getpid.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "threadimpl.h"
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
_threadgetpid(void)
|
||||
{
|
||||
return getpid();
|
||||
}
|
||||
135
src/libthread/id.c
Normal file
135
src/libthread/id.c
Normal 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
49
src/libthread/iocall.c
Normal 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
16
src/libthread/ioclose.c
Normal 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
21
src/libthread/iodial.c
Normal 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
20
src/libthread/ioopen.c
Normal 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
179
src/libthread/ioproc.3
Normal 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
74
src/libthread/ioproc.c
Normal 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
20
src/libthread/ioread.c
Normal 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
21
src/libthread/ioreadn.c
Normal 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
16
src/libthread/iosleep.c
Normal 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
21
src/libthread/iowrite.c
Normal 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
89
src/libthread/kill.c
Normal 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
24
src/libthread/label.h
Normal 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
35
src/libthread/lib.c
Normal 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
124
src/libthread/main.c
Normal 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
8
src/libthread/memset.c
Normal 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
8
src/libthread/memsetd.c
Normal 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
2
src/libthread/mkfile
Normal file
@@ -0,0 +1,2 @@
|
||||
<../libutf/mkfile
|
||||
|
||||
143
src/libthread/note.c
Normal file
143
src/libthread/note.c
Normal 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
64
src/libthread/proctab.c
Normal 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
13
src/libthread/ref.c
Normal 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
104
src/libthread/rendez.c
Normal 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
26
src/libthread/rpm.spec
Normal 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
192
src/libthread/sched.c
Normal 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
34
src/libthread/texec.c
Normal 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
576
src/libthread/thread.3
Normal 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
132
src/libthread/thread.h
Normal 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
219
src/libthread/threadimpl.h
Normal 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
BIN
src/libthread/tprimes
Executable file
Binary file not shown.
62
src/libthread/tprimes.c
Normal file
62
src/libthread/tprimes.c
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user