├── .github └── workflows │ ├── c.yml │ └── coverity.yml ├── 386-ucontext.h ├── COPYRIGHT ├── Makefile ├── Makefile.sun ├── README.md ├── amd64-ucontext.h ├── asm.S ├── channel.c ├── context.c ├── echo.c ├── fd.c ├── httpload.c ├── ip.c ├── ip.h ├── makesun ├── mips-ucontext.h ├── net.c ├── power-ucontext.h ├── primes.c ├── print.c ├── qlock.c ├── rendez.c ├── task.c ├── task.h ├── taskimpl.h ├── tcpload.c ├── tcpproxy.c ├── testdelay.c └── testdelay1.c /.github/workflows/c.yml: -------------------------------------------------------------------------------- 1 | name: C 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: make clean 17 | run: make clean 18 | - name: make 19 | run: make 20 | -------------------------------------------------------------------------------- /.github/workflows/coverity.yml: -------------------------------------------------------------------------------- 1 | name: Coverity Scan 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | coverity: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: vapier/coverity-scan-action@v1 13 | with: 14 | email: ${{ secrets.COVERITY_SCAN_EMAIL }} 15 | token: ${{ secrets.COVERITY_SCAN_TOKEN }} 16 | -------------------------------------------------------------------------------- /386-ucontext.h: -------------------------------------------------------------------------------- 1 | #define setcontext(u) setmcontext(&(u)->uc_mcontext) 2 | #define getcontext(u) getmcontext(&(u)->uc_mcontext) 3 | typedef struct mcontext mcontext_t; 4 | typedef struct ucontext ucontext_t; 5 | 6 | extern int swapcontext(ucontext_t*, const ucontext_t*); 7 | extern void makecontext(ucontext_t*, void(*)(), int, ...); 8 | extern int getmcontext(mcontext_t*); 9 | extern void setmcontext(const mcontext_t*); 10 | 11 | /*- 12 | * Copyright (c) 1999 Marcel Moolenaar 13 | * All rights reserved. 14 | * 15 | * Redistribution and use in source and binary forms, with or without 16 | * modification, are permitted provided that the following conditions 17 | * are met: 18 | * 1. Redistributions of source code must retain the above copyright 19 | * notice, this list of conditions and the following disclaimer 20 | * in this position and unchanged. 21 | * 2. Redistributions in binary form must reproduce the above copyright 22 | * notice, this list of conditions and the following disclaimer in the 23 | * documentation and/or other materials provided with the distribution. 24 | * 3. The name of the author may not be used to endorse or promote products 25 | * derived from this software without specific prior written permission. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 28 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 29 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 31 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 32 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 36 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | * 38 | * $FreeBSD: src/sys/sys/ucontext.h,v 1.4 1999/10/11 20:33:17 luoqi Exp $ 39 | */ 40 | 41 | /* #include */ 42 | 43 | /*- 44 | * Copyright (c) 1999 Marcel Moolenaar 45 | * All rights reserved. 46 | * 47 | * Redistribution and use in source and binary forms, with or without 48 | * modification, are permitted provided that the following conditions 49 | * are met: 50 | * 1. Redistributions of source code must retain the above copyright 51 | * notice, this list of conditions and the following disclaimer 52 | * in this position and unchanged. 53 | * 2. Redistributions in binary form must reproduce the above copyright 54 | * notice, this list of conditions and the following disclaimer in the 55 | * documentation and/or other materials provided with the distribution. 56 | * 3. The name of the author may not be used to endorse or promote products 57 | * derived from this software without specific prior written permission. 58 | * 59 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 60 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 61 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 62 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 63 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 64 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 65 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 66 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 67 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 68 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 69 | * 70 | * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $ 71 | */ 72 | 73 | struct mcontext { 74 | /* 75 | * The first 20 fields must match the definition of 76 | * sigcontext. So that we can support sigcontext 77 | * and ucontext_t at the same time. 78 | */ 79 | int mc_onstack; /* XXX - sigcontext compat. */ 80 | int mc_gs; 81 | int mc_fs; 82 | int mc_es; 83 | int mc_ds; 84 | int mc_edi; 85 | int mc_esi; 86 | int mc_ebp; 87 | int mc_isp; 88 | int mc_ebx; 89 | int mc_edx; 90 | int mc_ecx; 91 | int mc_eax; 92 | int mc_trapno; 93 | int mc_err; 94 | int mc_eip; 95 | int mc_cs; 96 | int mc_eflags; 97 | int mc_esp; /* machine state */ 98 | int mc_ss; 99 | 100 | int mc_fpregs[28]; /* env87 + fpacc87 + u_long */ 101 | int __spare__[17]; 102 | }; 103 | 104 | struct ucontext { 105 | /* 106 | * Keep the order of the first two fields. Also, 107 | * keep them the first two fields in the structure. 108 | * This way we can have a union with struct 109 | * sigcontext and ucontext_t. This allows us to 110 | * support them both at the same time. 111 | * note: the union is not defined, though. 112 | */ 113 | sigset_t uc_sigmask; 114 | mcontext_t uc_mcontext; 115 | 116 | struct __ucontext *uc_link; 117 | stack_t uc_stack; 118 | int __spare__[8]; 119 | }; 120 | 121 | 122 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | 2 | This software was developed as part of a project at MIT. 3 | 4 | Copyright (c) 2005-2007 Russ Cox, 5 | Massachusetts Institute of Technology 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | "Software"), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | === 27 | 28 | Contains parts of an earlier library that has: 29 | 30 | /* 31 | * The authors of this software are Rob Pike, Sape Mullender, and Russ Cox 32 | * Copyright (c) 2003 by Lucent Technologies. 33 | * Permission to use, copy, modify, and distribute this software for any 34 | * purpose without fee is hereby granted, provided that this entire notice 35 | * is included in all copies of any software which is or includes a copy 36 | * or modification of this software and in all copies of the supporting 37 | * documentation for such software. 38 | * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED 39 | * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY 40 | * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY 41 | * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. 42 | */ 43 | 44 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIB=libtask.a 2 | TCPLIBS= 3 | 4 | ASM=asm.o 5 | OFILES=\ 6 | $(ASM)\ 7 | channel.o\ 8 | context.o\ 9 | fd.o\ 10 | net.o\ 11 | print.o\ 12 | qlock.o\ 13 | rendez.o\ 14 | task.o\ 15 | ip.o\ 16 | 17 | all: $(LIB) echo httpload primes tcpload tcpproxy testdelay 18 | 19 | $(OFILES): taskimpl.h task.h 386-ucontext.h power-ucontext.h ip.h 20 | 21 | AS=gcc -c 22 | CC=gcc 23 | CFLAGS=-Wall -Wextra -c -I. -ggdb 24 | #CFLAGS+=-DUSE_VALGRIND -I/usr/include/valgrind 25 | 26 | %.o: %.S 27 | $(AS) $*.S 28 | 29 | %.o: %.c 30 | $(CC) $(CFLAGS) $*.c 31 | 32 | $(LIB): $(OFILES) 33 | ar rvc $(LIB) $(OFILES) 34 | 35 | echo: echo.o $(LIB) 36 | $(CC) -o echo echo.o $(LIB) 37 | 38 | primes: primes.o $(LIB) 39 | $(CC) -o primes primes.o $(LIB) 40 | 41 | tcpload: tcpload.o $(LIB) 42 | $(CC) -o tcpload tcpload.o $(LIB) 43 | 44 | tcpproxy: tcpproxy.o $(LIB) 45 | $(CC) -o tcpproxy tcpproxy.o $(LIB) $(TCPLIBS) 46 | 47 | httpload: httpload.o $(LIB) 48 | $(CC) -o httpload httpload.o $(LIB) 49 | 50 | testdelay: testdelay.o $(LIB) 51 | $(CC) -o testdelay testdelay.o $(LIB) 52 | 53 | testdelay1: testdelay1.o $(LIB) 54 | $(CC) -o testdelay1 testdelay1.o $(LIB) 55 | 56 | clean: 57 | rm -f *.o echo primes tcpload tcpproxy httpload testdelay testdelay1 $(LIB) 58 | 59 | install: $(LIB) 60 | cp $(LIB) /usr/local/lib 61 | cp task.h /usr/local/include 62 | 63 | -------------------------------------------------------------------------------- /Makefile.sun: -------------------------------------------------------------------------------- 1 | LIB=libtask.a 2 | 3 | OFILES=\ 4 | channel.o\ 5 | context.o\ 6 | fd.o\ 7 | net.o\ 8 | print.o\ 9 | qlock.o\ 10 | rendez.o\ 11 | task.o\ 12 | ip.o\ 13 | 14 | all: $(LIB) primes tcpproxy testdelay 15 | 16 | $(OFILES): taskimpl.h task.h 386-ucontext.h power-ucontext.h ip.h 17 | 18 | AS=as 19 | CC=cc 20 | CFLAGS=-c -I. -g 21 | 22 | %.o: %.S 23 | $(AS) $*.S 24 | 25 | %.o: %.c 26 | $(CC) $(CFLAGS) $*.c 27 | 28 | $(LIB): $(OFILES) 29 | ar rvc $(LIB) $(OFILES) 30 | 31 | primes: primes.o $(LIB) 32 | $(CC) -o primes primes.o $(LIB) 33 | 34 | tcpproxy: tcpproxy.o $(LIB) 35 | $(CC) -o tcpproxy tcpproxy.o $(LIB) 36 | 37 | httpload: httpload.o $(LIB) 38 | $(CC) -o httpload httpload.o $(LIB) 39 | 40 | stdlogger: stdlogger.o $(LIB) 41 | $(CC) -o stdlogger stdlogger.o $(LIB) 42 | 43 | testdelay: testdelay.o $(LIB) 44 | $(CC) -o testdelay testdelay.o $(LIB) 45 | 46 | clock: clock.o $(LIB) 47 | $(CC) -o clock clock.o $(LIB) 48 | 49 | clean: 50 | rm *.o tcpproxy primes $(LIB) 51 | 52 | tgz: 53 | tar czvf libtask.tgz COPYRIGHT README *.[chS] Makefile 54 | 55 | install: $(LIB) 56 | cp $(LIB) /usr/local/lib 57 | cp task.h /usr/local/include 58 | 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Libtask: a Coroutine Library for C and Unix 2 | =========================================== 3 | 4 | [![Build Status](https://github.com/0intro/libtask/workflows/C/badge.svg)](https://github.com/0intro/libtask/actions/workflows/c.yml) 5 | [![Coverity Scan Status](https://scan.coverity.com/projects/libtask/badge.svg)](https://scan.coverity.com/projects/libtask) 6 | 7 | Libtask is a simple coroutine library. It runs on Linux (ARM, MIPS, and x86), 8 | FreeBSD (x86), OS X (PowerPC x86, and x86-64), and SunOS Solaris (Sparc), 9 | and is easy to port to other systems. 10 | 11 | Libtask gives the programmer the illusion of threads, but 12 | the operating system sees only a single kernel thread. 13 | For clarity, we refer to the coroutines as "tasks," not threads. 14 | 15 | Scheduling is cooperative. Only one task runs at a time, 16 | and it cannot be rescheduled without explicitly giving up 17 | the CPU. Most of the functions provided in task.h do have 18 | the possibility of going to sleep. Programs using the task 19 | functions should #include . 20 | 21 | Basic task manipulation 22 | ----------------------- 23 | 24 | int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize); 25 | 26 | Create a new task running f(arg) on a stack of size stacksize. 27 | 28 | void tasksystem(void); 29 | 30 | Mark the current task as a "system" task. These are ignored 31 | for the purposes of deciding the program is done running 32 | (see taskexit next). 33 | 34 | void taskexit(int status); 35 | 36 | Exit the current task. If this is the last non-system task, 37 | exit the entire program using the given exit status. 38 | 39 | void taskexitall(int status); 40 | 41 | Exit the entire program, using the given exit status. 42 | 43 | void taskmain(int argc, char *argv[]); 44 | 45 | Write this function instead of main. Libtask provides its own main. 46 | 47 | int taskyield(void); 48 | 49 | Explicitly give up the CPU. The current task will be scheduled 50 | again once all the other currently-ready tasks have a chance 51 | to run. Returns the number of other tasks that ran while the 52 | current task was waiting. (Zero means there are no other tasks 53 | trying to run.) 54 | 55 | int taskdelay(unsigned int ms) 56 | 57 | Explicitly give up the CPU for at least ms milliseconds. 58 | Other tasks continue to run during this time. 59 | 60 | void** taskdata(void); 61 | 62 | Return a pointer to a single per-task void* pointer. 63 | You can use this as a per-task storage place. 64 | 65 | void needstack(int n); 66 | 67 | Tell the task library that you need at least n bytes left 68 | on the stack. If you don't have it, the task library will call abort. 69 | (It's hard to figure out how big stacks should be. I usually make 70 | them really big (say 32768) and then don't worry about it.) 71 | 72 | void taskname(char*, ...); 73 | 74 | Takes an argument list like printf. Sets the current task's name. 75 | 76 | char* taskgetname(void); 77 | 78 | Returns the current task's name. Is the actual buffer; do not free. 79 | 80 | void taskstate(char*, ...); 81 | char* taskgetstate(void); 82 | 83 | Like taskname and taskgetname but for the task state. 84 | 85 | When you send a tasked program a SIGQUIT (or SIGINFO, on BSD) 86 | it will print a list of all its tasks and their names and states. 87 | This is useful for debugging why your program isn't doing anything! 88 | 89 | unsigned int taskid(void); 90 | 91 | Return the unique task id for the current task. 92 | 93 | Non-blocking I/O 94 | ---------------- 95 | 96 | There is a small amount of runtime support for non-blocking I/O 97 | on file descriptors. 98 | 99 | int fdnoblock(int fd); 100 | 101 | Sets I/O on the given fd to be non-blocking. Should be 102 | called before any of the other fd routines. 103 | 104 | int fdread(int, void*, int); 105 | 106 | Like regular read(), but puts task to sleep while waiting for 107 | data instead of blocking the whole program. 108 | 109 | int fdreadn(int, void*, int); 110 | 111 | Like fdread(), but does successive read calls until n bytes have 112 | been read, or a read system call returns a non-positive count. 113 | 114 | int fdwrite(int, void*, int); 115 | 116 | Like regular write(), but puts task to sleep while waiting to 117 | write data instead of blocking the whole program. 118 | 119 | void fdwait(int fd, int rw); 120 | 121 | Low-level call sitting underneath fdread and fdwrite. 122 | Puts task to sleep while waiting for I/O to be possible on fd. 123 | Rw specifies type of I/O: 'r' means read, 'w' means write, 124 | anything else means just exceptional conditions (hang up, etc.) 125 | The 'r' and 'w' also wake up for exceptional conditions. 126 | 127 | Network I/O 128 | ----------- 129 | 130 | These are convenient packaging of the ugly Unix socket routines. 131 | They can all put the current task to sleep during the call. 132 | 133 | int netannounce(int proto, char *address, int port) 134 | 135 | Start a network listener running on address and port of protocol. 136 | Proto is either TCP or UDP. Port is a port number. Address is a 137 | string version of a host name or IP address. If address is null, 138 | then announce binds to the given port on all available interfaces. 139 | Returns a fd to use with netaccept. 140 | Examples: netannounce(TCP, "localhost", 80) or 141 | netannounce(TCP, "127.0.0.1", 80) or netannounce(TCP, 0, 80). 142 | 143 | int netaccept(int fd, char *server, int *port) 144 | 145 | Get the next connection that comes in to the listener fd. 146 | Returns a fd to use to talk to the guy who just connected. 147 | If server is not null, it must point at a buffer of at least 148 | 46 bytes that is filled in with the remote IP address. 149 | If port is not null, it is filled in with the report port. 150 | Example: 151 | char server[46]; 152 | int port; 153 | 154 | if(netaccept(fd, server, &port) >= 0) 155 | printf("connect from %s:%d", server, port); 156 | 157 | int netdial(int proto, char *name, int port) 158 | 159 | Create a new (outgoing) connection to a particular host. 160 | Name can be an ip address or a domain name. If it's a domain name, 161 | the entire program will block while the name is resolved 162 | (the DNS library does not provide a nice non-blocking interface). 163 | Example: netdial(TCP, "www.google.com", 80) 164 | or netdial(TCP, "18.26.4.9", 80) 165 | 166 | Time 167 | ---- 168 | 169 | unsigned int taskdelay(unsigned int ms) 170 | 171 | Put the current task to sleep for approximately ms milliseconds. 172 | Return the actual amount of time slept, in milliseconds. 173 | 174 | Example programs 175 | ---------------- 176 | 177 | In this directory, tcpproxy.c is a simple TCP proxy that illustrates 178 | most of the above. You can run 179 | 180 | tcpproxy 1234 www.google.com 80 181 | 182 | and then you should be able to visit http://localhost:1234/ and see Google. 183 | 184 | Other examples are: 185 | primes.c - simple prime sieve 186 | httpload.c - simple HTTP load generator 187 | testdelay.c - test taskdelay() 188 | 189 | Building 190 | -------- 191 | 192 | To build, run make. You can run make install to copy task.h and 193 | libtask.a to the appropriate places in /usr/local. Then you 194 | should be able to just link with -ltask in your programs 195 | that use it. 196 | 197 | On SunOS Solaris machines, run makesun instead of just make. 198 | 199 | Contact Info 200 | ------------ 201 | 202 | Please email me with questions or problems. 203 | 204 | Russ Cox 205 | rsc@swtch.com 206 | 207 | 208 | Stuff you probably won't use at first but might want to know about eventually 209 | ----------------------------------------------------------------------------- 210 | 211 | void tasksleep(Rendez*); 212 | int taskwakeup(Rendez*); 213 | int taskwakeupall(Rendez*); 214 | 215 | A Rendez is a condition variable. You can declare a new one by 216 | just allocating memory for it (or putting it in another structure) 217 | and then zeroing the memory. Tasksleep(r) 'sleeps on r', giving 218 | up the CPU. Multiple tasks can sleep on a single Rendez. 219 | When another task comes along and calls taskwakeup(r), 220 | the first task sleeping on r (if any) will be woken up. 221 | Taskwakeupall(r) wakes up all the tasks sleeping on r. 222 | They both return the actual number of tasks awakened. 223 | 224 | 225 | 226 | void qlock(QLock*); 227 | int canqlock(QLock*); 228 | void qunlock(QLock*); 229 | 230 | You probably won't need locks because of the cooperative 231 | scheduling, but if you do, here are some. You can make a new 232 | QLock by just declaring it and zeroing the memory. 233 | Calling qlock will give up the CPU if the lock is held by someone else. 234 | Calling qunlock will not give up the CPU. 235 | Calling canqlock tries to lock the lock, but will not give up the CPU. 236 | It returns 1 if the lock was acquired, 0 if it cannot be at this time. 237 | 238 | void rlock(RWLock*); 239 | int canrlock(RWLock*); 240 | void runlock(RWLock*); 241 | 242 | void wlock(RWLock*); 243 | int canwlock(RWLock*); 244 | void wunlock(RWLock*); 245 | 246 | RWLocks are reader-writer locks. Any number of readers 247 | can lock them at once, but only one writer at a time. 248 | If a writer is holding it, there can't be any readers. 249 | 250 | 251 | Channel *chancreate(int, int); 252 | etc. 253 | 254 | Channels are buffered communication pipes you can 255 | use to send messages between tasks. Some people like 256 | doing most of the inter-task communication using channels. 257 | 258 | For details on channels see the description of channels in 259 | http://swtch.com/usr/local/plan9/man/man3/thread.html and 260 | http://swtch.com/~rsc/thread/ 261 | and also the example program primes.c, which implements 262 | a concurrent prime sieve. 263 | -------------------------------------------------------------------------------- /amd64-ucontext.h: -------------------------------------------------------------------------------- 1 | #define setcontext(u) setmcontext(&(u)->uc_mcontext) 2 | #define getcontext(u) getmcontext(&(u)->uc_mcontext) 3 | typedef struct mcontext mcontext_t; 4 | typedef struct ucontext ucontext_t; 5 | 6 | extern int swapcontext(ucontext_t*, const ucontext_t*); 7 | extern void makecontext(ucontext_t*, void(*)(), int, ...); 8 | extern int getmcontext(mcontext_t*); 9 | extern void setmcontext(const mcontext_t*); 10 | 11 | /*- 12 | * Copyright (c) 1999 Marcel Moolenaar 13 | * All rights reserved. 14 | * 15 | * Redistribution and use in source and binary forms, with or without 16 | * modification, are permitted provided that the following conditions 17 | * are met: 18 | * 1. Redistributions of source code must retain the above copyright 19 | * notice, this list of conditions and the following disclaimer 20 | * in this position and unchanged. 21 | * 2. Redistributions in binary form must reproduce the above copyright 22 | * notice, this list of conditions and the following disclaimer in the 23 | * documentation and/or other materials provided with the distribution. 24 | * 3. The name of the author may not be used to endorse or promote products 25 | * derived from this software without specific prior written permission. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 28 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 29 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 31 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 32 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 36 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | * 38 | * $FreeBSD: src/sys/sys/ucontext.h,v 1.4 1999/10/11 20:33:17 luoqi Exp $ 39 | */ 40 | 41 | /* #include */ 42 | 43 | /*- 44 | * Copyright (c) 1999 Marcel Moolenaar 45 | * All rights reserved. 46 | * 47 | * Redistribution and use in source and binary forms, with or without 48 | * modification, are permitted provided that the following conditions 49 | * are met: 50 | * 1. Redistributions of source code must retain the above copyright 51 | * notice, this list of conditions and the following disclaimer 52 | * in this position and unchanged. 53 | * 2. Redistributions in binary form must reproduce the above copyright 54 | * notice, this list of conditions and the following disclaimer in the 55 | * documentation and/or other materials provided with the distribution. 56 | * 3. The name of the author may not be used to endorse or promote products 57 | * derived from this software without specific prior written permission. 58 | * 59 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 60 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 61 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 62 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 63 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 64 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 65 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 66 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 67 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 68 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 69 | * 70 | * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $ 71 | */ 72 | 73 | struct mcontext { 74 | /* 75 | * The first 20 fields must match the definition of 76 | * sigcontext. So that we can support sigcontext 77 | * and ucontext_t at the same time. 78 | */ 79 | long mc_onstack; /* XXX - sigcontext compat. */ 80 | long mc_rdi; /* machine state (struct trapframe) */ 81 | long mc_rsi; 82 | long mc_rdx; 83 | long mc_rcx; 84 | long mc_r8; 85 | long mc_r9; 86 | long mc_rax; 87 | long mc_rbx; 88 | long mc_rbp; 89 | long mc_r10; 90 | long mc_r11; 91 | long mc_r12; 92 | long mc_r13; 93 | long mc_r14; 94 | long mc_r15; 95 | long mc_trapno; 96 | long mc_addr; 97 | long mc_flags; 98 | long mc_err; 99 | long mc_rip; 100 | long mc_cs; 101 | long mc_rflags; 102 | long mc_rsp; 103 | long mc_ss; 104 | 105 | long mc_len; /* sizeof(mcontext_t) */ 106 | #define _MC_FPFMT_NODEV 0x10000 /* device not present or configured */ 107 | #define _MC_FPFMT_XMM 0x10002 108 | long mc_fpformat; 109 | #define _MC_FPOWNED_NONE 0x20000 /* FP state not used */ 110 | #define _MC_FPOWNED_FPU 0x20001 /* FP state came from FPU */ 111 | #define _MC_FPOWNED_PCB 0x20002 /* FP state came from PCB */ 112 | long mc_ownedfp; 113 | /* 114 | * See for the internals of mc_fpstate[]. 115 | */ 116 | long mc_fpstate[64]; 117 | long mc_spare[8]; 118 | }; 119 | 120 | struct ucontext { 121 | /* 122 | * Keep the order of the first two fields. Also, 123 | * keep them the first two fields in the structure. 124 | * This way we can have a union with struct 125 | * sigcontext and ucontext_t. This allows us to 126 | * support them both at the same time. 127 | * note: the union is not defined, though. 128 | */ 129 | sigset_t uc_sigmask; 130 | mcontext_t uc_mcontext; 131 | 132 | struct __ucontext *uc_link; 133 | stack_t uc_stack; 134 | int __spare__[8]; 135 | }; 136 | 137 | 138 | -------------------------------------------------------------------------------- /asm.S: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ 2 | 3 | #if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5 4 | #define NEEDX86CONTEXT 1 5 | #define SET setmcontext 6 | #define GET getmcontext 7 | #endif 8 | 9 | #if defined(__OpenBSD__) && defined(__i386__) 10 | #define NEEDX86CONTEXT 1 11 | #define SET setmcontext 12 | #define GET getmcontext 13 | #endif 14 | 15 | #if defined(__APPLE__) 16 | #if defined(__i386__) 17 | #define NEEDX86CONTEXT 1 18 | #define SET _setmcontext 19 | #define GET _getmcontext 20 | #elif defined(__x86_64__) 21 | #define NEEDAMD64CONTEXT 1 22 | #define SET _setmcontext 23 | #define GET _getmcontext 24 | #else 25 | #define NEEDPOWERCONTEXT 1 26 | #define SET __setmcontext 27 | #define GET __getmcontext 28 | #endif 29 | #endif 30 | 31 | #if defined(__linux__) && defined(__amd64__) 32 | #define NEEDAMD64CONTEXT 1 33 | #define SET setmcontext 34 | #define GET getmcontext 35 | #endif 36 | 37 | #if defined(__linux__) && defined(__arm__) 38 | #define NEEDARMCONTEXT 1 39 | #define SET setmcontext 40 | #define GET getmcontext 41 | #endif 42 | 43 | #if defined(__linux__) && defined(__mips__) 44 | #define NEEDMIPSCONTEXT 1 45 | #define SET setmcontext 46 | #define GET getmcontext 47 | #endif 48 | 49 | #ifdef NEEDX86CONTEXT 50 | .globl SET 51 | SET: 52 | movl 4(%esp), %eax 53 | 54 | movl 8(%eax), %fs 55 | movl 12(%eax), %es 56 | movl 16(%eax), %ds 57 | movl 76(%eax), %ss 58 | movl 20(%eax), %edi 59 | movl 24(%eax), %esi 60 | movl 28(%eax), %ebp 61 | movl 36(%eax), %ebx 62 | movl 40(%eax), %edx 63 | movl 44(%eax), %ecx 64 | 65 | movl 72(%eax), %esp 66 | pushl 60(%eax) /* new %eip */ 67 | movl 48(%eax), %eax 68 | ret 69 | 70 | .globl GET 71 | GET: 72 | movl 4(%esp), %eax 73 | 74 | movl %fs, 8(%eax) 75 | movl %es, 12(%eax) 76 | movl %ds, 16(%eax) 77 | movl %ss, 76(%eax) 78 | movl %edi, 20(%eax) 79 | movl %esi, 24(%eax) 80 | movl %ebp, 28(%eax) 81 | movl %ebx, 36(%eax) 82 | movl %edx, 40(%eax) 83 | movl %ecx, 44(%eax) 84 | 85 | movl $1, 48(%eax) /* %eax */ 86 | movl (%esp), %ecx /* %eip */ 87 | movl %ecx, 60(%eax) 88 | leal 4(%esp), %ecx /* %esp */ 89 | movl %ecx, 72(%eax) 90 | 91 | movl 44(%eax), %ecx /* restore %ecx */ 92 | movl $0, %eax 93 | ret 94 | #endif 95 | 96 | #ifdef NEEDAMD64CONTEXT 97 | .globl SET 98 | SET: 99 | movq 16(%rdi), %rsi 100 | movq 24(%rdi), %rdx 101 | movq 32(%rdi), %rcx 102 | movq 40(%rdi), %r8 103 | movq 48(%rdi), %r9 104 | movq 56(%rdi), %rax 105 | movq 64(%rdi), %rbx 106 | movq 72(%rdi), %rbp 107 | movq 80(%rdi), %r10 108 | movq 88(%rdi), %r11 109 | movq 96(%rdi), %r12 110 | movq 104(%rdi), %r13 111 | movq 112(%rdi), %r14 112 | movq 120(%rdi), %r15 113 | movq 184(%rdi), %rsp 114 | pushq 160(%rdi) /* new %eip */ 115 | movq 8(%rdi), %rdi 116 | ret 117 | 118 | .globl GET 119 | GET: 120 | movq %rdi, 8(%rdi) 121 | movq %rsi, 16(%rdi) 122 | movq %rdx, 24(%rdi) 123 | movq %rcx, 32(%rdi) 124 | movq %r8, 40(%rdi) 125 | movq %r9, 48(%rdi) 126 | movq $1, 56(%rdi) /* %rax */ 127 | movq %rbx, 64(%rdi) 128 | movq %rbp, 72(%rdi) 129 | movq %r10, 80(%rdi) 130 | movq %r11, 88(%rdi) 131 | movq %r12, 96(%rdi) 132 | movq %r13, 104(%rdi) 133 | movq %r14, 112(%rdi) 134 | movq %r15, 120(%rdi) 135 | 136 | movq (%rsp), %rcx /* %rip */ 137 | movq %rcx, 160(%rdi) 138 | leaq 8(%rsp), %rcx /* %rsp */ 139 | movq %rcx, 184(%rdi) 140 | 141 | movq 32(%rdi), %rcx /* restore %rcx */ 142 | movq $0, %rax 143 | ret 144 | #endif 145 | 146 | #ifdef NEEDPOWERCONTEXT 147 | /* get FPR and VR use flags with sc 0x7FF3 */ 148 | /* get vsave with mfspr reg, 256 */ 149 | 150 | .text 151 | .align 2 152 | 153 | .globl GET 154 | GET: /* xxx: instruction scheduling */ 155 | mflr r0 156 | mfcr r5 157 | mfctr r6 158 | mfxer r7 159 | stw r0, 0*4(r3) 160 | stw r5, 1*4(r3) 161 | stw r6, 2*4(r3) 162 | stw r7, 3*4(r3) 163 | 164 | stw r1, 4*4(r3) 165 | stw r2, 5*4(r3) 166 | li r5, 1 /* return value for setmcontext */ 167 | stw r5, 6*4(r3) 168 | 169 | stw r13, (0+7)*4(r3) /* callee-save GPRs */ 170 | stw r14, (1+7)*4(r3) /* xxx: block move */ 171 | stw r15, (2+7)*4(r3) 172 | stw r16, (3+7)*4(r3) 173 | stw r17, (4+7)*4(r3) 174 | stw r18, (5+7)*4(r3) 175 | stw r19, (6+7)*4(r3) 176 | stw r20, (7+7)*4(r3) 177 | stw r21, (8+7)*4(r3) 178 | stw r22, (9+7)*4(r3) 179 | stw r23, (10+7)*4(r3) 180 | stw r24, (11+7)*4(r3) 181 | stw r25, (12+7)*4(r3) 182 | stw r26, (13+7)*4(r3) 183 | stw r27, (14+7)*4(r3) 184 | stw r28, (15+7)*4(r3) 185 | stw r29, (16+7)*4(r3) 186 | stw r30, (17+7)*4(r3) 187 | stw r31, (18+7)*4(r3) 188 | 189 | li r3, 0 /* return */ 190 | blr 191 | 192 | .globl SET 193 | SET: 194 | lwz r13, (0+7)*4(r3) /* callee-save GPRs */ 195 | lwz r14, (1+7)*4(r3) /* xxx: block move */ 196 | lwz r15, (2+7)*4(r3) 197 | lwz r16, (3+7)*4(r3) 198 | lwz r17, (4+7)*4(r3) 199 | lwz r18, (5+7)*4(r3) 200 | lwz r19, (6+7)*4(r3) 201 | lwz r20, (7+7)*4(r3) 202 | lwz r21, (8+7)*4(r3) 203 | lwz r22, (9+7)*4(r3) 204 | lwz r23, (10+7)*4(r3) 205 | lwz r24, (11+7)*4(r3) 206 | lwz r25, (12+7)*4(r3) 207 | lwz r26, (13+7)*4(r3) 208 | lwz r27, (14+7)*4(r3) 209 | lwz r28, (15+7)*4(r3) 210 | lwz r29, (16+7)*4(r3) 211 | lwz r30, (17+7)*4(r3) 212 | lwz r31, (18+7)*4(r3) 213 | 214 | lwz r1, 4*4(r3) 215 | lwz r2, 5*4(r3) 216 | 217 | lwz r0, 0*4(r3) 218 | mtlr r0 219 | lwz r0, 1*4(r3) 220 | mtcr r0 /* mtcrf 0xFF, r0 */ 221 | lwz r0, 2*4(r3) 222 | mtctr r0 223 | lwz r0, 3*4(r3) 224 | mtxer r0 225 | 226 | lwz r3, 6*4(r3) 227 | blr 228 | #endif 229 | 230 | #ifdef NEEDARMCONTEXT 231 | .globl GET 232 | GET: 233 | str r1, [r0,#4] 234 | str r2, [r0,#8] 235 | str r3, [r0,#12] 236 | str r4, [r0,#16] 237 | str r5, [r0,#20] 238 | str r6, [r0,#24] 239 | str r7, [r0,#28] 240 | str r8, [r0,#32] 241 | str r9, [r0,#36] 242 | str r10, [r0,#40] 243 | str r11, [r0,#44] 244 | str r12, [r0,#48] 245 | str r13, [r0,#52] 246 | str r14, [r0,#56] 247 | /* store 1 as r0-to-restore */ 248 | mov r1, #1 249 | str r1, [r0] 250 | /* return 0 */ 251 | mov r0, #0 252 | mov pc, lr 253 | 254 | .globl SET 255 | SET: 256 | ldr r1, [r0,#4] 257 | ldr r2, [r0,#8] 258 | ldr r3, [r0,#12] 259 | ldr r4, [r0,#16] 260 | ldr r5, [r0,#20] 261 | ldr r6, [r0,#24] 262 | ldr r7, [r0,#28] 263 | ldr r8, [r0,#32] 264 | ldr r9, [r0,#36] 265 | ldr r10, [r0,#40] 266 | ldr r11, [r0,#44] 267 | ldr r12, [r0,#48] 268 | ldr r13, [r0,#52] 269 | ldr r14, [r0,#56] 270 | ldr r0, [r0] 271 | mov pc, lr 272 | #endif 273 | 274 | #ifdef NEEDMIPSCONTEXT 275 | .globl GET 276 | GET: 277 | sw $4, 24($4) 278 | sw $5, 28($4) 279 | sw $6, 32($4) 280 | sw $7, 36($4) 281 | 282 | sw $16, 72($4) 283 | sw $17, 76($4) 284 | sw $18, 80($4) 285 | sw $19, 84($4) 286 | sw $20, 88($4) 287 | sw $21, 92($4) 288 | sw $22, 96($4) 289 | sw $23, 100($4) 290 | 291 | sw $28, 120($4) /* gp */ 292 | sw $29, 124($4) /* sp */ 293 | sw $30, 128($4) /* fp */ 294 | sw $31, 132($4) /* ra */ 295 | 296 | xor $2, $2, $2 297 | j $31 298 | nop 299 | 300 | .globl SET 301 | SET: 302 | lw $16, 72($4) 303 | lw $17, 76($4) 304 | lw $18, 80($4) 305 | lw $19, 84($4) 306 | lw $20, 88($4) 307 | lw $21, 92($4) 308 | lw $22, 96($4) 309 | lw $23, 100($4) 310 | 311 | lw $28, 120($4) /* gp */ 312 | lw $29, 124($4) /* sp */ 313 | lw $30, 128($4) /* fp */ 314 | 315 | /* 316 | * If we set $31 directly and j $31, 317 | * we would loose the outer return address. 318 | * Use a temporary register, then. 319 | */ 320 | lw $8, 132($4) /* ra */ 321 | 322 | /* bug: not setting the pc causes a bus error */ 323 | lw $25, 132($4) /* pc */ 324 | 325 | lw $5, 28($4) 326 | lw $6, 32($4) 327 | lw $7, 36($4) 328 | lw $4, 24($4) 329 | 330 | j $8 331 | nop 332 | #endif 333 | -------------------------------------------------------------------------------- /channel.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */ 2 | 3 | #include "taskimpl.h" 4 | 5 | Channel* 6 | chancreate(int elemsize, int bufsize) 7 | { 8 | Channel *c; 9 | 10 | c = malloc(sizeof *c+bufsize*elemsize); 11 | if(c == nil){ 12 | fprint(2, "chancreate malloc: %r"); 13 | exit(1); 14 | } 15 | memset(c, 0, sizeof *c); 16 | c->elemsize = elemsize; 17 | c->bufsize = bufsize; 18 | c->nbuf = 0; 19 | c->buf = (uchar*)(c+1); 20 | return c; 21 | } 22 | 23 | /* bug - work out races */ 24 | void 25 | chanfree(Channel *c) 26 | { 27 | if(c == nil) 28 | return; 29 | free(c->name); 30 | free(c->arecv.a); 31 | free(c->asend.a); 32 | free(c); 33 | } 34 | 35 | static void 36 | addarray(Altarray *a, Alt *alt) 37 | { 38 | if(a->n == a->m){ 39 | a->m += 16; 40 | a->a = realloc(a->a, a->m*sizeof a->a[0]); 41 | } 42 | a->a[a->n++] = alt; 43 | } 44 | 45 | static void 46 | delarray(Altarray *a, int i) 47 | { 48 | --a->n; 49 | a->a[i] = a->a[a->n]; 50 | } 51 | 52 | /* 53 | * doesn't really work for things other than CHANSND and CHANRCV 54 | * but is only used as arg to chanarray, which can handle it 55 | */ 56 | #define otherop(op) (CHANSND+CHANRCV-(op)) 57 | 58 | static Altarray* 59 | chanarray(Channel *c, uint op) 60 | { 61 | switch(op){ 62 | default: 63 | return nil; 64 | case CHANSND: 65 | return &c->asend; 66 | case CHANRCV: 67 | return &c->arecv; 68 | } 69 | } 70 | 71 | static int 72 | altcanexec(Alt *a) 73 | { 74 | Altarray *ar; 75 | Channel *c; 76 | 77 | if(a->op == CHANNOP) 78 | return 0; 79 | c = a->c; 80 | if(c->bufsize == 0){ 81 | ar = chanarray(c, otherop(a->op)); 82 | return ar && ar->n; 83 | }else{ 84 | switch(a->op){ 85 | default: 86 | return 0; 87 | case CHANSND: 88 | return c->nbuf < c->bufsize; 89 | case CHANRCV: 90 | return c->nbuf > 0; 91 | } 92 | } 93 | } 94 | 95 | static void 96 | altqueue(Alt *a) 97 | { 98 | Altarray *ar; 99 | 100 | ar = chanarray(a->c, a->op); 101 | addarray(ar, a); 102 | } 103 | 104 | static void 105 | altdequeue(Alt *a) 106 | { 107 | unsigned int i; 108 | Altarray *ar; 109 | 110 | ar = chanarray(a->c, a->op); 111 | if(ar == nil){ 112 | fprint(2, "bad use of altdequeue op=%d\n", a->op); 113 | abort(); 114 | } 115 | 116 | for(i=0; in; i++) 117 | if(ar->a[i] == a){ 118 | delarray(ar, i); 119 | return; 120 | } 121 | fprint(2, "cannot find self in altdq\n"); 122 | abort(); 123 | } 124 | 125 | static void 126 | altalldequeue(Alt *a) 127 | { 128 | int i; 129 | 130 | for(i=0; a[i].op!=CHANEND && a[i].op!=CHANNOBLK; i++) 131 | if(a[i].op != CHANNOP) 132 | altdequeue(&a[i]); 133 | } 134 | 135 | static void 136 | amove(void *dst, void *src, uint n) 137 | { 138 | if(dst){ 139 | if(src == nil) 140 | memset(dst, 0, n); 141 | else 142 | memmove(dst, src, n); 143 | } 144 | } 145 | 146 | /* 147 | * Actually move the data around. There are up to three 148 | * players: the sender, the receiver, and the channel itself. 149 | * If the channel is unbuffered or the buffer is empty, 150 | * data goes from sender to receiver. If the channel is full, 151 | * the receiver removes some from the channel and the sender 152 | * gets to put some in. 153 | */ 154 | static void 155 | altcopy(Alt *s, Alt *r) 156 | { 157 | Alt *t; 158 | Channel *c; 159 | uchar *cp; 160 | 161 | /* 162 | * Work out who is sender and who is receiver 163 | */ 164 | if(s == nil && r == nil) 165 | return; 166 | assert(s != nil); 167 | c = s->c; 168 | if(s->op == CHANRCV){ 169 | t = s; 170 | s = r; 171 | r = t; 172 | } 173 | assert(s==nil || s->op == CHANSND); 174 | assert(r==nil || r->op == CHANRCV); 175 | 176 | /* 177 | * Channel is empty (or unbuffered) - copy directly. 178 | */ 179 | if(s && r && c->nbuf == 0){ 180 | amove(r->v, s->v, c->elemsize); 181 | return; 182 | } 183 | 184 | /* 185 | * Otherwise it's always okay to receive and then send. 186 | */ 187 | if(r){ 188 | cp = c->buf + c->off*c->elemsize; 189 | amove(r->v, cp, c->elemsize); 190 | --c->nbuf; 191 | if(++c->off == c->bufsize) 192 | c->off = 0; 193 | } 194 | if(s){ 195 | cp = c->buf + (c->off+c->nbuf)%c->bufsize*c->elemsize; 196 | amove(cp, s->v, c->elemsize); 197 | ++c->nbuf; 198 | } 199 | } 200 | 201 | static void 202 | altexec(Alt *a) 203 | { 204 | int i; 205 | Altarray *ar; 206 | Alt *other; 207 | Channel *c; 208 | 209 | c = a->c; 210 | ar = chanarray(c, otherop(a->op)); 211 | if(ar && ar->n){ 212 | i = rand()%ar->n; 213 | other = ar->a[i]; 214 | altcopy(a, other); 215 | altalldequeue(other->xalt); 216 | other->xalt[0].xalt = other; 217 | taskready(other->task); 218 | }else 219 | altcopy(a, nil); 220 | } 221 | 222 | #define dbgalt 0 223 | int 224 | chanalt(Alt *a) 225 | { 226 | int i, j, ncan, n, canblock; 227 | Channel *c; 228 | Task *t; 229 | 230 | needstack(512); 231 | for(i=0; a[i].op != CHANEND && a[i].op != CHANNOBLK; i++) 232 | ; 233 | n = i; 234 | canblock = a[i].op == CHANEND; 235 | 236 | t = taskrunning; 237 | for(i=0; iname) print("%s", c->name); else print("%p", c); } 247 | if(altcanexec(&a[i])){ 248 | if(dbgalt) print("*"); 249 | ncan++; 250 | } 251 | } 252 | if(ncan){ 253 | j = rand()%ncan; 254 | for(i=0; i %c:", "esrnb"[a[i].op]); 260 | if(c->name) print("%s", c->name); else print("%p", c); 261 | print("\n"); 262 | } 263 | altexec(&a[i]); 264 | return i; 265 | } 266 | } 267 | } 268 | } 269 | if(dbgalt)print("\n"); 270 | 271 | if(!canblock) 272 | return -1; 273 | 274 | for(i=0; iuc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(ulong); 51 | sp = tos - 16; 52 | ucp->mc.pc = (long)func; 53 | ucp->mc.sp = (long)sp; 54 | va_start(arg, argc); 55 | ucp->mc.r3 = va_arg(arg, long); 56 | va_end(arg); 57 | } 58 | #endif 59 | 60 | #ifdef NEEDX86MAKECONTEXT 61 | void 62 | makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) 63 | { 64 | int *sp; 65 | 66 | sp = (int*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/4; 67 | sp -= argc; 68 | sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ 69 | memmove(sp, &argc+1, argc*sizeof(int)); 70 | 71 | *--sp = 0; /* return address */ 72 | ucp->uc_mcontext.mc_eip = (long)func; 73 | ucp->uc_mcontext.mc_esp = (int)sp; 74 | } 75 | #endif 76 | 77 | #ifdef NEEDAMD64MAKECONTEXT 78 | void 79 | makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) 80 | { 81 | long *sp; 82 | va_list va; 83 | 84 | memset(&ucp->uc_mcontext, 0, sizeof ucp->uc_mcontext); 85 | if(argc != 2) 86 | *(volatile int*)0 = 0; 87 | va_start(va, argc); 88 | ucp->uc_mcontext.mc_rdi = va_arg(va, int); 89 | ucp->uc_mcontext.mc_rsi = va_arg(va, int); 90 | va_end(va); 91 | sp = (long*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(long); 92 | sp -= argc; 93 | sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ 94 | *--sp = 0; /* return address */ 95 | ucp->uc_mcontext.mc_rip = (long)func; 96 | ucp->uc_mcontext.mc_rsp = (long)sp; 97 | } 98 | #endif 99 | 100 | #ifdef NEEDARMMAKECONTEXT 101 | void 102 | makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) 103 | { 104 | int i, *sp; 105 | va_list arg; 106 | 107 | sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4; 108 | va_start(arg, argc); 109 | for(i=0; i<4 && iuc_mcontext.gregs[i] = va_arg(arg, uint); 111 | va_end(arg); 112 | uc->uc_mcontext.gregs[13] = (uint)sp; 113 | uc->uc_mcontext.gregs[14] = (uint)fn; 114 | } 115 | #endif 116 | 117 | #ifdef NEEDMIPSMAKECONTEXT 118 | void 119 | makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) 120 | { 121 | int i, *sp; 122 | va_list arg; 123 | 124 | va_start(arg, argc); 125 | sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4; 126 | for(i=0; i<4 && iuc_mcontext.mc_regs[i+4] = va_arg(arg, int); 128 | va_end(arg); 129 | uc->uc_mcontext.mc_regs[29] = (int)sp; 130 | uc->uc_mcontext.mc_regs[31] = (int)fn; 131 | } 132 | #endif 133 | 134 | #ifdef NEEDSWAPCONTEXT 135 | int 136 | swapcontext(ucontext_t *oucp, const ucontext_t *ucp) 137 | { 138 | if(getcontext(oucp) == 0) 139 | setcontext(ucp); 140 | return 0; 141 | } 142 | #endif 143 | 144 | -------------------------------------------------------------------------------- /echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | enum 10 | { 11 | STACK = 32768 12 | }; 13 | 14 | static int verbose; 15 | 16 | char *server; 17 | void echotask(void*); 18 | 19 | int* 20 | mkfd2(int fd1, int fd2) 21 | { 22 | int *a; 23 | 24 | a = malloc(2*sizeof a[0]); 25 | if(a == 0){ 26 | fprintf(stderr, "out of memory\n"); 27 | abort(); 28 | } 29 | a[0] = fd1; 30 | a[1] = fd2; 31 | return a; 32 | } 33 | 34 | void 35 | taskmain(int argc, char **argv) 36 | { 37 | int cfd, fd; 38 | int rport; 39 | char remote[46]; 40 | 41 | if(argc != 3){ 42 | fprintf(stderr, "usage: tcpproxy server port\n"); 43 | taskexitall(1); 44 | } 45 | server = argv[1]; 46 | 47 | if((fd = netannounce(TCP, 0, atoi(argv[2]))) < 0){ 48 | fprintf(stderr, "cannot announce on tcp port %d: %s\n", atoi(argv[1]), strerror(errno)); 49 | taskexitall(1); 50 | } 51 | if(fdnoblock(fd) < 0){ 52 | fprintf(stderr, "fdnoblock\n"); 53 | taskexitall(1); 54 | } 55 | while((cfd = netaccept(fd, remote, &rport)) >= 0){ 56 | if(verbose) 57 | fprintf(stderr, "connection from %s:%d\n", remote, rport); 58 | taskcreate(echotask, (void*)(uintptr_t)cfd, STACK); 59 | } 60 | } 61 | 62 | void 63 | echotask(void *v) 64 | { 65 | char buf[512]; 66 | int fd, n; 67 | 68 | fd = (int)(uintptr_t)v; 69 | 70 | while((n = fdread(fd, buf, sizeof buf)) > 0) 71 | fdwrite(fd, buf, n); 72 | close(fd); 73 | } 74 | -------------------------------------------------------------------------------- /fd.c: -------------------------------------------------------------------------------- 1 | #include "taskimpl.h" 2 | #if USE_EPOLL 3 | #include 4 | #else 5 | #include 6 | #endif 7 | #include 8 | 9 | #if USE_EPOLL 10 | static int epfd; 11 | #else 12 | enum 13 | { 14 | MAXFD = 1024 15 | }; 16 | 17 | static struct pollfd pollfd[MAXFD]; 18 | static Task *polltask[MAXFD]; 19 | static int npollfd; 20 | #endif 21 | 22 | static int startedfdtask; 23 | static Tasklist sleeping; 24 | static int sleepingcounted; 25 | static uvlong nsec(void); 26 | 27 | #if USE_EPOLL 28 | 29 | void 30 | fdtask(void *v) 31 | { 32 | struct epoll_event events[1000]; 33 | int i, ms, nevents; 34 | Task *t; 35 | uvlong now; 36 | 37 | USED(v); 38 | tasksystem(); 39 | taskname("fdtask"); 40 | for(;;){ 41 | /* let everyone else run */ 42 | while(taskyield() > 0) 43 | ; 44 | /* we're the only one runnable - poll for i/o */ 45 | errno = 0; 46 | taskstate("epoll"); 47 | if((t=sleeping.head) == nil) 48 | ms = -1; 49 | else{ 50 | /* sleep at most 5s */ 51 | now = nsec(); 52 | if(now >= t->alarmtime) 53 | ms = 0; 54 | else if(now+5*1000*1000*1000LL >= t->alarmtime) 55 | ms = (t->alarmtime - now)/1000000; 56 | else 57 | ms = 5000; 58 | } 59 | if((nevents = epoll_wait(epfd, events, 1000, ms)) < 0){ 60 | if(errno == EINTR) 61 | continue; 62 | fprint(2, "epoll: %s\n", strerror(errno)); 63 | taskexitall(0); 64 | } 65 | 66 | /* wake up the guys who deserve it */ 67 | for(i=0; i= t->alarmtime){ 73 | deltask(&sleeping, t); 74 | if(!t->system && --sleepingcounted == 0) 75 | taskcount--; 76 | taskready(t); 77 | } 78 | } 79 | } 80 | 81 | #else 82 | 83 | void 84 | fdtask(void *v) 85 | { 86 | int i, ms; 87 | Task *t; 88 | uvlong now; 89 | 90 | USED(v); 91 | tasksystem(); 92 | taskname("fdtask"); 93 | for(;;){ 94 | /* let everyone else run */ 95 | while(taskyield() > 0) 96 | ; 97 | /* we're the only one runnable - poll for i/o */ 98 | errno = 0; 99 | taskstate("poll"); 100 | if((t=sleeping.head) == nil) 101 | ms = -1; 102 | else{ 103 | /* sleep at most 5s */ 104 | now = nsec(); 105 | if(now >= t->alarmtime) 106 | ms = 0; 107 | else if(now+5*1000*1000*1000LL >= t->alarmtime) 108 | ms = (t->alarmtime - now)/1000000; 109 | else 110 | ms = 5000; 111 | } 112 | if(poll(pollfd, npollfd, ms) < 0){ 113 | if(errno == EINTR) 114 | continue; 115 | fprint(2, "poll: %s\n", strerror(errno)); 116 | taskexitall(0); 117 | } 118 | 119 | /* wake up the guys who deserve it */ 120 | for(i=0; i= t->alarmtime){ 131 | deltask(&sleeping, t); 132 | if(!t->system && --sleepingcounted == 0) 133 | taskcount--; 134 | taskready(t); 135 | } 136 | } 137 | } 138 | 139 | #endif 140 | 141 | uint 142 | taskdelay(uint ms) 143 | { 144 | uvlong when, now; 145 | Task *t; 146 | 147 | if(!startedfdtask){ 148 | startedfdtask = 1; 149 | #if USE_EPOLL 150 | epfd = epoll_create(1); 151 | if(epfd < 0) 152 | abort(); 153 | #endif 154 | taskcreate(fdtask, 0, 32768); 155 | } 156 | 157 | now = nsec(); 158 | when = now+(uvlong)ms*1000000; 159 | for(t=sleeping.head; t!=nil && t->alarmtime < when; t=t->next) 160 | ; 161 | 162 | if(t){ 163 | taskrunning->prev = t->prev; 164 | taskrunning->next = t; 165 | }else{ 166 | taskrunning->prev = sleeping.tail; 167 | taskrunning->next = nil; 168 | } 169 | 170 | t = taskrunning; 171 | t->alarmtime = when; 172 | if(t->prev) 173 | t->prev->next = t; 174 | else 175 | sleeping.head = t; 176 | if(t->next) 177 | t->next->prev = t; 178 | else 179 | sleeping.tail = t; 180 | 181 | if(!t->system && sleepingcounted++ == 0) 182 | taskcount++; 183 | taskswitch(); 184 | 185 | return (nsec() - now)/1000000; 186 | } 187 | 188 | #if USE_EPOLL 189 | 190 | void 191 | fdwait(int fd, int rw) 192 | { 193 | struct epoll_event ev; 194 | int bits, duped, r; 195 | 196 | if(!startedfdtask){ 197 | startedfdtask = 1; 198 | epfd = epoll_create(1); 199 | if(epfd < 0) 200 | abort(); 201 | taskcreate(fdtask, 0, 32768); 202 | } 203 | 204 | taskstate("fdwait for %s", rw=='r' ? "read" : rw=='w' ? "write" : "error"); 205 | bits = 0; 206 | switch(rw){ 207 | case 'r': 208 | bits |= EPOLLIN | EPOLLPRI; 209 | break; 210 | case 'w': 211 | bits |= EPOLLOUT; 212 | break; 213 | } 214 | 215 | memset(&ev, 0, sizeof(ev)); 216 | ev.events = bits; 217 | ev.data.ptr = taskrunning; 218 | r = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); 219 | duped = 0; 220 | if(r < 0 || errno == EEXIST){ 221 | duped = 1; 222 | fd = dup(fd); 223 | if(fd < 0) 224 | abort(); 225 | r = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); 226 | if(r != 0) 227 | abort(); 228 | } 229 | taskswitch(); 230 | epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev); 231 | if(duped) 232 | close(fd); 233 | } 234 | 235 | #else 236 | 237 | void 238 | fdwait(int fd, int rw) 239 | { 240 | int bits; 241 | 242 | if(!startedfdtask){ 243 | startedfdtask = 1; 244 | taskcreate(fdtask, 0, 32768); 245 | } 246 | 247 | if(npollfd >= MAXFD){ 248 | fprint(2, "too many poll file descriptors\n"); 249 | abort(); 250 | } 251 | 252 | taskstate("fdwait for %s", rw=='r' ? "read" : rw=='w' ? "write" : "error"); 253 | bits = 0; 254 | switch(rw){ 255 | case 'r': 256 | bits |= POLLIN; 257 | break; 258 | case 'w': 259 | bits |= POLLOUT; 260 | break; 261 | } 262 | 263 | polltask[npollfd] = taskrunning; 264 | pollfd[npollfd].fd = fd; 265 | pollfd[npollfd].events = bits; 266 | pollfd[npollfd].revents = 0; 267 | npollfd++; 268 | taskswitch(); 269 | } 270 | 271 | #endif 272 | 273 | /* Like fdread but always calls fdwait before reading. */ 274 | int 275 | fdread1(int fd, void *buf, int n) 276 | { 277 | int m; 278 | 279 | do 280 | fdwait(fd, 'r'); 281 | while((m = read(fd, buf, n)) < 0 && errno == EAGAIN); 282 | return m; 283 | } 284 | 285 | int 286 | fdread(int fd, void *buf, int n) 287 | { 288 | int m; 289 | 290 | while((m=read(fd, buf, n)) < 0 && errno == EAGAIN) 291 | fdwait(fd, 'r'); 292 | return m; 293 | } 294 | 295 | int 296 | fdreadn(int fd, void *buf, int n) 297 | { 298 | int m, tot; 299 | 300 | for(tot=0; tot 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | enum 9 | { 10 | STACK = 32768 11 | }; 12 | 13 | char *server; 14 | char *url; 15 | 16 | void fetchtask(void*); 17 | 18 | void 19 | taskmain(int argc, char **argv) 20 | { 21 | int i, n; 22 | 23 | if(argc != 4){ 24 | fprintf(stderr, "usage: httpload n server url\n"); 25 | taskexitall(1); 26 | } 27 | n = atoi(argv[1]); 28 | server = argv[2]; 29 | url = argv[3]; 30 | 31 | for(i=0; i 1) 34 | ; 35 | sleep(1); 36 | } 37 | } 38 | 39 | void 40 | fetchtask(void *v) 41 | { 42 | int fd, n; 43 | char buf[512]; 44 | 45 | (void)v; 46 | 47 | fprintf(stderr, "starting...\n"); 48 | for(;;){ 49 | if((fd = netdial(TCP, server, 80)) < 0){ 50 | fprintf(stderr, "dial %s: %s (%s)\n", server, strerror(errno), taskgetstate()); 51 | continue; 52 | } 53 | snprintf(buf, sizeof buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", url, server); 54 | fdwrite(fd, buf, strlen(buf)); 55 | while((n = fdread(fd, buf, sizeof buf)) > 0) 56 | ; 57 | close(fd); 58 | write(1, ".", 1); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ip.c: -------------------------------------------------------------------------------- 1 | #include "taskimpl.h" 2 | #include 3 | #include 4 | #include "ip.h" 5 | 6 | void 7 | hnputl(void *p, uint v) 8 | { 9 | uchar *a; 10 | 11 | a = p; 12 | a[0] = v>>24; 13 | a[1] = v>>16; 14 | a[2] = v>>8; 15 | a[3] = v; 16 | } 17 | 18 | void 19 | hnputs(void *p, ushort v) 20 | { 21 | uchar *a; 22 | 23 | a = p; 24 | a[0] = v>>8; 25 | a[1] = v; 26 | } 27 | 28 | uint32_t 29 | nhgetl(void *p) 30 | { 31 | unsigned char *a; 32 | 33 | a = p; 34 | return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); 35 | } 36 | 37 | uint16_t 38 | nhgets(void *p) 39 | { 40 | unsigned char *a; 41 | 42 | a = p; 43 | return (a[0]<<8)|(a[1]<<0); 44 | } 45 | 46 | char* 47 | v4parseip(unsigned char *to, char *from) 48 | { 49 | int i; 50 | char *p; 51 | 52 | p = from; 53 | for(i = 0; i < 4 && *p; i++){ 54 | to[i] = strtoul(p, &p, 0); 55 | if(*p == '.') 56 | p++; 57 | } 58 | switch(CLASS(to)){ 59 | case 0: /* class A - 1 uchar net */ 60 | case 1: 61 | if(i == 3){ 62 | to[3] = to[2]; 63 | to[2] = to[1]; 64 | to[1] = 0; 65 | } else if (i == 2){ 66 | to[3] = to[1]; 67 | to[1] = 0; 68 | } 69 | break; 70 | case 2: /* class B - 2 uchar net */ 71 | if(i == 3){ 72 | to[3] = to[2]; 73 | to[2] = 0; 74 | } 75 | break; 76 | } 77 | return p; 78 | } 79 | 80 | static int 81 | ipcharok(int c) 82 | { 83 | return c == '.' || c == ':' || (isascii(c) && isxdigit(c)); 84 | } 85 | 86 | static int 87 | delimchar(int c) 88 | { 89 | if(c == '\0') 90 | return 1; 91 | if(c == '.' || c == ':' || (isascii(c) && isalnum(c))) 92 | return 0; 93 | return 1; 94 | } 95 | 96 | uint32_t 97 | parseip(uchar *to, char *from) 98 | { 99 | int i, elipsis = 0, v4 = 1; 100 | uint32_t x; 101 | char *p, *op; 102 | 103 | memset(to, 0, IPaddrlen); 104 | p = from; 105 | for(i = 0; i < IPaddrlen && ipcharok(*p); i+=2){ 106 | op = p; 107 | x = strtoul(p, &p, 16); 108 | if(*p == '.' || (*p == 0 && i == 0)){ /* ends with v4? */ 109 | p = v4parseip(to+i, op); 110 | i += 4; 111 | break; 112 | } 113 | /* v6: at most 4 hex digits, followed by colon or delim */ 114 | if(x != (ushort)x || (*p != ':' && !delimchar(*p))) { 115 | memset(to, 0, IPaddrlen); 116 | return -1; /* parse error */ 117 | } 118 | to[i] = x>>8; 119 | to[i+1] = x; 120 | if(*p == ':'){ 121 | v4 = 0; 122 | if(*++p == ':'){ /* :: is elided zero short(s) */ 123 | if (elipsis) { 124 | memset(to, 0, IPaddrlen); 125 | return -1; /* second :: */ 126 | } 127 | elipsis = i+2; 128 | p++; 129 | } 130 | } else if (p == op) /* strtoul made no progress? */ 131 | break; 132 | } 133 | if (p == from || !delimchar(*p)) { 134 | memset(to, 0, IPaddrlen); 135 | return -1; /* parse error */ 136 | } 137 | if(i < IPaddrlen){ 138 | memmove(&to[elipsis+IPaddrlen-i], &to[elipsis], i-elipsis); 139 | memset(&to[elipsis], 0, IPaddrlen-i); 140 | } 141 | if(v4){ 142 | to[10] = to[11] = 0xff; 143 | return nhgetl(to + IPv4off); 144 | } else 145 | return 6; 146 | } 147 | 148 | uchar IPnoaddr[IPaddrlen]; 149 | 150 | uchar v4prefix[IPaddrlen] = { 151 | 0, 0, 0, 0, 152 | 0, 0, 0, 0, 153 | 0, 0, 0xff, 0xff, 154 | 0, 0, 0, 0 155 | }; 156 | 157 | int 158 | isv4(uchar *ip) 159 | { 160 | return memcmp(ip, v4prefix, IPv4off) == 0; 161 | } 162 | 163 | void 164 | v4tov6(unsigned char *v6, unsigned char *v4) 165 | { 166 | v6[0] = 0; 167 | v6[1] = 0; 168 | v6[2] = 0; 169 | v6[3] = 0; 170 | v6[4] = 0; 171 | v6[5] = 0; 172 | v6[6] = 0; 173 | v6[7] = 0; 174 | v6[8] = 0; 175 | v6[9] = 0; 176 | v6[10] = 0xff; 177 | v6[11] = 0xff; 178 | v6[12] = v4[0]; 179 | v6[13] = v4[1]; 180 | v6[14] = v4[2]; 181 | v6[15] = v4[3]; 182 | } 183 | 184 | int 185 | v6tov4(unsigned char *v4, unsigned char *v6) 186 | { 187 | if(v6[0] == 0 188 | && v6[1] == 0 189 | && v6[2] == 0 190 | && v6[3] == 0 191 | && v6[4] == 0 192 | && v6[5] == 0 193 | && v6[6] == 0 194 | && v6[7] == 0 195 | && v6[8] == 0 196 | && v6[9] == 0 197 | && v6[10] == 0xff 198 | && v6[11] == 0xff) 199 | { 200 | v4[0] = v6[12]; 201 | v4[1] = v6[13]; 202 | v4[2] = v6[14]; 203 | v4[3] = v6[15]; 204 | return 0; 205 | } else { 206 | memset(v4, 0, 4); 207 | if(memcmp(v6, IPnoaddr, IPaddrlen) == 0) 208 | return 0; 209 | return -1; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /ip.h: -------------------------------------------------------------------------------- 1 | #ifndef _IP_H_ 2 | #define _IP_H_ 1 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | enum 9 | { 10 | IPaddrlen = 16, 11 | IPv4addrlen = 4, 12 | IPv4off = 12 13 | }; 14 | 15 | int isv4(unsigned char*); 16 | uint32_t parseip(unsigned char*, char*); 17 | char* v4parseip(unsigned char*, char*); 18 | 19 | void hnputl(void*, uint32_t); 20 | void hnputs(void*, uint16_t); 21 | uint32_t nhgetl(void*); 22 | uint16_t nhgets(void*); 23 | 24 | int v6tov4(unsigned char*, unsigned char*); 25 | void v4tov6(unsigned char*, unsigned char*); 26 | 27 | #define CLASS(p) ((*(uchar*)(p))>>6) 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | #endif 33 | -------------------------------------------------------------------------------- /makesun: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | case "x$CC" in 4 | x|xcc) 5 | CC=cc 6 | CFLAGS="-mt -g -O -c -xCC -D__sun__ -I." 7 | ;; 8 | xgcc) 9 | CC=gcc 10 | CFLAGS="-Wall -c -I." 11 | ;; 12 | *) 13 | echo 'unknown $CC' 14 | exit 1 15 | esac 16 | 17 | u=`uname` 18 | v=`uname -r` 19 | s=`echo $u$v | tr '. ' '__'` 20 | CFLAGS="$CFLAGS -D__${s}__" 21 | 22 | make "CC=$CC" "CFLAGS=$CFLAGS" "ASM=" "TCPLIBS=-lsocket -lnsl" 23 | -------------------------------------------------------------------------------- /mips-ucontext.h: -------------------------------------------------------------------------------- 1 | typedef struct mcontext mcontext_t; 2 | typedef struct ucontext ucontext_t; 3 | 4 | extern int swapcontext(ucontext_t*, const ucontext_t*); 5 | extern void makecontext(ucontext_t*, void(*)(), int, ...); 6 | 7 | /* 8 | * Copyright (c) 1992, 1993 9 | * The Regents of the University of California. All rights reserved. 10 | * 11 | * This code is derived from software contributed to Berkeley by 12 | * Ralph Campbell. 13 | * 14 | * Redistribution and use in source and binary forms, with or without 15 | * modification, are permitted provided that the following conditions 16 | * are met: 17 | * 1. Redistributions of source code must retain the above copyright 18 | * notice, this list of conditions and the following disclaimer. 19 | * 2. Redistributions in binary form must reproduce the above copyright 20 | * notice, this list of conditions and the following disclaimer in the 21 | * documentation and/or other materials provided with the distribution. 22 | * 4. Neither the name of the University nor the names of its contributors 23 | * may be used to endorse or promote products derived from this software 24 | * without specific prior written permission. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 | * SUCH DAMAGE. 37 | * 38 | * @(#)ucontext.h 8.1 (Berkeley) 6/10/93 39 | * JNPR: ucontext.h,v 1.2 2007/08/09 11:23:32 katta 40 | * $FreeBSD: src/sys/mips/include/ucontext.h,v 1.2 2010/01/10 19:50:24 imp Exp $ 41 | */ 42 | 43 | struct mcontext { 44 | /* 45 | * These fields must match the corresponding fields in struct 46 | * sigcontext which follow 'sc_mask'. That way we can support 47 | * struct sigcontext and ucontext_t at the same time. 48 | */ 49 | int mc_onstack; /* sigstack state to restore */ 50 | int mc_pc; /* pc at time of signal */ 51 | int mc_regs[32]; /* processor regs 0 to 31 */ 52 | int sr; /* status register */ 53 | int mullo, mulhi; /* mullo and mulhi registers... */ 54 | int mc_fpused; /* fp has been used */ 55 | int mc_fpregs[33]; /* fp regs 0 to 31 and csr */ 56 | int mc_fpc_eir; /* fp exception instruction reg */ 57 | void *mc_tls; /* pointer to TLS area */ 58 | int __spare__[8]; /* XXX reserved */ 59 | }; 60 | 61 | struct ucontext { 62 | /* 63 | * Keep the order of the first two fields. Also, 64 | * keep them the first two fields in the structure. 65 | * This way we can have a union with struct 66 | * sigcontext and ucontext_t. This allows us to 67 | * support them both at the same time. 68 | * note: the union is not defined, though. 69 | */ 70 | sigset_t uc_sigmask; 71 | mcontext_t uc_mcontext; 72 | 73 | struct __ucontext *uc_link; 74 | stack_t uc_stack; 75 | int uc_flags; 76 | int __spare__[4]; 77 | }; 78 | -------------------------------------------------------------------------------- /net.c: -------------------------------------------------------------------------------- 1 | #include "taskimpl.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ip.h" 10 | 11 | static int 12 | family(unsigned char *addr) 13 | { 14 | if(isv4(addr)) 15 | return AF_INET; 16 | return AF_INET6; 17 | } 18 | 19 | int 20 | netannounce(int istcp, char *server, int port) 21 | { 22 | int fd, n, proto; 23 | struct sockaddr_storage ss; 24 | socklen_t sn; 25 | unsigned char ip[IPaddrlen]; 26 | 27 | taskstate("netannounce"); 28 | proto = istcp ? SOCK_STREAM : SOCK_DGRAM; 29 | memset(&ss, 0, sizeof ss); 30 | if(server != nil && strcmp(server, "*") != 0){ 31 | if(netlookup(server, ip) < 0){ 32 | taskstate("netlookup failed"); 33 | return -1; 34 | } 35 | ss.ss_family = family(ip); 36 | switch(ss.ss_family){ 37 | case AF_INET: 38 | v6tov4((unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr, ip); 39 | break; 40 | case AF_INET6: 41 | memcpy(&((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, ip, sizeof(struct in6_addr)); 42 | break; 43 | } 44 | }else{ 45 | ss.ss_family = AF_INET6; 46 | ((struct sockaddr_in6*)&ss)->sin6_addr = in6addr_any; 47 | } 48 | switch(ss.ss_family){ 49 | case AF_INET: 50 | hnputs(&((struct sockaddr_in*)&ss)->sin_port, port); 51 | break; 52 | case AF_INET6: 53 | hnputs(&((struct sockaddr_in6*)&ss)->sin6_port, port); 54 | break; 55 | } 56 | if((fd = socket(ss.ss_family, proto, 0)) < 0){ 57 | taskstate("socket failed"); 58 | return -1; 59 | } 60 | 61 | /* set reuse flag for tcp */ 62 | sn = sizeof n; 63 | if(istcp && getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0){ 64 | n = 1; 65 | if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n) < 0){ 66 | close(fd); 67 | return -1; 68 | } 69 | } 70 | 71 | if(bind(fd, (struct sockaddr*)&ss, sizeof ss) < 0){ 72 | taskstate("bind failed"); 73 | close(fd); 74 | return -1; 75 | } 76 | 77 | if(proto == SOCK_STREAM) 78 | listen(fd, 16); 79 | 80 | if(fdnoblock(fd) < 0){ 81 | close(fd); 82 | return -1; 83 | } 84 | taskstate("netannounce succeeded"); 85 | return fd; 86 | } 87 | 88 | int 89 | netaccept(int fd, char *server, int *port) 90 | { 91 | int cfd, one; 92 | struct sockaddr_storage ss; 93 | socklen_t len; 94 | 95 | fdwait(fd, 'r'); 96 | 97 | taskstate("netaccept"); 98 | len = sizeof ss; 99 | if((cfd = accept(fd, (void*)&ss, &len)) < 0){ 100 | taskstate("accept failed"); 101 | return -1; 102 | } 103 | 104 | switch(ss.ss_family){ 105 | case AF_INET: 106 | if(server) 107 | inet_ntop(AF_INET, &((struct sockaddr_in*)&ss)->sin_addr.s_addr, server, INET_ADDRSTRLEN); 108 | if(port) 109 | *port = nhgets(&((struct sockaddr_in*)&ss)->sin_port); 110 | break; 111 | case AF_INET6: 112 | if(server) 113 | inet_ntop(AF_INET6, &((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, server, INET6_ADDRSTRLEN); 114 | if(port) 115 | *port = nhgets(&((struct sockaddr_in6*)&ss)->sin6_port); 116 | break; 117 | } 118 | if(fdnoblock(cfd) < 0){ 119 | close(cfd); 120 | return -1; 121 | } 122 | one = 1; 123 | if(setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one) < 0){ 124 | close(cfd); 125 | return -1; 126 | } 127 | taskstate("netaccept succeeded"); 128 | return cfd; 129 | } 130 | 131 | int 132 | netlookup(char *name, unsigned char *ip) 133 | { 134 | struct addrinfo *result; 135 | 136 | if(parseip(ip, name) == 0) 137 | return 0; 138 | 139 | /* BUG - Name resolution blocks. Need a non-blocking DNS. */ 140 | taskstate("netlookup"); 141 | if(getaddrinfo(name, NULL, NULL, &result) == 0) { 142 | switch (result->ai_family) { 143 | case AF_INET: 144 | v4tov6(ip, (unsigned char*)&((struct sockaddr_in*)result->ai_addr)->sin_addr.s_addr); 145 | break; 146 | case AF_INET6: 147 | memcpy(ip, (unsigned char*)&((struct sockaddr_in6*)result->ai_addr)->sin6_addr.s6_addr, sizeof(struct in6_addr)); 148 | break; 149 | } 150 | taskstate("netlookup succeeded"); 151 | freeaddrinfo(result); 152 | return 0; 153 | } 154 | 155 | taskstate("netlookup failed"); 156 | freeaddrinfo(result); 157 | return -1; 158 | } 159 | 160 | int 161 | netdial(int istcp, char *server, int port) 162 | { 163 | int proto, fd, n; 164 | unsigned char ip[IPaddrlen]; 165 | struct sockaddr_storage ss; 166 | socklen_t sn; 167 | 168 | if(netlookup(server, ip) < 0) 169 | return -1; 170 | 171 | taskstate("netdial"); 172 | proto = istcp ? SOCK_STREAM : SOCK_DGRAM; 173 | if((fd = socket(family(ip), proto, 0)) < 0){ 174 | taskstate("socket failed"); 175 | return -1; 176 | } 177 | if(fdnoblock(fd) < 0){ 178 | close(fd); 179 | return -1; 180 | } 181 | 182 | /* for udp */ 183 | if(!istcp){ 184 | n = 1; 185 | if(setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof n) < 0){ 186 | close(fd); 187 | return -1; 188 | } 189 | } 190 | 191 | /* start connecting */ 192 | memset(&ss, 0, sizeof ss); 193 | ss.ss_family = family(ip); 194 | switch(ss.ss_family){ 195 | case AF_INET: 196 | v6tov4((unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr, ip); 197 | hnputs(&((struct sockaddr_in*)&ss)->sin_port, port); 198 | break; 199 | case AF_INET6: 200 | memcpy(&((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, ip, sizeof(struct in6_addr)); 201 | hnputs(&((struct sockaddr_in6*)&ss)->sin6_port, port); 202 | break; 203 | } 204 | 205 | if(connect(fd, (struct sockaddr*)&ss, sizeof ss) < 0 && errno != EINPROGRESS){ 206 | taskstate("connect failed"); 207 | close(fd); 208 | return -1; 209 | } 210 | 211 | /* wait for finish */ 212 | fdwait(fd, 'w'); 213 | sn = sizeof ss; 214 | if(getpeername(fd, (struct sockaddr*)&ss, &sn) >= 0){ 215 | taskstate("connect succeeded"); 216 | return fd; 217 | } 218 | 219 | /* report error */ 220 | sn = sizeof n; 221 | if(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&n, &sn) < 0){ 222 | close(fd); 223 | return -1; 224 | } 225 | if(n == 0) 226 | n = ECONNREFUSED; 227 | close(fd); 228 | taskstate("connect failed"); 229 | errno = n; 230 | return -1; 231 | } 232 | 233 | -------------------------------------------------------------------------------- /power-ucontext.h: -------------------------------------------------------------------------------- 1 | #define setcontext(u) _setmcontext(&(u)->mc) 2 | #define getcontext(u) _getmcontext(&(u)->mc) 3 | typedef struct mcontext mcontext_t; 4 | typedef struct ucontext ucontext_t; 5 | struct mcontext 6 | { 7 | ulong pc; /* lr */ 8 | ulong cr; /* mfcr */ 9 | ulong ctr; /* mfcr */ 10 | ulong xer; /* mfcr */ 11 | ulong sp; /* callee saved: r1 */ 12 | ulong toc; /* callee saved: r2 */ 13 | ulong r3; /* first arg to function, return register: r3 */ 14 | ulong gpr[19]; /* callee saved: r13-r31 */ 15 | /* 16 | // XXX: currently do not save vector registers or floating-point state 17 | // ulong pad; 18 | // uvlong fpr[18]; / * callee saved: f14-f31 * / 19 | // ulong vr[4*12]; / * callee saved: v20-v31, 256-bits each * / 20 | */ 21 | }; 22 | 23 | struct ucontext 24 | { 25 | struct { 26 | void *ss_sp; 27 | uint ss_size; 28 | } uc_stack; 29 | sigset_t uc_sigmask; 30 | mcontext_t mc; 31 | }; 32 | 33 | void makecontext(ucontext_t*, void(*)(void), int, ...); 34 | int swapcontext(ucontext_t*, const ucontext_t*); 35 | int _getmcontext(mcontext_t*); 36 | void _setmcontext(const mcontext_t*); 37 | 38 | -------------------------------------------------------------------------------- /primes.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int quiet; 9 | int goal; 10 | int buffer; 11 | 12 | void 13 | primetask(void *arg) 14 | { 15 | Channel *c, *nc; 16 | int p, i; 17 | c = arg; 18 | 19 | p = chanrecvul(c); 20 | if(p > goal) 21 | taskexitall(0); 22 | if(!quiet) 23 | printf("%d\n", p); 24 | nc = chancreate(sizeof(unsigned long), buffer); 25 | taskcreate(primetask, nc, 32768); 26 | for(;;){ 27 | i = chanrecvul(c); 28 | if(i%p) 29 | chansendul(nc, i); 30 | } 31 | } 32 | 33 | void 34 | taskmain(int argc, char **argv) 35 | { 36 | int i; 37 | Channel *c; 38 | 39 | if(argc>1) 40 | goal = atoi(argv[1]); 41 | else 42 | goal = 100; 43 | printf("goal=%d\n", goal); 44 | 45 | c = chancreate(sizeof(unsigned long), buffer); 46 | taskcreate(primetask, c, 32768); 47 | for(i=2;; i++) 48 | chansendul(c, i); 49 | } 50 | 51 | void* 52 | emalloc(unsigned long n) 53 | { 54 | return calloc(n ,1); 55 | } 56 | -------------------------------------------------------------------------------- /print.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2004 Russ Cox. See COPYRIGHT. */ 2 | 3 | #include "taskimpl.h" 4 | #include /* for strerror! */ 5 | 6 | /* 7 | * Stripped down print library. Plan 9 interface, new code. 8 | */ 9 | 10 | enum 11 | { 12 | FlagLong = 1<<0, 13 | FlagLongLong = 1<<1, 14 | FlagUnsigned = 1<<2, 15 | }; 16 | 17 | static char* 18 | printstr(char *dst, char *edst, char *s, int size) 19 | { 20 | int l, n, sign; 21 | 22 | sign = 1; 23 | if(size < 0){ 24 | size = -size; 25 | sign = -1; 26 | } 27 | if(dst >= edst) 28 | return dst; 29 | l = strlen(s); 30 | n = l; 31 | if(n < size) 32 | n = size; 33 | if(n >= edst-dst) 34 | n = (edst-dst)-1; 35 | if(l > n) 36 | l = n; 37 | if(sign < 0){ 38 | memmove(dst, s, l); 39 | if(n-l) 40 | memset(dst+l, ' ', n-l); 41 | }else{ 42 | if(n-l) 43 | memset(dst, ' ', n-l); 44 | memmove(dst+n-l, s, l); 45 | } 46 | return dst+n; 47 | } 48 | 49 | char* 50 | vseprint(char *dst, char *edst, char *fmt, va_list arg) 51 | { 52 | int fl, size, sign, base; 53 | char *p, *w; 54 | char cbuf[2]; 55 | 56 | w = dst; 57 | for(p=fmt; *p && wowner == nil){ 10 | l->owner = taskrunning; 11 | return 1; 12 | } 13 | if(!block) 14 | return 0; 15 | addtask(&l->waiting, taskrunning); 16 | taskstate("qlock"); 17 | taskswitch(); 18 | if(l->owner != taskrunning){ 19 | fprint(2, "qlock: owner=%p self=%p oops\n", l->owner, taskrunning); 20 | abort(); 21 | } 22 | return 1; 23 | } 24 | 25 | void 26 | qlock(QLock *l) 27 | { 28 | _qlock(l, 1); 29 | } 30 | 31 | int 32 | canqlock(QLock *l) 33 | { 34 | return _qlock(l, 0); 35 | } 36 | 37 | void 38 | qunlock(QLock *l) 39 | { 40 | Task *ready; 41 | 42 | if(l->owner == 0){ 43 | fprint(2, "qunlock: owner=0\n"); 44 | abort(); 45 | } 46 | if((l->owner = ready = l->waiting.head) != nil){ 47 | deltask(&l->waiting, ready); 48 | taskready(ready); 49 | } 50 | } 51 | 52 | static int 53 | _rlock(RWLock *l, int block) 54 | { 55 | if(l->writer == nil && l->wwaiting.head == nil){ 56 | l->readers++; 57 | return 1; 58 | } 59 | if(!block) 60 | return 0; 61 | addtask(&l->rwaiting, taskrunning); 62 | taskstate("rlock"); 63 | taskswitch(); 64 | return 1; 65 | } 66 | 67 | void 68 | rlock(RWLock *l) 69 | { 70 | _rlock(l, 1); 71 | } 72 | 73 | int 74 | canrlock(RWLock *l) 75 | { 76 | return _rlock(l, 0); 77 | } 78 | 79 | static int 80 | _wlock(RWLock *l, int block) 81 | { 82 | if(l->writer == nil && l->readers == 0){ 83 | l->writer = taskrunning; 84 | return 1; 85 | } 86 | if(!block) 87 | return 0; 88 | addtask(&l->wwaiting, taskrunning); 89 | taskstate("wlock"); 90 | taskswitch(); 91 | return 1; 92 | } 93 | 94 | void 95 | wlock(RWLock *l) 96 | { 97 | _wlock(l, 1); 98 | } 99 | 100 | int 101 | canwlock(RWLock *l) 102 | { 103 | return _wlock(l, 0); 104 | } 105 | 106 | void 107 | runlock(RWLock *l) 108 | { 109 | Task *t; 110 | 111 | if(--l->readers == 0 && (t = l->wwaiting.head) != nil){ 112 | deltask(&l->wwaiting, t); 113 | l->writer = t; 114 | taskready(t); 115 | } 116 | } 117 | 118 | void 119 | wunlock(RWLock *l) 120 | { 121 | Task *t; 122 | 123 | if(l->writer == nil){ 124 | fprint(2, "wunlock: not locked\n"); 125 | abort(); 126 | } 127 | l->writer = nil; 128 | if(l->readers != 0){ 129 | fprint(2, "wunlock: readers\n"); 130 | abort(); 131 | } 132 | while((t = l->rwaiting.head) != nil){ 133 | deltask(&l->rwaiting, t); 134 | l->readers++; 135 | taskready(t); 136 | } 137 | if(l->readers == 0 && (t = l->wwaiting.head) != nil){ 138 | deltask(&l->wwaiting, t); 139 | l->writer = t; 140 | taskready(t); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /rendez.c: -------------------------------------------------------------------------------- 1 | #include "taskimpl.h" 2 | 3 | /* 4 | * sleep and wakeup 5 | */ 6 | void 7 | tasksleep(Rendez *r) 8 | { 9 | addtask(&r->waiting, taskrunning); 10 | if(r->l) 11 | qunlock(r->l); 12 | taskstate("sleep"); 13 | taskswitch(); 14 | if(r->l) 15 | qlock(r->l); 16 | } 17 | 18 | static int 19 | _taskwakeup(Rendez *r, int all) 20 | { 21 | int i; 22 | Task *t; 23 | 24 | for(i=0;; i++){ 25 | if(i==1 && !all) 26 | break; 27 | if((t = r->waiting.head) == nil) 28 | break; 29 | deltask(&r->waiting, t); 30 | taskready(t); 31 | } 32 | return i; 33 | } 34 | 35 | int 36 | taskwakeup(Rendez *r) 37 | { 38 | return _taskwakeup(r, 0); 39 | } 40 | 41 | int 42 | taskwakeupall(Rendez *r) 43 | { 44 | return _taskwakeup(r, 1); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /task.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */ 2 | 3 | #include "taskimpl.h" 4 | #include 5 | #include 6 | #ifdef USE_VALGRIND 7 | #include 8 | #endif 9 | 10 | int taskdebuglevel; 11 | int taskcount; 12 | int tasknswitch; 13 | int taskexitval; 14 | Task *taskrunning; 15 | 16 | Context taskschedcontext; 17 | Tasklist taskrunqueue; 18 | 19 | Task **alltask; 20 | int nalltask; 21 | 22 | static char *argv0; 23 | static void contextswitch(Context *from, Context *to); 24 | 25 | static void 26 | taskdebug(char *fmt, ...) 27 | { 28 | va_list arg; 29 | char buf[128]; 30 | Task *t; 31 | char *p; 32 | static int fd = -1; 33 | 34 | if(taskdebuglevel == 0) 35 | return; 36 | 37 | va_start(arg, fmt); 38 | vfprint(1, fmt, arg); 39 | va_end(arg); 40 | 41 | if(fd < 0){ 42 | p = strrchr(argv0, '/'); 43 | if(p) 44 | p++; 45 | else 46 | p = argv0; 47 | snprint(buf, sizeof buf, "/tmp/%s.tlog", p); 48 | if((fd = open(buf, O_CREAT|O_WRONLY, 0666)) < 0) 49 | if((fd = open("/dev/null", O_WRONLY)) < 0) 50 | abort(); 51 | } 52 | 53 | va_start(arg, fmt); 54 | vsnprint(buf, sizeof buf, fmt, arg); 55 | va_end(arg); 56 | t = taskrunning; 57 | if(t) 58 | fprint(fd, "%d.%d: %s\n", getpid(), t->id, buf); 59 | else 60 | fprint(fd, "%d._: %s\n", getpid(), buf); 61 | } 62 | 63 | static void 64 | taskstart(uint y, uint x) 65 | { 66 | Task *t; 67 | ulong z; 68 | 69 | z = x<<16; /* hide undefined 32-bit shift from 32-bit compilers */ 70 | z <<= 16; 71 | z |= y; 72 | t = (Task*)z; 73 | 74 | //print("taskstart %p\n", t); 75 | t->startfn(t->startarg); 76 | //print("taskexits %p\n", t); 77 | taskexit(0); 78 | //print("not reacehd\n"); 79 | } 80 | 81 | static int taskidgen; 82 | 83 | static Task* 84 | taskalloc(void (*fn)(void*), void *arg, uint stack) 85 | { 86 | Task *t; 87 | sigset_t zero; 88 | uint x, y; 89 | ulong z; 90 | 91 | /* allocate the task and stack together */ 92 | t = malloc(sizeof *t+stack); 93 | if(t == nil){ 94 | fprint(2, "taskalloc malloc: %r\n"); 95 | abort(); 96 | } 97 | memset(t, 0, sizeof *t); 98 | t->stk = (uchar*)(t+1); 99 | t->stksize = stack; 100 | t->id = ++taskidgen; 101 | t->startfn = fn; 102 | t->startarg = arg; 103 | 104 | /* do a reasonable initialization */ 105 | memset(&t->context.uc, 0, sizeof t->context.uc); 106 | sigemptyset(&zero); 107 | sigprocmask(SIG_BLOCK, &zero, &t->context.uc.uc_sigmask); 108 | 109 | /* must initialize with current context */ 110 | if(getcontext(&t->context.uc) < 0){ 111 | fprint(2, "getcontext: %r\n"); 112 | abort(); 113 | } 114 | 115 | #ifdef USE_VALGRIND 116 | t->vid = VALGRIND_STACK_REGISTER(t->stk+8, t->stk+8 + t->stksize-64); 117 | #endif 118 | 119 | /* call makecontext to do the real work. */ 120 | /* leave a few words open on both ends */ 121 | t->context.uc.uc_stack.ss_sp = t->stk+8; 122 | t->context.uc.uc_stack.ss_size = t->stksize-64; 123 | #if defined(__sun__) && !defined(__MAKECONTEXT_V2_SOURCE) /* sigh */ 124 | #warning "doing sun thing" 125 | /* can avoid this with __MAKECONTEXT_V2_SOURCE but only on SunOS 5.9 */ 126 | t->context.uc.uc_stack.ss_sp = 127 | (char*)t->context.uc.uc_stack.ss_sp 128 | +t->context.uc.uc_stack.ss_size; 129 | #endif 130 | /* 131 | * All this magic is because you have to pass makecontext a 132 | * function that takes some number of word-sized variables, 133 | * and on 64-bit machines pointers are bigger than words. 134 | */ 135 | //print("make %p\n", t); 136 | z = (ulong)t; 137 | y = z; 138 | z >>= 16; /* hide undefined 32-bit shift from 32-bit compilers */ 139 | x = z>>16; 140 | makecontext(&t->context.uc, (void(*)())taskstart, 2, y, x); 141 | 142 | return t; 143 | } 144 | 145 | int 146 | taskcreate(void (*fn)(void*), void *arg, uint stack) 147 | { 148 | int id; 149 | Task *t; 150 | 151 | t = taskalloc(fn, arg, stack); 152 | taskcount++; 153 | id = t->id; 154 | if(nalltask%64 == 0){ 155 | alltask = realloc(alltask, (nalltask+64)*sizeof(alltask[0])); 156 | if(alltask == nil){ 157 | fprint(2, "out of memory\n"); 158 | abort(); 159 | } 160 | } 161 | t->alltaskslot = nalltask; 162 | alltask[nalltask++] = t; 163 | taskready(t); 164 | return id; 165 | } 166 | 167 | void 168 | tasksystem(void) 169 | { 170 | if(!taskrunning->system){ 171 | taskrunning->system = 1; 172 | --taskcount; 173 | } 174 | } 175 | 176 | void 177 | taskswitch(void) 178 | { 179 | needstack(0); 180 | contextswitch(&taskrunning->context, &taskschedcontext); 181 | } 182 | 183 | void 184 | taskready(Task *t) 185 | { 186 | t->ready = 1; 187 | addtask(&taskrunqueue, t); 188 | } 189 | 190 | int 191 | taskyield(void) 192 | { 193 | int n; 194 | 195 | n = tasknswitch; 196 | taskready(taskrunning); 197 | taskstate("yield"); 198 | taskswitch(); 199 | return tasknswitch - n - 1; 200 | } 201 | 202 | int 203 | anyready(void) 204 | { 205 | return taskrunqueue.head != nil; 206 | } 207 | 208 | void 209 | taskexitall(int val) 210 | { 211 | exit(val); 212 | } 213 | 214 | void 215 | taskexit(int val) 216 | { 217 | taskexitval = val; 218 | taskrunning->exiting = 1; 219 | taskswitch(); 220 | #ifdef USE_VALGRIND 221 | VALGRIND_STACK_DEREGISTER(taskrunning->vid); 222 | #endif 223 | } 224 | 225 | static void 226 | contextswitch(Context *from, Context *to) 227 | { 228 | if(swapcontext(&from->uc, &to->uc) < 0){ 229 | fprint(2, "swapcontext failed: %r\n"); 230 | assert(0); 231 | } 232 | } 233 | 234 | static void 235 | taskscheduler(void) 236 | { 237 | int i; 238 | Task *t; 239 | 240 | taskdebug("scheduler enter"); 241 | for(;;){ 242 | if(taskcount == 0) 243 | exit(taskexitval); 244 | t = taskrunqueue.head; 245 | if(t == nil){ 246 | fprint(2, "no runnable tasks! %d tasks stalled\n", taskcount); 247 | exit(1); 248 | } 249 | deltask(&taskrunqueue, t); 250 | t->ready = 0; 251 | taskrunning = t; 252 | tasknswitch++; 253 | taskdebug("run %d (%s)", t->id, t->name); 254 | contextswitch(&taskschedcontext, &t->context); 255 | //print("back in scheduler\n"); 256 | taskrunning = nil; 257 | if(t->exiting){ 258 | if(!t->system) 259 | taskcount--; 260 | i = t->alltaskslot; 261 | alltask[i] = alltask[--nalltask]; 262 | alltask[i]->alltaskslot = i; 263 | free(t); 264 | } 265 | } 266 | } 267 | 268 | void** 269 | taskdata(void) 270 | { 271 | return &taskrunning->udata; 272 | } 273 | 274 | /* 275 | * debugging 276 | */ 277 | void 278 | taskname(char *fmt, ...) 279 | { 280 | va_list arg; 281 | Task *t; 282 | 283 | if(taskdebuglevel == 0) 284 | return; 285 | 286 | t = taskrunning; 287 | va_start(arg, fmt); 288 | vsnprint(t->name, sizeof t->name, fmt, arg); 289 | va_end(arg); 290 | } 291 | 292 | char* 293 | taskgetname(void) 294 | { 295 | return taskrunning->name; 296 | } 297 | 298 | void 299 | taskstate(char *fmt, ...) 300 | { 301 | va_list arg; 302 | Task *t; 303 | 304 | if(taskdebuglevel == 0) 305 | return; 306 | 307 | t = taskrunning; 308 | va_start(arg, fmt); 309 | vsnprint(t->state, sizeof t->name, fmt, arg); 310 | va_end(arg); 311 | } 312 | 313 | char* 314 | taskgetstate(void) 315 | { 316 | return taskrunning->state; 317 | } 318 | 319 | void 320 | needstack(int n) 321 | { 322 | Task *t; 323 | 324 | t = taskrunning; 325 | 326 | if((char*)&t <= (char*)t->stk 327 | || (char*)&t - (char*)t->stk < 256+n){ 328 | fprint(2, "task stack overflow: &t=%p tstk=%p n=%d\n", &t, t->stk, 256+n); 329 | abort(); 330 | } 331 | } 332 | 333 | static void 334 | taskinfo(int s) 335 | { 336 | int i; 337 | Task *t; 338 | char *extra; 339 | 340 | USED(s); 341 | fprint(2, "task list:\n"); 342 | for(i=0; iready) 347 | extra = " (ready)"; 348 | else 349 | extra = ""; 350 | fprint(2, "%6d%c %-20s %s%s\n", 351 | t->id, t->system ? 's' : ' ', 352 | t->name, t->state, extra); 353 | } 354 | } 355 | 356 | /* 357 | * startup 358 | */ 359 | 360 | static int taskargc; 361 | static char **taskargv; 362 | int mainstacksize; 363 | 364 | static void 365 | taskmainstart(void *v) 366 | { 367 | USED(v); 368 | taskname("taskmain"); 369 | taskmain(taskargc, taskargv); 370 | } 371 | 372 | int 373 | main(int argc, char **argv) 374 | { 375 | struct sigaction sa, osa; 376 | 377 | memset(&sa, 0, sizeof sa); 378 | sa.sa_handler = taskinfo; 379 | sa.sa_flags = SA_RESTART; 380 | sigaction(SIGQUIT, &sa, &osa); 381 | 382 | #ifdef SIGINFO 383 | sigaction(SIGINFO, &sa, &osa); 384 | #endif 385 | 386 | argv0 = argv[0]; 387 | taskargc = argc; 388 | taskargv = argv; 389 | 390 | if(mainstacksize == 0) 391 | mainstacksize = 256*1024; 392 | taskcreate(taskmainstart, nil, mainstacksize); 393 | taskscheduler(); 394 | fprint(2, "taskscheduler returned in main!\n"); 395 | abort(); 396 | return 0; 397 | } 398 | 399 | /* 400 | * hooray for linked lists 401 | */ 402 | void 403 | addtask(Tasklist *l, Task *t) 404 | { 405 | if(l->tail){ 406 | l->tail->next = t; 407 | t->prev = l->tail; 408 | }else{ 409 | l->head = t; 410 | t->prev = nil; 411 | } 412 | l->tail = t; 413 | t->next = nil; 414 | } 415 | 416 | void 417 | deltask(Tasklist *l, Task *t) 418 | { 419 | if(t->prev) 420 | t->prev->next = t->next; 421 | else 422 | l->head = t->next; 423 | if(t->next) 424 | t->next->prev = t->prev; 425 | else 426 | l->tail = t->prev; 427 | } 428 | 429 | unsigned int 430 | taskid(void) 431 | { 432 | return taskrunning->id; 433 | } 434 | 435 | -------------------------------------------------------------------------------- /task.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */ 2 | 3 | #ifndef _TASK_H_ 4 | #define _TASK_H_ 1 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | /* 14 | * basic procs and threads 15 | */ 16 | 17 | typedef struct Task Task; 18 | typedef struct Tasklist Tasklist; 19 | 20 | int anyready(void); 21 | int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize); 22 | void taskexit(int); 23 | void taskexitall(int); 24 | void taskmain(int argc, char *argv[]); 25 | int taskyield(void); 26 | void** taskdata(void); 27 | void needstack(int); 28 | void taskname(char*, ...); 29 | void taskstate(char*, ...); 30 | char* taskgetname(void); 31 | char* taskgetstate(void); 32 | void tasksystem(void); 33 | unsigned int taskdelay(unsigned int); 34 | unsigned int taskid(void); 35 | 36 | struct Tasklist /* used internally */ 37 | { 38 | Task *head; 39 | Task *tail; 40 | }; 41 | 42 | /* 43 | * queuing locks 44 | */ 45 | typedef struct QLock QLock; 46 | struct QLock 47 | { 48 | Task *owner; 49 | Tasklist waiting; 50 | }; 51 | 52 | void qlock(QLock*); 53 | int canqlock(QLock*); 54 | void qunlock(QLock*); 55 | 56 | /* 57 | * reader-writer locks 58 | */ 59 | typedef struct RWLock RWLock; 60 | struct RWLock 61 | { 62 | int readers; 63 | Task *writer; 64 | Tasklist rwaiting; 65 | Tasklist wwaiting; 66 | }; 67 | 68 | void rlock(RWLock*); 69 | int canrlock(RWLock*); 70 | void runlock(RWLock*); 71 | 72 | void wlock(RWLock*); 73 | int canwlock(RWLock*); 74 | void wunlock(RWLock*); 75 | 76 | /* 77 | * sleep and wakeup (condition variables) 78 | */ 79 | typedef struct Rendez Rendez; 80 | 81 | struct Rendez 82 | { 83 | QLock *l; 84 | Tasklist waiting; 85 | }; 86 | 87 | void tasksleep(Rendez*); 88 | int taskwakeup(Rendez*); 89 | int taskwakeupall(Rendez*); 90 | 91 | /* 92 | * channel communication 93 | */ 94 | typedef struct Alt Alt; 95 | typedef struct Altarray Altarray; 96 | typedef struct Channel Channel; 97 | 98 | enum 99 | { 100 | CHANEND, 101 | CHANSND, 102 | CHANRCV, 103 | CHANNOP, 104 | CHANNOBLK, 105 | }; 106 | 107 | struct Alt 108 | { 109 | Channel *c; 110 | void *v; 111 | unsigned int op; 112 | Task *task; 113 | Alt *xalt; 114 | }; 115 | 116 | struct Altarray 117 | { 118 | Alt **a; 119 | unsigned int n; 120 | unsigned int m; 121 | }; 122 | 123 | struct Channel 124 | { 125 | unsigned int bufsize; 126 | unsigned int elemsize; 127 | unsigned char *buf; 128 | unsigned int nbuf; 129 | unsigned int off; 130 | Altarray asend; 131 | Altarray arecv; 132 | char *name; 133 | }; 134 | 135 | int chanalt(Alt *alts); 136 | Channel* chancreate(int elemsize, int elemcnt); 137 | void chanfree(Channel *c); 138 | int chaninit(Channel *c, int elemsize, int elemcnt); 139 | int channbrecv(Channel *c, void *v); 140 | void* channbrecvp(Channel *c); 141 | unsigned long channbrecvul(Channel *c); 142 | int channbsend(Channel *c, void *v); 143 | int channbsendp(Channel *c, void *v); 144 | int channbsendul(Channel *c, unsigned long v); 145 | int chanrecv(Channel *c, void *v); 146 | void* chanrecvp(Channel *c); 147 | unsigned long chanrecvul(Channel *c); 148 | int chansend(Channel *c, void *v); 149 | int chansendp(Channel *c, void *v); 150 | int chansendul(Channel *c, unsigned long v); 151 | 152 | /* 153 | * Threaded I/O. 154 | */ 155 | int fdread(int, void*, int); 156 | int fdread1(int, void*, int); /* always uses fdwait */ 157 | int fdreadn(int, void*, int); 158 | int fdwrite(int, void*, int); 159 | void fdwait(int, int); 160 | int fdnoblock(int); 161 | 162 | void fdtask(void*); 163 | 164 | /* 165 | * Network dialing - sets non-blocking automatically 166 | */ 167 | enum 168 | { 169 | UDP = 0, 170 | TCP = 1, 171 | }; 172 | 173 | int netannounce(int, char*, int); 174 | int netaccept(int, char*, int*); 175 | int netdial(int, char*, int); 176 | int netlookup(char*, unsigned char*); /* blocks entire program! */ 177 | int netdial(int, char*, int); 178 | 179 | #ifdef __cplusplus 180 | } 181 | #endif 182 | #endif 183 | 184 | -------------------------------------------------------------------------------- /taskimpl.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ 2 | 3 | #if defined(__sun__) 4 | # define __EXTENSIONS__ 1 /* SunOS */ 5 | # if defined(__SunOS5_6__) || defined(__SunOS5_7__) || defined(__SunOS5_8__) 6 | /* NOT USING #define __MAKECONTEXT_V2_SOURCE 1 / * SunOS */ 7 | # else 8 | # define __MAKECONTEXT_V2_SOURCE 1 9 | # endif 10 | #endif 11 | 12 | #define USED(x) if(x){}else{} 13 | 14 | #define USE_UCONTEXT 1 15 | 16 | #if defined(__OpenBSD__) || defined(__mips__) 17 | #undef USE_UCONTEXT 18 | #define USE_UCONTEXT 0 19 | #endif 20 | 21 | #if defined(__linux__) && defined(__amd64__) 22 | #define _UCONTEXT_H 1 23 | #define _SYS_UCONTEXT_H 1 24 | #undef USE_UCONTEXT 25 | #define USE_UCONTEXT 0 26 | #endif 27 | 28 | #if defined(__APPLE__) 29 | #include 30 | #if defined(MAC_OS_X_VERSION_10_5) 31 | #undef USE_UCONTEXT 32 | #define USE_UCONTEXT 0 33 | #endif 34 | #endif 35 | 36 | #define USE_EPOLL 1 37 | 38 | #if !defined(__linux) 39 | #undef USE_EPOLL 40 | #define USE_EPOLL 0 41 | #endif 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #if USE_UCONTEXT 55 | #include 56 | #endif 57 | #include 58 | #include 59 | #include "task.h" 60 | 61 | #define nil ((void*)0) 62 | #define nelem(x) (sizeof(x)/sizeof((x)[0])) 63 | 64 | #define ulong task_ulong 65 | #define uint task_uint 66 | #define uchar task_uchar 67 | #define ushort task_ushort 68 | #define uvlong task_uvlong 69 | #define vlong task_vlong 70 | 71 | typedef unsigned long ulong; 72 | typedef unsigned int uint; 73 | typedef unsigned char uchar; 74 | typedef unsigned short ushort; 75 | typedef unsigned long long uvlong; 76 | typedef long long vlong; 77 | 78 | #define print task_print 79 | #define fprint task_fprint 80 | #define snprint task_snprint 81 | #define seprint task_seprint 82 | #define vprint task_vprint 83 | #define vfprint task_vfprint 84 | #define vsnprint task_vsnprint 85 | #define vseprint task_vseprint 86 | #define strecpy task_strecpy 87 | 88 | int print(char*, ...); 89 | int fprint(int, char*, ...); 90 | char *snprint(char*, uint, char*, ...); 91 | char *seprint(char*, char*, char*, ...); 92 | int vprint(char*, va_list); 93 | int vfprint(int, char*, va_list); 94 | char *vsnprint(char*, uint, char*, va_list); 95 | char *vseprint(char*, char*, char*, va_list); 96 | char *strecpy(char*, char*, char*); 97 | 98 | #if defined(__FreeBSD__) && __FreeBSD__ < 5 99 | extern int getmcontext(mcontext_t*); 100 | extern void setmcontext(const mcontext_t*); 101 | #define setcontext(u) setmcontext(&(u)->uc_mcontext) 102 | #define getcontext(u) getmcontext(&(u)->uc_mcontext) 103 | extern int swapcontext(ucontext_t*, const ucontext_t*); 104 | extern void makecontext(ucontext_t*, void(*)(), int, ...); 105 | #endif 106 | 107 | #if defined(__linux__) && defined(__amd64__) 108 | # if defined(__i386__) 109 | # include "386-ucontext.h" 110 | # elif defined(__x86_64__) 111 | # include "amd64-ucontext.h" 112 | # endif 113 | extern int getmcontext(mcontext_t*); 114 | extern void setmcontext(const mcontext_t*); 115 | #define setcontext(u) setmcontext(&(u)->uc_mcontext) 116 | #define getcontext(u) getmcontext(&(u)->uc_mcontext) 117 | extern int swapcontext(ucontext_t*, const ucontext_t*); 118 | extern void makecontext(ucontext_t*, void(*)(), int, ...); 119 | #endif 120 | 121 | #if defined(__APPLE__) 122 | # define mcontext libthread_mcontext 123 | # define mcontext_t libthread_mcontext_t 124 | # define ucontext libthread_ucontext 125 | # define ucontext_t libthread_ucontext_t 126 | # if defined(__i386__) 127 | # include "386-ucontext.h" 128 | # elif defined(__x86_64__) 129 | # include "amd64-ucontext.h" 130 | # else 131 | # include "power-ucontext.h" 132 | # endif 133 | #endif 134 | 135 | #if defined(__OpenBSD__) 136 | # define mcontext libthread_mcontext 137 | # define mcontext_t libthread_mcontext_t 138 | # define ucontext libthread_ucontext 139 | # define ucontext_t libthread_ucontext_t 140 | # if defined __i386__ 141 | # include "386-ucontext.h" 142 | # else 143 | # include "power-ucontext.h" 144 | # endif 145 | extern pid_t rfork_thread(int, void*, int(*)(void*), void*); 146 | #endif 147 | 148 | #if 0 && defined(__sun__) 149 | # define mcontext libthread_mcontext 150 | # define mcontext_t libthread_mcontext_t 151 | # define ucontext libthread_ucontext 152 | # define ucontext_t libthread_ucontext_t 153 | # include "sparc-ucontext.h" 154 | #endif 155 | 156 | #if defined(__arm__) 157 | int getmcontext(mcontext_t*); 158 | void setmcontext(const mcontext_t*); 159 | #define setcontext(u) setmcontext(&(u)->uc_mcontext) 160 | #define getcontext(u) getmcontext(&(u)->uc_mcontext) 161 | #endif 162 | 163 | #if defined(__mips__) 164 | #include "mips-ucontext.h" 165 | int getmcontext(mcontext_t*); 166 | void setmcontext(const mcontext_t*); 167 | #define setcontext(u) setmcontext(&(u)->uc_mcontext) 168 | #define getcontext(u) getmcontext(&(u)->uc_mcontext) 169 | #endif 170 | 171 | typedef struct Context Context; 172 | 173 | enum 174 | { 175 | STACK = 8192 176 | }; 177 | 178 | struct Context 179 | { 180 | ucontext_t uc; 181 | }; 182 | 183 | struct Task 184 | { 185 | char name[256]; // offset known to acid 186 | char state[256]; 187 | Task *next; 188 | Task *prev; 189 | Task *allnext; 190 | Task *allprev; 191 | Context context; 192 | uvlong alarmtime; 193 | uint id; 194 | uchar *stk; 195 | uint stksize; 196 | int exiting; 197 | int alltaskslot; 198 | int system; 199 | int ready; 200 | void (*startfn)(void*); 201 | void *startarg; 202 | void *udata; 203 | #ifdef USE_VALGRIND 204 | int vid; 205 | #endif 206 | }; 207 | 208 | void taskready(Task*); 209 | void taskswitch(void); 210 | 211 | void addtask(Tasklist*, Task*); 212 | void deltask(Tasklist*, Task*); 213 | 214 | extern Task *taskrunning; 215 | extern int taskcount; 216 | -------------------------------------------------------------------------------- /tcpload.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | enum 9 | { 10 | STACK = 32768 11 | }; 12 | 13 | char *server; 14 | int port; 15 | 16 | void fetchtask(void*); 17 | 18 | void 19 | taskmain(int argc, char **argv) 20 | { 21 | int i, n; 22 | 23 | if(argc != 4){ 24 | fprintf(stderr, "usage: httpload n server port\n"); 25 | taskexitall(1); 26 | } 27 | n = atoi(argv[1]); 28 | server = argv[2]; 29 | port = atoi(argv[3]); 30 | 31 | for(i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | enum 10 | { 11 | STACK = 32768 12 | }; 13 | 14 | static int verbose; 15 | 16 | char *server; 17 | int port; 18 | void proxytask(void*); 19 | void rwtask(void*); 20 | 21 | int* 22 | mkfd2(int fd1, int fd2) 23 | { 24 | int *a; 25 | 26 | a = malloc(2*sizeof a[0]); 27 | if(a == 0){ 28 | fprintf(stderr, "out of memory\n"); 29 | abort(); 30 | } 31 | a[0] = fd1; 32 | a[1] = fd2; 33 | return a; 34 | } 35 | 36 | void 37 | taskmain(int argc, char **argv) 38 | { 39 | int cfd, fd; 40 | int rport; 41 | char remote[46]; 42 | 43 | if(argc != 4){ 44 | fprintf(stderr, "usage: tcpproxy localport server remoteport\n"); 45 | taskexitall(1); 46 | } 47 | server = argv[2]; 48 | port = atoi(argv[3]); 49 | 50 | if((fd = netannounce(TCP, 0, atoi(argv[1]))) < 0){ 51 | fprintf(stderr, "cannot announce on tcp port %d: %s\n", atoi(argv[1]), strerror(errno)); 52 | taskexitall(1); 53 | } 54 | if(fdnoblock(fd) < 0){ 55 | fprintf(stderr, "fdnoblock\n"); 56 | taskexitall(1); 57 | } 58 | while((cfd = netaccept(fd, remote, &rport)) >= 0){ 59 | if(verbose) 60 | fprintf(stderr, "connection from %s:%d\n", remote, rport); 61 | taskcreate(proxytask, (void*)(uintptr_t)cfd, STACK); 62 | } 63 | close(fd); 64 | } 65 | 66 | void 67 | proxytask(void *v) 68 | { 69 | int fd, remotefd; 70 | 71 | fd = (int)(uintptr_t)v; 72 | if((remotefd = netdial(TCP, server, port)) < 0){ 73 | close(fd); 74 | return; 75 | } 76 | 77 | if(verbose) 78 | fprintf(stderr, "connected to %s:%d\n", server, port); 79 | 80 | taskcreate(rwtask, mkfd2(fd, remotefd), STACK); 81 | taskcreate(rwtask, mkfd2(remotefd, fd), STACK); 82 | } 83 | 84 | void 85 | rwtask(void *v) 86 | { 87 | int *a, rfd, wfd, n; 88 | char buf[2048]; 89 | 90 | a = v; 91 | rfd = a[0]; 92 | wfd = a[1]; 93 | free(a); 94 | 95 | while((n = fdread(rfd, buf, sizeof buf)) > 0) 96 | fdwrite(wfd, buf, n); 97 | shutdown(wfd, SHUT_WR); 98 | close(rfd); 99 | } 100 | 101 | -------------------------------------------------------------------------------- /testdelay.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | enum { STACK = 32768 }; 9 | 10 | Channel *c; 11 | 12 | void 13 | delaytask(void *v) 14 | { 15 | taskdelay((int)(uintptr_t)v); 16 | printf("awake after %d ms\n", (int)(uintptr_t)v); 17 | chansendul(c, 0); 18 | } 19 | 20 | void 21 | taskmain(int argc, char **argv) 22 | { 23 | int i, n; 24 | 25 | c = chancreate(sizeof(unsigned long), 0); 26 | 27 | n = 0; 28 | for(i=1; i 2 | 3 | void 4 | taskmain(int argc, char *argv[]) 5 | { 6 | taskdelay(1000); 7 | } 8 | --------------------------------------------------------------------------------