├── .gitignore ├── 386-ucontext.h ├── COPYRIGHT ├── Makefile ├── README ├── README.md ├── amd64-ucontext.h ├── asm.S ├── channel.c ├── context.c ├── example.c ├── fd.c ├── httpload.c ├── makesun ├── mips-ucontext.h ├── net.c ├── power-ucontext.h ├── primes.c ├── print.c ├── qlock.c ├── rendez.c ├── task.c ├── task.h ├── taskimpl.h ├── tcpproxy.c ├── testdelay.c └── testdelay1.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | -------------------------------------------------------------------------------- /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 | 16 | all: $(LIB) example primes tcpproxy testdelay 17 | 18 | $(OFILES): taskimpl.h task.h 386-ucontext.h power-ucontext.h 19 | 20 | AS=gcc -c 21 | CC=gcc 22 | CFLAGS=-Wall -c -I. -ggdb 23 | 24 | %.o: %.S 25 | $(AS) $*.S 26 | 27 | %.o: %.c 28 | $(CC) $(CFLAGS) $*.c 29 | 30 | $(LIB): $(OFILES) 31 | ar rvc $(LIB) $(OFILES) 32 | 33 | example: example.o $(LIB) 34 | $(CC) -o example example.o $(LIB) 35 | 36 | primes: primes.o $(LIB) 37 | $(CC) -o primes primes.o $(LIB) 38 | 39 | tcpproxy: tcpproxy.o $(LIB) 40 | $(CC) -o tcpproxy tcpproxy.o $(LIB) $(TCPLIBS) 41 | 42 | httpload: httpload.o $(LIB) 43 | $(CC) -o httpload httpload.o $(LIB) 44 | 45 | testdelay: testdelay.o $(LIB) 46 | $(CC) -o testdelay testdelay.o $(LIB) 47 | 48 | testdelay1: testdelay1.o $(LIB) 49 | $(CC) -o testdelay1 testdelay1.o $(LIB) 50 | 51 | clean: 52 | rm -f *.o example primes tcpproxy testdelay testdelay1 httpload $(LIB) 53 | 54 | install: $(LIB) 55 | cp $(LIB) /usr/local/lib 56 | cp task.h /usr/local/include 57 | 58 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Libtask is a simple coroutine library. It runs on Linux (ARM, MIPS, and x86), 2 | FreeBSD (x86), OS X (PowerPC x86, and x86-64), and SunOS Solaris (Sparc), 3 | and is easy to port to other systems. 4 | 5 | Libtask gives the programmer the illusion of threads, but 6 | the operating system sees only a single kernel thread. 7 | For clarity, we refer to the coroutines as "tasks," not threads. 8 | 9 | Scheduling is cooperative. Only one task runs at a time, 10 | and it cannot be rescheduled without explicitly giving up 11 | the CPU. Most of the functions provided in task.h do have 12 | the possibility of going to sleep. Programs using the task 13 | functions should #include . 14 | 15 | --- Basic task manipulation 16 | 17 | int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize); 18 | 19 | Create a new task running f(arg) on a stack of size stacksize. 20 | 21 | void tasksystem(void); 22 | 23 | Mark the current task as a "system" task. These are ignored 24 | for the purposes of deciding the program is done running 25 | (see taskexit next). 26 | 27 | void taskexit(int status); 28 | 29 | Exit the current task. If this is the last non-system task, 30 | exit the entire program using the given exit status. 31 | 32 | void taskexitall(int status); 33 | 34 | Exit the entire program, using the given exit status. 35 | 36 | void taskmain(int argc, char *argv[]); 37 | 38 | Write this function instead of main. Libtask provides its own main. 39 | 40 | int taskyield(void); 41 | 42 | Explicitly give up the CPU. The current task will be scheduled 43 | again once all the other currently-ready tasks have a chance 44 | to run. Returns the number of other tasks that ran while the 45 | current task was waiting. (Zero means there are no other tasks 46 | trying to run.) 47 | 48 | int taskdelay(unsigned int ms) 49 | 50 | Explicitly give up the CPU for at least ms milliseconds. 51 | Other tasks continue to run during this time. 52 | 53 | void** taskdata(void); 54 | 55 | Return a pointer to a single per-task void* pointer. 56 | You can use this as a per-task storage place. 57 | 58 | void needstack(int n); 59 | 60 | Tell the task library that you need at least n bytes left 61 | on the stack. If you don't have it, the task library will call abort. 62 | (It's hard to figure out how big stacks should be. I usually make 63 | them really big (say 32768) and then don't worry about it.) 64 | 65 | void taskname(char*, ...); 66 | 67 | Takes an argument list like printf. Sets the current task's name. 68 | 69 | char* taskgetname(void); 70 | 71 | Returns the current task's name. Is the actual buffer; do not free. 72 | 73 | void taskstate(char*, ...); 74 | char* taskgetstate(void); 75 | 76 | Like taskname and taskgetname but for the task state. 77 | 78 | When you send a tasked program a SIGQUIT (or SIGINFO, on BSD) 79 | it will print a list of all its tasks and their names and states. 80 | This is useful for debugging why your program isn't doing anything! 81 | 82 | unsigned int taskid(void); 83 | 84 | Return the unique task id for the current task. 85 | 86 | --- Non-blocking I/O 87 | 88 | There is a small amount of runtime support for non-blocking I/O 89 | on file descriptors. 90 | 91 | int fdnoblock(int fd); 92 | 93 | Sets I/O on the given fd to be non-blocking. Should be 94 | called before any of the other fd routines. 95 | 96 | int fdread(int, void*, int); 97 | 98 | Like regular read(), but puts task to sleep while waiting for 99 | data instead of blocking the whole program. 100 | 101 | int fdwrite(int, void*, int); 102 | 103 | Like regular write(), but puts task to sleep while waiting to 104 | write data instead of blocking the whole program. 105 | 106 | void fdwait(int fd, int rw); 107 | 108 | Low-level call sitting underneath fdread and fdwrite. 109 | Puts task to sleep while waiting for I/O to be possible on fd. 110 | Rw specifies type of I/O: 'r' means read, 'w' means write, 111 | anything else means just exceptional conditions (hang up, etc.) 112 | The 'r' and 'w' also wake up for exceptional conditions. 113 | 114 | --- Network I/O 115 | 116 | These are convenient packaging of the ugly Unix socket routines. 117 | They can all put the current task to sleep during the call. 118 | 119 | int netannounce(int proto, char *address, int port) 120 | 121 | Start a network listener running on address and port of protocol. 122 | Proto is either TCP or UDP. Port is a port number. Address is a 123 | string version of a host name or IP address. If address is null, 124 | then announce binds to the given port on all available interfaces. 125 | Returns a fd to use with netaccept. 126 | Examples: netannounce(TCP, "localhost", 80) or 127 | netannounce(TCP, "127.0.0.1", 80) or netannounce(TCP, 0, 80). 128 | 129 | int netaccept(int fd, char *server, int *port) 130 | 131 | Get the next connection that comes in to the listener fd. 132 | Returns a fd to use to talk to the guy who just connected. 133 | If server is not null, it must point at a buffer of at least 134 | 16 bytes that is filled in with the remote IP address. 135 | If port is not null, it is filled in with the report port. 136 | Example: 137 | char server[16]; 138 | int port; 139 | 140 | if(netaccept(fd, server, &port) >= 0) 141 | printf("connect from %s:%d", server, port); 142 | 143 | int netdial(int proto, char *name, int port) 144 | 145 | Create a new (outgoing) connection to a particular host. 146 | Name can be an ip address or a domain name. If it's a domain name, 147 | the entire program will block while the name is resolved 148 | (the DNS library does not provide a nice non-blocking interface). 149 | Example: netdial(TCP, "www.google.com", 80) 150 | or netdial(TCP, "18.26.4.9", 80) 151 | 152 | --- Time 153 | 154 | unsigned int taskdelay(unsigned int ms) 155 | 156 | Put the current task to sleep for approximately ms milliseconds. 157 | Return the actual amount of time slept, in milliseconds. 158 | 159 | --- Example programs 160 | 161 | In this directory, tcpproxy.c is a simple TCP proxy that illustrates 162 | most of the above. You can run 163 | 164 | tcpproxy 1234 www.google.com 80 165 | 166 | and then you should be able to visit http://localhost:1234/ and see Google. 167 | 168 | Other examples are: 169 | primes.c - simple prime sieve 170 | httpload.c - simple HTTP load generator 171 | testdelay.c - test taskdelay() 172 | 173 | --- Building 174 | 175 | To build, run make. You can run make install to copy task.h and 176 | libtask.a to the appropriate places in /usr/local. Then you 177 | should be able to just link with -ltask in your programs 178 | that use it. 179 | 180 | On SunOS Solaris machines, run makesun instead of just make. 181 | 182 | --- Contact Info 183 | 184 | Please email me with questions or problems. 185 | 186 | Russ Cox 187 | rsc@swtch.com 188 | 189 | 190 | --- Stuff you probably won't use at first --- 191 | --- but might want to know about eventually --- 192 | 193 | void tasksleep(Rendez*); 194 | int taskwakeup(Rendez*); 195 | int taskwakeupall(Rendez*); 196 | 197 | A Rendez is a condition variable. You can declare a new one by 198 | just allocating memory for it (or putting it in another structure) 199 | and then zeroing the memory. Tasksleep(r) 'sleeps on r', giving 200 | up the CPU. Multiple tasks can sleep on a single Rendez. 201 | When another task comes along and calls taskwakeup(r), 202 | the first task sleeping on r (if any) will be woken up. 203 | Taskwakeupall(r) wakes up all the tasks sleeping on r. 204 | They both return the actual number of tasks awakened. 205 | 206 | 207 | 208 | void qlock(QLock*); 209 | int canqlock(QLock*); 210 | void qunlock(QLock*); 211 | 212 | You probably won't need locks because of the cooperative 213 | scheduling, but if you do, here are some. You can make a new 214 | QLock by just declaring it and zeroing the memory. 215 | Calling qlock will give up the CPU if the lock is held by someone else. 216 | Calling qunlock will not give up the CPU. 217 | Calling canqlock tries to lock the lock, but will not give up the CPU. 218 | It returns 1 if the lock was acquired, 0 if it cannot be at this time. 219 | 220 | void rlock(RWLock*); 221 | int canrlock(RWLock*); 222 | void runlock(RWLock*); 223 | 224 | void wlock(RWLock*); 225 | int canwlock(RWLock*); 226 | void wunlock(RWLock*); 227 | 228 | RWLocks are reader-writer locks. Any number of readers 229 | can lock them at once, but only one writer at a time. 230 | If a writer is holding it, there can't be any readers. 231 | 232 | 233 | Channel *chancreate(int, int); 234 | etc. 235 | 236 | Channels are buffered communication pipes you can 237 | use to send messages between tasks. Some people like 238 | doing most of the inter-task communication using channels. 239 | 240 | For details on channels see the description of channels in 241 | http://swtch.com/usr/local/plan9/man/man3/thread.html and 242 | http://swtch.com/~rsc/thread/ 243 | and also the example program primes.c, which implements 244 | a concurrent prime sieve. 245 | 246 | 247 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Libtask is a simple coroutine library. It runs on Linux (ARM, MIPS, and x86), 2 | FreeBSD (x86), OS X (PowerPC x86, and x86-64), and SunOS Solaris (Sparc), 3 | and is easy to port to other systems. 4 | 5 | Libtask gives the programmer the illusion of threads, but 6 | the operating system sees only a single kernel thread. 7 | For clarity, we refer to the coroutines as "tasks," not threads. 8 | 9 | Scheduling is cooperative. Only one task runs at a time, 10 | and it cannot be rescheduled without explicitly giving up 11 | the CPU. Most of the functions provided in task.h do have 12 | the possibility of going to sleep. Programs using the task 13 | functions should #include . 14 | 15 | --- Basic task manipulation 16 | 17 | int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize); 18 | 19 | Create a new task running f(arg) on a stack of size stacksize. 20 | 21 | void tasksystem(void); 22 | 23 | Mark the current task as a "system" task. These are ignored 24 | for the purposes of deciding the program is done running 25 | (see taskexit next). 26 | 27 | void taskexit(int status); 28 | 29 | Exit the current task. If this is the last non-system task, 30 | exit the entire program using the given exit status. 31 | 32 | void taskexitall(int status); 33 | 34 | Exit the entire program, using the given exit status. 35 | 36 | void taskmain(int argc, char *argv[]); 37 | 38 | Write this function instead of main. Libtask provides its own main. 39 | 40 | int taskyield(void); 41 | 42 | Explicitly give up the CPU. The current task will be scheduled 43 | again once all the other currently-ready tasks have a chance 44 | to run. Returns the number of other tasks that ran while the 45 | current task was waiting. (Zero means there are no other tasks 46 | trying to run.) 47 | 48 | int taskdelay(unsigned int ms) 49 | 50 | Explicitly give up the CPU for at least ms milliseconds. 51 | Other tasks continue to run during this time. 52 | 53 | void** taskdata(void); 54 | 55 | Return a pointer to a single per-task void* pointer. 56 | You can use this as a per-task storage place. 57 | 58 | void needstack(int n); 59 | 60 | Tell the task library that you need at least n bytes left 61 | on the stack. If you don't have it, the task library will call abort. 62 | (It's hard to figure out how big stacks should be. I usually make 63 | them really big (say 32768) and then don't worry about it.) 64 | 65 | void taskname(char*, ...); 66 | 67 | Takes an argument list like printf. Sets the current task's name. 68 | 69 | char* taskgetname(void); 70 | 71 | Returns the current task's name. Is the actual buffer; do not free. 72 | 73 | void taskstate(char*, ...); 74 | char* taskgetstate(void); 75 | 76 | Like taskname and taskgetname but for the task state. 77 | 78 | When you send a tasked program a SIGQUIT (or SIGINFO, on BSD) 79 | it will print a list of all its tasks and their names and states. 80 | This is useful for debugging why your program isn't doing anything! 81 | 82 | unsigned int taskid(void); 83 | 84 | Return the unique task id for the current task. 85 | 86 | --- Non-blocking I/O 87 | 88 | There is a small amount of runtime support for non-blocking I/O 89 | on file descriptors. 90 | 91 | int fdnoblock(int fd); 92 | 93 | Sets I/O on the given fd to be non-blocking. Should be 94 | called before any of the other fd routines. 95 | 96 | int fdread(int, void*, int); 97 | 98 | Like regular read(), but puts task to sleep while waiting for 99 | data instead of blocking the whole program. 100 | 101 | int fdwrite(int, void*, int); 102 | 103 | Like regular write(), but puts task to sleep while waiting to 104 | write data instead of blocking the whole program. 105 | 106 | void fdwait(int fd, int rw); 107 | 108 | Low-level call sitting underneath fdread and fdwrite. 109 | Puts task to sleep while waiting for I/O to be possible on fd. 110 | Rw specifies type of I/O: 'r' means read, 'w' means write, 111 | anything else means just exceptional conditions (hang up, etc.) 112 | The 'r' and 'w' also wake up for exceptional conditions. 113 | 114 | --- Network I/O 115 | 116 | These are convenient packaging of the ugly Unix socket routines. 117 | They can all put the current task to sleep during the call. 118 | 119 | int netannounce(int proto, char *address, int port) 120 | 121 | Start a network listener running on address and port of protocol. 122 | Proto is either TCP or UDP. Port is a port number. Address is a 123 | string version of a host name or IP address. If address is null, 124 | then announce binds to the given port on all available interfaces. 125 | Returns a fd to use with netaccept. 126 | Examples: netannounce(TCP, "localhost", 80) or 127 | netannounce(TCP, "127.0.0.1", 80) or netannounce(TCP, 0, 80). 128 | 129 | int netaccept(int fd, char *server, int *port) 130 | 131 | Get the next connection that comes in to the listener fd. 132 | Returns a fd to use to talk to the guy who just connected. 133 | If server is not null, it must point at a buffer of at least 134 | 16 bytes that is filled in with the remote IP address. 135 | If port is not null, it is filled in with the report port. 136 | Example: 137 | char server[16]; 138 | int port; 139 | 140 | if(netaccept(fd, server, &port) >= 0) 141 | printf("connect from %s:%d", server, port); 142 | 143 | int netdial(int proto, char *name, int port) 144 | 145 | Create a new (outgoing) connection to a particular host. 146 | Name can be an ip address or a domain name. If it's a domain name, 147 | the entire program will block while the name is resolved 148 | (the DNS library does not provide a nice non-blocking interface). 149 | Example: netdial(TCP, "www.google.com", 80) 150 | or netdial(TCP, "18.26.4.9", 80) 151 | 152 | --- Time 153 | 154 | unsigned int taskdelay(unsigned int ms) 155 | 156 | Put the current task to sleep for approximately ms milliseconds. 157 | Return the actual amount of time slept, in milliseconds. 158 | 159 | --- Example programs 160 | 161 | In this directory, tcpproxy.c is a simple TCP proxy that illustrates 162 | most of the above. You can run 163 | 164 | tcpproxy 1234 www.google.com 80 165 | 166 | and then you should be able to visit http://localhost:1234/ and see Google. 167 | 168 | Other examples are: 169 | primes.c - simple prime sieve 170 | httpload.c - simple HTTP load generator 171 | testdelay.c - test taskdelay() 172 | 173 | --- Building 174 | 175 | To build, run make. You can run make install to copy task.h and 176 | libtask.a to the appropriate places in /usr/local. Then you 177 | should be able to just link with -ltask in your programs 178 | that use it. 179 | 180 | On SunOS Solaris machines, run makesun instead of just make. 181 | 182 | --- Contact Info 183 | 184 | Please email me with questions or problems. 185 | 186 | Russ Cox 187 | rsc@swtch.com 188 | 189 | 190 | --- Stuff you probably won't use at first --- 191 | --- but might want to know about eventually --- 192 | 193 | void tasksleep(Rendez*); 194 | int taskwakeup(Rendez*); 195 | int taskwakeupall(Rendez*); 196 | 197 | A Rendez is a condition variable. You can declare a new one by 198 | just allocating memory for it (or putting it in another structure) 199 | and then zeroing the memory. Tasksleep(r) 'sleeps on r', giving 200 | up the CPU. Multiple tasks can sleep on a single Rendez. 201 | When another task comes along and calls taskwakeup(r), 202 | the first task sleeping on r (if any) will be woken up. 203 | Taskwakeupall(r) wakes up all the tasks sleeping on r. 204 | They both return the actual number of tasks awakened. 205 | 206 | 207 | 208 | void qlock(QLock*); 209 | int canqlock(QLock*); 210 | void qunlock(QLock*); 211 | 212 | You probably won't need locks because of the cooperative 213 | scheduling, but if you do, here are some. You can make a new 214 | QLock by just declaring it and zeroing the memory. 215 | Calling qlock will give up the CPU if the lock is held by someone else. 216 | Calling qunlock will not give up the CPU. 217 | Calling canqlock tries to lock the lock, but will not give up the CPU. 218 | It returns 1 if the lock was acquired, 0 if it cannot be at this time. 219 | 220 | void rlock(RWLock*); 221 | int canrlock(RWLock*); 222 | void runlock(RWLock*); 223 | 224 | void wlock(RWLock*); 225 | int canwlock(RWLock*); 226 | void wunlock(RWLock*); 227 | 228 | RWLocks are reader-writer locks. Any number of readers 229 | can lock them at once, but only one writer at a time. 230 | If a writer is holding it, there can't be any readers. 231 | 232 | 233 | Channel *chancreate(int, int); 234 | etc. 235 | 236 | Channels are buffered communication pipes you can 237 | use to send messages between tasks. Some people like 238 | doing most of the inter-task communication using channels. 239 | 240 | For details on channels see the description of channels in 241 | http://swtch.com/usr/local/plan9/man/man3/thread.html and 242 | http://swtch.com/~rsc/thread/ 243 | and also the example program primes.c, which implements 244 | a concurrent prime sieve. 245 | 246 | 247 | -------------------------------------------------------------------------------- /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(__arm__) 32 | #define NEEDARMCONTEXT 1 33 | #define SET setmcontext 34 | #define GET getmcontext 35 | #endif 36 | 37 | #if defined(__linux__) && defined(__mips__) 38 | #define NEEDMIPSCONTEXT 1 39 | #define SET setmcontext 40 | #define GET getmcontext 41 | #endif 42 | 43 | #ifdef NEEDX86CONTEXT 44 | .globl SET 45 | SET: 46 | movl 4(%esp), %eax 47 | 48 | movl 8(%eax), %fs 49 | movl 12(%eax), %es 50 | movl 16(%eax), %ds 51 | movl 76(%eax), %ss 52 | movl 20(%eax), %edi 53 | movl 24(%eax), %esi 54 | movl 28(%eax), %ebp 55 | movl 36(%eax), %ebx 56 | movl 40(%eax), %edx 57 | movl 44(%eax), %ecx 58 | 59 | movl 72(%eax), %esp 60 | pushl 60(%eax) /* new %eip */ 61 | movl 48(%eax), %eax 62 | ret 63 | 64 | .globl GET 65 | GET: 66 | movl 4(%esp), %eax 67 | 68 | movl %fs, 8(%eax) 69 | movl %es, 12(%eax) 70 | movl %ds, 16(%eax) 71 | movl %ss, 76(%eax) 72 | movl %edi, 20(%eax) 73 | movl %esi, 24(%eax) 74 | movl %ebp, 28(%eax) 75 | movl %ebx, 36(%eax) 76 | movl %edx, 40(%eax) 77 | movl %ecx, 44(%eax) 78 | 79 | movl $1, 48(%eax) /* %eax */ 80 | movl (%esp), %ecx /* %eip */ 81 | movl %ecx, 60(%eax) 82 | leal 4(%esp), %ecx /* %esp */ 83 | movl %ecx, 72(%eax) 84 | 85 | movl 44(%eax), %ecx /* restore %ecx */ 86 | movl $0, %eax 87 | ret 88 | #endif 89 | 90 | #ifdef NEEDAMD64CONTEXT 91 | .globl SET 92 | SET: 93 | movq 16(%rdi), %rsi 94 | movq 24(%rdi), %rdx 95 | movq 32(%rdi), %rcx 96 | movq 40(%rdi), %r8 97 | movq 48(%rdi), %r9 98 | movq 56(%rdi), %rax 99 | movq 64(%rdi), %rbx 100 | movq 72(%rdi), %rbp 101 | movq 80(%rdi), %r10 102 | movq 88(%rdi), %r11 103 | movq 96(%rdi), %r12 104 | movq 104(%rdi), %r13 105 | movq 112(%rdi), %r14 106 | movq 120(%rdi), %r15 107 | movq 184(%rdi), %rsp 108 | pushq 160(%rdi) /* new %eip */ 109 | movq 8(%rdi), %rdi 110 | ret 111 | 112 | .globl GET 113 | GET: 114 | movq %rdi, 8(%rdi) 115 | movq %rsi, 16(%rdi) 116 | movq %rdx, 24(%rdi) 117 | movq %rcx, 32(%rdi) 118 | movq %r8, 40(%rdi) 119 | movq %r9, 48(%rdi) 120 | movq $1, 56(%rdi) /* %rax */ 121 | movq %rbx, 64(%rdi) 122 | movq %rbp, 72(%rdi) 123 | movq %r10, 80(%rdi) 124 | movq %r11, 88(%rdi) 125 | movq %r12, 96(%rdi) 126 | movq %r13, 104(%rdi) 127 | movq %r14, 112(%rdi) 128 | movq %r15, 120(%rdi) 129 | 130 | movq (%rsp), %rcx /* %rip */ 131 | movq %rcx, 160(%rdi) 132 | leaq 8(%rsp), %rcx /* %rsp */ 133 | movq %rcx, 184(%rdi) 134 | 135 | movq 32(%rdi), %rcx /* restore %rcx */ 136 | movq $0, %rax 137 | ret 138 | #endif 139 | 140 | #ifdef NEEDPOWERCONTEXT 141 | /* get FPR and VR use flags with sc 0x7FF3 */ 142 | /* get vsave with mfspr reg, 256 */ 143 | 144 | .text 145 | .align 2 146 | 147 | .globl GET 148 | GET: /* xxx: instruction scheduling */ 149 | mflr r0 150 | mfcr r5 151 | mfctr r6 152 | mfxer r7 153 | stw r0, 0*4(r3) 154 | stw r5, 1*4(r3) 155 | stw r6, 2*4(r3) 156 | stw r7, 3*4(r3) 157 | 158 | stw r1, 4*4(r3) 159 | stw r2, 5*4(r3) 160 | li r5, 1 /* return value for setmcontext */ 161 | stw r5, 6*4(r3) 162 | 163 | stw r13, (0+7)*4(r3) /* callee-save GPRs */ 164 | stw r14, (1+7)*4(r3) /* xxx: block move */ 165 | stw r15, (2+7)*4(r3) 166 | stw r16, (3+7)*4(r3) 167 | stw r17, (4+7)*4(r3) 168 | stw r18, (5+7)*4(r3) 169 | stw r19, (6+7)*4(r3) 170 | stw r20, (7+7)*4(r3) 171 | stw r21, (8+7)*4(r3) 172 | stw r22, (9+7)*4(r3) 173 | stw r23, (10+7)*4(r3) 174 | stw r24, (11+7)*4(r3) 175 | stw r25, (12+7)*4(r3) 176 | stw r26, (13+7)*4(r3) 177 | stw r27, (14+7)*4(r3) 178 | stw r28, (15+7)*4(r3) 179 | stw r29, (16+7)*4(r3) 180 | stw r30, (17+7)*4(r3) 181 | stw r31, (18+7)*4(r3) 182 | 183 | li r3, 0 /* return */ 184 | blr 185 | 186 | .globl SET 187 | SET: 188 | lwz r13, (0+7)*4(r3) /* callee-save GPRs */ 189 | lwz r14, (1+7)*4(r3) /* xxx: block move */ 190 | lwz r15, (2+7)*4(r3) 191 | lwz r16, (3+7)*4(r3) 192 | lwz r17, (4+7)*4(r3) 193 | lwz r18, (5+7)*4(r3) 194 | lwz r19, (6+7)*4(r3) 195 | lwz r20, (7+7)*4(r3) 196 | lwz r21, (8+7)*4(r3) 197 | lwz r22, (9+7)*4(r3) 198 | lwz r23, (10+7)*4(r3) 199 | lwz r24, (11+7)*4(r3) 200 | lwz r25, (12+7)*4(r3) 201 | lwz r26, (13+7)*4(r3) 202 | lwz r27, (14+7)*4(r3) 203 | lwz r28, (15+7)*4(r3) 204 | lwz r29, (16+7)*4(r3) 205 | lwz r30, (17+7)*4(r3) 206 | lwz r31, (18+7)*4(r3) 207 | 208 | lwz r1, 4*4(r3) 209 | lwz r2, 5*4(r3) 210 | 211 | lwz r0, 0*4(r3) 212 | mtlr r0 213 | lwz r0, 1*4(r3) 214 | mtcr r0 /* mtcrf 0xFF, r0 */ 215 | lwz r0, 2*4(r3) 216 | mtctr r0 217 | lwz r0, 3*4(r3) 218 | mtxer r0 219 | 220 | lwz r3, 6*4(r3) 221 | blr 222 | #endif 223 | 224 | #ifdef NEEDARMCONTEXT 225 | .globl GET 226 | GET: 227 | str r1, [r0,#4] 228 | str r2, [r0,#8] 229 | str r3, [r0,#12] 230 | str r4, [r0,#16] 231 | str r5, [r0,#20] 232 | str r6, [r0,#24] 233 | str r7, [r0,#28] 234 | str r8, [r0,#32] 235 | str r9, [r0,#36] 236 | str r10, [r0,#40] 237 | str r11, [r0,#44] 238 | str r12, [r0,#48] 239 | str r13, [r0,#52] 240 | str r14, [r0,#56] 241 | /* store 1 as r0-to-restore */ 242 | mov r1, #1 243 | str r1, [r0] 244 | /* return 0 */ 245 | mov r0, #0 246 | mov pc, lr 247 | 248 | .globl SET 249 | SET: 250 | ldr r1, [r0,#4] 251 | ldr r2, [r0,#8] 252 | ldr r3, [r0,#12] 253 | ldr r4, [r0,#16] 254 | ldr r5, [r0,#20] 255 | ldr r6, [r0,#24] 256 | ldr r7, [r0,#28] 257 | ldr r8, [r0,#32] 258 | ldr r9, [r0,#36] 259 | ldr r10, [r0,#40] 260 | ldr r11, [r0,#44] 261 | ldr r12, [r0,#48] 262 | ldr r13, [r0,#52] 263 | ldr r14, [r0,#56] 264 | ldr r0, [r0] 265 | mov pc, lr 266 | #endif 267 | 268 | #ifdef NEEDMIPSCONTEXT 269 | .globl GET 270 | GET: 271 | sw $4, 24($4) 272 | sw $5, 28($4) 273 | sw $6, 32($4) 274 | sw $7, 36($4) 275 | 276 | sw $16, 72($4) 277 | sw $17, 76($4) 278 | sw $18, 80($4) 279 | sw $19, 84($4) 280 | sw $20, 88($4) 281 | sw $21, 92($4) 282 | sw $22, 96($4) 283 | sw $23, 100($4) 284 | 285 | sw $28, 120($4) /* gp */ 286 | sw $29, 124($4) /* sp */ 287 | sw $30, 128($4) /* fp */ 288 | sw $31, 132($4) /* ra */ 289 | 290 | xor $2, $2, $2 291 | j $31 292 | nop 293 | 294 | .globl SET 295 | SET: 296 | lw $16, 72($4) 297 | lw $17, 76($4) 298 | lw $18, 80($4) 299 | lw $19, 84($4) 300 | lw $20, 88($4) 301 | lw $21, 92($4) 302 | lw $22, 96($4) 303 | lw $23, 100($4) 304 | 305 | lw $28, 120($4) /* gp */ 306 | lw $29, 124($4) /* sp */ 307 | lw $30, 128($4) /* fp */ 308 | 309 | /* 310 | * If we set $31 directly and j $31, 311 | * we would loose the outer return address. 312 | * Use a temporary register, then. 313 | */ 314 | lw $8, 132($4) /* ra */ 315 | 316 | /* bug: not setting the pc causes a bus error */ 317 | lw $25, 132($4) /* pc */ 318 | 319 | lw $5, 28($4) 320 | lw $6, 32($4) 321 | lw $7, 36($4) 322 | lw $4, 24($4) 323 | 324 | j $8 325 | nop 326 | #endif 327 | -------------------------------------------------------------------------------- /channel.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */ 2 | 3 | #include "taskimpl.h" 4 | 5 | // 创建 channel, element 指定 channel 的元素个数, bufsize 指定元素占用字节数 6 | Channel* 7 | chancreate(int elemsize, int bufsize) 8 | { 9 | Channel *c; 10 | 11 | c = malloc(sizeof *c+bufsize*elemsize); 12 | if(c == nil){ 13 | fprint(2, "chancreate malloc: %r"); 14 | exit(1); 15 | } 16 | memset(c, 0, sizeof *c); 17 | c->elemsize = elemsize; // 元素数目 18 | c->bufsize = bufsize; // 元素字节数 19 | c->nbuf = 0; // 当前使用字节数 20 | // 因为 malloc 申请的是一片内存 21 | // 所以 buf 的偏移就是加上 channel 的头部长度 22 | // 这里写法有点那什么, c + 1 因为 c 结构是 channel, 所以 + 1相当于 + sizeof(*c) 23 | c->buf = (uchar*)(c+1); 24 | return c; 25 | } 26 | 27 | /* bug - work out races */ 28 | // 释放 channel 29 | void 30 | chanfree(Channel *c) 31 | { 32 | if(c == nil) 33 | return; 34 | free(c->name); 35 | free(c->arecv.a); 36 | free(c->asend.a); 37 | free(c); 38 | } 39 | 40 | // 添加 Alt 到数组 41 | static void 42 | addarray(Altarray *a, Alt *alt) 43 | { 44 | if(a->n == a->m){ 45 | a->m += 16; 46 | a->a = realloc(a->a, a->m*sizeof a->a[0]); 47 | } 48 | a->a[a->n++] = alt; 49 | } 50 | 51 | // 元素删除 52 | static void 53 | delarray(Altarray *a, int i) 54 | { 55 | --a->n; 56 | a->a[i] = a->a[a->n]; 57 | } 58 | 59 | /* 60 | * doesn't really work for things other than CHANSND and CHANRCV 61 | * but is only used as arg to chanarray, which can handle it 62 | */ 63 | // op 只能是 send, recv 64 | #define otherop(op) (CHANSND+CHANRCV-(op)) 65 | 66 | static Altarray* 67 | chanarray(Channel *c, uint op) 68 | { 69 | switch(op){ 70 | default: 71 | return nil; 72 | case CHANSND: 73 | return &c->asend; 74 | case CHANRCV: 75 | return &c->arecv; 76 | } 77 | } 78 | 79 | // alt 能否操作 80 | // 如果可以发送或者接收数据返回 1, 其他返回 0 81 | static int 82 | altcanexec(Alt *a) 83 | { 84 | Altarray *ar; 85 | Channel *c; 86 | 87 | // 空操作 88 | if(a->op == CHANNOP) 89 | return 0; 90 | c = a->c; 91 | if(c->bufsize == 0){ // channel 没有分配空间 92 | ar = chanarray(c, otherop(a->op)); 93 | return ar && ar->n; 94 | }else{ 95 | switch(a->op){ 96 | default: 97 | return 0; 98 | case CHANSND: 99 | // 如果 buf 还有空间, 认为可以写入, 返回 1 100 | return c->nbuf < c->bufsize; 101 | case CHANRCV: 102 | // 如果 buf 还有数据, 认为有数据可以读, 返回 1 103 | return c->nbuf > 0; 104 | } 105 | } 106 | } 107 | 108 | // 根据alt 的操作类型找到 alt 队列, 并入队 109 | static void 110 | altqueue(Alt *a) 111 | { 112 | Altarray *ar; 113 | 114 | ar = chanarray(a->c, a->op); 115 | addarray(ar, a); 116 | } 117 | 118 | static void 119 | altdequeue(Alt *a) 120 | { 121 | int i; 122 | Altarray *ar; 123 | 124 | // 根据操作类型,获取队列 125 | ar = chanarray(a->c, a->op); 126 | if(ar == nil){ 127 | fprint(2, "bad use of altdequeue op=%d\n", a->op); 128 | abort(); 129 | } 130 | 131 | // 队列查找元素并删除 132 | for(i=0; in; i++) 133 | if(ar->a[i] == a){ 134 | delarray(ar, i); 135 | return; 136 | } 137 | fprint(2, "cannot find self in altdq\n"); 138 | abort(); 139 | } 140 | 141 | // 删除全部的 Alt 142 | static void 143 | altalldequeue(Alt *a) 144 | { 145 | int i; 146 | 147 | for(i=0; a[i].op!=CHANEND && a[i].op!=CHANNOBLK; i++) 148 | if(a[i].op != CHANNOP) 149 | altdequeue(&a[i]); 150 | } 151 | 152 | // 把 src 拷贝到 dest, 如果 src = nil, dst 置 0 153 | static void 154 | amove(void *dst, void *src, uint n) 155 | { 156 | if(dst){ 157 | if(src == nil) 158 | memset(dst, 0, n); 159 | else 160 | memmove(dst, src, n); 161 | } 162 | } 163 | 164 | /* 165 | * Actually move the data around. There are up to three 166 | * players: the sender, the receiver, and the channel itself. 167 | * If the channel is unbuffered or the buffer is empty, 168 | * data goes from sender to receiver. If the channel is full, 169 | * the receiver removes some from the channel and the sender 170 | * gets to put some in. 171 | */ 172 | // 用来接收或者发送的时候,进行数据拷贝 173 | // 把变量拷贝到接收的缓冲区, 获取放到发送的缓冲区 174 | static void 175 | altcopy(Alt *s, Alt *r) 176 | { 177 | Alt *t; 178 | Channel *c; 179 | uchar *cp; 180 | 181 | /* 182 | * Work out who is sender and who is receiver 183 | */ 184 | if(s == nil && r == nil) 185 | return; 186 | assert(s != nil); 187 | // 获取源的 channel 188 | c = s->c; 189 | // 如果 s 是接收方,调换 r(recv) 和 s(send) 190 | // 下面就可以认为 r是 recv, s 是 send 191 | if(s->op == CHANRCV){ 192 | t = s; 193 | s = r; 194 | r = t; 195 | } 196 | assert(s==nil || s->op == CHANSND); 197 | assert(r==nil || r->op == CHANRCV); 198 | 199 | /* 200 | * Channel is empty (or unbuffered) - copy directly. 201 | */ 202 | // channel 是非缓冲channel(元素不是数组), 直接拷贝 203 | if(s && r && c->nbuf == 0){ 204 | amove(r->v, s->v, c->elemsize); 205 | return; 206 | } 207 | 208 | /* 209 | * Otherwise it's always okay to receive and then send. 210 | */ 211 | if(r){ 212 | // 如果是接收, 那么就是从 buffer 读取数据 213 | // cp 读取位置 214 | cp = c->buf + c->off*c->elemsize; 215 | // 拷贝元素到接收者的变量 216 | amove(r->v, cp, c->elemsize); 217 | // 缓冲区可读元素减掉一 218 | --c->nbuf; 219 | // 如果 off 到buffer末尾, 说明发送结束,从头开始 220 | if(++c->off == c->bufsize) 221 | c->off = 0; 222 | } 223 | if(s){ 224 | // 计算接收缓冲区的位置 225 | cp = c->buf + (c->off+c->nbuf)%c->bufsize*c->elemsize; 226 | // 发送的变量拷贝到 channel的 buffer 227 | amove(cp, s->v, c->elemsize); 228 | // 缓冲区可读元素加 1 229 | ++c->nbuf; 230 | } 231 | } 232 | 233 | static void 234 | altexec(Alt *a) 235 | { 236 | int i; 237 | Altarray *ar; 238 | Alt *other; 239 | Channel *c; 240 | 241 | c = a->c; 242 | // 获取出相反操作的队列, 如果 a 是 recv, 就拿出 send 队列 243 | // 这个是阻塞发送或者接收的场景使用, 优先处理阻塞的任务 244 | // 这里为什么选择相反操作的队列可以明白吧? 245 | // 你发送数据的时候,这时候当然要唤醒等待读的协程了 246 | ar = chanarray(c, otherop(a->op)); 247 | if(ar && ar->n){ 248 | // 随机选择一个 Alt 249 | i = rand()%ar->n; 250 | other = ar->a[i]; 251 | // 拷贝数据 252 | altcopy(a, other); 253 | // 删除任务已经完成的相关操作 254 | altalldequeue(other->xalt); 255 | // 假设有 a0, a1, a2,初始化的时候 a0, a1, a2 的 xalt 都指向了 a0 256 | // 这时候如果到 a2 阻塞了, 那么 other 就为 a2 257 | // other->xalt[0] 就为 a0,然后把 a0->xalt 设置 other(a2) 258 | other->xalt[0].xalt = other; 259 | // 把阻塞住的任务重新设置为就绪状态 260 | taskready(other->task); 261 | }else 262 | altcopy(a, nil); // 同步的时候使用, 直接写入buffer, 或者读取 buffer 263 | } 264 | 265 | #define dbgalt 0 266 | int 267 | chanalt(Alt *a) 268 | { 269 | int i, j, ncan, n, canblock; 270 | Channel *c; 271 | Task *t; 272 | 273 | // 检查栈空间 274 | needstack(512); 275 | // 检查 alt 数组, 直到遇到 op = CHANEND 或者 CHANNOBLK 停止 276 | for(i=0; a[i].op != CHANEND && a[i].op != CHANNOBLK; i++) 277 | ; 278 | n = i; 279 | // 判断最后一个 op 是否为 CHANEND, 是的话,操作就为 block 280 | canblock = a[i].op == CHANEND; 281 | 282 | // t 设置为当前执行的协程 283 | t = taskrunning; 284 | for(i=0; iname) print("%s", c->name); else print("%p", c); } 294 | // 检查并计算可以执行的 Alt 数目 295 | if(altcanexec(&a[i])){ // 判断是否可以接收或者发送 296 | if(dbgalt) print("*"); 297 | ncan++; 298 | } 299 | } 300 | 301 | if(ncan){ 302 | j = rand()%ncan; 303 | // n 个可执行的 Alt 随机执行一条,然后返回 304 | for(i=0; i %c:", "esrnb"[a[i].op]); 311 | if(c->name) print("%s", c->name); else print("%p", c); 312 | print("\n"); 313 | } 314 | // 执行 315 | altexec(&a[i]); 316 | return i; 317 | } 318 | } 319 | } 320 | } 321 | if(dbgalt)print("\n"); 322 | 323 | // 不能阻塞就返回 -1 324 | if(!canblock) 325 | return -1; 326 | 327 | // 可以阻塞, 就加入异步队列, 等待可以写或者读再唤醒 328 | for(i=0; iuc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(ulong); 46 | sp = tos - 16; 47 | ucp->mc.pc = (long)func; 48 | ucp->mc.sp = (long)sp; 49 | va_start(arg, argc); 50 | ucp->mc.r3 = va_arg(arg, long); 51 | va_end(arg); 52 | } 53 | #endif 54 | 55 | #ifdef NEEDX86MAKECONTEXT 56 | void 57 | makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) 58 | { 59 | int *sp; 60 | 61 | sp = (int*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/4; 62 | sp -= argc; 63 | sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ 64 | memmove(sp, &argc+1, argc*sizeof(int)); 65 | 66 | *--sp = 0; /* return address */ 67 | ucp->uc_mcontext.mc_eip = (long)func; 68 | ucp->uc_mcontext.mc_esp = (int)sp; 69 | } 70 | #endif 71 | 72 | #ifdef NEEDAMD64MAKECONTEXT 73 | void 74 | makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) 75 | { 76 | long *sp; 77 | va_list va; 78 | 79 | memset(&ucp->uc_mcontext, 0, sizeof ucp->uc_mcontext); 80 | if(argc != 2) 81 | *(int*)0 = 0; 82 | va_start(va, argc); 83 | ucp->uc_mcontext.mc_rdi = va_arg(va, int); 84 | ucp->uc_mcontext.mc_rsi = va_arg(va, int); 85 | va_end(va); 86 | sp = (long*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(long); 87 | sp -= argc; 88 | sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ 89 | *--sp = 0; /* return address */ 90 | ucp->uc_mcontext.mc_rip = (long)func; 91 | ucp->uc_mcontext.mc_rsp = (long)sp; 92 | } 93 | #endif 94 | 95 | #ifdef NEEDARMMAKECONTEXT 96 | void 97 | makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) 98 | { 99 | int i, *sp; 100 | va_list arg; 101 | 102 | sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4; 103 | va_start(arg, argc); 104 | for(i=0; i<4 && iuc_mcontext.gregs[i] = va_arg(arg, uint); 106 | va_end(arg); 107 | uc->uc_mcontext.gregs[13] = (uint)sp; 108 | uc->uc_mcontext.gregs[14] = (uint)fn; 109 | } 110 | #endif 111 | 112 | #ifdef NEEDMIPSMAKECONTEXT 113 | void 114 | makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) 115 | { 116 | int i, *sp; 117 | va_list arg; 118 | 119 | va_start(arg, argc); 120 | sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4; 121 | for(i=0; i<4 && iuc_mcontext.mc_regs[i+4] = va_arg(arg, int); 123 | va_end(arg); 124 | uc->uc_mcontext.mc_regs[29] = (int)sp; 125 | uc->uc_mcontext.mc_regs[31] = (int)fn; 126 | } 127 | #endif 128 | 129 | #ifdef NEEDSWAPCONTEXT 130 | int 131 | swapcontext(ucontext_t *oucp, const ucontext_t *ucp) 132 | { 133 | if(getcontext(oucp) == 0) 134 | setcontext(ucp); 135 | return 0; 136 | } 137 | #endif 138 | 139 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void 7 | counttask1(void *arg) 8 | { 9 | int i; 10 | for( i = 0; i < 5; i++) { 11 | printf("task1: %d\n", i); 12 | taskyield(); 13 | } 14 | } 15 | 16 | void 17 | counttask2(void *arg) 18 | { 19 | int i; 20 | for( i = 5; i < 10; i++) { 21 | printf("task2: %d\n", i); 22 | taskyield(); 23 | } 24 | } 25 | 26 | void 27 | taskmain(int argc, char **argv) 28 | { 29 | taskcreate(counttask1, NULL, 32768); 30 | taskcreate(counttask2, NULL, 32768); 31 | } 32 | -------------------------------------------------------------------------------- /fd.c: -------------------------------------------------------------------------------- 1 | #include "taskimpl.h" 2 | #include 3 | #include 4 | 5 | enum 6 | { 7 | MAXFD = 1024 8 | }; 9 | 10 | static struct pollfd pollfd[MAXFD]; 11 | static Task *polltask[MAXFD]; 12 | static int npollfd; 13 | static int startedfdtask; 14 | static Tasklist sleeping; 15 | static int sleepingcounted; 16 | static uvlong nsec(void); 17 | 18 | // 事件管理的系统协程 19 | void 20 | fdtask(void *v) 21 | { 22 | int i, ms; 23 | Task *t; 24 | uvlong now; 25 | 26 | // task 标记为系统任务 27 | tasksystem(); 28 | taskname("fdtask"); 29 | for(;;){ 30 | /* let everyone else run */ 31 | // 让党员先跑。哦,不,让其他 task 先跑 32 | while(taskyield() > 0) 33 | ; 34 | /* we're the only one runnable - poll for i/o */ 35 | errno = 0; 36 | taskstate("poll"); 37 | // 设置 poll 等待时间 38 | // 如果sleep 队列没有 task 等待唤醒,那么这个 task 也会无限等待 39 | // 如果没有延时任务, 启动这个 task, 说明是有其他的 fd 读写? 40 | if((t=sleeping.head) == nil) 41 | ms = -1; 42 | else{ 43 | /* sleep at most 5s */ 44 | // 如果是有定时任务,那么最多 sleep 5秒 45 | now = nsec(); 46 | if(now >= t->alarmtime) 47 | ms = 0; // poll 设置为 0 为立即返回 48 | else if(now+5*1000*1000*1000LL >= t->alarmtime) 49 | ms = (t->alarmtime - now)/1000000; 50 | else 51 | ms = 5000; 52 | } 53 | // 查询读写事件 54 | if(poll(pollfd, npollfd, ms) < 0){ 55 | if(errno == EINTR) 56 | continue; 57 | fprint(2, "poll: %s\n", strerror(errno)); 58 | taskexitall(0); 59 | } 60 | 61 | /* wake up the guys who deserve it */ 62 | // 如果有事件到来,唤醒等待的协程 63 | for(i=0; i= t->alarmtime){ 77 | deltask(&sleeping, t); 78 | if(!t->system && --sleepingcounted == 0) 79 | taskcount--; 80 | taskready(t); 81 | } 82 | } 83 | } 84 | 85 | // 延时任务 86 | uint 87 | taskdelay(uint ms) 88 | { 89 | uvlong when, now; 90 | Task *t; 91 | 92 | // 启动 fdtask, 作为系统任务 93 | if(!startedfdtask){ 94 | startedfdtask = 1; 95 | taskcreate(fdtask, 0, 32768); 96 | } 97 | 98 | now = nsec(); 99 | // 设置唤醒时间 100 | when = now+(uvlong)ms*1000000; 101 | // 根据唤醒时间来对 task 进行排序 102 | // 这里是为了找到最后一个唤醒时间比当前任务早的task 103 | for(t=sleeping.head; t!=nil && t->alarmtime < when; t=t->next) 104 | ; 105 | 106 | if(t){ 107 | // t != nil, 说明是有任务的唤醒时间比当前的任务的早 108 | // 把当前任务放到唤醒时间比它小的位置 109 | taskrunning->prev = t->prev; 110 | taskrunning->next = t; 111 | }else{ 112 | // t == nil 说明遍历到了 sleep 任务的尾部,仍然没有需要唤醒的任务 113 | // 也就是当前任务的唤醒时间也是最大的,放到唤醒队列的尾部 114 | taskrunning->prev = sleeping.tail; 115 | taskrunning->next = nil; 116 | } 117 | 118 | // 设置该任务的唤醒时间 119 | t = taskrunning; 120 | t->alarmtime = when; 121 | // 上面只是把该任务的指针设置好 122 | // 因为这个是双向链表,这里要设置前一个 task 的指针指向这个 task 123 | if(t->prev) 124 | t->prev->next = t; 125 | else 126 | sleeping.head = t; 127 | if(t->next) 128 | t->next->prev = t; 129 | else 130 | sleeping.tail = t; 131 | 132 | // 唤醒队列长度计数 133 | if(!t->system && sleepingcounted++ == 0) 134 | taskcount++; 135 | // 切换到其他的 task 136 | taskswitch(); 137 | 138 | // 如果又切回来,说明该任务又加到了就绪队列,然后被执行到 139 | // 也就是休眠的时间到了, 被唤醒 140 | return (nsec() - now)/1000000; 141 | } 142 | 143 | // 添加读写事件到 epoll/kqueue 144 | void 145 | fdwait(int fd, int rw) 146 | { 147 | int bits; 148 | 149 | if(!startedfdtask){ 150 | // 第一次调用,创建 fdtask 151 | // 这个协程会定时轮询是否有读写事件到来 152 | startedfdtask = 1; 153 | taskcreate(fdtask, 0, 32768); 154 | } 155 | 156 | // 限制最大的连接数,为什么要限制??? 157 | if(npollfd >= MAXFD){ 158 | fprint(2, "too many poll file descriptors\n"); 159 | abort(); 160 | } 161 | 162 | taskstate("fdwait for %s", rw=='r' ? "read" : rw=='w' ? "write" : "error"); 163 | bits = 0; 164 | switch(rw){ 165 | case 'r': 166 | bits |= POLLIN; 167 | break; 168 | case 'w': 169 | bits |= POLLOUT; 170 | break; 171 | } 172 | 173 | // 设置 fd 属于哪个协程 174 | polltask[npollfd] = taskrunning; 175 | // 设置fd 176 | pollfd[npollfd].fd = fd; 177 | // 设置读写位 178 | pollfd[npollfd].events = bits; 179 | pollfd[npollfd].revents = 0; 180 | npollfd++; 181 | // 出让 cpu 182 | taskswitch(); 183 | } 184 | 185 | /* Like fdread but always calls fdwait before reading. */ 186 | int 187 | fdread1(int fd, void *buf, int n) 188 | { 189 | int m; 190 | 191 | do 192 | fdwait(fd, 'r'); 193 | while((m = read(fd, buf, n)) < 0 && errno == EAGAIN); 194 | return m; 195 | } 196 | 197 | int 198 | fdread(int fd, void *buf, int n) 199 | { 200 | int m; 201 | 202 | while((m=read(fd, buf, n)) < 0 && errno == EAGAIN) 203 | fdwait(fd, 'r'); 204 | return m; 205 | } 206 | 207 | int 208 | fdwrite(int fd, void *buf, int n) 209 | { 210 | int m, tot; 211 | 212 | 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 | // 协程数目 28 | n = atoi(argv[1]); 29 | // 压测域名对应的 ip 30 | server = argv[2]; 31 | // 压测的 url 32 | url = argv[3]; 33 | 34 | // 这个例子应该是不完整的, 所以没生成二进制文件 35 | // 因为 fetchtask 一旦开起来就再也不出让 cpu 36 | // 也就是其他的协程根本调度不了, 只会创建一个协程 37 | for(i=0; i 1) 42 | ; 43 | sleep(1); 44 | } 45 | } 46 | 47 | void 48 | fetchtask(void *v) 49 | { 50 | int fd, n; 51 | char buf[512]; 52 | 53 | fprintf(stderr, "starting...\n"); 54 | // 死循环不断连接服务和发送请求 55 | for(;;){ 56 | // 连接 http 服务 57 | if((fd = netdial(TCP, server, 80)) < 0){ 58 | fprintf(stderr, "dial %s: %s (%s)\n", server, strerror(errno), taskgetstate()); 59 | continue; 60 | } 61 | // 发送数据并读取响应 62 | snprintf(buf, sizeof buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", url, server); 63 | fdwrite(fd, buf, strlen(buf)); 64 | while((n = fdread(fd, buf, sizeof buf)) > 0) 65 | ; 66 | // 关闭连接 67 | close(fd); 68 | // 打印一个 . 到屏幕 69 | write(1, ".", 1); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /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 | 9 | // 开启监听 10 | int 11 | netannounce(int istcp, char *server, int port) 12 | { 13 | int fd, n, proto; 14 | struct sockaddr_in sa; 15 | socklen_t sn; 16 | uint32_t ip; 17 | 18 | taskstate("netannounce"); 19 | // 协议是 tcp 还是 udp 20 | proto = istcp ? SOCK_STREAM : SOCK_DGRAM; 21 | memset(&sa, 0, sizeof sa); 22 | sa.sin_family = AF_INET; 23 | if(server != nil && strcmp(server, "*") != 0){ 24 | // 根据 server 来获取 ip,server 可以是 ip 或者域名 25 | if(netlookup(server, &ip) < 0){ 26 | taskstate("netlookup failed"); 27 | return -1; 28 | } 29 | memmove(&sa.sin_addr, &ip, 4); 30 | } 31 | // 端口转为网络字节序, 忘了是大端还是小端,懒,不想查 32 | sa.sin_port = htons(port); 33 | // 设置协议 34 | if((fd = socket(AF_INET, proto, 0)) < 0){ 35 | taskstate("socket failed"); 36 | return -1; 37 | } 38 | 39 | /* set reuse flag for tcp */ 40 | // 设置 resue 选项, 当 time_wait 过多的时候,可以重用端口 41 | if(istcp && getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0){ 42 | n = 1; 43 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); 44 | } 45 | 46 | // 绑定监听的地址和端口到 fd 47 | if(bind(fd, (struct sockaddr*)&sa, sizeof sa) < 0){ 48 | taskstate("bind failed"); 49 | close(fd); 50 | return -1; 51 | } 52 | 53 | // 如果是 tcp 就开启监听 54 | if(proto == SOCK_STREAM) 55 | listen(fd, 16); 56 | 57 | // 设置为非阻塞 58 | fdnoblock(fd); 59 | taskstate("netannounce succeeded"); 60 | return fd; 61 | } 62 | 63 | // 非阻塞的接收新连接 64 | int 65 | netaccept(int fd, char *server, int *port) 66 | { 67 | int cfd, one; 68 | struct sockaddr_in sa; 69 | uchar *ip; 70 | socklen_t len; 71 | 72 | // 注册读事件, 出让 cpu 直到事件到来才会重新调度 73 | fdwait(fd, 'r'); 74 | 75 | taskstate("netaccept"); 76 | len = sizeof sa; 77 | // accept 新连接 78 | if((cfd = accept(fd, (void*)&sa, &len)) < 0){ 79 | taskstate("accept failed"); 80 | return -1; 81 | } 82 | if(server){ 83 | ip = (uchar*)&sa.sin_addr; 84 | snprint(server, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); 85 | } 86 | if(port) 87 | *port = ntohs(sa.sin_port); 88 | // 连接 fd 设置为非阻塞 89 | fdnoblock(cfd); 90 | one = 1; 91 | setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one); 92 | taskstate("netaccept succeeded"); 93 | return cfd; 94 | } 95 | 96 | #define CLASS(p) ((*(unsigned char*)(p))>>6) 97 | static int 98 | parseip(char *name, uint32_t *ip) 99 | { 100 | unsigned char addr[4]; 101 | char *p; 102 | int i, x; 103 | 104 | p = name; 105 | // ipv4 解析 106 | for(i=0; i<4 && *p; i++){ 107 | x = strtoul(p, &p, 0); 108 | // x.x.x.x ipv4 单个字段不会超过 255 109 | if(x < 0 || x >= 256) 110 | return -1; 111 | // 数值接着的应该为 . 112 | if(*p != '.' && *p != 0) 113 | return -1; 114 | if(*p == '.') 115 | p++; 116 | addr[i] = x; 117 | } 118 | 119 | // 根据第一个数值的高两位来确定 ip 地址的类型 120 | // 0 为 A 类, 1.0.0.1-126.255.255.254 121 | // 1 为 B 类, 128.1.0.1-191.255.255.254 122 | // 2 为 C 类, 192.0.1.1-223.255.255.254 123 | // 如果不足 4个段,那么低的段使用 0 124 | switch(CLASS(addr)){ 125 | case 0: 126 | case 1: 127 | if(i == 3){ 128 | addr[3] = addr[2]; 129 | addr[2] = addr[1]; 130 | addr[1] = 0; 131 | }else if(i == 2){ 132 | addr[3] = addr[1]; 133 | addr[2] = 0; 134 | addr[1] = 0; 135 | }else if(i != 4) 136 | return -1; 137 | break; 138 | case 2: 139 | if(i == 3){ 140 | addr[3] = addr[2]; 141 | addr[2] = 0; 142 | }else if(i != 4) 143 | return -1; 144 | break; 145 | } 146 | *ip = *(uint32_t*)addr; 147 | return 0; 148 | } 149 | 150 | int 151 | netlookup(char *name, uint32_t *ip) 152 | { 153 | struct hostent *he; 154 | 155 | // 解析是否为ip 156 | if(parseip(name, ip) >= 0) 157 | return 0; 158 | 159 | /* BUG - Name resolution blocks. Need a non-blocking DNS. */ 160 | taskstate("netlookup"); 161 | // 这里的 DNS 解析会阻塞, 需要非阻塞的 DNS 解析 162 | if((he = gethostbyname(name)) != 0){ 163 | *ip = *(uint32_t*)he->h_addr; 164 | taskstate("netlookup succeeded"); 165 | return 0; 166 | } 167 | 168 | // 解析失败 169 | taskstate("netlookup failed"); 170 | return -1; 171 | } 172 | 173 | int 174 | netdial(int istcp, char *server, int port) 175 | { 176 | int proto, fd, n; 177 | uint32_t ip; 178 | struct sockaddr_in sa; 179 | socklen_t sn; 180 | 181 | // 解析 ip 182 | if(netlookup(server, &ip) < 0) 183 | return -1; 184 | 185 | taskstate("netdial"); 186 | // tcp 还是 udp 187 | proto = istcp ? SOCK_STREAM : SOCK_DGRAM; 188 | if((fd = socket(AF_INET, proto, 0)) < 0){ 189 | taskstate("socket failed"); 190 | return -1; 191 | } 192 | // 设置为非阻塞 193 | fdnoblock(fd); 194 | 195 | /* for udp */ 196 | // udp 才设置的选项 197 | if(!istcp){ 198 | n = 1; 199 | setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof n); 200 | } 201 | 202 | /* start connecting */ 203 | memset(&sa, 0, sizeof sa); 204 | memmove(&sa.sin_addr, &ip, 4); 205 | sa.sin_family = AF_INET; 206 | sa.sin_port = htons(port); 207 | // 连接服务 208 | if(connect(fd, (struct sockaddr*)&sa, sizeof sa) < 0 && errno != EINPROGRESS){ 209 | taskstate("connect failed"); 210 | close(fd); 211 | return -1; 212 | } 213 | 214 | /* wait for finish */ 215 | // 等待连接成功, 变成可写即是连接成功 216 | fdwait(fd, 'w'); 217 | sn = sizeof sa; 218 | // 获取远程ip和端口 219 | if(getpeername(fd, (struct sockaddr*)&sa, &sn) >= 0){ 220 | taskstate("connect succeeded"); 221 | return fd; 222 | } 223 | 224 | /* report error */ 225 | sn = sizeof n; 226 | // 获取是否有连接错误 227 | getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&n, &sn); 228 | if(n == 0) 229 | n = ECONNREFUSED; 230 | close(fd); 231 | taskstate("connect failed"); 232 | errno = n; 233 | return -1; 234 | } 235 | 236 | -------------------------------------------------------------------------------- /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 | 57 | long 58 | lrand(void) 59 | { 60 | return rand(); 61 | } 62 | -------------------------------------------------------------------------------- /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){ 11 | l->owner = taskrunning; 12 | return 1; 13 | } 14 | // 锁被别人占用, 如果不阻塞等待,那么直接返回 15 | if(!block) 16 | return 0; 17 | // 把需要等待的协程加入队列, unlock 的时候会把协程加入可运行队列,重新调度 18 | addtask(&l->waiting, taskrunning); 19 | // 状态设置为等待锁 20 | taskstate("qlock"); 21 | // 出让 cpu 22 | taskswitch(); 23 | // 重新调度的时候,如果发现锁拥有者,说明调度出问题了。 24 | // 因为 unlock 把等待协程加入可运行队列的前提是把锁所有权给这个等待的协程 25 | if(l->owner != taskrunning){ 26 | fprint(2, "qlock: owner=%p self=%p oops\n", l->owner, taskrunning); 27 | abort(); 28 | } 29 | return 1; 30 | } 31 | 32 | // 如果锁正在被其他的协程占用,会阻塞等待 33 | void 34 | qlock(QLock *l) 35 | { 36 | _qlock(l, 1); 37 | } 38 | 39 | // 只是尝试获取锁, 如果被其他地方占用,就不等待返回 40 | // 跟 try lock 类似 41 | int 42 | canqlock(QLock *l) 43 | { 44 | return _qlock(l, 0); 45 | } 46 | 47 | // 释放锁 48 | void 49 | qunlock(QLock *l) 50 | { 51 | Task *ready; 52 | 53 | // 如果锁不是自己,那你释放毛呢~~ 54 | // 这个直接 abort, 好那个.. 55 | if(l->owner == 0){ 56 | fprint(2, "qunlock: owner=0\n"); 57 | abort(); 58 | } 59 | // 释放锁的时候,检查一下等待队列,如果有就拿到第一个,然后把他设置为锁的拥有者 60 | // 再把获得锁的协程设置为可运行状态 61 | if((l->owner = ready = l->waiting.head) != nil){ 62 | // 从等待队列删除 63 | deltask(&l->waiting, ready); 64 | // 加入到可运行队列,等待调度 65 | taskready(ready); 66 | } 67 | } 68 | 69 | // 读写锁,这个和上面的互斥锁有什么区别,或者使用场景? 70 | // 我是不会说的 71 | static int 72 | _rlock(RWLock *l, int block) 73 | { 74 | // 检查当前是否有正在写入? 如果没有对读计数 +1 75 | // 读写锁的读锁是可以共享的,写是互斥的(包括和读互斥) 76 | if(l->writer == nil && l->wwaiting.head == nil){ 77 | l->readers++; 78 | return 1; 79 | } 80 | 81 | // 同样判断是否等待 82 | if(!block) 83 | return 0; 84 | // 添加到等待队列 85 | addtask(&l->rwaiting, taskrunning); 86 | // 状态设置为等待读锁 87 | taskstate("rlock"); 88 | // 切换到其他协程 89 | taskswitch(); 90 | return 1; 91 | } 92 | 93 | // 阻塞的获取读锁 94 | void 95 | rlock(RWLock *l) 96 | { 97 | _rlock(l, 1); 98 | } 99 | 100 | // 非阻塞的获取读锁, 拿不到就快跑 101 | int 102 | canrlock(RWLock *l) 103 | { 104 | return _rlock(l, 0); 105 | } 106 | 107 | // 获取写锁 108 | static int 109 | _wlock(RWLock *l, int block) 110 | { 111 | // 因为写和其他的读写操作是互斥的, 获取锁的前提是没有人正在读写 112 | if(l->writer == nil && l->readers == 0){ 113 | l->writer = taskrunning; 114 | return 1; 115 | } 116 | // 叔叔我们不等了 117 | if(!block) 118 | return 0; 119 | // 把协程加入写等待队列 120 | addtask(&l->wwaiting, taskrunning); 121 | // 设置为等待写锁 122 | taskstate("wlock"); 123 | // cpu 切换去干点别的吧 124 | taskswitch(); 125 | return 1; 126 | } 127 | 128 | // 阻塞的等待写锁 129 | void 130 | wlock(RWLock *l) 131 | { 132 | _wlock(l, 1); 133 | } 134 | 135 | // 非阻塞的等待写锁 136 | int 137 | canwlock(RWLock *l) 138 | { 139 | return _wlock(l, 0); 140 | } 141 | 142 | // 读锁释放 143 | void 144 | runlock(RWLock *l) 145 | { 146 | Task *t; 147 | 148 | // 对当前读的数量 -1, 如果发现没人占用了读锁, 就看看是否有等待写锁的 149 | // 有的话,就把这个等待的协程加入到可运行队列 150 | if(--l->readers == 0 && (t = l->wwaiting.head) != nil){ 151 | deltask(&l->wwaiting, t); 152 | l->writer = t; 153 | taskready(t); 154 | } 155 | } 156 | 157 | // 写锁释放 158 | void 159 | wunlock(RWLock *l) 160 | { 161 | Task *t; 162 | 163 | // 写锁又不是你拿的, 你释放个 jj 164 | if(l->writer == nil){ 165 | fprint(2, "wunlock: not locked\n"); 166 | abort(); 167 | } 168 | // 把写锁拥有者清空 169 | l->writer = nil; 170 | // 这时候不可能有读, 因为读写是互斥的 171 | if(l->readers != 0){ 172 | fprint(2, "wunlock: readers\n"); 173 | abort(); 174 | } 175 | // 把所有等待读锁的协程都变成可运行状态 176 | while((t = l->rwaiting.head) != nil){ 177 | deltask(&l->rwaiting, t); 178 | l->readers++; 179 | taskready(t); 180 | } 181 | // 上面先判断上面是否有等待读锁,这里再来判断是否等待写的协程 182 | // 也就是说读优先 183 | if(l->readers == 0 && (t = l->wwaiting.head) != nil){ 184 | deltask(&l->wwaiting, t); 185 | l->writer = t; 186 | taskready(t); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /rendez.c: -------------------------------------------------------------------------------- 1 | #include "taskimpl.h" 2 | 3 | /* 4 | * sleep and wakeup 5 | */ 6 | // 类似条件变量的功能 7 | // tasksleep 等待某一个资源,然后进入休眠状态 8 | // 当条件满足的时候去调用 taskwakeup 来唤醒协程来 9 | void 10 | tasksleep(Rendez *r) 11 | { 12 | // 把当前运行的协程假如等待队列 13 | addtask(&r->waiting, taskrunning); 14 | // 如果占用着锁就先释放 15 | if(r->l) 16 | qunlock(r->l); 17 | taskstate("sleep"); 18 | // 释放 Cpu, 切换到调度协程 19 | taskswitch(); 20 | // 如果有锁, 被唤醒的时候要去获取锁 21 | if(r->l) 22 | qlock(r->l); 23 | } 24 | 25 | // 唤醒协程 26 | static int 27 | _taskwakeup(Rendez *r, int all) 28 | { 29 | int i; 30 | Task *t; 31 | 32 | for(i=0;; i++){ 33 | // 只唤醒一个? 34 | if(i==1 && !all) 35 | break; 36 | // 如果没有休眠的协程, 就退出 37 | if((t = r->waiting.head) == nil) 38 | break; 39 | // 从休眠队列删除, 加入就绪队列 40 | deltask(&r->waiting, t); 41 | taskready(t); 42 | } 43 | return i; 44 | } 45 | 46 | // 唤醒一个任务 47 | int 48 | taskwakeup(Rendez *r) 49 | { 50 | return _taskwakeup(r, 0); 51 | } 52 | 53 | // 唤醒所有任务 54 | int 55 | taskwakeupall(Rendez *r) 56 | { 57 | return _taskwakeup(r, 1); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /task.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */ 2 | 3 | #include "taskimpl.h" 4 | #include 5 | #include 6 | 7 | int taskdebuglevel; 8 | int taskcount; 9 | int tasknswitch; 10 | int taskexitval; 11 | Task *taskrunning; 12 | 13 | Context taskschedcontext; 14 | Tasklist taskrunqueue; // 就绪队列 15 | 16 | Task **alltask; 17 | int nalltask; 18 | 19 | static char *argv0; 20 | static void contextswitch(Context *from, Context *to); 21 | 22 | static void 23 | taskdebug(char *fmt, ...) 24 | { 25 | va_list arg; 26 | char buf[128]; 27 | Task *t; 28 | char *p; 29 | static int fd = -1; 30 | 31 | return; 32 | va_start(arg, fmt); 33 | vfprint(1, fmt, arg); 34 | va_end(arg); 35 | return; 36 | 37 | if(fd < 0){ 38 | p = strrchr(argv0, '/'); 39 | if(p) 40 | p++; 41 | else 42 | p = argv0; 43 | snprint(buf, sizeof buf, "/tmp/%s.tlog", p); 44 | if((fd = open(buf, O_CREAT|O_WRONLY, 0666)) < 0) 45 | fd = open("/dev/null", O_WRONLY); 46 | } 47 | 48 | va_start(arg, fmt); 49 | vsnprint(buf, sizeof buf, fmt, arg); 50 | va_end(arg); 51 | t = taskrunning; 52 | if(t) 53 | fprint(fd, "%d.%d: %s\n", getpid(), t->id, buf); 54 | else 55 | fprint(fd, "%d._: %s\n", getpid(), buf); 56 | } 57 | 58 | static void 59 | taskstart(uint y, uint x) 60 | { 61 | Task *t; 62 | ulong z; 63 | 64 | z = x<<16; /* hide undefined 32-bit shift from 32-bit compilers */ 65 | z <<= 16; 66 | z |= y; 67 | t = (Task*)z; 68 | 69 | //print("taskstart %p\n", t); 70 | t->startfn(t->startarg); 71 | //print("taskexits %p\n", t); 72 | taskexit(0); 73 | //print("not reacehd\n"); 74 | } 75 | 76 | static int taskidgen; 77 | 78 | static Task* 79 | taskalloc(void (*fn)(void*), void *arg, uint stack) 80 | { 81 | Task *t; 82 | sigset_t zero; 83 | uint x, y; 84 | ulong z; 85 | 86 | /* allocate the task and stack together */ 87 | // 协程结构和栈空间在连续的内存,这个可以有效减少内存碎片 88 | t = malloc(sizeof *t+stack); 89 | if(t == nil){ 90 | fprint(2, "taskalloc malloc: %r\n"); 91 | abort(); 92 | } 93 | 94 | memset(t, 0, sizeof *t); 95 | // 栈指针 96 | t->stk = (uchar*)(t+1); 97 | // 栈大小 98 | t->stksize = stack; 99 | // 协程id 100 | t->id = ++taskidgen; 101 | // 协程对应执行方法 102 | t->startfn = fn; 103 | // 参数 104 | t->startarg = arg; 105 | 106 | /* do a reasonable initialization */ 107 | memset(&t->context.uc, 0, sizeof t->context.uc); 108 | sigemptyset(&zero); 109 | sigprocmask(SIG_BLOCK, &zero, &t->context.uc.uc_sigmask); 110 | 111 | /* must initialize with current context */ 112 | // 获取当前上下文作为当前协程的上下文 113 | if(getcontext(&t->context.uc) < 0){ 114 | fprint(2, "getcontext: %r\n"); 115 | abort(); 116 | } 117 | 118 | /* call makecontext to do the real work. */ 119 | /* leave a few words open on both ends */ 120 | // 在栈的开头和结束分别留了 8 和 64 bytes空间,不知道干嘛 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 | // SunOS5.9 的栈是向低地址增长 127 | t->context.uc.uc_stack.ss_sp = 128 | (char*)t->context.uc.uc_stack.ss_sp 129 | +t->context.uc.uc_stack.ss_size; 130 | #endif 131 | /* 132 | * All this magic is because you have to pass makecontext a 133 | * function that takes some number of word-sized variables, 134 | * and on 64-bit machines pointers are bigger than words. 135 | */ 136 | //print("make %p\n", t); 137 | // 把 64bit 的指针 z 拆分成两个 32bit 的 x << 32 | y 138 | // 避免不兼容 139 | z = (ulong)t; 140 | y = z; 141 | z >>= 16; /* hide undefined 32-bit shift from 32-bit compilers */ 142 | x = z>>16; 143 | // 函数是干嘛见 man(3) makecontext 好么 144 | // 对用户线程的上下文信息进行修改, 包括指定新的栈地址等 145 | makecontext(&t->context.uc, (void(*)())taskstart, 2, y, x); 146 | 147 | return t; 148 | } 149 | 150 | // 使用函数 fn 创建一个新的协程, 并添加到协程队列 151 | int 152 | taskcreate(void (*fn)(void*), void *arg, uint stack) 153 | { 154 | int id; 155 | Task *t; 156 | 157 | // 申请协程结构的空间以及初始化 158 | t = taskalloc(fn, arg, stack); 159 | // 当前协程数目 +1 160 | taskcount++; 161 | id = t->id; 162 | // 存储所有协程的数组是否需要扩容 163 | // 为什么是 nalltask % 64 == 0 是扩容的条件? 164 | // 因为每次增加64,所以每当能整除的时候说明,空间用完 165 | if(nalltask%64 == 0){ 166 | alltask = realloc(alltask, (nalltask+64)*sizeof(alltask[0])); 167 | if(alltask == nil){ 168 | fprint(2, "out of memory\n"); 169 | abort(); 170 | } 171 | } 172 | 173 | // 放到数组尾部 174 | t->alltaskslot = nalltask; 175 | alltask[nalltask++] = t; 176 | 177 | // 协程状态设置为就绪, 可以进行调度 178 | taskready(t); 179 | return id; 180 | } 181 | 182 | // 把当前的协程标识为系统协程 183 | void 184 | tasksystem(void) 185 | { 186 | if(!taskrunning->system){ 187 | taskrunning->system = 1; 188 | --taskcount; 189 | } 190 | } 191 | 192 | // 协程切换 193 | void 194 | taskswitch(void) 195 | { 196 | needstack(0); 197 | contextswitch(&taskrunning->context, &taskschedcontext); 198 | } 199 | 200 | // 协程添加到就绪队列 201 | void 202 | taskready(Task *t) 203 | { 204 | t->ready = 1; 205 | addtask(&taskrunqueue, t); 206 | } 207 | 208 | // 协程让出 cpu 209 | int 210 | taskyield(void) 211 | { 212 | int n; 213 | 214 | n = tasknswitch; 215 | // 添加到就绪队列的尾部, 相当于让排在它后面的协程优先调度 216 | taskready(taskrunning); 217 | // 状态标识为 yield 218 | taskstate("yield"); 219 | // 切换上下文 220 | taskswitch(); 221 | // tasknswitch表示协程调度次数,出让到再次调用,两次相减就是出让的调度次数 222 | return tasknswitch - n - 1; 223 | } 224 | 225 | // 是否有就绪的协程可以调度 226 | int 227 | anyready(void) 228 | { 229 | return taskrunqueue.head != nil; 230 | } 231 | 232 | // 退出所有协程 233 | void 234 | taskexitall(int val) 235 | { 236 | exit(val); 237 | } 238 | 239 | // 退出当前执行的协程 240 | void 241 | taskexit(int val) 242 | { 243 | taskexitval = val; 244 | taskrunning->exiting = 1; 245 | taskswitch(); 246 | } 247 | 248 | // 上下文切换, 相当于协程切换 249 | static void 250 | contextswitch(Context *from, Context *to) 251 | { 252 | // swapcontext功能见 man(3) swapcontext 253 | if(swapcontext(&from->uc, &to->uc) < 0){ 254 | fprint(2, "swapcontext failed: %r\n"); 255 | assert(0); 256 | } 257 | } 258 | 259 | static void 260 | taskscheduler(void) 261 | { 262 | int i; 263 | Task *t; 264 | 265 | taskdebug("scheduler enter"); 266 | for(;;){ 267 | // 当前除了系统协程外没有其他协程, 退出 268 | if(taskcount == 0) 269 | exit(taskexitval); 270 | 271 | // 处理的顺序是 FIFO 272 | t = taskrunqueue.head; 273 | if(t == nil){ 274 | fprint(2, "no runnable tasks! %d tasks stalled\n", taskcount); 275 | exit(1); 276 | } 277 | // 从就绪队列里面去掉这个协程 278 | deltask(&taskrunqueue, t); 279 | t->ready = 0; 280 | 281 | // 把拿出来的就绪任务设置为当前正在执行的任务 282 | taskrunning = t; 283 | // 协程调度计数 +1 284 | tasknswitch++; 285 | taskdebug("run %d (%s)", t->id, t->name); 286 | 287 | // 切换到刚拿到的协程, 调度的核心 288 | // 使用 taskschedcontext 保留老的堆栈状态, 把 t->context 设置为新的执行上下文 289 | // 后面通过 taskswitch 切换回调度协程 290 | contextswitch(&taskschedcontext, &t->context); 291 | //print("back in scheduler\n"); 292 | taskrunning = nil; 293 | 294 | if(t->exiting){ // 协程执行完退出 295 | // 系统协程不计数 296 | if(!t->system) 297 | taskcount--; 298 | 299 | // 把数组中最后一个任务放到要删除的任务的位置 300 | // 这里判断一下当前任务是不是已经在数据尾部会更加优雅? 301 | i = t->alltaskslot; 302 | alltask[i] = alltask[--nalltask]; 303 | alltask[i]->alltaskslot = i; 304 | // 释放任务 305 | free(t); 306 | } 307 | } 308 | } 309 | 310 | void** 311 | taskdata(void) 312 | { 313 | return &taskrunning->udata; 314 | } 315 | 316 | /* 317 | * debugging 318 | */ 319 | void 320 | taskname(char *fmt, ...) 321 | { 322 | va_list arg; 323 | Task *t; 324 | 325 | t = taskrunning; 326 | va_start(arg, fmt); 327 | vsnprint(t->name, sizeof t->name, fmt, arg); 328 | va_end(arg); 329 | } 330 | 331 | char* 332 | taskgetname(void) 333 | { 334 | return taskrunning->name; 335 | } 336 | 337 | // 设置当前协程状态 338 | void 339 | taskstate(char *fmt, ...) 340 | { 341 | va_list arg; 342 | Task *t; 343 | 344 | t = taskrunning; 345 | va_start(arg, fmt); 346 | vsnprint(t->state, sizeof t->name, fmt, arg); 347 | va_end(arg); 348 | } 349 | 350 | // 获取当前协程状态 351 | char* 352 | taskgetstate(void) 353 | { 354 | return taskrunning->state; 355 | } 356 | 357 | // 检查栈空间是否足够 n bytes 358 | void 359 | needstack(int n) 360 | { 361 | Task *t; 362 | 363 | t = taskrunning; 364 | 365 | // 实现检查的技巧比较 hacker 366 | // 通过创建临时变量t, 这时候t一定是在栈顶 367 | // 栈顶一定大于栈底 368 | if((char*)&t <= (char*)t->stk 369 | || (char*)&t - (char*)t->stk < 256+n){ 370 | fprint(2, "task stack overflow: &t=%p tstk=%p n=%d\n", &t, t->stk, 256+n); 371 | abort(); 372 | } 373 | } 374 | 375 | // 打印协程信息, 收到退出信号的时候会调用 376 | static void 377 | taskinfo(int s) 378 | { 379 | int i; 380 | Task *t; 381 | char *extra; 382 | 383 | fprint(2, "task list:\n"); 384 | for(i=0; iready) 389 | extra = " (ready)"; 390 | else 391 | extra = ""; 392 | fprint(2, "%6d%c %-20s %s%s\n", 393 | t->id, t->system ? 's' : ' ', 394 | t->name, t->state, extra); 395 | } 396 | } 397 | 398 | /* 399 | * startup 400 | */ 401 | 402 | static int taskargc; 403 | static char **taskargv; 404 | int mainstacksize; 405 | 406 | // 主协程 407 | static void 408 | taskmainstart(void *v) 409 | { 410 | taskname("taskmain"); 411 | taskmain(taskargc, taskargv); 412 | } 413 | 414 | int 415 | main(int argc, char **argv) 416 | { 417 | struct sigaction sa, osa; 418 | 419 | memset(&sa, 0, sizeof sa); 420 | 421 | // 设置 quit 信号时,对应处理函数为 taskinfo 422 | // 这个函数会在收到退出信号时, 打印当前的所有协程信息 423 | sa.sa_handler = taskinfo; 424 | sa.sa_flags = SA_RESTART; 425 | sigaction(SIGQUIT, &sa, &osa); 426 | 427 | #ifdef SIGINFO 428 | sigaction(SIGINFO, &sa, &osa); 429 | #endif 430 | 431 | argv0 = argv[0]; 432 | taskargc = argc; 433 | taskargv = argv; 434 | 435 | // 设置默认的最小栈大小 436 | if(mainstacksize == 0) 437 | mainstacksize = 256*1024; 438 | 439 | // 创建一个主协程 440 | taskcreate(taskmainstart, nil, mainstacksize); 441 | // 开始协程调度 442 | taskscheduler(); 443 | 444 | fprint(2, "taskscheduler returned in main!\n"); 445 | abort(); 446 | return 0; 447 | } 448 | 449 | /* 450 | * hooray for linked lists 451 | */ 452 | void 453 | addtask(Tasklist *l, Task *t) 454 | { 455 | if(l->tail){ 456 | l->tail->next = t; 457 | t->prev = l->tail; 458 | }else{ 459 | l->head = t; 460 | t->prev = nil; 461 | } 462 | l->tail = t; 463 | t->next = nil; 464 | } 465 | 466 | void 467 | deltask(Tasklist *l, Task *t) 468 | { 469 | if(t->prev) 470 | t->prev->next = t->next; 471 | else 472 | l->head = t->next; 473 | if(t->next) 474 | t->next->prev = t->prev; 475 | else 476 | l->tail = t->prev; 477 | } 478 | 479 | // 协程id 480 | unsigned int 481 | taskid(void) 482 | { 483 | return taskrunning->id; 484 | } 485 | 486 | -------------------------------------------------------------------------------- /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; // channel 的名字 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 fdwrite(int, void*, int); 158 | void fdwait(int, int); 159 | int fdnoblock(int); 160 | 161 | void fdtask(void*); 162 | 163 | /* 164 | * Network dialing - sets non-blocking automatically 165 | */ 166 | enum 167 | { 168 | UDP = 0, 169 | TCP = 1, 170 | }; 171 | 172 | int netannounce(int, char*, int); 173 | int netaccept(int, char*, int*); 174 | int netdial(int, char*, int); 175 | int netlookup(char*, uint32_t*); /* blocks entire program! */ 176 | int netdial(int, char*, int); 177 | 178 | #ifdef __cplusplus 179 | } 180 | #endif 181 | #endif 182 | 183 | -------------------------------------------------------------------------------- /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 USE_UCONTEXT 1 13 | 14 | #if defined(__OpenBSD__) || defined(__mips__) 15 | #undef USE_UCONTEXT 16 | #define USE_UCONTEXT 0 17 | #endif 18 | 19 | #if defined(__APPLE__) 20 | #include 21 | #if defined(MAC_OS_X_VERSION_10_5) 22 | #undef USE_UCONTEXT 23 | #define USE_UCONTEXT 0 24 | #endif 25 | #endif 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #if USE_UCONTEXT 39 | #include 40 | #endif 41 | #include 42 | #include 43 | #include "task.h" 44 | 45 | #define nil ((void*)0) 46 | #define nelem(x) (sizeof(x)/sizeof((x)[0])) 47 | 48 | #define ulong task_ulong 49 | #define uint task_uint 50 | #define uchar task_uchar 51 | #define ushort task_ushort 52 | #define uvlong task_uvlong 53 | #define vlong task_vlong 54 | 55 | typedef unsigned long ulong; 56 | typedef unsigned int uint; 57 | typedef unsigned char uchar; 58 | typedef unsigned short ushort; 59 | typedef unsigned long long uvlong; 60 | typedef long long vlong; 61 | 62 | #define print task_print 63 | #define fprint task_fprint 64 | #define snprint task_snprint 65 | #define seprint task_seprint 66 | #define vprint task_vprint 67 | #define vfprint task_vfprint 68 | #define vsnprint task_vsnprint 69 | #define vseprint task_vseprint 70 | #define strecpy task_strecpy 71 | 72 | int print(char*, ...); 73 | int fprint(int, char*, ...); 74 | char *snprint(char*, uint, char*, ...); 75 | char *seprint(char*, char*, char*, ...); 76 | int vprint(char*, va_list); 77 | int vfprint(int, char*, va_list); 78 | char *vsnprint(char*, uint, char*, va_list); 79 | char *vseprint(char*, char*, char*, va_list); 80 | char *strecpy(char*, char*, char*); 81 | 82 | #if defined(__FreeBSD__) && __FreeBSD__ < 5 83 | extern int getmcontext(mcontext_t*); 84 | extern void setmcontext(const mcontext_t*); 85 | #define setcontext(u) setmcontext(&(u)->uc_mcontext) 86 | #define getcontext(u) getmcontext(&(u)->uc_mcontext) 87 | extern int swapcontext(ucontext_t*, const ucontext_t*); 88 | extern void makecontext(ucontext_t*, void(*)(), int, ...); 89 | #endif 90 | 91 | #if defined(__APPLE__) 92 | # define mcontext libthread_mcontext 93 | # define mcontext_t libthread_mcontext_t 94 | # define ucontext libthread_ucontext 95 | # define ucontext_t libthread_ucontext_t 96 | # if defined(__i386__) 97 | # include "386-ucontext.h" 98 | # elif defined(__x86_64__) 99 | # include "amd64-ucontext.h" 100 | # else 101 | # include "power-ucontext.h" 102 | # endif 103 | #endif 104 | 105 | #if defined(__OpenBSD__) 106 | # define mcontext libthread_mcontext 107 | # define mcontext_t libthread_mcontext_t 108 | # define ucontext libthread_ucontext 109 | # define ucontext_t libthread_ucontext_t 110 | # if defined __i386__ 111 | # include "386-ucontext.h" 112 | # else 113 | # include "power-ucontext.h" 114 | # endif 115 | extern pid_t rfork_thread(int, void*, int(*)(void*), void*); 116 | #endif 117 | 118 | #if 0 && defined(__sun__) 119 | # define mcontext libthread_mcontext 120 | # define mcontext_t libthread_mcontext_t 121 | # define ucontext libthread_ucontext 122 | # define ucontext_t libthread_ucontext_t 123 | # include "sparc-ucontext.h" 124 | #endif 125 | 126 | #if defined(__arm__) 127 | int getmcontext(mcontext_t*); 128 | void setmcontext(const mcontext_t*); 129 | #define setcontext(u) setmcontext(&(u)->uc_mcontext) 130 | #define getcontext(u) getmcontext(&(u)->uc_mcontext) 131 | #endif 132 | 133 | #if defined(__mips__) 134 | #include "mips-ucontext.h" 135 | int getmcontext(mcontext_t*); 136 | void setmcontext(const mcontext_t*); 137 | #define setcontext(u) setmcontext(&(u)->uc_mcontext) 138 | #define getcontext(u) getmcontext(&(u)->uc_mcontext) 139 | #endif 140 | 141 | typedef struct Context Context; 142 | 143 | enum 144 | { 145 | STACK = 8192 146 | }; 147 | 148 | struct Context 149 | { 150 | ucontext_t uc; 151 | }; 152 | 153 | struct Task 154 | { 155 | char name[256]; // offset known to acid 156 | char state[256]; 157 | Task *next; 158 | Task *prev; 159 | Task *allnext; 160 | Task *allprev; 161 | // 协程对应的上下文信息 162 | Context context; 163 | // 如果是延时协程, 这个字段指定延时时间, 164 | uvlong alarmtime; 165 | // 协程id 166 | uint id; 167 | // 栈指针 168 | uchar *stk; 169 | // 栈大小 170 | uint stksize; 171 | // 是否已经退出 172 | int exiting; 173 | // 任务在所有协程数组中的索引 174 | int alltaskslot; 175 | // 是否为系统协程, 在计算是否还有用户协程时,会忽略 system = 1 的协程 176 | int system; 177 | // 是否在就绪队列中 178 | int ready; 179 | // 协程对应函数 180 | void (*startfn)(void*); 181 | void *startarg; 182 | void *udata; 183 | }; 184 | 185 | void taskready(Task*); 186 | void taskswitch(void); 187 | 188 | void addtask(Tasklist*, Task*); 189 | void deltask(Tasklist*, Task*); 190 | 191 | extern Task *taskrunning; 192 | extern int taskcount; 193 | -------------------------------------------------------------------------------- /tcpproxy.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 | char *server; 15 | int port; 16 | void proxytask(void*); 17 | void rwtask(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[16]; 40 | 41 | if(argc != 4){ 42 | fprintf(stderr, "usage: tcpproxy localport server remoteport\n"); 43 | taskexitall(1); 44 | } 45 | // 远程服务的 ip 46 | server = argv[2]; 47 | // 远程服务的端口 48 | port = atoi(argv[3]); 49 | 50 | // 开启 tcp 监听 51 | if((fd = netannounce(TCP, 0, atoi(argv[1]))) < 0){ 52 | fprintf(stderr, "cannot announce on tcp port %d: %s\n", atoi(argv[1]), strerror(errno)); 53 | taskexitall(1); 54 | } 55 | // 监听的 fd 设置为非阻塞, netannounce 里面设置过了,作者年纪大了,记性可能不是很好 56 | fdnoblock(fd); 57 | // 开始接收连接, 内部如果没有连接到来会出让 cpu, 所以这里不会阻塞 58 | while((cfd = netaccept(fd, remote, &rport)) >= 0){ 59 | fprintf(stderr, "connection from %s:%d\n", remote, rport); 60 | // 每次来一个新连接都创建一个协程来处理这个请求 61 | taskcreate(proxytask, (void*)cfd, STACK); 62 | } 63 | } 64 | 65 | void 66 | proxytask(void *v) 67 | { 68 | int fd, remotefd; 69 | 70 | fd = (int)v; 71 | // 连接远程 server 72 | if((remotefd = netdial(TCP, server, port)) < 0){ 73 | close(fd); 74 | return; 75 | } 76 | 77 | fprintf(stderr, "connected to %s:%d\n", server, port); 78 | 79 | // 透传请求,需要从客户端读取,然后写到远程 80 | taskcreate(rwtask, mkfd2(fd, remotefd), STACK); 81 | // 透传响应,需要从服务端读取, 然后写到客户端 82 | taskcreate(rwtask, mkfd2(remotefd, fd), STACK); 83 | } 84 | 85 | void 86 | rwtask(void *v) 87 | { 88 | int *a, rfd, wfd, n; 89 | char buf[2048]; 90 | 91 | a = v; 92 | rfd = a[0]; 93 | wfd = a[1]; 94 | free(a); 95 | 96 | // 一边读,一边写 97 | while((n = fdread(rfd, buf, sizeof buf)) > 0) 98 | fdwrite(wfd, buf, n); 99 | // 关闭 100 | shutdown(wfd, SHUT_WR); 101 | close(rfd); 102 | } 103 | 104 | -------------------------------------------------------------------------------- /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 | // sleep v ms后唤醒 16 | taskdelay((int)v); 17 | printf("awake after %d ms\n", (int)v); 18 | // 写入channel, 后面会有recv channel 来计数 19 | chansendul(c, 0); 20 | } 21 | 22 | void 23 | taskmain(int argc, char **argv) 24 | { 25 | int i, n; 26 | 27 | // 创建长度为 0 大小的 channel,用来做同步 28 | c = chancreate(sizeof(unsigned long), 0); 29 | 30 | n = 0; 31 | for(i=1; i 2 | 3 | void 4 | taskmain(int argc, char *argv[]) 5 | { 6 | taskdelay(1000); 7 | } 8 | --------------------------------------------------------------------------------