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