libtask as of 2008/08/01

This commit is contained in:
Russ Cox 2009-10-10 13:04:03 -07:00
commit 42f058999e
22 changed files with 3077 additions and 0 deletions

121
386-ucontext.h Normal file
View File

@ -0,0 +1,121 @@
#define setcontext(u) setmcontext(&(u)->uc_mcontext)
#define getcontext(u) getmcontext(&(u)->uc_mcontext)
typedef struct mcontext mcontext_t;
typedef struct ucontext ucontext_t;
extern int swapcontext(ucontext_t*, const ucontext_t*);
extern void makecontext(ucontext_t*, void(*)(), int, ...);
extern int getmcontext(mcontext_t*);
extern void setmcontext(const mcontext_t*);
/*-
* Copyright (c) 1999 Marcel Moolenaar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD: src/sys/sys/ucontext.h,v 1.4 1999/10/11 20:33:17 luoqi Exp $
*/
/* #include <machine/ucontext.h> */
/*-
* Copyright (c) 1999 Marcel Moolenaar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $
*/
struct mcontext {
/*
* The first 20 fields must match the definition of
* sigcontext. So that we can support sigcontext
* and ucontext_t at the same time.
*/
int mc_onstack; /* XXX - sigcontext compat. */
int mc_gs;
int mc_fs;
int mc_es;
int mc_ds;
int mc_edi;
int mc_esi;
int mc_ebp;
int mc_isp;
int mc_ebx;
int mc_edx;
int mc_ecx;
int mc_eax;
int mc_trapno;
int mc_err;
int mc_eip;
int mc_cs;
int mc_eflags;
int mc_esp; /* machine state */
int mc_ss;
int mc_fpregs[28]; /* env87 + fpacc87 + u_long */
int __spare__[17];
};
struct ucontext {
/*
* Keep the order of the first two fields. Also,
* keep them the first two fields in the structure.
* This way we can have a union with struct
* sigcontext and ucontext_t. This allows us to
* support them both at the same time.
* note: the union is not defined, though.
*/
sigset_t uc_sigmask;
mcontext_t uc_mcontext;
struct __ucontext *uc_link;
stack_t uc_stack;
int __spare__[8];
};

43
COPYRIGHT Normal file
View File

@ -0,0 +1,43 @@
This software was developed as part of a project at MIT.
Copyright (c) 2005-2007 Russ Cox,
Massachusetts Institute of Technology
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
===
Contains parts of an earlier library that has:
/*
* The authors of this software are Rob Pike, Sape Mullender, and Russ Cox
* 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.
*/

57
Makefile Normal file
View File

@ -0,0 +1,57 @@
LIB=libtask.a
TCPLIBS=
ASM=asm.o
OFILES=\
$(ASM)\
channel.o\
context.o\
fd.o\
net.o\
print.o\
qlock.o\
rendez.o\
task.o\
all: $(LIB) primes tcpproxy testdelay
$(OFILES): taskimpl.h task.h 386-ucontext.h power-ucontext.h
AS=gcc -c
CC=gcc
CFLAGS=-Wall -c -I. -ggdb
%.o: %.S
$(AS) $*.S
%.o: %.c
$(CC) $(CFLAGS) $*.c
$(LIB): $(OFILES)
ar rvc $(LIB) $(OFILES)
primes: primes.o $(LIB)
$(CC) -o primes primes.o $(LIB)
tcpproxy: tcpproxy.o $(LIB)
$(CC) -o tcpproxy tcpproxy.o $(LIB) $(TCPLIBS)
httpload: httpload.o $(LIB)
$(CC) -o httpload httpload.o $(LIB)
testdelay: testdelay.o $(LIB)
$(CC) -o testdelay testdelay.o $(LIB)
testdelay1: testdelay1.o $(LIB)
$(CC) -o testdelay1 testdelay1.o $(LIB)
clean:
rm -f *.o primes tcpproxy testdelay testdelay1 httpload $(LIB)
tgz:
tar czvf libtask.tgz COPYRIGHT README *.[chS] Makefile makesun
install: $(LIB)
cp $(LIB) /usr/local/lib
cp task.h /usr/local/include

246
README Normal file
View File

