mirror of
https://github.com/0intro/libtask
synced 2024-11-23 14:19:46 +03:00
libtask as of 2008/08/01
This commit is contained in:
commit
42f058999e
121
386-ucontext.h
Normal file
121
386-ucontext.h
Normal 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
43
COPYRIGHT
Normal 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
57
Makefile
Normal 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
246
README
Normal 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
206
asm.S
Normal 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
385
channel.c
Normal 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
90
context.c
Normal 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
202
fd.c
Normal 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
58
httpload.c
Normal 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
22
makesun
Executable 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
203
net.c
Normal 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
37
power-ucontext.h
Normal 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
61
primes.c
Normal 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
252
print.c
Normal 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
142
qlock.c
Normal 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
46
rendez.c
Normal 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
413
task.c
Normal 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
182
task.h
Normal 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
172
taskimpl.h
Normal 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
92
tcpproxy.c
Normal 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
40
testdelay.c
Normal 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
7
testdelay1.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include <task.h>
|
||||
|
||||
void
|
||||
taskmain(int argc, char *argv[])
|
||||
{
|
||||
taskdelay(1000);
|
||||
}
|
Loading…
Reference in New Issue
Block a user