├── .gitignore ├── 386-ucontext.h ├── COPYRIGHT ├── LICENSE ├── Makefile ├── README.md ├── amd64-ucontext.h ├── asm.S ├── channel.c ├── context.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 | *.o 2 | libtask.a 3 | primes 4 | tcpproxy 5 | testdelay 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 theanarkh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /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) 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 | primes: primes.o $(LIB) 34 | $(CC) -o primes primes.o $(LIB) 35 | 36 | tcpproxy: tcpproxy.o $(LIB) 37 | $(CC) -o tcpproxy tcpproxy.o $(LIB) $(TCPLIBS) 38 | 39 | httpload: httpload.o $(LIB) 40 | $(CC) -o httpload httpload.o $(LIB) 41 | 42 | testdelay: testdelay.o $(LIB) 43 | $(CC) -o testdelay testdelay.o $(LIB) 44 | 45 | testdelay1: testdelay1.o $(LIB) 46 | $(CC) -o testdelay1 testdelay1.o $(LIB) 47 | 48 | clean: 49 | rm -f *.o primes tcpproxy testdelay testdelay1 httpload $(LIB) 50 | 51 | install: $(LIB) 52 | cp $(LIB) /usr/local/lib 53 | cp task.h /usr/local/include 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### libtask源码分析,libtask是一个协程库,由google的Russ Cox大佬写的( https://swtch.com/libtask/ )。 2 | ### 之前也分析过一些相关的代码 3 | ### 1 [理解协程的实现](https://mp.weixin.qq.com/s/Yq-SaG5HJenzpxGyztz8RQ) 4 | ### 2 [云风coroutine库源码分析](https://mp.weixin.qq.com/s/bOHD8AUdM6WbCN2R0a3p0Q) 5 |
6 | 7 | ### 下面是该库的文档和说明 8 | 9 | (Fork addendum: This is a fork of libtask that adds support for epoll() 10 | and other thing helpful for writing high-performance network servers 11 | using libtask on Linux. 12 | 13 | Now back to your regularly scheduled README. -jamwt) 14 | 15 | -------------------------------------------------------------------------------- 16 | 17 | Libtask is a simple coroutine library. It runs on Linux (ARM, MIPS, and x86), 18 | FreeBSD (x86), OS X (PowerPC x86, and x86-64), and SunOS Solaris (Sparc), 19 | and is easy to port to other systems. 20 | 21 | Libtask gives the programmer the illusion of threads, but 22 | the operating system sees only a single kernel thread. 23 | For clarity, we refer to the coroutines as "tasks," not threads. 24 | 25 | Scheduling is cooperative. Only one task runs at a time, 26 | and it cannot be rescheduled without explicitly giving up 27 | the CPU. Most of the functions provided in task.h do have 28 | the possibility of going to sleep. Programs using the task 29 | functions should #include . 30 | 31 | --- Basic task manipulation 32 | 33 | int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize); 34 | 35 | Create a new task running f(arg) on a stack of size stacksize. 36 | 37 | void tasksystem(void); 38 | 39 | Mark the current task as a "system" task. These are ignored 40 | for the purposes of deciding the program is done running 41 | (see taskexit next). 42 | 43 | void taskexit(int status); 44 | 45 | Exit the current task. If this is the last non-system task, 46 | exit the entire program using the given exit status. 47 | 48 | void taskexitall(int status); 49 | 50 | Exit the entire program, using the given exit status. 51 | 52 | void taskmain(int argc, char *argv[]); 53 | 54 | Write this function instead of main. Libtask provides its own main. 55 | 56 | int taskyield(void); 57 | 58 | Explicitly give up the CPU. The current task will be scheduled 59 | again once all the other currently-ready tasks have a chance 60 | to run. Returns the number of other tasks that ran while the 61 | current task was waiting. (Zero means there are no other tasks 62 | trying to run.) 63 | 64 | int taskdelay(unsigned int ms) 65 | 66 | Explicitly give up the CPU for at least ms milliseconds. 67 | Other tasks continue to run during this time. 68 | 69 | void** taskdata(void); 70 | 71 | Return a pointer to a single per-task void* pointer. 72 | You can use this as a per-task storage place. 73 | 74 | void needstack(int n); 75 | 76 | Tell the task library that you need at least n bytes left 77 | on the stack. If you don't have it, the task library will call abort. 78 | (It's hard to figure out how big stacks should be. I usually make 79 | them really big (say 32768) and then don't worry about it.) 80 | 81 | void taskname(char*, ...); 82 | 83 | Takes an argument list like printf. Sets the current task's name. 84 | 85 | char* taskgetname(void); 86 | 87 | Returns the current task's name. Is the actual buffer; do not free. 88 | 89 | void taskstate(char*, ...); 90 | char* taskgetstate(void); 91 | 92 | Like taskname and taskgetname but for the task state. 93 | 94 | When you send a tasked program a SIGQUIT (or SIGINFO, on BSD) 95 | it will print a list of all its tasks and their names and states. 96 | This is useful for debugging why your program isn't doing anything! 97 | 98 | unsigned int taskid(void); 99 | 100 | Return the unique task id for the current task. 101 | 102 | --- Non-blocking I/O 103 | 104 | There is a small amount of runtime support for non-blocking I/O 105 | on file descriptors. 106 | 107 | int fdnoblock(int fd); 108 | 109 | Sets I/O on the given fd to be non-blocking. Should be 110 | called before any of the other fd routines. 111 | 112 | int fdread(int, void*, int); 113 | 114 | Like regular read(), but puts task to sleep while waiting for 115 | data instead of blocking the whole program. 116 | 117 | int fdwrite(int, void*, int); 118 | 119 | Like regular write(), but puts task to sleep while waiting to 120 | write data instead of blocking the whole program. 121 | 122 | void fdwait(int fd, int rw); 123 | 124 | Low-level call sitting underneath fdread and fdwrite. 125 | Puts task to sleep while waiting for I/O to be possible on fd. 126 | Rw specifies type of I/O: 'r' means read, 'w' means write, 127 | anything else means just exceptional conditions (hang up, etc.) 128 | The 'r' and 'w' also wake up for exceptional conditions. 129 | 130 | --- Network I/O 131 | 132 | These are convenient packaging of the ugly Unix socket routines. 133 | They can all put the current task to sleep during the call. 134 | 135 | int netannounce(int proto, char *address, int port) 136 | 137 | Start a network listener running on address and port of protocol. 138 | Proto is either TCP or UDP. Port is a port number. Address is a 139 | string version of a host name or IP address. If address is null, 140 | then announce binds to the given port on all available interfaces. 141 | Returns a fd to use with netaccept. 142 | Examples: netannounce(TCP, "localhost", 80) or 143 | netannounce(TCP, "127.0.0.1", 80) or netannounce(TCP, 0, 80). 144 | 145 | int netaccept(int fd, char *server, int *port) 146 | 147 | Get the next connection that comes in to the listener fd. 148 | Returns a fd to use to talk to the guy who just connected. 149 | If server is not null, it must point at a buffer of at least 150 | 16 bytes that is filled in with the remote IP address. 151 | If port is not null, it is filled in with the report port. 152 | Example: 153 | char server[16]; 154 | int port; 155 | 156 | if(netaccept(fd, server, &port) >= 0) 157 | printf("connect from %s:%d", server, port); 158 | 159 | int netdial(int proto, char *name, int port) 160 | 161 | Create a new (outgoing) connection to a particular host. 162 | Name can be an ip address or a domain name. If it's a domain name, 163 | the entire program will block while the name is resolved 164 | (the DNS library does not provide a nice non-blocking interface). 165 | Example: netdial(TCP, "www.google.com", 80) 166 | or netdial(TCP, "18.26.4.9", 80) 167 | 168 | --- Time 169 | 170 | unsigned int taskdelay(unsigned int ms) 171 | 172 | Put the current task to sleep for approximately ms milliseconds. 173 | Return the actual amount of time slept, in milliseconds. 174 | 175 | --- Example programs 176 | 177 | In this directory, tcpproxy.c is a simple TCP proxy that illustrates 178 | most of the above. You can run 179 | 180 | tcpproxy 1234 www.google.com 80 181 | 182 | and then you should be able to visit http://localhost:1234/ and see Google. 183 | 184 | Other examples are: 185 | primes.c - simple prime sieve 186 | httpload.c - simple HTTP load generator 187 | testdelay.c - test taskdelay() 188 | 189 | --- Building 190 | 191 | To build, run make. You can run make install to copy task.h and 192 | libtask.a to the appropriate places in /usr/local. Then you 193 | should be able to just link with -ltask in your programs 194 | that use it. 195 | 196 | On SunOS Solaris machines, run makesun instead of just make. 197 | 198 | --- Contact Info 199 | 200 | Please email me with questions or problems. 201 | 202 | Russ Cox 203 | rsc@swtch.com 204 | 205 | 206 | --- Stuff you probably won't use at first --- 207 | --- but might want to know about eventually --- 208 | 209 | void tasksleep(Rendez*); 210 | int taskwakeup(Rendez*); 211 | int taskwakeupall(Rendez*); 212 | 213 | A Rendez is a condition variable. You can declare a new one by 214 | just allocating memory for it (or putting it in another structure) 215 | and then zeroing the memory. Tasksleep(r) 'sleeps on r', giving 216 | up the CPU. Multiple tasks can sleep on a single Rendez. 217 | When another task comes along and calls taskwakeup(r), 218 | the first task sleeping on r (if any) will be woken up. 219 | Taskwakeupall(r) wakes up all the tasks sleeping on r. 220 | They both return the actual number of tasks awakened. 221 | 222 | 223 | 224 | void qlock(QLock*); 225 | int canqlock(QLock*); 226 | void qunlock(QLock*); 227 | 228 | You probably won't need locks because of the cooperative 229 | scheduling, but if you do, here are some. You can make a new 230 | QLock by just declaring it and zeroing the memory. 231 | Calling qlock will give up the CPU if the lock is held by someone else. 232 | Calling qunlock will not give up the CPU. 233 | Calling canqlock tries to lock the lock, but will not give up the CPU. 234 | It returns 1 if the lock was acquired, 0 if it cannot be at this time. 235 | 236 | void rlock(RWLock*); 237 | int canrlock(RWLock*); 238 | void runlock(RWLock*); 239 | 240 | void wlock(RWLock*); 241 | int canwlock(RWLock*); 242 | void wunlock(RWLock*); 243 | 244 | RWLocks are reader-writer locks. Any number of readers 245 | can lock them at once, but only one writer at a time. 246 | If a writer is holding it, there can't be any readers. 247 | 248 | 249 | Channel *chancreate(int, int); 250 | etc. 251 | 252 | Channels are buffered communication pipes you can 253 | use to send messages between tasks. Some people like 254 | doing most of the inter-task communication using channels. 255 | 256 | For details on channels see the description of channels in 257 | http://swtch.com/usr/local/plan9/man/man3/thread.html and 258 | http://swtch.com/~rsc/thread/ 259 | and also the example program primes.c, which implements 260 | a concurrent prime sieve. 261 | 262 | 263 | -------------------------------------------------------------------------------- /amd64-ucontext.h: -------------------------------------------------------------------------------- 1 | #define setcontext(u) setmcontext(&(u)->uc_mcontext) 2 | #define getcontext(u) getmcontext(&(u)->uc_mcontext) 3 | typedef struct mcontext mcontext_t; 4 | typedef struct ucontext ucontext_t; 5 | 6 | extern int swapcontext(ucontext_t*, const ucontext_t*); 7 | extern void makecontext(ucontext_t*, void(*)(), int, ...); 8 | extern int getmcontext(mcontext_t*); 9 | extern void setmcontext(const mcontext_t*); 10 | 11 | /*- 12 | * Copyright (c) 1999 Marcel Moolenaar 13 | * All rights reserved. 14 | * 15 | * Redistribution and use in source and binary forms, with or without 16 | * modification, are permitted provided that the following conditions 17 | * are met: 18 | * 1. Redistributions of source code must retain the above copyright 19 | * notice, this list of conditions and the following disclaimer 20 | * in this position and unchanged. 21 | * 2. Redistributions in binary form must reproduce the above copyright 22 | * notice, this list of conditions and the following disclaimer in the 23 | * documentation and/or other materials provided with the distribution. 24 | * 3. The name of the author may not be used to endorse or promote products 25 | * derived from this software without specific prior written permission. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 28 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 29 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 31 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 32 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 36 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | * 38 | * $FreeBSD: src/sys/sys/ucontext.h,v 1.4 1999/10/11 20:33:17 luoqi Exp $ 39 | */ 40 | 41 | /* #include */ 42 | 43 | /*- 44 | * Copyright (c) 1999 Marcel Moolenaar 45 | * All rights reserved. 46 | * 47 | * Redistribution and use in source and binary forms, with or without 48 | * modification, are permitted provided that the following conditions 49 | * are met: 50 | * 1. Redistributions of source code must retain the above copyright 51 | * notice, this list of conditions and the following disclaimer 52 | * in this position and unchanged. 53 | * 2. Redistributions in binary form must reproduce the above copyright 54 | * notice, this list of conditions and the following disclaimer in the 55 | * documentation and/or other materials provided with the distribution. 56 | * 3. The name of the author may not be used to endorse or promote products 57 | * derived from this software without specific prior written permission. 58 | * 59 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 60 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 61 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 62 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 63 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 64 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 65 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 66 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 67 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 68 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 69 | * 70 | * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $ 71 | */ 72 | 73 | struct mcontext { 74 | /* 75 | * The first 20 fields must match the definition of 76 | * sigcontext. So that we can support sigcontext 77 | * and ucontext_t at the same time. 78 | */ 79 | long mc_onstack; /* XXX - sigcontext compat. */ 80 | long mc_rdi; /* machine state (struct trapframe) */ 81 | long mc_rsi; 82 | long mc_rdx; 83 | long mc_rcx; 84 | long mc_r8; 85 | long mc_r9; 86 | long mc_rax; 87 | long mc_rbx; 88 | long mc_rbp; 89 | long mc_r10; 90 | long mc_r11; 91 | long mc_r12; 92 | long mc_r13; 93 | long mc_r14; 94 | long mc_r15; 95 | long mc_trapno; 96 | long mc_addr; 97 | long mc_flags; 98 | long mc_err; 99 | long mc_rip; 100 | long mc_cs; 101 | long mc_rflags; 102 | long mc_rsp; 103 | long mc_ss; 104 | 105 | long mc_len; /* sizeof(mcontext_t) */ 106 | #define _MC_FPFMT_NODEV 0x10000 /* device not present or configured */ 107 | #define _MC_FPFMT_XMM 0x10002 108 | long mc_fpformat; 109 | #define _MC_FPOWNED_NONE 0x20000 /* FP state not used */ 110 | #define _MC_FPOWNED_FPU 0x20001 /* FP state came from FPU */ 111 | #define _MC_FPOWNED_PCB 0x20002 /* FP state came from PCB */ 112 | long mc_ownedfp; 113 | /* 114 | * See for the internals of mc_fpstate[]. 115 | */ 116 | long mc_fpstate[64]; 117 | long mc_spare[8]; 118 | }; 119 | 120 | struct ucontext { 121 | /* 122 | * Keep the order of the first two fields. Also, 123 | * keep them the first two fields in the structure. 124 | * This way we can have a union with struct 125 | * sigcontext and ucontext_t. This allows us to 126 | * support them both at the same time. 127 | * note: the union is not defined, though. 128 | */ 129 | sigset_t uc_sigmask; 130 | mcontext_t uc_mcontext; 131 | 132 | struct __ucontext *uc_link; 133 | stack_t uc_stack; 134 | int __spare__[8]; 135 | }; 136 | 137 | 138 | -------------------------------------------------------------------------------- /asm.S: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ 2 | 3 | #if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5 4 | #define NEEDX86CONTEXT 1 5 | #define SET setmcontext 6 | #define GET getmcontext 7 | #endif 8 | 9 | #if defined(__OpenBSD__) && defined(__i386__) 10 | #define NEEDX86CONTEXT 1 11 | #define SET setmcontext 12 | #define GET getmcontext 13 | #endif 14 | 15 | #if defined(__APPLE__) 16 | #if defined(__i386__) 17 | #define NEEDX86CONTEXT 1 18 | #define SET _setmcontext 19 | #define GET _getmcontext 20 | #elif defined(__x86_64__) 21 | #define NEEDAMD64CONTEXT 1 22 | #define SET _setmcontext 23 | #define GET _getmcontext 24 | #else 25 | #define NEEDPOWERCONTEXT 1 26 | #define SET __setmcontext 27 | #define GET __getmcontext 28 | #endif 29 | #endif 30 | 31 | #if defined(__linux__) && defined(__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* 6 | chancreate(int elemsize, int bufsize) 7 | { 8 | Channel *c; 9 | 10 | c = malloc(sizeof *c+bufsize*elemsize); 11 | if(c == nil){ 12 | fprint(2, "chancreate malloc: %r"); 13 | exit(1); 14 | } 15 | memset(c, 0, sizeof *c); 16 | // 元素个数 17 | c->elemsize = elemsize; 18 | // 单个元素大小 19 | c->bufsize = bufsize; 20 | // 已使用个数 21 | c->nbuf = 0; 22 | // 存储数据的首地址 23 | c->buf = (uchar*)(c+1); 24 | return c; 25 | } 26 | 27 | /* bug - work out races */ 28 | void 29 | chanfree(Channel *c) 30 | { 31 | if(c == nil) 32 | return; 33 | free(c->name); 34 | free(c->arecv.a); 35 | free(c->asend.a); 36 | free(c); 37 | } 38 | 39 | // 往数组增加一个元素 40 | static void 41 | addarray(Altarray *a, Alt *alt) 42 | { 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 | // 插入 49 | a->a[a->n++] = alt; 50 | } 51 | 52 | // 删除索引为i的元素,并且把最后一个元素放到被删除元素的位置 53 | static void 54 | delarray(Altarray *a, int i) 55 | { 56 | --a->n; 57 | a->a[i] = a->a[a->n]; 58 | } 59 | 60 | /* 61 | * doesn't really work for things other than CHANSND and CHANRCV 62 | * but is only used as arg to chanarray, which can handle it 63 | */ 64 | #define otherop(op) (CHANSND+CHANRCV-(op)) 65 | 66 | // 返回发送、接收队列的地址 67 | static Altarray* 68 | chanarray(Channel *c, uint op) 69 | { 70 | switch(op){ 71 | default: 72 | return nil; 73 | case CHANSND: 74 | return &c->asend; 75 | case CHANRCV: 76 | return &c->arecv; 77 | } 78 | } 79 | 80 | static int 81 | altcanexec(Alt *a) 82 | { 83 | Altarray *ar; 84 | Channel *c; 85 | 86 | if(a->op == CHANNOP) 87 | return 0; 88 | c = a->c; 89 | if(c->bufsize == 0){ 90 | ar = chanarray(c, otherop(a->op)); 91 | return ar && ar->n; 92 | }else{ 93 | switch(a->op){ 94 | default: 95 | return 0; 96 | case CHANSND: 97 | return c->nbuf < c->bufsize; 98 | case CHANRCV: 99 | return c->nbuf > 0; 100 | } 101 | } 102 | } 103 | 104 | static void 105 | altqueue(Alt *a) 106 | { 107 | Altarray *ar; 108 | 109 | ar = chanarray(a->c, a->op); 110 | addarray(ar, a); 111 | } 112 | 113 | static void 114 | altdequeue(Alt *a) 115 | { 116 | int i; 117 | Altarray *ar; 118 | 119 | ar = chanarray(a->c, a->op); 120 | if(ar == nil){ 121 | fprint(2, "bad use of altdequeue op=%d\n", a->op); 122 | abort(); 123 | } 124 | 125 | for(i=0; in; i++) 126 | if(ar->a[i] == a){ 127 | delarray(ar, i); 128 | return; 129 | } 130 | fprint(2, "cannot find self in altdq\n"); 131 | abort(); 132 | } 133 | 134 | static void 135 | altalldequeue(Alt *a) 136 | { 137 | int i; 138 | 139 | for(i=0; a[i].op!=CHANEND && a[i].op!=CHANNOBLK; i++) 140 | if(a[i].op != CHANNOP) 141 | altdequeue(&a[i]); 142 | } 143 | 144 | static void 145 | amove(void *dst, void *src, uint n) 146 | { 147 | if(dst){ 148 | if(src == nil) 149 | memset(dst, 0, n); 150 | else 151 | memmove(dst, src, n); 152 | } 153 | } 154 | 155 | /* 156 | * Actually move the data around. There are up to three 157 | * players: the sender, the receiver, and the channel itself. 158 | * If the channel is unbuffered or the buffer is empty, 159 | * data goes from sender to receiver. If the channel is full, 160 | * the receiver removes some from the channel and the sender 161 | * gets to put some in. 162 | */ 163 | static void 164 | altcopy(Alt *s, Alt *r) 165 | { 166 | Alt *t; 167 | Channel *c; 168 | uchar *cp; 169 | 170 | /* 171 | * Work out who is sender and who is receiver 172 | */ 173 | if(s == nil && r == nil) 174 | return; 175 | assert(s != nil); 176 | c = s->c; 177 | if(s->op == CHANRCV){ 178 | t = s; 179 | s = r; 180 | r = t; 181 | } 182 | assert(s==nil || s->op == CHANSND); 183 | assert(r==nil || r->op == CHANRCV); 184 | 185 | /* 186 | * Channel is empty (or unbuffered) - copy directly. 187 | */ 188 | if(s && r && c->nbuf == 0){ 189 | amove(r->v, s->v, c->elemsize); 190 | return; 191 | } 192 | 193 | /* 194 | * Otherwise it's always okay to receive and then send. 195 | */ 196 | if(r){ 197 | cp = c->buf + c->off*c->elemsize; 198 | amove(r->v, cp, c->elemsize); 199 | --c->nbuf; 200 | if(++c->off == c->bufsize) 201 | c->off = 0; 202 | } 203 | if(s){ 204 | cp = c->buf + (c->off+c->nbuf)%c->bufsize*c->elemsize; 205 | amove(cp, s->v, c->elemsize); 206 | ++c->nbuf; 207 | } 208 | } 209 | 210 | static void 211 | altexec(Alt *a) 212 | { 213 | int i; 214 | Altarray *ar; 215 | Alt *other; 216 | Channel *c; 217 | 218 | c = a->c; 219 | ar = chanarray(c, otherop(a->op)); 220 | if(ar && ar->n){ 221 | i = rand()%ar->n; 222 | other = ar->a[i]; 223 | altcopy(a, other); 224 | altalldequeue(other->xalt); 225 | other->xalt[0].xalt = other; 226 | taskready(other->task); 227 | }else 228 | altcopy(a, nil); 229 | } 230 | 231 | #define dbgalt 0 232 | int 233 | chanalt(Alt *a) 234 | { 235 | int i, j, ncan, n, canblock; 236 | Channel *c; 237 | Task *t; 238 | 239 | needstack(512); 240 | for(i=0; a[i].op != CHANEND && a[i].op != CHANNOBLK; i++) 241 | ; 242 | n = i; 243 | canblock = a[i].op == CHANEND; 244 | 245 | t = taskrunning; 246 | for(i=0; iname) print("%s", c->name); else print("%p", c); } 256 | if(altcanexec(&a[i])){ 257 | if(dbgalt) print("*"); 258 | ncan++; 259 | } 260 | } 261 | if(ncan){ 262 | j = rand()%ncan; 263 | for(i=0; i %c:", "esrnb"[a[i].op]); 269 | if(c->name) print("%s", c->name); else print("%p", c); 270 | print("\n"); 271 | } 272 | altexec(&a[i]); 273 | return i; 274 | } 275 | } 276 | } 277 | } 278 | if(dbgalt)print("\n"); 279 | 280 | if(!canblock) 281 | return -1; 282 | 283 | 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 | -------------------------------------------------------------------------------- /fd.c: -------------------------------------------------------------------------------- 1 | #include "taskimpl.h" 2 | #include 3 | 4 | 5 | static Tasklist sleeping; 6 | static int sleepingcounted; 7 | static uvlong nsec(void); 8 | static int startedfdtask; 9 | 10 | #ifndef __linux__ 11 | 12 | #include 13 | enum 14 | { 15 | MAXFD = 1024 16 | }; 17 | 18 | static struct pollfd pollfd[MAXFD]; 19 | static Task *polltask[MAXFD]; 20 | static int npollfd; 21 | 22 | void 23 | fdtask(void *v) 24 | { 25 | int i, ms; 26 | Task *t; 27 | uvlong now; 28 | 29 | tasksystem(); 30 | taskname("fdtask"); 31 | for(;;){ 32 | /* let everyone else run */ 33 | while(taskyield() > 0) 34 | ; 35 | /* we're the only one runnable - poll for i/o */ 36 | errno = 0; 37 | taskstate("poll"); 38 | if((t=sleeping.head) == nil) 39 | ms = -1; 40 | else{ 41 | /* sleep at most 5s */ 42 | now = nsec(); 43 | if(now >= t->alarmtime) 44 | ms = 0; 45 | else if(now+5*1000*1000*1000LL >= t->alarmtime) 46 | ms = (t->alarmtime - now)/1000000; 47 | else 48 | ms = 5000; 49 | } 50 | if(poll(pollfd, npollfd, ms) < 0){ 51 | if(errno == EINTR) 52 | continue; 53 | fprint(2, "poll: %s\n", strerror(errno)); 54 | taskexitall(0); 55 | } 56 | 57 | /* wake up the guys who deserve it */ 58 | for(i=0; i= t->alarmtime){ 69 | deltask(&sleeping, t); 70 | if(!t->system && --sleepingcounted == 0) 71 | taskcount--; 72 | taskready(t); 73 | } 74 | } 75 | } 76 | 77 | void 78 | fdwait(int fd, int rw) 79 | { 80 | int bits; 81 | 82 | if(!startedfdtask){ 83 | startedfdtask = 1; 84 | taskcreate(fdtask, 0, 32768); 85 | } 86 | 87 | if(npollfd >= MAXFD){ 88 | fprint(2, "too many poll file descriptors\n"); 89 | abort(); 90 | } 91 | 92 | taskstate("fdwait for %s", rw=='r' ? "read" : rw=='w' ? "write" : "error"); 93 | bits = 0; 94 | switch(rw){ 95 | case 'r': 96 | bits |= POLLIN; 97 | break; 98 | case 'w': 99 | bits |= POLLOUT; 100 | break; 101 | } 102 | 103 | polltask[npollfd] = taskrunning; 104 | pollfd[npollfd].fd = fd; 105 | pollfd[npollfd].events = bits; 106 | pollfd[npollfd].revents = 0; 107 | npollfd++; 108 | taskswitch(); 109 | } 110 | 111 | #else /* HAVE_EPOLL */ 112 | 113 | // Scalable Linux-specific implementation 114 | #include 115 | 116 | static int epfd; 117 | 118 | void 119 | fdtask(void *v) 120 | { 121 | int i, ms; 122 | Task *t; 123 | uvlong now; 124 | // 变成系统协程 125 | tasksystem(); 126 | taskname("fdtask"); 127 | struct epoll_event events[1000]; 128 | for(;;){ 129 | /* let everyone else run */ 130 | // 大于0说明还有其他就绪协程可执行,则让给他们执行,否则往下执行 131 | while(taskyield() > 0) 132 | ; 133 | /* we're the only one runnable - poll for i/o */ 134 | errno = 0; 135 | taskstate("epoll"); 136 | // 没有定时事件则一直阻塞 137 | if((t=sleeping.head) == nil) 138 | ms = -1; 139 | else{ 140 | /* sleep at most 5s */ 141 | now = nsec(); 142 | if(now >= t->alarmtime) 143 | ms = 0; 144 | else if(now+5*1000*1000*1000LL >= t->alarmtime) 145 | ms = (t->alarmtime - now)/1000000; 146 | else 147 | ms = 5000; 148 | } 149 | int nevents; 150 | // 等待事件发生,ms是等待的超时时间 151 | if((nevents = epoll_wait(epfd, events, 1000, ms)) < 0){ 152 | if(errno == EINTR) 153 | continue; 154 | fprint(2, "epoll: %s\n", strerror(errno)); 155 | taskexitall(0); 156 | } 157 | 158 | /* wake up the guys who deserve it */ 159 | // 事件触发,把对应协程插入就绪队列 160 | for(i=0; i= t->alarmtime){ 167 | deltask(&sleeping, t); 168 | if(!t->system && --sleepingcounted == 0) 169 | taskcount--; 170 | taskready(t); 171 | } 172 | } 173 | } 174 | 175 | // 协程因为等待io需要切换 176 | void 177 | fdwait(int fd, int rw) 178 | { 179 | // 是否已经初始化epoll 180 | if(!startedfdtask){ 181 | startedfdtask = 1; 182 | epfd = epoll_create(1); 183 | assert(epfd >= 0); 184 | // 没有初始化则创建一个协程,做io管理 185 | taskcreate(fdtask, 0, 32768); 186 | } 187 | 188 | taskstate("fdwait for %s", rw=='r' ? "read" : rw=='w' ? "write" : "error"); 189 | struct epoll_event ev = {0}; 190 | // 记录事件对应的协程和感兴趣的事件 191 | ev.data.ptr = taskrunning; 192 | switch(rw){ 193 | case 'r': 194 | ev.events |= EPOLLIN | EPOLLPRI; 195 | break; 196 | case 'w': 197 | ev.events |= EPOLLOUT; 198 | break; 199 | } 200 | 201 | int r = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); 202 | int duped = 0; 203 | if (r < 0 || errno == EEXIST) { 204 | duped = 1; 205 | fd = dup(fd); 206 | int r = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); 207 | assert(r == 0); 208 | } 209 | // 切换到其他协程,等待被唤醒 210 | taskswitch(); 211 | // 唤醒后函数刚才注册的事件 212 | epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev); 213 | if (duped) 214 | close(fd); 215 | } 216 | 217 | #endif /* HAVE_EPOLL */ 218 | // 协程主动睡眠一段时间 219 | uint 220 | taskdelay(uint ms) 221 | { 222 | uvlong when, now; 223 | Task *t; 224 | 225 | if(!startedfdtask){ 226 | startedfdtask = 1; 227 | #ifdef __linux__ 228 | epfd = epoll_create(1); 229 | assert(epfd >= 0); 230 | #endif 231 | taskcreate(fdtask, 0, 32768); 232 | } 233 | 234 | now = nsec(); 235 | // 睡眠到什么时候 236 | when = now+(uvlong)ms*1000000; 237 | // 找到插入的位置,超时时间从小到大,即快到期的在前面 238 | for(t=sleeping.head; t!=nil && t->alarmtime < when; t=t->next) 239 | ; 240 | 241 | if(t){ 242 | taskrunning->prev = t->prev; 243 | taskrunning->next = t; 244 | }else{ 245 | taskrunning->prev = sleeping.tail; 246 | taskrunning->next = nil; 247 | } 248 | 249 | t = taskrunning; 250 | // 记录协程的绝对超时时间 251 | t->alarmtime = when; 252 | if(t->prev) 253 | t->prev->next = t; 254 | else 255 | sleeping.head = t; 256 | if(t->next) 257 | t->next->prev = t; 258 | else 259 | sleeping.tail = t; 260 | 261 | if(!t->system && sleepingcounted++ == 0) 262 | taskcount++; 263 | // 切换 264 | taskswitch(); 265 | // 被唤醒后,计算过去的时间间隔 266 | return (nsec() - now)/1000000; 267 | } 268 | 269 | 270 | /* Like fdread but always calls fdwait before reading. */ 271 | int 272 | fdread1(int fd, void *buf, int n) 273 | { 274 | int m; 275 | // 注册到epoll,然后会被切换到其他协程,切换回来的时候,说明io事件触发了,进行读 276 | do 277 | fdwait(fd, 'r'); 278 | while((m = read(fd, buf, n)) < 0 && errno == EAGAIN); 279 | return m; 280 | } 281 | 282 | int 283 | fdread(int fd, void *buf, int n) 284 | { 285 | int m; 286 | // 非阻塞读,如果不满足则再注册到epoll,参考fdread1 287 | while((m=read(fd, buf, n)) < 0 && errno == EAGAIN) 288 | fdwait(fd, 'r'); 289 | return m; 290 | } 291 | 292 | // 293 | int 294 | fdwrite(int fd, void *buf, int n) 295 | { 296 | int m, tot; 297 | // 非阻塞写,写失败,则注册等待写事件到epoll,事件触发后,继续写入未写成功的 298 | for(tot=0; tot 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | enum 9 | { 10 | STACK = 32768 11 | }; 12 | 13 | char *server; 14 | char *url; 15 | 16 | void fetchtask(void*); 17 | 18 | void 19 | taskmain(int argc, char **argv) 20 | { 21 | int i, n; 22 | 23 | if(argc != 4){ 24 | fprintf(stderr, "usage: httpload n server url\n"); 25 | taskexitall(1); 26 | } 27 | n = atoi(argv[1]); 28 | server = argv[2]; 29 | url = argv[3]; 30 | 31 | for(i=0; i 1) 34 | ; 35 | sleep(1); 36 | } 37 | } 38 | 39 | void 40 | fetchtask(void *v) 41 | { 42 | int fd, n; 43 | char buf[512]; 44 | 45 | fprintf(stderr, "starting...\n"); 46 | for(;;){ 47 | if((fd = netdial(TCP, server, 80)) < 0){ 48 | fprintf(stderr, "dial %s: %s (%s)\n", server, strerror(errno), taskgetstate()); 49 | continue; 50 | } 51 | snprintf(buf, sizeof buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", url, server); 52 | fdwrite(fd, buf, strlen(buf)); 53 | while((n = fdread(fd, buf, sizeof buf)) > 0) 54 | ; 55 | close(fd); 56 | write(1, ".", 1); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /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 | proto = istcp ? SOCK_STREAM : SOCK_DGRAM; 20 | memset(&sa, 0, sizeof sa); 21 | sa.sin_family = AF_INET; 22 | if(server != nil && strcmp(server, "*") != 0){ 23 | if(netlookup(server, &ip) < 0){ 24 | taskstate("netlookup failed"); 25 | return -1; 26 | } 27 | memmove(&sa.sin_addr, &ip, 4); 28 | } 29 | sa.sin_port = htons(port); 30 | if((fd = socket(AF_INET, proto, 0)) < 0){ 31 | taskstate("socket failed"); 32 | return -1; 33 | } 34 | 35 | /* set reuse flag for tcp */ 36 | if(istcp && getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0){ 37 | n = 1; 38 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); 39 | } 40 | 41 | if(bind(fd, (struct sockaddr*)&sa, sizeof sa) < 0){ 42 | taskstate("bind failed"); 43 | close(fd); 44 | return -1; 45 | } 46 | 47 | if(proto == SOCK_STREAM) 48 | listen(fd, 16); 49 | 50 | fdnoblock(fd); 51 | taskstate("netannounce succeeded"); 52 | return fd; 53 | } 54 | 55 | // 处理(摘下)连接 56 | int 57 | netaccept(int fd, char *server, int *port) 58 | { 59 | int cfd, one; 60 | struct sockaddr_in sa; 61 | uchar *ip; 62 | socklen_t len; 63 | // 注册事件到epoll,等待事件触发 64 | fdwait(fd, 'r'); 65 | 66 | taskstate("netaccept"); 67 | len = sizeof sa; 68 | // 触发后说明有连接了,则执行accept 69 | if((cfd = accept(fd, (void*)&sa, &len)) < 0){ 70 | taskstate("accept failed"); 71 | return -1; 72 | } 73 | if(server){ 74 | ip = (uchar*)&sa.sin_addr; 75 | snprint(server, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); 76 | } 77 | if(port) 78 | *port = ntohs(sa.sin_port); 79 | // 和客户端通信的fd也改成非阻塞模式 80 | fdnoblock(cfd); 81 | one = 1; 82 | setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one); 83 | taskstate("netaccept succeeded"); 84 | return cfd; 85 | } 86 | 87 | #define CLASS(p) ((*(unsigned char*)(p))>>6) 88 | static int 89 | parseip(char *name, uint32_t *ip) 90 | { 91 | unsigned char addr[4]; 92 | char *p; 93 | int i, x; 94 | 95 | p = name; 96 | for(i=0; i<4 && *p; i++){ 97 | x = strtoul(p, &p, 0); 98 | if(x < 0 || x >= 256) 99 | return -1; 100 | if(*p != '.' && *p != 0) 101 | return -1; 102 | if(*p == '.') 103 | p++; 104 | addr[i] = x; 105 | } 106 | 107 | switch(CLASS(addr)){ 108 | case 0: 109 | case 1: 110 | if(i == 3){ 111 | addr[3] = addr[2]; 112 | addr[2] = addr[1]; 113 | addr[1] = 0; 114 | }else if(i == 2){ 115 | addr[3] = addr[1]; 116 | addr[2] = 0; 117 | addr[1] = 0; 118 | }else if(i != 4) 119 | return -1; 120 | break; 121 | case 2: 122 | if(i == 3){ 123 | addr[3] = addr[2]; 124 | addr[2] = 0; 125 | }else if(i != 4) 126 | return -1; 127 | break; 128 | } 129 | *ip = *(uint32_t*)addr; 130 | return 0; 131 | } 132 | 133 | int 134 | netlookup(char *name, uint32_t *ip) 135 | { 136 | struct hostent *he; 137 | 138 | if(parseip(name, ip) >= 0) 139 | return 0; 140 | 141 | /* BUG - Name resolution blocks. Need a non-blocking DNS. */ 142 | taskstate("netlookup"); 143 | if((he = gethostbyname(name)) != 0){ 144 | *ip = *(uint32_t*)he->h_addr; 145 | taskstate("netlookup succeeded"); 146 | return 0; 147 | } 148 | 149 | taskstate("netlookup failed"); 150 | return -1; 151 | } 152 | 153 | // 客户端 154 | int 155 | netdial(int istcp, char *server, int port) 156 | { 157 | int proto, fd, n; 158 | uint32_t ip; 159 | struct sockaddr_in sa; 160 | socklen_t sn; 161 | 162 | if(netlookup(server, &ip) < 0) 163 | return -1; 164 | 165 | taskstate("netdial"); 166 | proto = istcp ? SOCK_STREAM : SOCK_DGRAM; 167 | if((fd = socket(AF_INET, proto, 0)) < 0){ 168 | taskstate("socket failed"); 169 | return -1; 170 | } 171 | fdnoblock(fd); 172 | 173 | /* for udp */ 174 | if(!istcp){ 175 | n = 1; 176 | setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof n); 177 | } 178 | 179 | /* start connecting */ 180 | memset(&sa, 0, sizeof sa); 181 | memmove(&sa.sin_addr, &ip, 4); 182 | sa.sin_family = AF_INET; 183 | sa.sin_port = htons(port); 184 | if(connect(fd, (struct sockaddr*)&sa, sizeof sa) < 0 && errno != EINPROGRESS){ 185 | taskstate("connect failed"); 186 | close(fd); 187 | return -1; 188 | } 189 | 190 | /* wait for finish */ 191 | fdwait(fd, 'w'); 192 | sn = sizeof sa; 193 | if(getpeername(fd, (struct sockaddr*)&sa, &sn) >= 0){ 194 | taskstate("connect succeeded"); 195 | return fd; 196 | } 197 | 198 | /* report error */ 199 | sn = sizeof n; 200 | getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&n, &sn); 201 | if(n == 0) 202 | n = ECONNREFUSED; 203 | close(fd); 204 | taskstate("connect failed"); 205 | errno = n; 206 | return -1; 207 | } 208 | 209 | -------------------------------------------------------------------------------- /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){ 12 | l->owner = taskrunning; 13 | return 1; 14 | } 15 | // 非阻塞,则直接返回,0表示加锁失败 16 | if(!block) 17 | return 0; 18 | // 插入等待锁队列 19 | addtask(&l->waiting, taskrunning); 20 | taskstate("qlock"); 21 | // 切换到其他协程 22 | taskswitch(); 23 | // 切换回来时,如果持有锁的协程不是当前协程,则异常退出,因为只有持有锁才会被切换回来,见unqlock 24 | if(l->owner != taskrunning){ 25 | fprint(2, "qlock: owner=%p self=%p oops\n", l->owner, taskrunning); 26 | abort(); 27 | } 28 | return 1; 29 | } 30 | 31 | // 阻塞式加锁 32 | void 33 | qlock(QLock *l) 34 | { 35 | _qlock(l, 1); 36 | } 37 | 38 | // 非阻塞式加锁 39 | int 40 | canqlock(QLock *l) 41 | { 42 | return _qlock(l, 0); 43 | } 44 | 45 | // 释放锁 46 | void 47 | qunlock(QLock *l) 48 | { 49 | Task *ready; 50 | // 锁并没有持有者,异常退出 51 | if(l->owner == 0){ 52 | fprint(2, "qunlock: owner=0\n"); 53 | abort(); 54 | } 55 | // 如果还有协程在等待该锁,则置为持有者,并且从等待队列中删除,然后修改状态为就绪并加入就绪队列 56 | if((l->owner = ready = l->waiting.head) != nil){ 57 | deltask(&l->waiting, ready); 58 | taskready(ready); 59 | } 60 | } 61 | 62 | // 加读锁 63 | static int 64 | _rlock(RWLock *l, int block) 65 | { 66 | /* 67 | 没有正在写并且没有等待写,则加锁成功,并且读者数加一 68 | */ 69 | if(l->writer == nil && l->wwaiting.head == nil){ 70 | l->readers++; 71 | return 1; 72 | } 73 | // 非阻塞则直接返回 74 | if(!block) 75 | return 0; 76 | // 插入等待读队列 77 | addtask(&l->rwaiting, taskrunning); 78 | taskstate("rlock"); 79 | // 切换上下文 80 | taskswitch(); 81 | // 切换回来了,说明加锁成功 82 | return 1; 83 | } 84 | 85 | // 阻塞时加读锁 86 | void 87 | rlock(RWLock *l) 88 | { 89 | _rlock(l, 1); 90 | } 91 | 92 | // 非阻塞式加读锁 93 | int 94 | canrlock(RWLock *l) 95 | { 96 | return _rlock(l, 0); 97 | } 98 | 99 | // 加写锁 100 | static int 101 | _wlock(RWLock *l, int block) 102 | { 103 | // 没有正在写并且没有正在读,则加锁成功,并置写者为当前协程 104 | if(l->writer == nil && l->readers == 0){ 105 | l->writer = taskrunning; 106 | return 1; 107 | } 108 | // 非阻塞则直接返回 109 | if(!block) 110 | return 0; 111 | // 加入等待写队列 112 | addtask(&l->wwaiting, taskrunning); 113 | taskstate("wlock"); 114 | // 切换 115 | taskswitch(); 116 | // 切换回来说明拿到锁了 117 | return 1; 118 | } 119 | 120 | // 阻塞时加写锁 121 | void 122 | wlock(RWLock *l) 123 | { 124 | _wlock(l, 1); 125 | } 126 | 127 | // 非阻塞时加写锁 128 | int 129 | canwlock(RWLock *l) 130 | { 131 | return _wlock(l, 0); 132 | } 133 | 134 | // 释放读锁 135 | void 136 | runlock(RWLock *l) 137 | { 138 | Task *t; 139 | // 读者减一,如果等于0并且有等待写的协程,则队列第一个协程持有该锁 140 | if(--l->readers == 0 && (t = l->wwaiting.head) != nil){ 141 | deltask(&l->wwaiting, t); 142 | l->writer = t; 143 | taskready(t); 144 | } 145 | } 146 | 147 | // 释放写锁 148 | void 149 | wunlock(RWLock *l) 150 | { 151 | Task *t; 152 | // 没有正在写,异常退出 153 | if(l->writer == nil){ 154 | fprint(2, "wunlock: not locked\n"); 155 | abort(); 156 | } 157 | // 置空,没有协程正在写 158 | l->writer = nil; 159 | // 有正在读,异常退出,写的时候,是无法读的 160 | if(l->readers != 0){ 161 | fprint(2, "wunlock: readers\n"); 162 | abort(); 163 | } 164 | // 释放写锁时,优先让读者持有锁,因为读者可以共享持有锁,提高并发 165 | // 读可以共享,把等待读的协程都加入就绪队列,并持有锁 166 | while((t = l->rwaiting.head) != nil){ 167 | deltask(&l->rwaiting, t); 168 | l->readers++; 169 | taskready(t); 170 | } 171 | // 释放写锁时,如果又没有读者,并且有等待写的协程,则队列的第一个等待写的协程持有锁 172 | if(l->readers == 0 && (t = l->wwaiting.head) != nil){ 173 | deltask(&l->wwaiting, t); 174 | l->writer = t; 175 | taskready(t); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /rendez.c: -------------------------------------------------------------------------------- 1 | #include "taskimpl.h" 2 | 3 | /* 4 | * sleep and wakeup 5 | */ 6 | // 把当前协程投入睡眠 7 | void 8 | tasksleep(Rendez *r) 9 | { 10 | // 插入等待队列 11 | addtask(&r->waiting, taskrunning); 12 | // 持有锁则先释放 13 | if(r->l) 14 | qunlock(r->l); 15 | taskstate("sleep"); 16 | // 切换协程 17 | taskswitch(); 18 | // 切换回来时加锁 19 | if(r->l) 20 | qlock(r->l); 21 | } 22 | 23 | // 唤醒协程,all代表是否唤醒所有 24 | static int 25 | _taskwakeup(Rendez *r, int all) 26 | { 27 | int i; 28 | Task *t; 29 | 30 | for(i=0;; i++){ 31 | // 不是唤醒所有,则在唤醒第一个协程后就跳出循环 32 | if(i==1 && !all) 33 | break; 34 | // 没有可唤醒的协程则跳出 35 | if((t = r->waiting.head) == nil) 36 | break; 37 | // 移出等待队列,插入就绪队列 38 | deltask(&r->waiting, t); 39 | taskready(t); 40 | } 41 | return i; 42 | } 43 | 44 | int 45 | taskwakeup(Rendez *r) 46 | { 47 | return _taskwakeup(r, 0); 48 | } 49 | 50 | int 51 | taskwakeupall(Rendez *r) 52 | { 53 | return _taskwakeup(r, 1); 54 | } 55 | 56 | -------------------------------------------------------------------------------- /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 | // 全局变量初始化为0 10 | int tasknswitch; 11 | int taskexitval; 12 | Task *taskrunning; 13 | 14 | Context taskschedcontext; 15 | Tasklist taskrunqueue; 16 | 17 | Task **alltask; 18 | // alltask的当前索引 19 | int nalltask; 20 | 21 | static char *argv0; 22 | static void contextswitch(Context *from, Context *to); 23 | 24 | static void 25 | taskdebug(char *fmt, ...) 26 | { 27 | va_list arg; 28 | char buf[128]; 29 | Task *t; 30 | char *p; 31 | static int fd = -1; 32 | 33 | return; 34 | va_start(arg, fmt); 35 | vfprint(1, fmt, arg); 36 | va_end(arg); 37 | return; 38 | 39 | if(fd < 0){ 40 | p = strrchr(argv0, '/'); 41 | if(p) 42 | p++; 43 | else 44 | p = argv0; 45 | snprint(buf, sizeof buf, "/tmp/%s.tlog", p); 46 | if((fd = open(buf, O_CREAT|O_WRONLY, 0666)) < 0) 47 | fd = open("/dev/null", O_WRONLY); 48 | } 49 | 50 | va_start(arg, fmt); 51 | vsnprint(buf, sizeof buf, fmt, arg); 52 | va_end(arg); 53 | t = taskrunning; 54 | if(t) 55 | fprint(fd, "%d.%d: %s\n", getpid(), t->id, buf); 56 | else 57 | fprint(fd, "%d._: %s\n", getpid(), buf); 58 | } 59 | 60 | static void 61 | taskstart(uint y, uint x) 62 | { 63 | Task *t; 64 | ulong z; 65 | 66 | z = x<<16; /* hide undefined 32-bit shift from 32-bit compilers */ 67 | z <<= 16; 68 | z |= y; 69 | t = (Task*)z; 70 | 71 | //print("taskstart %p\n", t); 72 | t->startfn(t->startarg); 73 | //print("taskexits %p\n", t); 74 | taskexit(0); 75 | //print("not reacehd\n"); 76 | } 77 | 78 | static int taskidgen; 79 | 80 | // 分配一个协程所需要的内存和初始化某些字段 81 | static Task* 82 | taskalloc(void (*fn)(void*), void *arg, uint stack) 83 | { 84 | Task *t; 85 | sigset_t zero; 86 | uint x, y; 87 | ulong z; 88 | 89 | /* allocate the task and stack together */ 90 | // 结构体本身的大小+栈大小 91 | t = malloc(sizeof *t+stack); 92 | if(t == nil){ 93 | fprint(2, "taskalloc malloc: %r\n"); 94 | abort(); 95 | } 96 | memset(t, 0, sizeof *t); 97 | // 栈的内存位置 98 | t->stk = (uchar*)(t+1); 99 | // 栈大小 100 | t->stksize = stack; 101 | // 协程id 102 | t->id = ++taskidgen; 103 | // 协程工作函数和参数 104 | t->startfn = fn; 105 | t->startarg = arg; 106 | 107 | /* do a reasonable initialization */ 108 | memset(&t->context.uc, 0, sizeof t->context.uc); 109 | sigemptyset(&zero); 110 | // 初始化uc_sigmask字段为空,即不阻塞信号 111 | sigprocmask(SIG_BLOCK, &zero, &t->context.uc.uc_sigmask); 112 | 113 | /* must initialize with current context */ 114 | // 初始化uc字段 115 | if(getcontext(&t->context.uc) < 0){ 116 | fprint(2, "getcontext: %r\n"); 117 | abort(); 118 | } 119 | 120 | /* call makecontext to do the real work. */ 121 | /* leave a few words open on both ends */ 122 | // 设置协程执行时的栈位置和大小 123 | t->context.uc.uc_stack.ss_sp = t->stk+8; 124 | t->context.uc.uc_stack.ss_size = t->stksize-64; 125 | #if defined(__sun__) && !defined(__MAKECONTEXT_V2_SOURCE) /* sigh */ 126 | #warning "doing sun thing" 127 | /* can avoid this with __MAKECONTEXT_V2_SOURCE but only on SunOS 5.9 */ 128 | t->context.uc.uc_stack.ss_sp = 129 | (char*)t->context.uc.uc_stack.ss_sp 130 | +t->context.uc.uc_stack.ss_size; 131 | #endif 132 | /* 133 | * All this magic is because you have to pass makecontext a 134 | * function that takes some number of word-sized variables, 135 | * and on 64-bit machines pointers are bigger than words. 136 | */ 137 | //print("make %p\n", t); 138 | z = (ulong)t; 139 | y = z; 140 | z >>= 16; /* hide undefined 32-bit shift from 32-bit compilers */ 141 | x = z>>16; 142 | // 保存信息到uc字段 143 | makecontext(&t->context.uc, (void(*)())taskstart, 2, y, x); 144 | 145 | return t; 146 | } 147 | 148 | // 创建一个协程 149 | int 150 | taskcreate(void (*fn)(void*), void *arg, uint stack) 151 | { 152 | int id; 153 | Task *t; 154 | 155 | t = taskalloc(fn, arg, stack); 156 | taskcount++; 157 | id = t->id; 158 | // 扩容 159 | if(nalltask%64 == 0){ 160 | alltask = realloc(alltask, (nalltask+64)*sizeof(alltask[0])); 161 | if(alltask == nil){ 162 | fprint(2, "out of memory\n"); 163 | abort(); 164 | } 165 | } 166 | // 记录位置 167 | t->alltaskslot = nalltask; 168 | // 保存到alltask中 169 | alltask[nalltask++] = t; 170 | // 修改状态为就绪,可以被调度,并且加入到就绪队列 171 | taskready(t); 172 | return id; 173 | } 174 | 175 | // 把当前协程切换为洗协程,taskcount减一,taskcount是记录用户协程个数的 176 | void 177 | tasksystem(void) 178 | { 179 | if(!taskrunning->system){ 180 | taskrunning->system = 1; 181 | --taskcount; 182 | } 183 | } 184 | 185 | /* 186 | 切换协程,taskrunning是正在执行的协程,taskschedcontext是调度协程(主线程)的上下文, 187 | 切换到调度中心,并保持当前上下文到taskrunning->context 188 | */ 189 | void 190 | taskswitch(void) 191 | { 192 | needstack(0); 193 | contextswitch(&taskrunning->context, &taskschedcontext); 194 | } 195 | 196 | // 修改协程的状态为就绪并加入就绪队列 197 | void 198 | taskready(Task *t) 199 | { 200 | t->ready = 1; 201 | addtask(&taskrunqueue, t); 202 | } 203 | 204 | // 协程主动让出cpu 205 | int 206 | taskyield(void) 207 | { 208 | int n; 209 | 210 | n = tasknswitch; 211 | // 插入就绪队列,等待后续的调度 212 | taskready(taskrunning); 213 | taskstate("yield"); 214 | // 切换协程 215 | taskswitch(); 216 | // 等于0说明当前只有自己一个协程,调度的时候tasknswitch加一,所以这里减一 217 | return tasknswitch - n - 1; 218 | } 219 | 220 | // 是否至少有一个协程处于就绪状态 221 | int 222 | anyready(void) 223 | { 224 | return taskrunqueue.head != nil; 225 | } 226 | 227 | void 228 | taskexitall(int val) 229 | { 230 | exit(val); 231 | } 232 | 233 | void 234 | taskexit(int val) 235 | { 236 | taskexitval = val; 237 | taskrunning->exiting = 1; 238 | taskswitch(); 239 | } 240 | 241 | // 真正切换协程的逻辑 242 | static void 243 | contextswitch(Context *from, Context *to) 244 | { 245 | if(swapcontext(&from->uc, &to->uc) < 0){ 246 | fprint(2, "swapcontext failed: %r\n"); 247 | assert(0); 248 | } 249 | } 250 | 251 | // 协程调度中心 252 | static void 253 | taskscheduler(void) 254 | { 255 | int i; 256 | Task *t; 257 | 258 | taskdebug("scheduler enter"); 259 | for(;;){ 260 | // 没有用户协程了,则退出 261 | if(taskcount == 0) 262 | exit(taskexitval); 263 | // 从就绪队列拿出一个协程 264 | t = taskrunqueue.head; 265 | if(t == nil){ 266 | fprint(2, "no runnable tasks! %d tasks stalled\n", taskcount); 267 | exit(1); 268 | } 269 | // 从就绪队列删除该协程 270 | deltask(&taskrunqueue, t); 271 | t->ready = 0; 272 | // 保存真正执行的协程 273 | taskrunning = t; 274 | // 切换次数加一 275 | tasknswitch++; 276 | taskdebug("run %d (%s)", t->id, t->name); 277 | // 切换到t执行,并且保存当前上下文到taskschedcontext(即下面要执行的代码) 278 | contextswitch(&taskschedcontext, &t->context); 279 | //print("back in scheduler\n"); 280 | // 执行到这说明没有协程在执行,置空 281 | taskrunning = nil; 282 | // 协程退出了 283 | if(t->exiting){ 284 | // 不是系统协程,则个数减一 285 | if(!t->system) 286 | taskcount--; 287 | // 当前协程在alltask的索引 288 | i = t->alltaskslot; 289 | // 把最后一个协程换到当前协程的位置,因为他要退出了 290 | alltask[i] = alltask[--nalltask]; 291 | // 更新被置换协程的索引 292 | alltask[i]->alltaskslot = i; 293 | // 释放堆内存 294 | free(t); 295 | } 296 | } 297 | } 298 | 299 | void** 300 | taskdata(void) 301 | { 302 | return &taskrunning->udata; 303 | } 304 | 305 | /* 306 | * debugging 307 | */ 308 | void 309 | taskname(char *fmt, ...) 310 | { 311 | va_list arg; 312 | Task *t; 313 | 314 | t = taskrunning; 315 | va_start(arg, fmt); 316 | vsnprint(t->name, sizeof t->name, fmt, arg); 317 | va_end(arg); 318 | } 319 | 320 | char* 321 | taskgetname(void) 322 | { 323 | return taskrunning->name; 324 | } 325 | 326 | void 327 | taskstate(char *fmt, ...) 328 | { 329 | va_list arg; 330 | Task *t; 331 | 332 | t = taskrunning; 333 | va_start(arg, fmt); 334 | vsnprint(t->state, sizeof t->name, fmt, arg); 335 | va_end(arg); 336 | } 337 | 338 | char* 339 | taskgetstate(void) 340 | { 341 | return taskrunning->state; 342 | } 343 | 344 | void 345 | needstack(int n) 346 | { 347 | Task *t; 348 | 349 | t = taskrunning; 350 | 351 | if((char*)&t <= (char*)t->stk 352 | || (char*)&t - (char*)t->stk < 256+n){ 353 | fprint(2, "task stack overflow: &t=%p tstk=%p n=%d\n", &t, t->stk, 256+n); 354 | abort(); 355 | } 356 | } 357 | 358 | static void 359 | taskinfo(int s) 360 | { 361 | int i; 362 | Task *t; 363 | char *extra; 364 | 365 | fprint(2, "task list:\n"); 366 | for(i=0; iready) 371 | extra = " (ready)"; 372 | else 373 | extra = ""; 374 | fprint(2, "%6d%c %-20s %s%s\n", 375 | t->id, t->system ? 's' : ' ', 376 | t->name, t->state, extra); 377 | } 378 | } 379 | 380 | /* 381 | * startup 382 | */ 383 | 384 | static int taskargc; 385 | static char **taskargv; 386 | int mainstacksize; 387 | 388 | // 协程的入口函数 389 | static void 390 | taskmainstart(void *v) 391 | { 392 | taskname("taskmain"); 393 | // 用户需要定义taskmain函数,参数是命令函数参数 394 | taskmain(taskargc, taskargv); 395 | } 396 | 397 | int 398 | main(int argc, char **argv) 399 | { 400 | struct sigaction sa, osa; 401 | // 注册SIGQUIT信号处理函数 402 | memset(&sa, 0, sizeof sa); 403 | sa.sa_handler = taskinfo; 404 | sa.sa_flags = SA_RESTART; 405 | sigaction(SIGQUIT, &sa, &osa); 406 | 407 | #ifdef SIGINFO 408 | sigaction(SIGINFO, &sa, &osa); 409 | #endif 410 | // 保存命令行参数 411 | argv0 = argv[0]; 412 | taskargc = argc; 413 | taskargv = argv; 414 | 415 | if(mainstacksize == 0) 416 | mainstacksize = 256*1024; 417 | // 创建第一个协程 418 | taskcreate(taskmainstart, nil, mainstacksize); 419 | // 开始调度 420 | taskscheduler(); 421 | fprint(2, "taskscheduler returned in main!\n"); 422 | abort(); 423 | return 0; 424 | } 425 | 426 | /* 427 | * hooray for linked lists 428 | */ 429 | // 把协程插入队列中,如果之前在其他队列,则会被移除 430 | void 431 | addtask(Tasklist *l, Task *t) 432 | { 433 | if(l->tail){ 434 | l->tail->next = t; 435 | t->prev = l->tail; 436 | }else{ 437 | l->head = t; 438 | t->prev = nil; 439 | } 440 | l->tail = t; 441 | t->next = nil; 442 | } 443 | 444 | void 445 | deltask(Tasklist *l, Task *t) 446 | { 447 | if(t->prev) 448 | t->prev->next = t->next; 449 | else 450 | l->head = t->next; 451 | if(t->next) 452 | t->next->prev = t->prev; 453 | else 454 | l->tail = t->prev; 455 | } 456 | 457 | unsigned int 458 | taskid(void) 459 | { 460 | return taskrunning->id; 461 | } 462 | 463 | -------------------------------------------------------------------------------- /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 | // 锁持有者 49 | Task *owner; 50 | // 等待该锁的队列 51 | Tasklist waiting; 52 | }; 53 | 54 | void qlock(QLock*); 55 | int canqlock(QLock*); 56 | void qunlock(QLock*); 57 | 58 | /* 59 | * reader-writer locks 60 | */ 61 | typedef struct RWLock RWLock; 62 | struct RWLock 63 | { 64 | int readers; 65 | Task *writer; 66 | Tasklist rwaiting; 67 | Tasklist wwaiting; 68 | }; 69 | 70 | void rlock(RWLock*); 71 | int canrlock(RWLock*); 72 | void runlock(RWLock*); 73 | 74 | void wlock(RWLock*); 75 | int canwlock(RWLock*); 76 | void wunlock(RWLock*); 77 | 78 | /* 79 | * sleep and wakeup (condition variables) 80 | */ 81 | typedef struct Rendez Rendez; 82 | 83 | struct Rendez 84 | { 85 | QLock *l; 86 | Tasklist waiting; 87 | }; 88 | 89 | void tasksleep(Rendez*); 90 | int taskwakeup(Rendez*); 91 | int taskwakeupall(Rendez*); 92 | 93 | /* 94 | * channel communication 95 | */ 96 | typedef struct Alt Alt; 97 | typedef struct Altarray Altarray; 98 | typedef struct Channel Channel; 99 | 100 | enum 101 | { 102 | CHANEND, 103 | CHANSND, 104 | CHANRCV, 105 | CHANNOP, 106 | CHANNOBLK, 107 | }; 108 | 109 | struct Alt 110 | { 111 | Channel *c; 112 | void *v; 113 | unsigned int op; 114 | Task *task; 115 | Alt *xalt; 116 | }; 117 | 118 | struct Altarray 119 | { 120 | Alt **a; 121 | // 当前数组大小 122 | unsigned int n; 123 | // 大小阈值 124 | unsigned int m; 125 | }; 126 | // 通信的通道 127 | struct Channel 128 | { 129 | // 元素个数 130 | unsigned int bufsize; 131 | // 单个元素大小 132 | unsigned int elemsize; 133 | // 存储数据的地址 134 | unsigned char *buf; 135 | // 已使用元素个数 136 | unsigned int nbuf; 137 | unsigned int off; 138 | // 接收、发送队列 139 | Altarray asend; 140 | Altarray arecv; 141 | char *name; 142 | }; 143 | 144 | int chanalt(Alt *alts); 145 | Channel* chancreate(int elemsize, int elemcnt); 146 | void chanfree(Channel *c); 147 | int chaninit(Channel *c, int elemsize, int elemcnt); 148 | int channbrecv(Channel *c, void *v); 149 | void* channbrecvp(Channel *c); 150 | unsigned long channbrecvul(Channel *c); 151 | int channbsend(Channel *c, void *v); 152 | int channbsendp(Channel *c, void *v); 153 | int channbsendul(Channel *c, unsigned long v); 154 | int chanrecv(Channel *c, void *v); 155 | void* chanrecvp(Channel *c); 156 | unsigned long chanrecvul(Channel *c); 157 | int chansend(Channel *c, void *v); 158 | int chansendp(Channel *c, void *v); 159 | int chansendul(Channel *c, unsigned long v); 160 | 161 | /* 162 | * Threaded I/O. 163 | */ 164 | int fdread(int, void*, int); 165 | int fdread1(int, void*, int); /* always uses fdwait */ 166 | int fdwrite(int, void*, int); 167 | void fdwait(int, int); 168 | int fdnoblock(int); 169 | 170 | void fdtask(void*); 171 | 172 | /* 173 | * Network dialing - sets non-blocking automatically 174 | */ 175 | enum 176 | { 177 | UDP = 0, 178 | TCP = 1, 179 | }; 180 | 181 | int netannounce(int, char*, int); 182 | int netaccept(int, char*, int*); 183 | int netdial(int, char*, int); 184 | int netlookup(char*, uint32_t*); /* blocks entire program! */ 185 | int netdial(int, char*, int); 186 | 187 | #ifdef __cplusplus 188 | } 189 | #endif 190 | #endif 191 | 192 | -------------------------------------------------------------------------------- /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 | Context context; 162 | uvlong alarmtime; 163 | uint id; 164 | uchar *stk; 165 | uint stksize; 166 | int exiting; 167 | int alltaskslot; 168 | int system; 169 | int ready; 170 | void (*startfn)(void*); 171 | void *startarg; 172 | void *udata; 173 | }; 174 | 175 | void taskready(Task*); 176 | void taskswitch(void); 177 | 178 | void addtask(Tasklist*, Task*); 179 | void deltask(Tasklist*, Task*); 180 | 181 | extern Task *taskrunning; 182 | extern int taskcount; 183 | -------------------------------------------------------------------------------- /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 | server = argv[2]; 46 | port = atoi(argv[3]); 47 | // 启动一个tcp服务器 48 | if((fd = netannounce(TCP, 0, atoi(argv[1]))) < 0){ 49 | fprintf(stderr, "cannot announce on tcp port %d: %s\n", atoi(argv[1]), strerror(errno)); 50 | taskexitall(1); 51 | } 52 | fdnoblock(fd); 53 | // accept成功后创建一个客户端协程 54 | while((cfd = netaccept(fd, remote, &rport)) >= 0){ 55 | fprintf(stderr, "connection from %s:%d\n", remote, rport); 56 | taskcreate(proxytask, (void*)cfd, STACK); 57 | } 58 | } 59 | 60 | void 61 | proxytask(void *v) 62 | { 63 | int fd, remotefd; 64 | 65 | fd = (int)v; 66 | if((remotefd = netdial(TCP, server, port)) < 0){ 67 | close(fd); 68 | return; 69 | } 70 | 71 | fprintf(stderr, "connected to %s:%d\n", server, port); 72 | // 启动两个协程充当客户端和服务器的处理逻辑 73 | taskcreate(rwtask, mkfd2(fd, remotefd), STACK); 74 | taskcreate(rwtask, mkfd2(remotefd, fd), STACK); 75 | } 76 | 77 | void 78 | rwtask(void *v) 79 | { 80 | int *a, rfd, wfd, n; 81 | char buf[2048]; 82 | 83 | a = v; 84 | rfd = a[0]; 85 | wfd = a[1]; 86 | free(a); 87 | 88 | while((n = fdread(rfd, buf, sizeof buf)) > 0) 89 | fdwrite(wfd, buf, n); 90 | shutdown(wfd, SHUT_WR); 91 | close(rfd); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /testdelay.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | enum { STACK = 32768 }; 9 | 10 | Channel *c; 11 | 12 | void 13 | delaytask(void *v) 14 | { 15 | taskdelay((int)v); 16 | printf("awake after %d ms\n", (int)v); 17 | chansendul(c, 0); 18 | } 19 | 20 | void 21 | taskmain(int argc, char **argv) 22 | { 23 | int i, n; 24 | 25 | c = chancreate(sizeof(unsigned long), 0); 26 | 27 | n = 0; 28 | for(i=1; i 2 | 3 | void 4 | taskmain(int argc, char *argv[]) 5 | { 6 | taskdelay(1000); 7 | } 8 | --------------------------------------------------------------------------------