@ -0,0 +1,246 @@
Libtask is a simple coroutine library. It runs on Linux (ARM and x86),
FreeBSD (x86), OS X (PowerPC and x86), and SunOS Solaris (Sparc), and is
easy to port to other systems.
Libtask gives the programmer the illusion of threads, but
the operating system sees only a single kernel thread.
For clarity, we refer to the coroutines as "tasks," not threads.
Scheduling is cooperative. Only one task runs at a time,
and it cannot be rescheduled without explicitly giving up
the CPU. Most of the functions provided in task.h do have
the possibility of going to sleep. Programs using the task
functions should #include <task.h>.
--- Basic task manipulation
int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize);
Create a new task running f(arg) on a stack of size stacksize.
void tasksystem(void);
Mark the current task as a "system" task. These are ignored
for the purposes of deciding the program is done running
(see taskexit next).
void taskexit(int status);
Exit the current task. If this is the last non-system task,
exit the entire program using the given exit status.
void taskexitall(int status);
Exit the entire program, using the given exit status.
void taskmain(int argc, char *argv[]);
Write this function instead of main. Libtask provides its own main.
int taskyield(void);
Explicitly give up the CPU. The current task will be scheduled
again once all the other currently-ready tasks have a chance
to run. Returns the number of other tasks that ran while the
current task was waiting. (Zero means there are no other tasks
trying to run.)
int taskdelay(unsigned int ms)
Explicitly give up the CPU for at least ms milliseconds.
Other tasks continue to run during this time.
void** taskdata(void);
Return a pointer to a single per-task void* pointer.
You can use this as a per-task storage place.
void needstack(int n);
Tell the task library that you need at least n bytes left
on the stack. If you don't have it, the task library will call abort.
(It's hard to figure out how big stacks should be. I usually make
them really big (say 32768) and then don't worry about it.)
void taskname(char*, ...);
Takes an argument list like printf. Sets the current task's name.
char* taskgetname(void);
Returns the current task's name. Is the actual buffer; do not free.
void taskstate(char*, ...);
char* taskgetstate(void);
Like taskname and taskgetname but for the task state.
When you send a tasked program a SIGQUIT (or SIGINFO, on BSD)
it will print a list of all its tasks and their names and states.
This is useful for debugging why your program isn't doing anything!
unsigned int taskid(void);
Return the unique task id for the current task.
--- Non-blocking I/O
There is a small amount of runtime support for non-blocking I/O
on file descriptors.
int fdnoblock(int fd);
Sets I/O on the given fd to be non-blocking. Should be
called before any of the other fd routines.
int fdread(int, void*, int);
Like regular read(), but puts task to sleep while waiting for
data instead of blocking the whole program.
int fdwrite(int, void*, int);
Like regular write(), but puts task to sleep while waiting to
write data instead of blocking the whole program.
void fdwait(int fd, int rw);
Low-level call sitting underneath fdread and fdwrite.
Puts task to sleep while waiting for I/O to be possible on fd.
Rw specifies type of I/O: 'r' means read, 'w' means write,
anything else means just exceptional conditions (hang up, etc.)
The 'r' and 'w' also wake up for exceptional conditions.
--- Network I/O
These are convenient packaging of the ugly Unix socket routines.
They can all put the current task to sleep during the call.
int netannounce(int proto, char *address, int port)
Start a network listener running on address and port of protocol.
Proto is either TCP or UDP. Port is a port number. Address is a
string version of a host name or IP address. If address is null,
then announce binds to the given port on all available interfaces.
Returns a fd to use with netaccept.
Examples: netannounce(TCP, "localhost", 80) or
netannounce(TCP, "127.0.0.1", 80) or netannounce(TCP, 0, 80).
int netaccept(int fd, char *server, int *port)
Get the next connection that comes in to the listener fd.
Returns a fd to use to talk to the guy who just connected.
If server is not null, it must point at a buffer of at least
16 bytes that is filled in with the remote IP address.
If port is not null, it is filled in with the report port.
Example:
char server[16];
int port;
if(netaccept(fd, server, &port) >= 0)
printf("connect from %s:%d", server, port);
int netdial(int proto, char *name, int port)
Create a new (outgoing) connection to a particular host.
Name can be an ip address or a domain name. If it's a domain name,
the entire program will block while the name is resolved
(the DNS library does not provide a nice non-blocking interface).
Example: netdial(TCP, "www.google.com", 80)
or netdial(TCP, "18.26.4.9", 80)
--- Time
unsigned int taskdelay(unsigned int ms)
Put the current task to sleep for approximately ms milliseconds.
Return the actual amount of time slept, in milliseconds.
--- Example programs
In this directory, tcpproxy.c is a simple TCP proxy that illustrates
most of the above. You can run
tcpproxy 1234 www.google.com 80
and then you should be able to visit http://localhost:1234/ and see Google.
Other examples are:
primes.c - simple prime sieve
httpload.c - simple HTTP load generator
testdelay.c - test taskdelay()
--- Building
To build, run make. You can run make install to copy task.h and
libtask.a to the appropriate places in /usr/local. Then you
should be able to just link with -ltask in your programs
that use it.
On SunOS Solaris machines, run makesun instead of just make.
--- Contact Info
Please email me with questions or problems.
Russ Cox
rsc@swtch.com
--- Stuff you probably won't use at first ---
--- but might want to know about eventually ---
void tasksleep(Rendez*);
int taskwakeup(Rendez*);
int taskwakeupall(Rendez*);
A Rendez is a condition variable. You can declare a new one by
just allocating memory for it (or putting it in another structure)
and then zeroing the memory. Tasksleep(r) 'sleeps on r', giving
up the CPU. Multiple tasks can sleep on a single Rendez.
When another task comes along and calls taskwakeup(r),
the first task sleeping on r (if any) will be woken up.
Taskwakeupall(r) wakes up all the tasks sleeping on r.
They both return the actual number of tasks awakened.
void qlock(QLock*);
int canqlock(QLock*);
void qunlock(QLock*);
You probably won't need locks because of the cooperative
scheduling, but if you do, here are some. You can make a new
QLock by just declaring it and zeroing the memory.
Calling qlock will give up the CPU if the lock is held by someone else.
Calling qunlock will not give up the CPU.
Calling canqlock tries to lock the lock, but will not give up the CPU.
It returns 1 if the lock was acquired, 0 if it cannot be at this time.
void rlock(RWLock*);
int canrlock(RWLock*);
void runlock(RWLock*);
void wlock(RWLock*);
int canwlock(RWLock*);
void wunlock(RWLock*);
RWLocks are reader-writer locks. Any number of readers
can lock them at once, but only one writer at a time.
If a writer is holding it, there can't be any readers.
Channel *chancreate(int, int);
etc.
Channels are buffered communication pipes you can
use to send messages between tasks. Some people like
doing most of the inter-task communication using channels.
For details on channels see the description of channels in
http://swtch.com/usr/local/plan9/man/man3/thread.html and
http://swtch.com/~rsc/thread/
and also the example program primes.c, which implements
a concurrent prime sieve.

206
asm.S Normal file
View File

@ -0,0 +1,206 @@
/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */
#if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5
#define NEEDX86CONTEXT 1
#define SET setmcontext
#define GET getmcontext
#endif
#if defined(__OpenBSD__) && defined(__i386__)
#define NEEDX86CONTEXT 1
#define SET setmcontext
#define GET getmcontext
#endif
#if defined(__APPLE__) && defined(__i386__)
#define NEEDX86CONTEXT 1
#define SET _setmcontext
#define GET _getmcontext
#endif
#if defined(__APPLE__) && !defined(__i386__)
#define NEEDPOWERCONTEXT 1
#define SET __setmcontext
#define GET __getmcontext
#endif
#if defined(__linux__) && defined(__arm__)
#define NEEDARMCONTEXT 1
#define SET setmcontext
#define GET getmcontext
#endif
#ifdef NEEDX86CONTEXT
.globl SET
SET:
movl 4(%esp), %eax
movl 8(%eax), %fs
movl 12(%eax), %es
movl 16(%eax), %ds
movl 76(%eax), %ss
movl 20(%eax), %edi
movl 24(%eax), %esi
movl 28(%eax), %ebp
movl 36(%eax), %ebx
movl 40(%eax), %edx
movl 44(%eax), %ecx
movl 72(%eax), %esp
pushl 60(%eax) /* new %eip */
movl 48(%eax), %eax
ret
.globl GET
GET:
movl 4(%esp), %eax
movl %fs, 8(%eax)
movl %es, 12(%eax)
movl %ds, 16(%eax)
movl %ss, 76(%eax)
movl %edi, 20(%eax)
movl %esi, 24(%eax)
movl %ebp, 28(%eax)
movl %ebx, 36(%eax)
movl %edx, 40(%eax)
movl %ecx, 44(%eax)
movl $1, 48(%eax) /* %eax */
movl (%esp), %ecx /* %eip */
movl %ecx, 60(%eax)
leal 4(%esp), %ecx /* %esp */
movl %ecx, 72(%eax)
movl 44(%eax), %ecx /* restore %ecx */
movl $0, %eax
ret
#endif
#ifdef NEEDPOWERCONTEXT
/* get FPR and VR use flags with sc 0x7FF3 */
/* get vsave with mfspr reg, 256 */
.text
.align 2
.globl GET
GET: /* xxx: instruction scheduling */
mflr r0
mfcr r5
mfctr r6
mfxer r7
stw r0, 0*4(r3)
stw r5, 1*4(r3)
stw r6, 2*4(r3)
stw r7, 3*4(r3)
stw r1, 4*4(r3)
stw r2, 5*4(r3)
li r5, 1 /* return value for setmcontext */
stw r5, 6*4(r3)
stw r13, (0+7)*4(r3) /* callee-save GPRs */
stw r14, (1+7)*4(r3) /* xxx: block move */
stw r15, (2+7)*4(r3)
stw r16, (3+7)*4(r3)
stw r17, (4+7)*4(r3)
stw r18, (5+7)*4(r3)
stw r19, (6+7)*4(r3)
stw r20, (7+7)*4(r3)
stw r21, (8+7)*4(r3)
stw r22, (9+7)*4(r3)
stw r23, (10+7)*4(r3)
stw r24, (11+7)*4(r3)
stw r25, (12+7)*4(r3)
stw r26, (13+7)*4(r3)
stw r27, (14+7)*4(r3)
stw r28, (15+7)*4(r3)
stw r29, (16+7)*4(r3)
stw r30, (17+7)*4(r3)
stw r31, (18+7)*4(r3)
li r3, 0 /* return */
blr
.globl SET
SET:
lwz r13, (0+7)*4(r3) /* callee-save GPRs */
lwz r14, (1+7)*4(r3) /* xxx: block move */
lwz r15, (2+7)*4(r3)
lwz r16, (3+7)*4(r3)
lwz r17, (4+7)*4(r3)
lwz r18, (5+7)*4(r3)
lwz r19, (6+7)*4(r3)
lwz r20, (7+7)*4(r3)
lwz r21, (8+7)*4(r3)
lwz r22, (9+7)*4(r3)
lwz r23, (10+7)*4(r3)
lwz r24, (11+7)*4(r3)
lwz r25, (12+7)*4(r3)
lwz r26, (13+7)*4(r3)
lwz r27, (14+7)*4(r3)
lwz r28, (15+7)*4(r3)
lwz r29, (16+7)*4(r3)
lwz r30, (17+7)*4(r3)
lwz r31, (18+7)*4(r3)
lwz r1, 4*4(r3)
lwz r2, 5*4(r3)
lwz r0, 0*4(r3)
mtlr r0
lwz r0, 1*4(r3)
mtcr r0 /* mtcrf 0xFF, r0 */
lwz r0, 2*4(r3)
mtctr r0
lwz r0, 3*4(r3)
mtxer r0
lwz r3, 6*4(r3)
blr
#endif
#ifdef NEEDARMCONTEXT
.globl GET
GET:
str r1, [r0,#4]
str r2, [r0,#8]
str r3, [r0,#12]
str r4, [r0,#16]
str r5, [r0,#20]
str r6, [r0,#24]
str r7, [r0,#28]
str r8, [r0,#32]
str r9, [r0,#36]
str r10, [r0,#40]
str r11, [r0,#44]
str r12, [r0,#48]
str r13, [r0,#52]
str r14, [r0,#56]
/* store 1 as r0-to-restore */
mov r1, #1
str r1, [r0]
/* return 0 */
mov r0, #0
mov pc, lr
.globl SET
SET:
ldr r1, [r0,#4]
ldr r2, [r0,#8]
ldr r3, [r0,#12]
ldr r4, [r0,#16]
ldr r5, [r0,#20]
ldr r6, [r0,#24]
ldr r7, [r0,#28]
ldr r8, [r0,#32]
ldr r9, [r0,#36]
ldr r10, [r0,#40]
ldr r11, [r0,#44]
ldr r12, [r0,#48]
ldr r13, [r0,#52]
ldr r14, [r0,#56]
ldr r0, [r0]
mov pc, lr
#endif

385
channel.c Normal file
View File

@ -0,0 +1,385 @@
/* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */
#include "taskimpl.h"
Channel*
chancreate(int elemsize, int bufsize)
{
Channel *c;
c = malloc(sizeof *c+bufsize*elemsize);
if(c == nil){
fprint(2, "chancreate malloc: %r");
exit(1);
}
memset(c, 0, sizeof *c);
c->elemsize = elemsize;
c->bufsize = bufsize;
c->nbuf = 0;
c->buf = (uchar*)(c+1);
return c;
}
/* bug - work out races */
void
chanfree(Channel *c)
{
if(c == nil)
return;
free(c->name);
free(c->arecv.a);
free(c->asend.a);
free(c);
}
static void
addarray(Altarray *a, Alt *alt)
{
if(a->n == a->m){
a->m += 16;
a->a = realloc(a->a, a->m*sizeof a->a[0]);
}
a->a[a->n++] = alt;
}
static void
delarray(Altarray *a, int i)
{
--a->n;
a->a[i] = a->a[a->n];
}
/*
* doesn't really work for things other than CHANSND and CHANRCV
* but is only used as arg to chanarray, which can handle it
*/
#define otherop(op) (CHANSND+CHANRCV-(op))
static Altarray*
chanarray(Channel *c, uint op)
{
switch(op){
default:
return nil;
case CHANSND:
return &c->asend;
case CHANRCV:
return &c->arecv;
}
}
static int
altcanexec(Alt *a)
{
Altarray *ar;
Channel *c;
if(a->op == CHANNOP)
return 0;
c = a->c;
if(c->bufsize == 0){
ar = chanarray(c, otherop(a->op));
return ar && ar->n;
}else{
switch(a->op){
default:
return 0;
case CHANSND:
return c->nbuf < c->bufsize;
case CHANRCV:
return c->nbuf > 0;
}
}
}
static void
altqueue(Alt *a)
{
Altarray *ar;
ar = chanarray(a->c, a->op);
addarray(ar, a);
}
static void
altdequeue(Alt *a)
{
int i;
Altarray *ar;
ar = chanarray(a->c, a->op);
if(ar == nil){
fprint(2, "bad use of altdequeue op=%d\n", a->op);
abort();
}
for(i=0; i<ar->n; i++)
if(ar->a[i] == a){
delarray(ar, i);
return;
}
fprint(2, "cannot find self in altdq\n");
abort();
}
static void
altalldequeue(Alt *a)
{
int i;
for(i=0; a[i].op!=CHANEND && a[i].op!=CHANNOBLK; i++)
if(a[i].op != CHANNOP)
altdequeue(&a[i]);
}
static void
amove(void *dst, void *src, uint n)
{
if(dst){
if(src == nil)
memset(dst, 0, n);
else
memmove(dst, src, n);
}
}
/*
* Actually move the data around. There are up to three
* players: the sender, the receiver, and the channel itself.
* If the channel is unbuffered or the buffer is empty,
* data goes from sender to receiver. If the channel is full,
* the receiver removes some from the channel and the sender
* gets to put some in.
*/
static void
altcopy(Alt *s, Alt *r)
{
Alt *t;
Channel *c;
uchar *cp;
/*
* Work out who is sender and who is receiver
*/
if(s == nil && r == nil)
return;
assert(s != nil);
c = s->c;
if(s->op == CHANRCV){
t = s;
s = r;
r = t;
}
assert(s==nil || s->op == CHANSND);
assert(r==nil || r->op == CHANRCV);
/*
* Channel is empty (or unbuffered) - copy directly.
*/
if(s && r && c->nbuf == 0){
amove(r->v, s->v, c->elemsize);
return;
}
/*
* Otherwise it's always okay to receive and then send.
*/
if(r){
cp = c->buf + c->off*c->elemsize;
amove(r->v, cp, c->elemsize);
--c->nbuf;
if(++c->off == c->bufsize)
c->off = 0;
}
if(s){
cp = c->buf + (c->off+c->nbuf)%c->bufsize*c->elemsize;
amove(cp, s->v, c->elemsize);
++c->nbuf;
}
}
static void
altexec(Alt *a)
{
int i;
Altarray *ar;
Alt *other;
Channel *c;
c = a->c;
ar = chanarray(c, otherop(a->op));
if(ar && ar->n){
i = rand()%ar->n;
other = ar->a[i];
altcopy(a, other);
altalldequeue(other->xalt);
other->xalt[0].xalt = other;
taskready(other->task);
}else
altcopy(a, nil);
}
#define dbgalt 0
int
chanalt(Alt *a)
{
int i, j, ncan, n, canblock;
Channel *c;
Task *t;
needstack(512);
for(i=0; a[i].op != CHANEND && a[i].op != CHANNOBLK; i++)
;
n = i;
canblock = a[i].op == CHANEND;
t = taskrunning;
for(i=0; i<n; i++){
a[i].task = t;
a[i].xalt = a;
}
if(dbgalt) print("alt ");
ncan = 0;
for(i=0; i<n; i++){
c = a[i].c;
if(dbgalt) print(" %c:", "esrnb"[a[i].op]);
if(dbgalt) { if(c->name) print("%s", c->name); else print("%p", c); }
if(altcanexec(&a[i])){
if(dbgalt) print("*");
ncan++;
}
}
if(ncan){
j = rand()%ncan;
for(i=0; i<n; i++){
if(altcanexec(&a[i])){
if(j-- == 0){
if(dbgalt){
c = a[i].c;
print(" => %c:", "esrnb"[a[i].op]);
if(c->name) print("%s", c->name); else print("%p", c);
print("\n");
}
altexec(&a[i]);
return i;
}
}
}
}
if(dbgalt)print("\n");
if(!canblock)
return -1;
for(i=0; i<n; i++){
if(a[i].op != CHANNOP)
altqueue(&a[i]);
}
taskswitch();
/*
* the guy who ran the op took care of dequeueing us
* and then set a[0].alt to the one that was executed.
*/
return a[0].xalt - a;
}
static int
_chanop(Channel *c, int op, void *p, int canblock)
{
Alt a[2];
a[0].c = c;
a[0].op = op;
a[0].v = p;
a[1].op = canblock ? CHANEND : CHANNOBLK;
if(chanalt(a) < 0)
return -1;
return 1;
}
int
chansend(Channel *c, void *v)
{
return _chanop(c, CHANSND, v, 1);
}
int
channbsend(Channel *c, void *v)
{
return _chanop(c, CHANSND, v, 0);
}
int
chanrecv(Channel *c, void *v)
{
return _chanop(c, CHANRCV, v, 1);
}
int
channbrecv(Channel *c, void *v)
{
return _chanop(c, CHANRCV, v, 0);
}
int
chansendp(Channel *c, void *v)
{
return _chanop(c, CHANSND, (void*)&v, 1);
}
void*
chanrecvp(Channel *c)
{
void *v;
_chanop(c, CHANRCV, (void*)&v, 1);
return v;
}
int
channbsendp(Channel *c, void *v)
{
return _chanop(c, CHANSND, (void*)&v, 0);
}
void*
channbrecvp(Channel *c)
{
void *v;
_chanop(c, CHANRCV, (void*)&v, 0);
return v;
}
int
chansendul(Channel *c, ulong val)
{
return _chanop(c, CHANSND, &val, 1);
}
ulong
chanrecvul(Channel *c)
{
ulong val;
_chanop(c, CHANRCV, &val, 1);
return val;
}
int
channbsendul(Channel *c, ulong val)
{
return _chanop(c, CHANSND, &val, 0);
}
ulong
channbrecvul(Channel *c)
{
ulong val;
_chanop(c, CHANRCV, &val, 0);
return val;
}

90
context.c Normal file
View File

@ -0,0 +1,90 @@
/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */
#include "taskimpl.h"
#if defined(__APPLE__) && defined(__i386__)
#define NEEDX86MAKECONTEXT
#define NEEDSWAPCONTEXT
#endif
#if defined(__APPLE__) && !defined(__i386__)
#define NEEDPOWERMAKECONTEXT
#define NEEDSWAPCONTEXT
#endif
#if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5
#define NEEDX86MAKECONTEXT
#define NEEDSWAPCONTEXT
#endif
#if defined(__OpenBSD__) && defined(__i386__)
#define NEEDX86MAKECONTEXT
#define NEEDSWAPCONTEXT
#endif
#if defined(__linux__) && defined(__arm__)
#define NEEDSWAPCONTEXT
#define NEEDARMMAKECONTEXT
#endif
#ifdef NEEDPOWERMAKECONTEXT
void
makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...)
{
ulong *sp, *tos;
va_list arg;
tos = (ulong*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(ulong);
sp = tos - 16;
ucp->mc.pc = (long)func;
ucp->mc.sp = (long)sp;
va_start(arg, argc);
ucp->mc.r3 = va_arg(arg, long);
va_end(arg);
}
#endif
#ifdef NEEDX86MAKECONTEXT
void
makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...)
{
int *sp;
sp = (int*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/4;
sp -= argc;
sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */
memmove(sp, &argc+1, argc*sizeof(int));
*--sp = 0; /* return address */
ucp->uc_mcontext.mc_eip = (long)func;
ucp->uc_mcontext.mc_esp = (int)sp;
}
#endif
#ifdef NEEDARMMAKECONTEXT
void
makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...)
{
int i, *sp;
va_list arg;
sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4;
va_start(arg, argc);
for(i=0; i<4 && i<argc; i++)
uc->uc_mcontext.gregs[i] = va_arg(arg, uint);
va_end(arg);
uc->uc_mcontext.gregs[13] = (uint)sp;
uc->uc_mcontext.gregs[14] = (uint)fn;
}
#endif
#ifdef NEEDSWAPCONTEXT
int
swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
{
if(getcontext(oucp) == 0)
setcontext(ucp);
return 0;
}
#endif

202
fd.c Normal file
View File

@ -0,0 +1,202 @@
#include "taskimpl.h"
#include <sys/poll.h>
#include <fcntl.h>
enum
{
MAXFD = 1024
};
static struct pollfd pollfd[MAXFD];
static Task *polltask[MAXFD];
static int npollfd;
static int startedfdtask;
static Tasklist sleeping;
static int sleepingcounted;
static uvlong nsec(void);
void
fdtask(void *v)
{
int i, ms;
Task *t;
uvlong now;
tasksystem();
taskname("fdtask");
for(;;){
/* let everyone else run */
while(taskyield() > 0)
;
/* we're the only one runnable - poll for i/o */
errno = 0;
taskstate("poll");
if((t=sleeping.head) == nil)
ms = -1;
else{
/* sleep at most 5s */
now = nsec();
if(now >= t->alarmtime)
ms = 0;
else if(now+5*1000*1000*1000LL >= t->alarmtime)
ms = (t->alarmtime - now)/1000000;
else
ms = 5000;
}
if(poll(pollfd, npollfd, ms) < 0){
if(errno == EINTR)
continue;
fprint(2, "poll: %s\n", strerror(errno));
taskexitall(0);
}
/* wake up the guys who deserve it */
for(i=0; i<npollfd; i++){
while(i < npollfd && pollfd[i].revents){
taskready(polltask[i]);
--npollfd;
pollfd[i] = pollfd[npollfd];
polltask[i] = polltask[npollfd];
}
}
now = nsec();
while((t=sleeping.head) && now >= t->alarmtime){
deltask(&sleeping, t);
if(!t->system && --sleepingcounted == 0)
taskcount--;
taskready(t);
}
}
}
uint
taskdelay(uint ms)
{
uvlong when, now;
Task *t;
if(!startedfdtask){
startedfdtask = 1;
taskcreate(fdtask, 0, 32768);
}
now = nsec();
when = now+(uvlong)ms*1000000;
for(t=sleeping.head; t!=nil && t->alarmtime < when; t=t->next)
;
if(t){
taskrunning->prev = t->prev;
taskrunning->next = t;
}else{
taskrunning->prev = sleeping.tail;
taskrunning->next = nil;
}
t = taskrunning;
t->alarmtime = when;
if(t->prev)
t->prev->next = t;
else
sleeping.head = t;
if(t->next)
t->next->prev = t;
else
sleeping.tail = t;
if(!t->system && sleepingcounted++ == 0)
taskcount++;
taskswitch();
return (nsec() - now)/1000000;
}
void
fdwait(int fd, int rw)
{
int bits;
if(!startedfdtask){
startedfdtask = 1;
taskcreate(fdtask, 0, 32768);
}
if(npollfd >= MAXFD){
fprint(2, "too many poll file descriptors\n");
abort();
}
taskstate("fdwait for %s", rw=='r' ? "read" : rw=='w' ? "write" : "error");
bits = 0;
switch(rw){
case 'r':
bits |= POLLIN;
break;
case 'w':
bits |= POLLOUT;
break;
}
polltask[npollfd] = taskrunning;
pollfd[npollfd].fd = fd;
pollfd[npollfd].events = bits;
pollfd[npollfd].revents = 0;
npollfd++;
taskswitch();
}
/* Like fdread but always calls fdwait before reading. */
int
fdread1(int fd, void *buf, int n)
{
int m;
do
fdwait(fd, 'r');
while((m = read(fd, buf, n)) < 0 && errno == EAGAIN);
return m;
}
int
fdread(int fd, void *buf, int n)
{
int m;
while((m=read(fd, buf, n)) < 0 && errno == EAGAIN)
fdwait(fd, 'r');
return m;
}
int
fdwrite(int fd, void *buf, int n)
{
int m, tot;
for(tot=0; tot<n; tot+=m){
while((m=write(fd, (char*)buf+tot, n-tot)) < 0 && errno == EAGAIN)
fdwait(fd, 'w');
if(m < 0)
return m;
if(m == 0)
break;
}
return tot;
}
int
fdnoblock(int fd)
{
return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK);
}
static uvlong
nsec(void)
{
struct timeval tv;
if(gettimeofday(&tv, 0) < 0)
return -1;
return (uvlong)tv.tv_sec*1000*1000*1000 + tv.tv_usec*1000;
}

58
httpload.c Normal file
View File

@ -0,0 +1,58 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <task.h>
#include <stdlib.h>
enum
{
STACK = 32768
};
char *server;
char *url;
void fetchtask(void*);
void
taskmain(int argc, char **argv)
{
int i, n;
if(argc != 4){
fprintf(stderr, "usage: httpload n server url\n");
taskexitall(1);
}
n = atoi(argv[1]);
server = argv[2];
url = argv[3];
for(i=0; i<n; i++){
taskcreate(fetchtask, 0, STACK);
while(taskyield() > 1)
;
sleep(1);
}
}
void
fetchtask(void *v)
{
int fd, n;
char buf[512];
fprintf(stderr, "starting...\n");
for(;;){
if((fd = netdial(TCP, server, 80)) < 0){
fprintf(stderr, "dial %s: %s (%s)\n", server, strerror(errno), taskgetstate());
continue;
}
snprintf(buf, sizeof buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", url, server);
fdwrite(fd, buf, strlen(buf));
while((n = fdread(fd, buf, sizeof buf)) > 0)
;
close(fd);
write(1, ".", 1);
}
}

22
makesun Executable file
View File

@ -0,0 +1,22 @@
#!/bin/sh
case "x$CC" in
x|xcc)
CC=cc
CFLAGS="-mt -g -O -c -xCC -D__sun__ -I."
;;
xgcc)
CC=gcc
CFLAGS="-Wall -c -I."
;;
*)
echo 'unknown $CC'
exit 1
esac
u=`uname`
v=`uname -r`
s=`echo $u$v | tr '. ' '__'`
CFLAGS="$CFLAGS -D__${s}__"
make "CC=$CC" "CFLAGS=$CFLAGS" "ASM=" "TCPLIBS=-lsocket -lnsl"

203
net.c Normal file
View File

@ -0,0 +1,203 @@
#include "taskimpl.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/poll.h>
int
netannounce(int istcp, char *server, int port)
{
int fd, n, proto;
struct sockaddr_in sa;
socklen_t sn;
uint32_t ip;
taskstate("netannounce");
proto = istcp ? SOCK_STREAM : SOCK_DGRAM;
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
if(server != nil && strcmp(server, "*") != 0){
if(netlookup(server, &ip) < 0){
taskstate("netlookup failed");
return -1;
}
memmove(&sa.sin_addr, &ip, 4);
}
sa.sin_port = htons(port);
if((fd = socket(AF_INET, proto, 0)) < 0){
taskstate("socket failed");
return -1;
}
/* set reuse flag for tcp */
if(istcp && getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0){
n = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
}
if(bind(fd, (struct sockaddr*)&sa, sizeof sa) < 0){
taskstate("bind failed");
close(fd);
return -1;
}
if(proto == SOCK_STREAM)
listen(fd, 16);
fdnoblock(fd);
taskstate("netannounce succeeded");
return fd;
}
int
netaccept(int fd, char *server, int *port)
{
int cfd, one;
struct sockaddr_in sa;
uchar *ip;
socklen_t len;
fdwait(fd, 'r');
taskstate("netaccept");
len = sizeof sa;
if((cfd = accept(fd, (void*)&sa, &len)) < 0){
taskstate("accept failed");
return -1;
}
if(server){
ip = (uchar*)&sa.sin_addr;
snprint(server, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
}
if(port)
*port = ntohs(sa.sin_port);
fdnoblock(cfd);
one = 1;
setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
taskstate("netaccept succeeded");
return cfd;
}
#define CLASS(p) ((*(unsigned char*)(p))>>6)
static int
parseip(char *name, uint32_t *ip)
{
unsigned char addr[4];
char *p;
int i, x;
p = name;
for(i=0; i<4 && *p; i++){
x = strtoul(p, &p, 0);
if(x < 0 || x >= 256)
return -1;
if(*p != '.' && *p != 0)
return -1;
if(*p == '.')
p++;
addr[i] = x;
}
switch(CLASS(addr)){
case 0:
case 1:
if(i == 3){
addr[3] = addr[2];
addr[2] = addr[1];
addr[1] = 0;
}else if(i == 2){
addr[3] = addr[1];
addr[2] = 0;
addr[1] = 0;
}else if(i != 4)
return -1;
break;
case 2:
if(i == 3){
addr[3] = addr[2];
addr[2] = 0;
}else if(i != 4)
return -1;
break;
}
*ip = *(uint32_t*)addr;
return 0;
}
int
netlookup(char *name, uint32_t *ip)
{
struct hostent *he;
if(parseip(name, ip) >= 0)
return 0;
/* BUG - Name resolution blocks. Need a non-blocking DNS. */
taskstate("netlookup");
if((he = gethostbyname(name)) != 0){
*ip = *(uint32_t*)he->h_addr;
taskstate("netlookup succeeded");
return 0;
}
taskstate("netlookup failed");
return -1;
}
int
netdial(int istcp, char *server, int port)
{
int proto, fd, n;
uint32_t ip;
struct sockaddr_in sa;
socklen_t sn;
if(netlookup(server, &ip) < 0)
return -1;
taskstate("netdial");
proto = istcp ? SOCK_STREAM : SOCK_DGRAM;
if((fd = socket(AF_INET, proto, 0)) < 0){
taskstate("socket failed");
return -1;
}
fdnoblock(fd);
/* for udp */
if(!istcp){
n = 1;
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof n);
}
/* start connecting */
memset(&sa, 0, sizeof sa);
memmove(&sa.sin_addr, &ip, 4);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
if(connect(fd, (struct sockaddr*)&sa, sizeof sa) < 0 && errno != EINPROGRESS){
taskstate("connect failed");
close(fd);
return -1;
}
/* wait for finish */
fdwait(fd, 'w');
sn = sizeof sa;
if(getpeername(fd, (struct sockaddr*)&sa, &sn) >= 0){
taskstate("connect succeeded");
return fd;
}
/* report error */
sn = sizeof n;
getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&n, &sn);
if(n == 0)
n = ECONNREFUSED;
close(fd);
taskstate("connect failed");
errno = n;
return -1;
}

37
power-ucontext.h Normal file
View File

@ -0,0 +1,37 @@
#define setcontext(u) _setmcontext(&(u)->mc)
#define getcontext(u) _getmcontext(&(u)->mc)
typedef struct mcontext mcontext_t;
typedef struct ucontext ucontext_t;
struct mcontext
{
ulong pc; /* lr */
ulong cr; /* mfcr */
ulong ctr; /* mfcr */
ulong xer; /* mfcr */
ulong sp; /* callee saved: r1 */
ulong toc; /* callee saved: r2 */
ulong r3; /* first arg to function, return register: r3 */
ulong gpr[19]; /* callee saved: r13-r31 */
/*
// XXX: currently do not save vector registers or floating-point state
// ulong pad;
// uvlong fpr[18]; / * callee saved: f14-f31 * /
// ulong vr[4*12]; / * callee saved: v20-v31, 256-bits each * /
*/
};
struct ucontext
{
struct {
void *ss_sp;
uint ss_size;
} uc_stack;
sigset_t uc_sigmask;
mcontext_t mc;
};
void makecontext(ucontext_t*, void(*)(void), int, ...);
int swapcontext(ucontext_t*, const ucontext_t*);
int _getmcontext(mcontext_t*);
void _setmcontext(const mcontext_t*);

61
primes.c Normal file
View File

@ -0,0 +1,61 @@
/* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <task.h>
int quiet;
int goal;
int buffer;
void
primetask(void *arg)
{
Channel *c, *nc;
int p, i;
c = arg;
p = chanrecvul(c);
if(p > goal)
taskexitall(0);
if(!quiet)
printf("%d\n", p);
nc = chancreate(sizeof(unsigned long), buffer);
taskcreate(primetask, nc, 32768);
for(;;){
i = chanrecvul(c);
if(i%p)
chansendul(nc, i);
}
}
void
taskmain(int argc, char **argv)
{
int i;
Channel *c;
if(argc>1)
goal = atoi(argv[1]);
else
goal = 100;
printf("goal=%d\n", goal);
c = chancreate(sizeof(unsigned long), buffer);
taskcreate(primetask, c, 32768);
for(i=2;; i++)
chansendul(c, i);
}
void*
emalloc(unsigned long n)
{
return calloc(n ,1);
}
long
lrand(void)
{
return rand();
}

252
print.c Normal file
View File

@ -0,0 +1,252 @@
/* Copyright (c) 2004 Russ Cox. See COPYRIGHT. */
#include "taskimpl.h"
#include <stdio.h> /* for strerror! */
/*
* Stripped down print library. Plan 9 interface, new code.
*/
enum
{
FlagLong = 1<<0,
FlagLongLong = 1<<1,
FlagUnsigned = 1<<2,
};
static char*
printstr(char *dst, char *edst, char *s, int size)
{
int l, n, sign;
sign = 1;
if(size < 0){
size = -size;
sign = -1;
}
if(dst >= edst)
return dst;
l = strlen(s);
n = l;
if(n < size)
n = size;
if(n >= edst-dst)
n = (edst-dst)-1;
if(l > n)
l = n;
if(sign < 0){
memmove(dst, s, l);
if(n-l)
memset(dst+l, ' ', n-l);
}else{
if(n-l)
memset(dst, ' ', n-l);
memmove(dst+n-l, s, l);
}
return dst+n;
}
char*
vseprint(char *dst, char *edst, char *fmt, va_list arg)
{
int fl, size, sign, base;
char *p, *w;
char cbuf[2];
w = dst;
for(p=fmt; *p && w<edst-1; p++){
switch(*p){
default:
*w++ = *p;
break;
case '%':
fl = 0;
size = 0;
sign = 1;
for(p++; *p; p++){
switch(*p){
case '-':
sign = -1;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
size = size*10 + *p-'0';
break;
case 'l':
if(fl&FlagLong)
fl |= FlagLongLong;
else
fl |= FlagLong;
break;
case 'u':
fl |= FlagUnsigned;
break;
case 'd':
base = 10;
goto num;
case 'o':
base = 8;
goto num;
case 'p':
case 'x':
base = 16;
goto num;
num:
{
static char digits[] = "0123456789abcdef";
char buf[30], *p;
int neg, zero;
uvlong luv;
if(fl&FlagLongLong){
if(fl&FlagUnsigned)
luv = va_arg(arg, uvlong);
else
luv = va_arg(arg, vlong);
}else{
if(fl&FlagLong){
if(fl&FlagUnsigned)
luv = va_arg(arg, ulong);
else
luv = va_arg(arg, long);
}else{
if(fl&FlagUnsigned)
luv = va_arg(arg, uint);
else
luv = va_arg(arg, int);
}
}
p = buf+sizeof buf;
neg = 0;
zero = 0;
if(!(fl&FlagUnsigned) && (vlong)luv < 0){
neg = 1;
luv = -luv;
}
if(luv == 0)
zero = 1;
*--p = 0;
while(luv){
*--p = digits[luv%base];
luv /= base;
}
if(base == 16){
*--p = 'x';
*--p = '0';
}
if(base == 8 || zero)
*--p = '0';
w = printstr(w, edst, p, size*sign);
goto break2;
}
case 'c':
cbuf[0] = va_arg(arg, int);
cbuf[1] = 0;
w = printstr(w, edst, cbuf, size*sign);
goto break2;
case 's':
w = printstr(w, edst, va_arg(arg, char*), size*sign);
goto break2;
case 'r':
w = printstr(w, edst, strerror(errno), size*sign);
goto break2;
default:
p = "X*verb*";
goto break2;
}
}
break2:
break;
}
}
assert(w < edst);
*w = 0;
return dst;
}
char*
vsnprint(char *dst, uint n, char *fmt, va_list arg)
{
return vseprint(dst, dst+n, fmt, arg);
}
char*
snprint(char *dst, uint n, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vsnprint(dst, n, fmt, arg);
va_end(arg);
return dst;
}
char*
seprint(char *dst, char *edst, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vseprint(dst, edst, fmt, arg);
va_end(arg);
return dst;
}
int
vfprint(int fd, char *fmt, va_list arg)
{
char buf[256];
vseprint(buf, buf+sizeof buf, fmt, arg);
return write(fd, buf, strlen(buf));
}
int
vprint(char *fmt, va_list arg)
{
return vfprint(1, fmt, arg);
}
int
fprint(int fd, char *fmt, ...)
{
int n;
va_list arg;
va_start(arg, fmt);
n = vfprint(fd, fmt, arg);
va_end(arg);
return n;
}
int
print(char *fmt, ...)
{
int n;
va_list arg;
va_start(arg, fmt);
n = vprint(fmt, arg);
va_end(arg);
return n;
}
char*
strecpy(char *dst, char *edst, char *src)
{
*printstr(dst, edst, src, 0) = 0;
return dst;
}

142
qlock.c Normal file
View File

@ -0,0 +1,142 @@
#include "taskimpl.h"
/*
* locking
*/
static int
_qlock(QLock *l, int block)
{
if(l->owner == nil){
l->owner = taskrunning;
return 1;
}
if(!block)
return 0;
addtask(&l->waiting, taskrunning);
taskstate("qlock");
taskswitch();
if(l->owner != taskrunning){
fprint(2, "qlock: owner=%p self=%p oops\n", l->owner, taskrunning);
abort();
}
return 1;
}
void
qlock(QLock *l)
{
_qlock(l, 1);
}
int
canqlock(QLock *l)
{
return _qlock(l, 0);
}
void
qunlock(QLock *l)
{
Task *ready;
if(l->owner == 0){
fprint(2, "qunlock: owner=0\n");
abort();
}
if((l->owner = ready = l->waiting.head) != nil){
deltask(&l->waiting, ready);
taskready(ready);
}
}
static int
_rlock(RWLock *l, int block)
{
if(l->writer == nil && l->wwaiting.head == nil){
l->readers++;
return 1;
}
if(!block)
return 0;
addtask(&l->rwaiting, taskrunning);
taskstate("rlock");
taskswitch();
return 1;
}
void
rlock(RWLock *l)
{
_rlock(l, 1);
}
int
canrlock(RWLock *l)
{
return _rlock(l, 0);
}
static int
_wlock(RWLock *l, int block)
{
if(l->writer == nil && l->readers == 0){
l->writer = taskrunning;
return 1;
}
if(!block)
return 0;
addtask(&l->wwaiting, taskrunning);
taskstate("wlock");
taskswitch();
return 1;
}
void
wlock(RWLock *l)
{
_wlock(l, 1);
}
int
canwlock(RWLock *l)
{
return _wlock(l, 0);
}
void
runlock(RWLock *l)
{
Task *t;
if(--l->readers == 0 && (t = l->wwaiting.head) != nil){
deltask(&l->wwaiting, t);
l->writer = t;
taskready(t);
}
}
void
wunlock(RWLock *l)
{
Task *t;
if(l->writer == nil){
fprint(2, "wunlock: not locked\n");
abort();
}
l->writer = nil;
if(l->readers != 0){
fprint(2, "wunlock: readers\n");
abort();
}
while((t = l->rwaiting.head) != nil){
deltask(&l->rwaiting, t);
l->readers++;
taskready(t);
}
if(l->readers == 0 && (t = l->wwaiting.head) != nil){
deltask(&l->wwaiting, t);
l->writer = t;
taskready(t);
}
}

46
rendez.c Normal file
View File

@ -0,0 +1,46 @@
#include "taskimpl.h"
/*
* sleep and wakeup
*/
void
tasksleep(Rendez *r)
{
addtask(&r->waiting, taskrunning);
if(r->l)
qunlock(r->l);
taskstate("sleep");
taskswitch();
if(r->l)
qlock(r->l);
}
static int
_taskwakeup(Rendez *r, int all)
{
int i;
Task *t;
for(i=0;; i++){
if(i==1 && !all)
break;
if((t = r->waiting.head) == nil)
break;
deltask(&r->waiting, t);
taskready(t);
}
return i;
}
int
taskwakeup(Rendez *r)
{
return _taskwakeup(r, 0);
}
int
taskwakeupall(Rendez *r)
{
return _taskwakeup(r, 1);
}

413
task.c Normal file
View File

@ -0,0 +1,413 @@
/* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */
#include "taskimpl.h"
#include <fcntl.h>
#include <stdio.h>
int taskdebuglevel;
int taskcount;
int tasknswitch;
int taskexitval;
Task *taskrunning;
Context taskschedcontext;
Tasklist taskrunqueue;
Task **alltask;
int nalltask;
static char *argv0;
static void contextswitch(Context *from, Context *to);
static void
taskdebug(char *fmt, ...)
{
va_list arg;
char buf[128];
Task *t;
char *p;
static int fd = -1;
return;
va_start(arg, fmt);
vfprint(1, fmt, arg);
va_end(arg);
return;
if(fd < 0){
p = strrchr(argv0, '/');
if(p)
p++;
else
p = argv0;
snprint(buf, sizeof buf, "/tmp/%s.tlog", p);
if((fd = open(buf, O_CREAT|O_WRONLY, 0666)) < 0)
fd = open("/dev/null", O_WRONLY);
}
va_start(arg, fmt);
vsnprint(buf, sizeof buf, fmt, arg);
va_end(arg);
t = taskrunning;
if(t)
fprint(fd, "%d.%d: %s\n", getpid(), t->id, buf);
else
fprint(fd, "%d._: %s\n", getpid(), buf);
}
static void
taskstart(uint y, uint x)
{
Task *t;
ulong z;
z = x<<16; /* hide undefined 32-bit shift from 32-bit compilers */
z <<= 16;
z |= y;
t = (Task*)z;
//print("taskstart %p\n", t);
t->startfn(t->startarg);
//print("taskexits %p\n", t);
taskexit(0);
//print("not reacehd\n");
}
static int taskidgen;
static Task*
taskalloc(void (*fn)(void*), void *arg, uint stack)
{
Task *t;
sigset_t zero;
uint x, y;
ulong z;
/* allocate the task and stack together */
t = malloc(sizeof *t+stack);
if(t == nil){
fprint(2, "taskalloc malloc: %r\n");
abort();
}
memset(t, 0, sizeof *t);
t->stk = (uchar*)(t+1);
t->stksize = stack;
t->id = ++taskidgen;
t->startfn = fn;
t->startarg = arg;
/* do a reasonable initialization */
memset(&t->context.uc, 0, sizeof t->context.uc);
sigemptyset(&zero);
sigprocmask(SIG_BLOCK, &zero, &t->context.uc.uc_sigmask);
/* must initialize with current context */
if(getcontext(&t->context.uc) < 0){
fprint(2, "getcontext: %r\n");
abort();
}
/* call makecontext to do the real work. */
/* leave a few words open on both ends */
t->context.uc.uc_stack.ss_sp = t->stk+8;
t->context.uc.uc_stack.ss_size = t->stksize-64;
#if defined(__sun__) && !defined(__MAKECONTEXT_V2_SOURCE) /* sigh */
#warning "doing sun thing"
/* can avoid this with __MAKECONTEXT_V2_SOURCE but only on SunOS 5.9 */
t->context.uc.uc_stack.ss_sp =
(char*)t->context.uc.uc_stack.ss_sp
+t->context.uc.uc_stack.ss_size;
#endif
/*
* All this magic is because you have to pass makecontext a
* function that takes some number of word-sized variables,
* and on 64-bit machines pointers are bigger than words.
*/
z = (ulong)t;
y = z;
z >>= 16; /* hide undefined 32-bit shift from 32-bit compilers */
x = z>>16;
makecontext(&t->context.uc, (void(*)())taskstart, 2, y, x);
return t;
}
int
taskcreate(void (*fn)(void*), void *arg, uint stack)
{
int id;
Task *t;
t = taskalloc(fn, arg, stack);
taskcount++;
id = t->id;
if(nalltask%64 == 0){
alltask = realloc(alltask, (nalltask+64)*sizeof(alltask[0]));
if(alltask == nil){
fprint(2, "out of memory\n");
abort();
}
}
t->alltaskslot = nalltask;
alltask[nalltask++] = t;
taskready(t);
return id;
}
void
tasksystem(void)
{
if(!taskrunning->system){
taskrunning->system = 1;
--taskcount;
}
}
void
taskswitch(void)
{
needstack(0);
contextswitch(&taskrunning->context, &taskschedcontext);
}
void
taskready(Task *t)
{
t->ready = 1;
addtask(&taskrunqueue, t);
}
int
taskyield(void)
{
int n;
n = tasknswitch;
taskready(taskrunning);
taskstate("yield");
taskswitch();
return tasknswitch - n - 1;
}
int
anyready(void)
{
return taskrunqueue.head != nil;
}
void
taskexitall(int val)
{
exit(val);
}
void
taskexit(int val)
{
taskexitval = val;
taskrunning->exiting = 1;
taskswitch();
}
static void
contextswitch(Context *from, Context *to)
{
if(swapcontext(&from->uc, &to->uc) < 0){
fprint(2, "swapcontext failed: %r\n");
assert(0);
}
}
static void
taskscheduler(void)
{
int i;
Task *t;
taskdebug("scheduler enter");
for(;;){
if(taskcount == 0)
exit(taskexitval);
t = taskrunqueue.head;
if(t == nil){
fprint(2, "no runnable tasks! %d tasks stalled\n", taskcount);
exit(1);
}
deltask(&taskrunqueue, t);
t->ready = 0;
taskrunning = t;
tasknswitch++;
taskdebug("run %d (%s)", t->id, t->name);
contextswitch(&taskschedcontext, &t->context);
//print("back in scheduler\n");
taskrunning = nil;
if(t->exiting){
if(!t->system)
taskcount--;
i = t->alltaskslot;
alltask[i] = alltask[--nalltask];
alltask[i]->alltaskslot = i;
free(t);
}
}
}
void**
taskdata(void)
{
return &taskrunning->udata;
}
/*
* debugging
*/
void
taskname(char *fmt, ...)
{
va_list arg;
Task *t;
t = taskrunning;
va_start(arg, fmt);
vsnprint(t->name, sizeof t->name, fmt, arg);
va_end(arg);
}
char*
taskgetname(void)
{
return taskrunning->name;
}
void
taskstate(char *fmt, ...)
{
va_list arg;
Task *t;
t = taskrunning;
va_start(arg, fmt);
vsnprint(t->state, sizeof t->name, fmt, arg);
va_end(arg);
}
char*
taskgetstate(void)
{
return taskrunning->state;
}
void
needstack(int n)
{
Task *t;
t = taskrunning;
if((char*)&t <= (char*)t->stk
|| (char*)&t - (char*)t->stk < 256+n){
fprint(2, "task stack overflow: &t=%p tstk=%p n=%d\n", &t, t->stk, 256+n);
abort();
}
}
static void
taskinfo(int s)
{
int i;
Task *t;
char *extra;
fprint(2, "task list:\n");
for(i=0; i<nalltask; i++){
t = alltask[i];
if(t == taskrunning)
extra = " (running)";
else if(t->ready)
extra = " (ready)";
else
extra = "";
fprint(2, "%6d%c %-20s %s%s\n",
t->id, t->system ? 's' : ' ',
t->name, t->state, extra);
}
}
/*
* startup
*/
static int taskargc;
static char **taskargv;
int mainstacksize;
static void
taskmainstart(void *v)
{
taskname("taskmain");
taskmain(taskargc, taskargv);
}
int
main(int argc, char **argv)
{
struct sigaction sa, osa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = taskinfo;
sa.sa_flags = SA_RESTART;
sigaction(SIGQUIT, &sa, &osa);
#ifdef SIGINFO
sigaction(SIGINFO, &sa, &osa);
#endif
argv0 = argv[0];
taskargc = argc;
taskargv = argv;
if(mainstacksize == 0)
mainstacksize = 256*1024;
taskcreate(taskmainstart, nil, mainstacksize);
taskscheduler();
fprint(2, "taskscheduler returned in main!\n");
abort();
return 0;
}
/*
* hooray for linked lists
*/
void
addtask(Tasklist *l, Task *t)
{
if(l->tail){
l->tail->next = t;
t->prev = l->tail;
}else{
l->head = t;
t->prev = nil;
}
l->tail = t;
t->next = nil;
}
void
deltask(Tasklist *l, Task *t)
{
if(t->prev)
t->prev->next = t->next;
else
l->head = t->next;
if(t->next)
t->next->prev = t->prev;
else
l->tail = t->prev;
}
unsigned int
taskid(void)
{
return taskrunning->id;
}

182
task.h Normal file
View File

@ -0,0 +1,182 @@
/* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */
#ifndef _TASK_H_
#define _TASK_H_ 1
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h>
#include <inttypes.h>
/*
* basic procs and threads
*/
typedef struct Task Task;
typedef struct Tasklist Tasklist;
int anyready(void);
int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize);
void taskexit(int);
void taskexitall(int);
void taskmain(int argc, char *argv[]);
int taskyield(void);
void** taskdata(void);
void needstack(int);
void taskname(char*, ...);
void taskstate(char*, ...);
char* taskgetname(void);
char* taskgetstate(void);
void tasksystem(void);
unsigned int taskdelay(unsigned int);
unsigned int taskid(void);
struct Tasklist /* used internally */
{
Task *head;
Task *tail;
};
/*
* queuing locks
*/
typedef struct QLock QLock;
struct QLock
{
Task *owner;
Tasklist waiting;
};
void qlock(QLock*);
int canqlock(QLock*);
void qunlock(QLock*);
/*
* reader-writer locks
*/
typedef struct RWLock RWLock;
struct RWLock
{
int readers;
Task *writer;
Tasklist rwaiting;
Tasklist wwaiting;
};
void rlock(RWLock*);
int canrlock(RWLock*);
void runlock(RWLock*);
void wlock(RWLock*);
int canwlock(RWLock*);
void wunlock(RWLock*);
/*
* sleep and wakeup (condition variables)
*/
typedef struct Rendez Rendez;
struct Rendez
{
QLock *l;
Tasklist waiting;
};
void tasksleep(Rendez*);
int taskwakeup(Rendez*);
int taskwakeupall(Rendez*);
/*
* channel communication
*/
typedef struct Alt Alt;
typedef struct Altarray Altarray;
typedef struct Channel Channel;
enum
{
CHANEND,
CHANSND,
CHANRCV,
CHANNOP,
CHANNOBLK,
};
struct Alt
{
Channel *c;
void *v;
unsigned int op;
Task *task;
Alt *xalt;
};
struct Altarray
{
Alt **a;
unsigned int n;
unsigned int m;
};
struct Channel
{
unsigned int bufsize;
unsigned int elemsize;
unsigned char *buf;
unsigned int nbuf;
unsigned int off;
Altarray asend;
Altarray arecv;
char *name;
};
int chanalt(Alt *alts);
Channel* chancreate(int elemsize, int elemcnt);
void chanfree(Channel *c);
int chaninit(Channel *c, int elemsize, int elemcnt);
int channbrecv(Channel *c, void *v);
void* channbrecvp(Channel *c);
unsigned long channbrecvul(Channel *c);
int channbsend(Channel *c, void *v);
int channbsendp(Channel *c, void *v);
int channbsendul(Channel *c, unsigned long v);
int chanrecv(Channel *c, void *v);
void* chanrecvp(Channel *c);
unsigned long chanrecvul(Channel *c);
int chansend(Channel *c, void *v);
int chansendp(Channel *c, void *v);
int chansendul(Channel *c, unsigned long v);
/*
* Threaded I/O.
*/
int fdread(int, void*, int);
int fdread1(int, void*, int); /* always uses fdwait */
int fdwrite(int, void*, int);
void fdwait(int, int);
int fdnoblock(int);
void fdtask(void*);
/*
* Network dialing - sets non-blocking automatically
*/
enum
{
UDP = 0,
TCP = 1,
};
int netannounce(int, char*, int);
int netaccept(int, char*, int*);
int netdial(int, char*, int);
int netlookup(char*, uint32_t*); /* blocks entire program! */
int netdial(int, char*, int);
#ifdef __cplusplus
}
#endif
#endif

172
taskimpl.h Normal file
View File

@ -0,0 +1,172 @@
/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */
#if defined(__sun__)
# define __EXTENSIONS__ 1 /* SunOS */
# if defined(__SunOS5_6__) || defined(__SunOS5_7__) || defined(__SunOS5_8__)
/* NOT USING #define __MAKECONTEXT_V2_SOURCE 1 / * SunOS */
# else
# define __MAKECONTEXT_V2_SOURCE 1
# endif
#endif
#define USE_UCONTEXT 1
#if defined(__OpenBSD__)
#undef USE_UCONTEXT
#define USE_UCONTEXT 0
#endif
#if defined(__APPLE__)
#include <AvailabilityMacros.h>
#if defined(MAC_OS_X_VERSION_10_5)
#undef USE_UCONTEXT
#define USE_UCONTEXT 0
#endif
#endif
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sched.h>
#include <signal.h>
#if USE_UCONTEXT
#include <ucontext.h>
#endif
#include <sys/utsname.h>
#include <inttypes.h>
#include "task.h"
#define nil ((void*)0)
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
#define ulong task_ulong
#define uint task_uint
#define uchar task_uchar
#define ushort task_ushort
#define uvlong task_uvlong
#define vlong task_vlong
typedef unsigned long ulong;
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long long uvlong;
typedef long long vlong;
#define print task_print
#define fprint task_fprint
#define snprint task_snprint
#define seprint task_seprint
#define vprint task_vprint
#define vfprint task_vfprint
#define vsnprint task_vsnprint
#define vseprint task_vseprint
#define strecpy task_strecpy
int print(char*, ...);
int fprint(int, char*, ...);
char *snprint(char*, uint, char*, ...);
char *seprint(char*, char*, char*, ...);
int vprint(char*, va_list);
int vfprint(int, char*, va_list);
char *vsnprint(char*, uint, char*, va_list);
char *vseprint(char*, char*, char*, va_list);
char *strecpy(char*, char*, char*);
#if defined(__FreeBSD__) && __FreeBSD__ < 5
extern int getmcontext(mcontext_t*);
extern void setmcontext(const mcontext_t*);
#define setcontext(u) setmcontext(&(u)->uc_mcontext)
#define getcontext(u) getmcontext(&(u)->uc_mcontext)
extern int swapcontext(ucontext_t*, const ucontext_t*);
extern void makecontext(ucontext_t*, void(*)(), int, ...);
#endif
#if defined(__APPLE__)
# define mcontext libthread_mcontext
# define mcontext_t libthread_mcontext_t
# define ucontext libthread_ucontext
# define ucontext_t libthread_ucontext_t
# if defined(__i386__)
# include "386-ucontext.h"
# else
# include "power-ucontext.h"
# endif
#endif
#if defined(__OpenBSD__)
# define mcontext libthread_mcontext
# define mcontext_t libthread_mcontext_t
# define ucontext libthread_ucontext
# define ucontext_t libthread_ucontext_t
# if defined __i386__
# include "386-ucontext.h"
# else
# include "power-ucontext.h"
# endif
extern pid_t rfork_thread(int, void*, int(*)(void*), void*);
#endif
#if 0 && defined(__sun__)
# define mcontext libthread_mcontext
# define mcontext_t libthread_mcontext_t
# define ucontext libthread_ucontext
# define ucontext_t libthread_ucontext_t
# include "sparc-ucontext.h"
#endif
#if defined(__arm__)
int getmcontext(mcontext_t*);
void setmcontext(const mcontext_t*);
#define setcontext(u) setmcontext(&(u)->uc_mcontext)
#define getcontext(u) getmcontext(&(u)->uc_mcontext)
#endif
typedef struct Context Context;
enum
{
STACK = 8192
};
struct Context
{
ucontext_t uc;
};
struct Task
{
char name[256]; // offset known to acid
char state[256];
Task *next;
Task *prev;
Task *allnext;
Task *allprev;
Context context;
uvlong alarmtime;
uint id;
uchar *stk;
uint stksize;
int exiting;
int alltaskslot;
int system;
int ready;
void (*startfn)(void*);
void *startarg;
void *udata;
};
void taskready(Task*);
void taskswitch(void);
void addtask(Tasklist*, Task*);
void deltask(Tasklist*, Task*);
extern Task *taskrunning;
extern int taskcount;

92
tcpproxy.c Normal file
View File

@ -0,0 +1,92 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <task.h>
#include <stdlib.h>
#include <sys/socket.h>
enum
{
STACK = 32768
};
char *server;
int port;
void proxytask(void*);
void rwtask(void*);
int*
mkfd2(int fd1, int fd2)
{
int *a;
a = malloc(2*sizeof a[0]);
if(a == 0){
fprintf(stderr, "out of memory\n");
abort();
}
a[0] = fd1;
a[1] = fd2;
return a;
}
void
taskmain(int argc, char **argv)
{
int cfd, fd;
int rport;
char remote[16];
if(argc != 4){
fprintf(stderr, "usage: tcpproxy localport server remoteport\n");
taskexitall(1);
}
server = argv[2];
port = atoi(argv[3]);
if((fd = netannounce(TCP, 0, atoi(argv[1]))) < 0){
fprintf(stderr, "cannot announce on tcp port %d: %s\n", atoi(argv[1]), strerror(errno));
taskexitall(1);
}
fdnoblock(fd);
while((cfd = netaccept(fd, remote, &rport)) >= 0){
fprintf(stderr, "connection from %s:%d\n", remote, rport);
taskcreate(proxytask, (void*)cfd, STACK);
}
}
void
proxytask(void *v)
{
int fd, remotefd;
fd = (int)v;
if((remotefd = netdial(TCP, server, port)) < 0){
close(fd);
return;
}
fprintf(stderr, "connected to %s:%d\n", server, port);
taskcreate(rwtask, mkfd2(fd, remotefd), STACK);
taskcreate(rwtask, mkfd2(remotefd, fd), STACK);
}
void
rwtask(void *v)
{
int *a, rfd, wfd, n;
char buf[2048];
a = v;
rfd = a[0];
wfd = a[1];
free(a);
while((n = fdread(rfd, buf, sizeof buf)) > 0)
fdwrite(wfd, buf, n);
shutdown(wfd, SHUT_WR);
close(rfd);
}

40
testdelay.c Normal file
View File

@ -0,0 +1,40 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <task.h>
enum { STACK = 32768 };
Channel *c;
void
delaytask(void *v)
{
taskdelay((int)v);
printf("awake after %d ms\n", (int)v);
chansendul(c, 0);
}
void
taskmain(int argc, char **argv)
{
int i, n;
c = chancreate(sizeof(unsigned long), 0);
n = 0;
for(i=1; i<argc; i++){
n++;
printf("x");
taskcreate(delaytask, (void*)atoi(argv[i]), STACK);
}
/* wait for n tasks to finish */
for(i=0; i<n; i++){
printf("y");
chanrecvul(c);
}
taskexitall(0);
}

7
testdelay1.c Normal file
View File

@ -0,0 +1,7 @@
#include <task.h>
void
taskmain(int argc, char *argv[])
{
taskdelay(1000);
}