--- /dev/null
+#define setcontext(u) setmcontext(&(u)->uc_mcontext)
+#define getcontext(u) getmcontext(&(u)->uc_mcontext)
+typedef struct mcontext mcontext_t;
+typedef struct ucontext ucontext_t;
+
+extern int swapcontext(ucontext_t*, const ucontext_t*);
+extern void makecontext(ucontext_t*, void(*)(), int, ...);
+extern int getmcontext(mcontext_t*);
+extern void setmcontext(const mcontext_t*);
+
+/*-
+ * Copyright (c) 1999 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/sys/ucontext.h,v 1.4 1999/10/11 20:33:17 luoqi Exp $
+ */
+
+/* #include <machine/ucontext.h> */
+
+/*-
+ * Copyright (c) 1999 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $
+ */
+
+struct mcontext {
+ /*
+ * The first 20 fields must match the definition of
+ * sigcontext. So that we can support sigcontext
+ * and ucontext_t at the same time.
+ */
+ int mc_onstack; /* XXX - sigcontext compat. */
+ int mc_gs;
+ int mc_fs;
+ int mc_es;
+ int mc_ds;
+ int mc_edi;
+ int mc_esi;
+ int mc_ebp;
+ int mc_isp;
+ int mc_ebx;
+ int mc_edx;
+ int mc_ecx;
+ int mc_eax;
+ int mc_trapno;
+ int mc_err;
+ int mc_eip;
+ int mc_cs;
+ int mc_eflags;
+ int mc_esp; /* machine state */
+ int mc_ss;
+
+ int mc_fpregs[28]; /* env87 + fpacc87 + u_long */
+ int __spare__[17];
+};
+
+struct ucontext {
+ /*
+ * Keep the order of the first two fields. Also,
+ * keep them the first two fields in the structure.
+ * This way we can have a union with struct
+ * sigcontext and ucontext_t. This allows us to
+ * support them both at the same time.
+ * note: the union is not defined, though.
+ */
+ sigset_t uc_sigmask;
+ mcontext_t uc_mcontext;
+
+ struct __ucontext *uc_link;
+ stack_t uc_stack;
+ int __spare__[8];
+};
--- /dev/null
+
+This software was developed as part of a project at MIT.
+
+Copyright (c) 2005-2007 Russ Cox,
+ Massachusetts Institute of Technology
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+===
+
+Contains parts of an earlier library that has:
+
+/*
+ * The authors of this software are Rob Pike, Sape Mullender, and Russ Cox
+ * Copyright (c) 2003 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+*/
--- /dev/null
+Libtask is a simple coroutine library. It runs on Linux (ARM, MIPS, and x86),
+FreeBSD (x86), OS X (PowerPC x86, and x86-64), and SunOS Solaris (Sparc),
+and is easy to port to other systems.
+
+Libtask gives the programmer the illusion of threads, but
+the operating system sees only a single kernel thread.
+For clarity, we refer to the coroutines as "tasks," not threads.
+
+Scheduling is cooperative. Only one task runs at a time,
+and it cannot be rescheduled without explicitly giving up
+the CPU. Most of the functions provided in task.h do have
+the possibility of going to sleep. Programs using the task
+functions should #include <task.h>.
+
+--- Basic task manipulation
+
+int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize);
+
+ Create a new task running f(arg) on a stack of size stacksize.
+
+void tasksystem(void);
+
+ Mark the current task as a "system" task. These are ignored
+ for the purposes of deciding the program is done running
+ (see taskexit next).
+
+void taskexit(int status);
+
+ Exit the current task. If this is the last non-system task,
+ exit the entire program using the given exit status.
+
+void taskexitall(int status);
+
+ Exit the entire program, using the given exit status.
+
+void taskmain(int argc, char *argv[]);
+
+ Write this function instead of main. Libtask provides its own main.
+
+int taskyield(void);
+
+ Explicitly give up the CPU. The current task will be scheduled
+ again once all the other currently-ready tasks have a chance
+ to run. Returns the number of other tasks that ran while the
+ current task was waiting. (Zero means there are no other tasks
+ trying to run.)
+
+int taskdelay(unsigned int ms)
+
+ Explicitly give up the CPU for at least ms milliseconds.
+ Other tasks continue to run during this time.
+
+void** taskdata(void);
+
+ Return a pointer to a single per-task void* pointer.
+ You can use this as a per-task storage place.
+
+void needstack(int n);
+
+ Tell the task library that you need at least n bytes left
+ on the stack. If you don't have it, the task library will call abort.
+ (It's hard to figure out how big stacks should be. I usually make
+ them really big (say 32768) and then don't worry about it.)
+
+void taskname(char*, ...);
+
+ Takes an argument list like printf. Sets the current task's name.
+
+char* taskgetname(void);
+
+ Returns the current task's name. Is the actual buffer; do not free.
+
+void taskstate(char*, ...);
+char* taskgetstate(void);
+
+ Like taskname and taskgetname but for the task state.
+
+ When you send a tasked program a SIGQUIT (or SIGINFO, on BSD)
+ it will print a list of all its tasks and their names and states.
+ This is useful for debugging why your program isn't doing anything!
+
+unsigned int taskid(void);
+
+ Return the unique task id for the current task.
+
+--- Non-blocking I/O
+
+There is a small amount of runtime support for non-blocking I/O
+on file descriptors.
+
+int fdnoblock(int fd);
+
+ Sets I/O on the given fd to be non-blocking. Should be
+ called before any of the other fd routines.
+
+int fdread(int, void*, int);
+
+ Like regular read(), but puts task to sleep while waiting for
+ data instead of blocking the whole program.
+
+int fdwrite(int, void*, int);
+
+ Like regular write(), but puts task to sleep while waiting to
+ write data instead of blocking the whole program.
+
+void fdwait(int fd, int rw);
+
+ Low-level call sitting underneath fdread and fdwrite.
+ Puts task to sleep while waiting for I/O to be possible on fd.
+ Rw specifies type of I/O: 'r' means read, 'w' means write,
+ anything else means just exceptional conditions (hang up, etc.)
+ The 'r' and 'w' also wake up for exceptional conditions.
+
+--- Network I/O
+
+These are convenient packaging of the ugly Unix socket routines.
+They can all put the current task to sleep during the call.
+
+int netannounce(int proto, char *address, int port)
+
+ Start a network listener running on address and port of protocol.
+ Proto is either TCP or UDP. Port is a port number. Address is a
+ string version of a host name or IP address. If address is null,
+ then announce binds to the given port on all available interfaces.
+ Returns a fd to use with netaccept.
+ Examples: netannounce(TCP, "localhost", 80) or
+ netannounce(TCP, "127.0.0.1", 80) or netannounce(TCP, 0, 80).
+
+int netaccept(int fd, char *server, int *port)
+
+ Get the next connection that comes in to the listener fd.
+ Returns a fd to use to talk to the guy who just connected.
+ If server is not null, it must point at a buffer of at least
+ 16 bytes that is filled in with the remote IP address.
+ If port is not null, it is filled in with the report port.
+ Example:
+ char server[16];
+ int port;
+
+ if(netaccept(fd, server, &port) >= 0)
+ printf("connect from %s:%d", server, port);
+
+int netdial(int proto, char *name, int port)
+
+ Create a new (outgoing) connection to a particular host.
+ Name can be an ip address or a domain name. If it's a domain name,
+ the entire program will block while the name is resolved
+ (the DNS library does not provide a nice non-blocking interface).
+ Example: netdial(TCP, "www.google.com", 80)
+ or netdial(TCP, "18.26.4.9", 80)
+
+--- Time
+
+unsigned int taskdelay(unsigned int ms)
+
+ Put the current task to sleep for approximately ms milliseconds.
+ Return the actual amount of time slept, in milliseconds.
+
+--- Example programs
+
+In this directory, tcpproxy.c is a simple TCP proxy that illustrates
+most of the above. You can run
+
+ tcpproxy 1234 www.google.com 80
+
+and then you should be able to visit http://localhost:1234/ and see Google.
+
+Other examples are:
+ primes.c - simple prime sieve
+ httpload.c - simple HTTP load generator
+ testdelay.c - test taskdelay()
+
+--- Building
+
+To build, run make. You can run make install to copy task.h and
+libtask.a to the appropriate places in /usr/local. Then you
+should be able to just link with -ltask in your programs
+that use it.
+
+On SunOS Solaris machines, run makesun instead of just make.
+
+--- Contact Info
+
+Please email me with questions or problems.
+
+Russ Cox
+rsc@swtch.com
+
+
+--- Stuff you probably won't use at first ---
+--- but might want to know about eventually ---
+
+void tasksleep(Rendez*);
+int taskwakeup(Rendez*);
+int taskwakeupall(Rendez*);
+
+ A Rendez is a condition variable. You can declare a new one by
+ just allocating memory for it (or putting it in another structure)
+ and then zeroing the memory. Tasksleep(r) 'sleeps on r', giving
+ up the CPU. Multiple tasks can sleep on a single Rendez.
+ When another task comes along and calls taskwakeup(r),
+ the first task sleeping on r (if any) will be woken up.
+ Taskwakeupall(r) wakes up all the tasks sleeping on r.
+ They both return the actual number of tasks awakened.
+
+
+
+void qlock(QLock*);
+int canqlock(QLock*);
+void qunlock(QLock*);
+
+ You probably won't need locks because of the cooperative
+ scheduling, but if you do, here are some. You can make a new
+ QLock by just declaring it and zeroing the memory.
+ Calling qlock will give up the CPU if the lock is held by someone else.
+ Calling qunlock will not give up the CPU.
+ Calling canqlock tries to lock the lock, but will not give up the CPU.
+ It returns 1 if the lock was acquired, 0 if it cannot be at this time.
+
+void rlock(RWLock*);
+int canrlock(RWLock*);
+void runlock(RWLock*);
+
+void wlock(RWLock*);
+int canwlock(RWLock*);
+void wunlock(RWLock*);
+
+ RWLocks are reader-writer locks. Any number of readers
+ can lock them at once, but only one writer at a time.
+ If a writer is holding it, there can't be any readers.
+
+
+Channel *chancreate(int, int);
+etc.
+
+ Channels are buffered communication pipes you can
+ use to send messages between tasks. Some people like
+ doing most of the inter-task communication using channels.
+
+ For details on channels see the description of channels in
+ http://swtch.com/usr/local/plan9/man/man3/thread.html and
+ http://swtch.com/~rsc/thread/
+ and also the example program primes.c, which implements
+ a concurrent prime sieve.
--- /dev/null
+#define setcontext(u) setmcontext(&(u)->uc_mcontext)
+#define getcontext(u) getmcontext(&(u)->uc_mcontext)
+typedef struct mcontext mcontext_t;
+typedef struct ucontext ucontext_t;
+
+extern int swapcontext(ucontext_t*, const ucontext_t*);
+extern void makecontext(ucontext_t*, void(*)(), int, ...);
+extern int getmcontext(mcontext_t*);
+extern void setmcontext(const mcontext_t*);
+
+/*-
+ * Copyright (c) 1999 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/sys/ucontext.h,v 1.4 1999/10/11 20:33:17 luoqi Exp $
+ */
+
+/* #include <machine/ucontext.h> */
+
+/*-
+ * Copyright (c) 1999 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $
+ */
+
+struct mcontext {
+ /*
+ * The first 20 fields must match the definition of
+ * sigcontext. So that we can support sigcontext
+ * and ucontext_t at the same time.
+ */
+ long mc_onstack; /* XXX - sigcontext compat. */
+ long mc_rdi; /* machine state (struct trapframe) */
+ long mc_rsi;
+ long mc_rdx;
+ long mc_rcx;
+ long mc_r8;
+ long mc_r9;
+ long mc_rax;
+ long mc_rbx;
+ long mc_rbp;
+ long mc_r10;
+ long mc_r11;
+ long mc_r12;
+ long mc_r13;
+ long mc_r14;
+ long mc_r15;
+ long mc_trapno;
+ long mc_addr;
+ long mc_flags;
+ long mc_err;
+ long mc_rip;
+ long mc_cs;
+ long mc_rflags;
+ long mc_rsp;
+ long mc_ss;
+
+ long mc_len; /* sizeof(mcontext_t) */
+#define _MC_FPFMT_NODEV 0x10000 /* device not present or configured */
+#define _MC_FPFMT_XMM 0x10002
+ long mc_fpformat;
+#define _MC_FPOWNED_NONE 0x20000 /* FP state not used */
+#define _MC_FPOWNED_FPU 0x20001 /* FP state came from FPU */
+#define _MC_FPOWNED_PCB 0x20002 /* FP state came from PCB */
+ long mc_ownedfp;
+ /*
+ * See <machine/fpu.h> for the internals of mc_fpstate[].
+ */
+ long mc_fpstate[64];
+ long mc_spare[8];
+};
+
+struct ucontext {
+ /*
+ * Keep the order of the first two fields. Also,
+ * keep them the first two fields in the structure.
+ * This way we can have a union with struct
+ * sigcontext and ucontext_t. This allows us to
+ * support them both at the same time.
+ * note: the union is not defined, though.
+ */
+ sigset_t uc_sigmask;
+ mcontext_t uc_mcontext;
+
+ struct __ucontext *uc_link;
+ stack_t uc_stack;
+ int __spare__[8];
+};
--- /dev/null
+/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */
+
+#if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5
+#define NEEDX86CONTEXT 1
+#define SET setmcontext
+#define GET getmcontext
+#endif
+
+#if defined(__OpenBSD__) && defined(__i386__)
+#define NEEDX86CONTEXT 1
+#define SET setmcontext
+#define GET getmcontext
+#endif
+
+#if defined(__APPLE__)
+#if defined(__i386__)
+#define NEEDX86CONTEXT 1
+#define SET _setmcontext
+#define GET _getmcontext
+#elif defined(__x86_64__)
+#define NEEDAMD64CONTEXT 1
+#define SET _setmcontext
+#define GET _getmcontext
+#else
+#define NEEDPOWERCONTEXT 1
+#define SET __setmcontext
+#define GET __getmcontext
+#endif
+#endif
+
+#if defined(__linux__) && defined(__arm__)
+#define NEEDARMCONTEXT 1
+#define SET setmcontext
+#define GET getmcontext
+#endif
+
+#if defined(__linux__) && defined(__mips__)
+#define NEEDMIPSCONTEXT 1
+#define SET setmcontext
+#define GET getmcontext
+#endif
+
+#ifdef NEEDX86CONTEXT
+.globl SET
+SET:
+ movl 4(%esp), %eax
+
+ movl 8(%eax), %fs
+ movl 12(%eax), %es
+ movl 16(%eax), %ds
+ movl 76(%eax), %ss
+ movl 20(%eax), %edi
+ movl 24(%eax), %esi
+ movl 28(%eax), %ebp
+ movl 36(%eax), %ebx
+ movl 40(%eax), %edx
+ movl 44(%eax), %ecx
+
+ movl 72(%eax), %esp
+ pushl 60(%eax) /* new %eip */
+ movl 48(%eax), %eax
+ ret
+
+.globl GET
+GET:
+ movl 4(%esp), %eax
+
+ movl %fs, 8(%eax)
+ movl %es, 12(%eax)
+ movl %ds, 16(%eax)
+ movl %ss, 76(%eax)
+ movl %edi, 20(%eax)
+ movl %esi, 24(%eax)
+ movl %ebp, 28(%eax)
+ movl %ebx, 36(%eax)
+ movl %edx, 40(%eax)
+ movl %ecx, 44(%eax)
+
+ movl $1, 48(%eax) /* %eax */
+ movl (%esp), %ecx /* %eip */
+ movl %ecx, 60(%eax)
+ leal 4(%esp), %ecx /* %esp */
+ movl %ecx, 72(%eax)
+
+ movl 44(%eax), %ecx /* restore %ecx */
+ movl $0, %eax
+ ret
+#endif
+
+#ifdef NEEDAMD64CONTEXT
+.globl SET
+SET:
+ movq 16(%rdi), %rsi
+ movq 24(%rdi), %rdx
+ movq 32(%rdi), %rcx
+ movq 40(%rdi), %r8
+ movq 48(%rdi), %r9
+ movq 56(%rdi), %rax
+ movq 64(%rdi), %rbx
+ movq 72(%rdi), %rbp
+ movq 80(%rdi), %r10
+ movq 88(%rdi), %r11
+ movq 96(%rdi), %r12
+ movq 104(%rdi), %r13
+ movq 112(%rdi), %r14
+ movq 120(%rdi), %r15
+ movq 184(%rdi), %rsp
+ pushq 160(%rdi) /* new %eip */
+ movq 8(%rdi), %rdi
+ ret
+
+.globl GET
+GET:
+ movq %rdi, 8(%rdi)
+ movq %rsi, 16(%rdi)
+ movq %rdx, 24(%rdi)
+ movq %rcx, 32(%rdi)
+ movq %r8, 40(%rdi)
+ movq %r9, 48(%rdi)
+ movq $1, 56(%rdi) /* %rax */
+ movq %rbx, 64(%rdi)
+ movq %rbp, 72(%rdi)
+ movq %r10, 80(%rdi)
+ movq %r11, 88(%rdi)
+ movq %r12, 96(%rdi)
+ movq %r13, 104(%rdi)
+ movq %r14, 112(%rdi)
+ movq %r15, 120(%rdi)
+
+ movq (%rsp), %rcx /* %rip */
+ movq %rcx, 160(%rdi)
+ leaq 8(%rsp), %rcx /* %rsp */
+ movq %rcx, 184(%rdi)
+
+ movq 32(%rdi), %rcx /* restore %rcx */
+ movq $0, %rax
+ ret
+#endif
+
+#ifdef NEEDPOWERCONTEXT
+/* get FPR and VR use flags with sc 0x7FF3 */
+/* get vsave with mfspr reg, 256 */
+
+.text
+.align 2
+
+.globl GET
+GET: /* xxx: instruction scheduling */
+ mflr r0
+ mfcr r5
+ mfctr r6
+ mfxer r7
+ stw r0, 0*4(r3)
+ stw r5, 1*4(r3)
+ stw r6, 2*4(r3)
+ stw r7, 3*4(r3)
+
+ stw r1, 4*4(r3)
+ stw r2, 5*4(r3)
+ li r5, 1 /* return value for setmcontext */
+ stw r5, 6*4(r3)
+
+ stw r13, (0+7)*4(r3) /* callee-save GPRs */
+ stw r14, (1+7)*4(r3) /* xxx: block move */
+ stw r15, (2+7)*4(r3)
+ stw r16, (3+7)*4(r3)
+ stw r17, (4+7)*4(r3)
+ stw r18, (5+7)*4(r3)
+ stw r19, (6+7)*4(r3)
+ stw r20, (7+7)*4(r3)
+ stw r21, (8+7)*4(r3)
+ stw r22, (9+7)*4(r3)
+ stw r23, (10+7)*4(r3)
+ stw r24, (11+7)*4(r3)
+ stw r25, (12+7)*4(r3)
+ stw r26, (13+7)*4(r3)
+ stw r27, (14+7)*4(r3)
+ stw r28, (15+7)*4(r3)
+ stw r29, (16+7)*4(r3)
+ stw r30, (17+7)*4(r3)
+ stw r31, (18+7)*4(r3)
+
+ li r3, 0 /* return */
+ blr
+
+.globl SET
+SET:
+ lwz r13, (0+7)*4(r3) /* callee-save GPRs */
+ lwz r14, (1+7)*4(r3) /* xxx: block move */
+ lwz r15, (2+7)*4(r3)
+ lwz r16, (3+7)*4(r3)
+ lwz r17, (4+7)*4(r3)
+ lwz r18, (5+7)*4(r3)
+ lwz r19, (6+7)*4(r3)
+ lwz r20, (7+7)*4(r3)
+ lwz r21, (8+7)*4(r3)
+ lwz r22, (9+7)*4(r3)
+ lwz r23, (10+7)*4(r3)
+ lwz r24, (11+7)*4(r3)
+ lwz r25, (12+7)*4(r3)
+ lwz r26, (13+7)*4(r3)
+ lwz r27, (14+7)*4(r3)
+ lwz r28, (15+7)*4(r3)
+ lwz r29, (16+7)*4(r3)
+ lwz r30, (17+7)*4(r3)
+ lwz r31, (18+7)*4(r3)
+
+ lwz r1, 4*4(r3)
+ lwz r2, 5*4(r3)
+
+ lwz r0, 0*4(r3)
+ mtlr r0
+ lwz r0, 1*4(r3)
+ mtcr r0 /* mtcrf 0xFF, r0 */
+ lwz r0, 2*4(r3)
+ mtctr r0
+ lwz r0, 3*4(r3)
+ mtxer r0
+
+ lwz r3, 6*4(r3)
+ blr
+#endif
+
+#ifdef NEEDARMCONTEXT
+.globl GET
+GET:
+ str r1, [r0,#4]
+ str r2, [r0,#8]
+ str r3, [r0,#12]
+ str r4, [r0,#16]
+ str r5, [r0,#20]
+ str r6, [r0,#24]
+ str r7, [r0,#28]
+ str r8, [r0,#32]
+ str r9, [r0,#36]
+ str r10, [r0,#40]
+ str r11, [r0,#44]
+ str r12, [r0,#48]
+ str r13, [r0,#52]
+ str r14, [r0,#56]
+ /* store 1 as r0-to-restore */
+ mov r1, #1
+ str r1, [r0]
+ /* return 0 */
+ mov r0, #0
+ mov pc, lr
+
+.globl SET
+SET:
+ ldr r1, [r0,#4]
+ ldr r2, [r0,#8]
+ ldr r3, [r0,#12]
+ ldr r4, [r0,#16]
+ ldr r5, [r0,#20]
+ ldr r6, [r0,#24]
+ ldr r7, [r0,#28]
+ ldr r8, [r0,#32]
+ ldr r9, [r0,#36]
+ ldr r10, [r0,#40]
+ ldr r11, [r0,#44]
+ ldr r12, [r0,#48]
+ ldr r13, [r0,#52]
+ ldr r14, [r0,#56]
+ ldr r0, [r0]
+ mov pc, lr
+#endif
+
+#ifdef NEEDMIPSCONTEXT
+.globl GET
+GET:
+ sw $4, 24($4)
+ sw $5, 28($4)
+ sw $6, 32($4)
+ sw $7, 36($4)
+
+ sw $16, 72($4)
+ sw $17, 76($4)
+ sw $18, 80($4)
+ sw $19, 84($4)
+ sw $20, 88($4)
+ sw $21, 92($4)
+ sw $22, 96($4)
+ sw $23, 100($4)
+
+ sw $28, 120($4) /* gp */
+ sw $29, 124($4) /* sp */
+ sw $30, 128($4) /* fp */
+ sw $31, 132($4) /* ra */
+
+ xor $2, $2, $2
+ j $31
+ nop
+
+.globl SET
+SET:
+ lw $16, 72($4)
+ lw $17, 76($4)
+ lw $18, 80($4)
+ lw $19, 84($4)
+ lw $20, 88($4)
+ lw $21, 92($4)
+ lw $22, 96($4)
+ lw $23, 100($4)
+
+ lw $28, 120($4) /* gp */
+ lw $29, 124($4) /* sp */
+ lw $30, 128($4) /* fp */
+
+ /*
+ * If we set $31 directly and j $31,
+ * we would loose the outer return address.
+ * Use a temporary register, then.
+ */
+ lw $8, 132($4) /* ra */
+
+ /* bug: not setting the pc causes a bus error */
+ lw $25, 132($4) /* pc */
+
+ lw $5, 28($4)
+ lw $6, 32($4)
+ lw $7, 36($4)
+ lw $4, 24($4)
+
+ j $8
+ nop
+#endif
--- /dev/null
+/* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */
+
+#include "taskimpl.h"
+
+Channel*
+chancreate(int elemsize, int bufsize)
+{
+ Channel *c;
+
+ c = malloc(sizeof *c+bufsize*elemsize);
+ if(c == nil){
+ fprint(2, "chancreate malloc: %r");
+ exit(1);
+ }
+ memset(c, 0, sizeof *c);
+ c->elemsize = elemsize;
+ c->bufsize = bufsize;
+ c->nbuf = 0;
+ c->buf = (uchar*)(c+1);
+ return c;
+}
+
+/* bug - work out races */
+void
+chanfree(Channel *c)
+{
+ if(c == nil)
+ return;
+ free(c->name);
+ free(c->arecv.a);
+ free(c->asend.a);
+ free(c);
+}
+
+static void
+addarray(Altarray *a, Alt *alt)
+{
+ if(a->n == a->m){
+ a->m += 16;
+ a->a = realloc(a->a, a->m*sizeof a->a[0]);
+ }
+ a->a[a->n++] = alt;
+}
+
+static void
+delarray(Altarray *a, int i)
+{
+ --a->n;
+ a->a[i] = a->a[a->n];
+}
+
+/*
+ * doesn't really work for things other than CHANSND and CHANRCV
+ * but is only used as arg to chanarray, which can handle it
+ */
+#define otherop(op) (CHANSND+CHANRCV-(op))
+
+static Altarray*
+chanarray(Channel *c, uint op)
+{
+ switch(op){
+ default:
+ return nil;
+ case CHANSND:
+ return &c->asend;
+ case CHANRCV:
+ return &c->arecv;
+ }
+}
+
+static int
+altcanexec(Alt *a)
+{
+ Altarray *ar;
+ Channel *c;
+
+ if(a->op == CHANNOP)
+ return 0;
+ c = a->c;
+ if(c->bufsize == 0){
+ ar = chanarray(c, otherop(a->op));
+ return ar && ar->n;
+ }else{
+ switch(a->op){
+ default:
+ return 0;
+ case CHANSND:
+ return c->nbuf < c->bufsize;
+ case CHANRCV:
+ return c->nbuf > 0;
+ }
+ }
+}
+
+static void
+altqueue(Alt *a)
+{
+ Altarray *ar;
+
+ ar = chanarray(a->c, a->op);
+ addarray(ar, a);
+}
+
+static void
+altdequeue(Alt *a)
+{
+ int i;
+ Altarray *ar;
+
+ ar = chanarray(a->c, a->op);
+ if(ar == nil){
+ fprint(2, "bad use of altdequeue op=%d\n", a->op);
+ abort();
+ }
+
+ for(i=0; i<ar->n; i++)
+ if(ar->a[i] == a){
+ delarray(ar, i);
+ return;
+ }
+ fprint(2, "cannot find self in altdq\n");
+ abort();
+}
+
+static void
+altalldequeue(Alt *a)
+{
+ int i;
+
+ for(i=0; a[i].op!=CHANEND && a[i].op!=CHANNOBLK; i++)
+ if(a[i].op != CHANNOP)
+ altdequeue(&a[i]);
+}
+
+static void
+amove(void *dst, void *src, uint n)
+{
+ if(dst){
+ if(src == nil)
+ memset(dst, 0, n);
+ else
+ memmove(dst, src, n);
+ }
+}
+
+/*
+ * Actually move the data around. There are up to three
+ * players: the sender, the receiver, and the channel itself.
+ * If the channel is unbuffered or the buffer is empty,
+ * data goes from sender to receiver. If the channel is full,
+ * the receiver removes some from the channel and the sender
+ * gets to put some in.
+ */
+static void
+altcopy(Alt *s, Alt *r)
+{
+ Alt *t;
+ Channel *c;
+ uchar *cp;
+
+ /*
+ * Work out who is sender and who is receiver
+ */
+ if(s == nil && r == nil)
+ return;
+ assert(s != nil);
+ c = s->c;
+ if(s->op == CHANRCV){
+ t = s;
+ s = r;
+ r = t;
+ }
+ assert(s==nil || s->op == CHANSND);
+ assert(r==nil || r->op == CHANRCV);
+
+ /*
+ * Channel is empty (or unbuffered) - copy directly.
+ */
+ if(s && r && c->nbuf == 0){
+ amove(r->v, s->v, c->elemsize);
+ return;
+ }
+
+ /*
+ * Otherwise it's always okay to receive and then send.
+ */
+ if(r){
+ cp = c->buf + c->off*c->elemsize;
+ amove(r->v, cp, c->elemsize);
+ --c->nbuf;
+ if(++c->off == c->bufsize)
+ c->off = 0;
+ }
+ if(s){
+ cp = c->buf + (c->off+c->nbuf)%c->bufsize*c->elemsize;
+ amove(cp, s->v, c->elemsize);
+ ++c->nbuf;
+ }
+}
+
+static void
+altexec(Alt *a)
+{
+ int i;
+ Altarray *ar;
+ Alt *other;
+ Channel *c;
+
+ c = a->c;
+ ar = chanarray(c, otherop(a->op));
+ if(ar && ar->n){
+ i = rand()%ar->n;
+ other = ar->a[i];
+ altcopy(a, other);
+ altalldequeue(other->xalt);
+ other->xalt[0].xalt = other;
+ taskready(other->task);
+ }else
+ altcopy(a, nil);
+}
+
+#define dbgalt 0
+int
+chanalt(Alt *a)
+{
+ int i, j, ncan, n, canblock;
+ Channel *c;
+ Task *t;
+
+ needstack(512);
+ for(i=0; a[i].op != CHANEND && a[i].op != CHANNOBLK; i++)
+ ;
+ n = i;
+ canblock = a[i].op == CHANEND;
+
+ t = taskrunning;
+ for(i=0; i<n; i++){
+ a[i].task = t;
+ a[i].xalt = a;
+ }
+if(dbgalt) print("alt ");
+ ncan = 0;
+ for(i=0; i<n; i++){
+ c = a[i].c;
+if(dbgalt) print(" %c:", "esrnb"[a[i].op]);
+if(dbgalt) { if(c->name) print("%s", c->name); else print("%p", c); }
+ if(altcanexec(&a[i])){
+if(dbgalt) print("*");
+ ncan++;
+ }
+ }
+ if(ncan){
+ j = rand()%ncan;
+ for(i=0; i<n; i++){
+ if(altcanexec(&a[i])){
+ if(j-- == 0){
+if(dbgalt){
+c = a[i].c;
+print(" => %c:", "esrnb"[a[i].op]);
+if(c->name) print("%s", c->name); else print("%p", c);
+print("\n");
+}
+ altexec(&a[i]);
+ return i;
+ }
+ }
+ }
+ }
+if(dbgalt)print("\n");
+
+ if(!canblock)
+ return -1;
+
+ for(i=0; i<n; i++){
+ if(a[i].op != CHANNOP)
+ altqueue(&a[i]);
+ }
+
+ taskswitch();
+
+ /*
+ * the guy who ran the op took care of dequeueing us
+ * and then set a[0].alt to the one that was executed.
+ */
+ return a[0].xalt - a;
+}
+
+static int
+_chanop(Channel *c, int op, void *p, int canblock)
+{
+ Alt a[2];
+
+ a[0].c = c;
+ a[0].op = op;
+ a[0].v = p;
+ a[1].op = canblock ? CHANEND : CHANNOBLK;
+ if(chanalt(a) < 0)
+ return -1;
+ return 1;
+}
+
+int
+chansend(Channel *c, void *v)
+{
+ return _chanop(c, CHANSND, v, 1);
+}
+
+int
+channbsend(Channel *c, void *v)
+{
+ return _chanop(c, CHANSND, v, 0);
+}
+
+int
+chanrecv(Channel *c, void *v)
+{
+ return _chanop(c, CHANRCV, v, 1);
+}
+
+int
+channbrecv(Channel *c, void *v)
+{
+ return _chanop(c, CHANRCV, v, 0);
+}
+
+int
+chansendp(Channel *c, void *v)
+{
+ return _chanop(c, CHANSND, (void*)&v, 1);
+}
+
+void*
+chanrecvp(Channel *c)
+{
+ void *v;
+
+ _chanop(c, CHANRCV, (void*)&v, 1);
+ return v;
+}
+
+int
+channbsendp(Channel *c, void *v)
+{
+ return _chanop(c, CHANSND, (void*)&v, 0);
+}
+
+void*
+channbrecvp(Channel *c)
+{
+ void *v;
+
+ _chanop(c, CHANRCV, (void*)&v, 0);
+ return v;
+}
+
+int
+chansendul(Channel *c, ulong val)
+{
+ return _chanop(c, CHANSND, &val, 1);
+}
+
+ulong
+chanrecvul(Channel *c)
+{
+ ulong val;
+
+ _chanop(c, CHANRCV, &val, 1);
+ return val;
+}
+
+int
+channbsendul(Channel *c, ulong val)
+{
+ return _chanop(c, CHANSND, &val, 0);
+}
+
+ulong
+channbrecvul(Channel *c)
+{
+ ulong val;
+
+ _chanop(c, CHANRCV, &val, 0);
+ return val;
+}
--- /dev/null
+/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */
+
+#include "taskimpl.h"
+
+#if defined(__APPLE__)
+#if defined(__i386__)
+#define NEEDX86MAKECONTEXT
+#define NEEDSWAPCONTEXT
+#elif defined(__x86_64__)
+#define NEEDAMD64MAKECONTEXT
+#define NEEDSWAPCONTEXT
+#else
+#define NEEDPOWERMAKECONTEXT
+#define NEEDSWAPCONTEXT
+#endif
+#endif
+
+#if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5
+#define NEEDX86MAKECONTEXT
+#define NEEDSWAPCONTEXT
+#endif
+
+#if defined(__OpenBSD__) && defined(__i386__)
+#define NEEDX86MAKECONTEXT
+#define NEEDSWAPCONTEXT
+#endif
+
+#if defined(__linux__) && defined(__arm__)
+#define NEEDSWAPCONTEXT
+#define NEEDARMMAKECONTEXT
+#endif
+
+#if defined(__linux__) && defined(__mips__)
+#define NEEDSWAPCONTEXT
+#define NEEDMIPSMAKECONTEXT
+#endif
+
+#ifdef NEEDPOWERMAKECONTEXT
+void
+makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...)
+{
+ ulong *sp, *tos;
+ va_list arg;
+
+ tos = (ulong*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(ulong);
+ sp = tos - 16;
+ ucp->mc.pc = (long)func;
+ ucp->mc.sp = (long)sp;
+ va_start(arg, argc);
+ ucp->mc.r3 = va_arg(arg, long);
+ va_end(arg);
+}
+#endif
+
+#ifdef NEEDX86MAKECONTEXT
+void
+makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...)
+{
+ int *sp;
+
+ sp = (int*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/4;
+ sp -= argc;
+ sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */
+ memmove(sp, &argc+1, argc*sizeof(int));
+
+ *--sp = 0; /* return address */
+ ucp->uc_mcontext.mc_eip = (long)func;
+ ucp->uc_mcontext.mc_esp = (int)sp;
+}
+#endif
+
+#ifdef NEEDAMD64MAKECONTEXT
+void
+makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...)
+{
+ long *sp;
+ va_list va;
+
+ memset(&ucp->uc_mcontext, 0, sizeof ucp->uc_mcontext);
+ if(argc != 2)
+#ifdef EMACS
+ abort ();
+#else
+ *(int*)0 = 0;
+#endif
+ va_start(va, argc);
+ ucp->uc_mcontext.mc_rdi = va_arg(va, int);
+ ucp->uc_mcontext.mc_rsi = va_arg(va, int);
+ va_end(va);
+ sp = (long*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(long);
+ sp -= argc;
+ sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */
+ *--sp = 0; /* return address */
+ ucp->uc_mcontext.mc_rip = (long)func;
+ ucp->uc_mcontext.mc_rsp = (long)sp;
+}
+#endif
+
+#ifdef NEEDARMMAKECONTEXT
+void
+makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...)
+{
+ int i, *sp;
+ va_list arg;
+
+ sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4;
+ va_start(arg, argc);
+ for(i=0; i<4 && i<argc; i++)
+ uc->uc_mcontext.gregs[i] = va_arg(arg, uint);
+ va_end(arg);
+ uc->uc_mcontext.gregs[13] = (uint)sp;
+ uc->uc_mcontext.gregs[14] = (uint)fn;
+}
+#endif
+
+#ifdef NEEDMIPSMAKECONTEXT
+void
+makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...)
+{
+ int i, *sp;
+ va_list arg;
+
+ va_start(arg, argc);
+ sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4;
+ for(i=0; i<4 && i<argc; i++)
+ uc->uc_mcontext.mc_regs[i+4] = va_arg(arg, int);
+ va_end(arg);
+ uc->uc_mcontext.mc_regs[29] = (int)sp;
+ uc->uc_mcontext.mc_regs[31] = (int)fn;
+}
+#endif
+
+#ifdef NEEDSWAPCONTEXT
+int
+swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
+{
+ if(getcontext(oucp) == 0)
+ setcontext(ucp);
+ return 0;
+}
+#endif
--- /dev/null
+#include "taskimpl.h"
+#include <sys/poll.h>
+#include <fcntl.h>
+
+enum
+{
+ MAXFD = 1024
+};
+
+static struct pollfd pollfd[MAXFD];
+static Task *polltask[MAXFD];
+static int npollfd;
+static int startedfdtask;
+static Tasklist sleeping;
+static int sleepingcounted;
+static uvlong nsec(void);
+
+void
+fdtask(void *v)
+{
+ int i, ms;
+ Task *t;
+ uvlong now;
+
+ tasksystem();
+ taskname("fdtask");
+ for(;;){
+ /* let everyone else run */
+ while(taskyield() > 0)
+ ;
+ /* we're the only one runnable - poll for i/o */
+ errno = 0;
+ taskstate("poll");
+ if((t=sleeping.head) == nil)
+ ms = -1;
+ else{
+ /* sleep at most 5s */
+ now = nsec();
+ if(now >= t->alarmtime)
+ ms = 0;
+ else if(now+5*1000*1000*1000LL >= t->alarmtime)
+ ms = (t->alarmtime - now)/1000000;
+ else
+ ms = 5000;
+ }
+ if(poll(pollfd, npollfd, ms) < 0){
+ if(errno == EINTR)
+ continue;
+ fprint(2, "poll: %s\n", strerror(errno));
+ taskexitall(0);
+ }
+
+ /* wake up the guys who deserve it */
+ for(i=0; i<npollfd; i++){
+ while(i < npollfd && pollfd[i].revents){
+ taskready(polltask[i]);
+ --npollfd;
+ pollfd[i] = pollfd[npollfd];
+ polltask[i] = polltask[npollfd];
+ }
+ }
+
+ now = nsec();
+ while((t=sleeping.head) && now >= t->alarmtime){
+ deltask(&sleeping, t);
+ if(!t->system && --sleepingcounted == 0)
+ taskcount--;
+ taskready(t);
+ }
+ }
+}
+
+uint
+taskdelay(uint ms)
+{
+ uvlong when, now;
+ Task *t;
+
+ if(!startedfdtask){
+ startedfdtask = 1;
+ taskcreate(fdtask, 0, 32768);
+ }
+
+ now = nsec();
+ when = now+(uvlong)ms*1000000;
+ for(t=sleeping.head; t!=nil && t->alarmtime < when; t=t->next)
+ ;
+
+ if(t){
+ taskrunning->prev = t->prev;
+ taskrunning->next = t;
+ }else{
+ taskrunning->prev = sleeping.tail;
+ taskrunning->next = nil;
+ }
+
+ t = taskrunning;
+ t->alarmtime = when;
+ if(t->prev)
+ t->prev->next = t;
+ else
+ sleeping.head = t;
+ if(t->next)
+ t->next->prev = t;
+ else
+ sleeping.tail = t;
+
+ if(!t->system && sleepingcounted++ == 0)
+ taskcount++;
+ taskswitch();
+
+ return (nsec() - now)/1000000;
+}
+
+void
+fdwait(int fd, int rw)
+{
+ int bits;
+
+ if(!startedfdtask){
+ startedfdtask = 1;
+ taskcreate(fdtask, 0, 32768);
+ }
+
+ if(npollfd >= MAXFD){
+ fprint(2, "too many poll file descriptors\n");
+ abort();
+ }
+
+ taskstate("fdwait for %s", rw=='r' ? "read" : rw=='w' ? "write" : "error");
+ bits = 0;
+ switch(rw){
+ case 'r':
+ bits |= POLLIN;
+ break;
+ case 'w':
+ bits |= POLLOUT;
+ break;
+ }
+
+ polltask[npollfd] = taskrunning;
+ pollfd[npollfd].fd = fd;
+ pollfd[npollfd].events = bits;
+ pollfd[npollfd].revents = 0;
+ npollfd++;
+ taskswitch();
+}
+
+/* Like fdread but always calls fdwait before reading. */
+int
+fdread1(int fd, void *buf, int n)
+{
+ int m;
+
+ do
+ fdwait(fd, 'r');
+ while((m = read(fd, buf, n)) < 0 && errno == EAGAIN);
+ return m;
+}
+
+int
+fdread(int fd, void *buf, int n)
+{
+ int m;
+
+ while((m=read(fd, buf, n)) < 0 && errno == EAGAIN)
+ fdwait(fd, 'r');
+ return m;
+}
+
+int
+fdwrite(int fd, void *buf, int n)
+{
+ int m, tot;
+
+ for(tot=0; tot<n; tot+=m){
+ while((m=write(fd, (char*)buf+tot, n-tot)) < 0 && errno == EAGAIN)
+ fdwait(fd, 'w');
+ if(m < 0)
+ return m;
+ if(m == 0)
+ break;
+ }
+ return tot;
+}
+
+int
+fdnoblock(int fd)
+{
+ return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK);
+}
+
+static uvlong
+nsec(void)
+{
+ struct timeval tv;
+
+ if(gettimeofday(&tv, 0) < 0)
+ return -1;
+ return (uvlong)tv.tv_sec*1000*1000*1000 + tv.tv_usec*1000;
+}
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <task.h>
+#include <stdlib.h>
+
+enum
+{
+ STACK = 32768
+};
+
+char *server;
+char *url;
+
+void fetchtask(void*);
+
+void
+taskmain(int argc, char **argv)
+{
+ int i, n;
+
+ if(argc != 4){
+ fprintf(stderr, "usage: httpload n server url\n");
+ taskexitall(1);
+ }
+ n = atoi(argv[1]);
+ server = argv[2];
+ url = argv[3];
+
+ for(i=0; i<n; i++){
+ taskcreate(fetchtask, 0, STACK);
+ while(taskyield() > 1)
+ ;
+ sleep(1);
+ }
+}
+
+void
+fetchtask(void *v)
+{
+ int fd, n;
+ char buf[512];
+
+ fprintf(stderr, "starting...\n");
+ for(;;){
+ if((fd = netdial(TCP, server, 80)) < 0){
+ fprintf(stderr, "dial %s: %s (%s)\n", server, strerror(errno), taskgetstate());
+ continue;
+ }
+ snprintf(buf, sizeof buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", url, server);
+ fdwrite(fd, buf, strlen(buf));
+ while((n = fdread(fd, buf, sizeof buf)) > 0)
+ ;
+ close(fd);
+ write(1, ".", 1);
+ }
+}
--- /dev/null
+#!/bin/sh
+
+case "x$CC" in
+x|xcc)
+ CC=cc
+ CFLAGS="-mt -g -O -c -xCC -D__sun__ -I."
+ ;;
+xgcc)
+ CC=gcc
+ CFLAGS="-Wall -c -I."
+ ;;
+*)
+ echo 'unknown $CC'
+ exit 1
+esac
+
+u=`uname`
+v=`uname -r`
+s=`echo $u$v | tr '. ' '__'`
+CFLAGS="$CFLAGS -D__${s}__"
+
+make "CC=$CC" "CFLAGS=$CFLAGS" "ASM=" "TCPLIBS=-lsocket -lnsl"
--- /dev/null
+typedef struct mcontext mcontext_t;
+typedef struct ucontext ucontext_t;
+
+extern int swapcontext(ucontext_t*, const ucontext_t*);
+extern void makecontext(ucontext_t*, void(*)(), int, ...);
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ucontext.h 8.1 (Berkeley) 6/10/93
+ * JNPR: ucontext.h,v 1.2 2007/08/09 11:23:32 katta
+ * $FreeBSD: src/sys/mips/include/ucontext.h,v 1.2 2010/01/10 19:50:24 imp Exp $
+ */
+
+struct mcontext {
+ /*
+ * These fields must match the corresponding fields in struct
+ * sigcontext which follow 'sc_mask'. That way we can support
+ * struct sigcontext and ucontext_t at the same time.
+ */
+ int mc_onstack; /* sigstack state to restore */
+ int mc_pc; /* pc at time of signal */
+ int mc_regs[32]; /* processor regs 0 to 31 */
+ int sr; /* status register */
+ int mullo, mulhi; /* mullo and mulhi registers... */
+ int mc_fpused; /* fp has been used */
+ int mc_fpregs[33]; /* fp regs 0 to 31 and csr */
+ int mc_fpc_eir; /* fp exception instruction reg */
+ void *mc_tls; /* pointer to TLS area */
+ int __spare__[8]; /* XXX reserved */
+};
+
+struct ucontext {
+ /*
+ * Keep the order of the first two fields. Also,
+ * keep them the first two fields in the structure.
+ * This way we can have a union with struct
+ * sigcontext and ucontext_t. This allows us to
+ * support them both at the same time.
+ * note: the union is not defined, though.
+ */
+ sigset_t uc_sigmask;
+ mcontext_t uc_mcontext;
+
+ struct __ucontext *uc_link;
+ stack_t uc_stack;
+ int uc_flags;
+ int __spare__[4];
+};
--- /dev/null
+#include "taskimpl.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/poll.h>
+
+int
+netannounce(int istcp, char *server, int port)
+{
+ int fd, n, proto;
+ struct sockaddr_in sa;
+ socklen_t sn;
+ uint32_t ip;
+
+ taskstate("netannounce");
+ proto = istcp ? SOCK_STREAM : SOCK_DGRAM;
+ memset(&sa, 0, sizeof sa);
+ sa.sin_family = AF_INET;
+ if(server != nil && strcmp(server, "*") != 0){
+ if(netlookup(server, &ip) < 0){
+ taskstate("netlookup failed");
+ return -1;
+ }
+ memmove(&sa.sin_addr, &ip, 4);
+ }
+ sa.sin_port = htons(port);
+ if((fd = socket(AF_INET, proto, 0)) < 0){
+ taskstate("socket failed");
+ return -1;
+ }
+
+ /* set reuse flag for tcp */
+ if(istcp && getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0){
+ n = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
+ }
+
+ if(bind(fd, (struct sockaddr*)&sa, sizeof sa) < 0){
+ taskstate("bind failed");
+ close(fd);
+ return -1;
+ }
+
+ if(proto == SOCK_STREAM)
+ listen(fd, 16);
+
+ fdnoblock(fd);
+ taskstate("netannounce succeeded");
+ return fd;
+}
+
+int
+netaccept(int fd, char *server, int *port)
+{
+ int cfd, one;
+ struct sockaddr_in sa;
+ uchar *ip;
+ socklen_t len;
+
+ fdwait(fd, 'r');
+
+ taskstate("netaccept");
+ len = sizeof sa;
+ if((cfd = accept(fd, (void*)&sa, &len)) < 0){
+ taskstate("accept failed");
+ return -1;
+ }
+ if(server){
+ ip = (uchar*)&sa.sin_addr;
+ snprint(server, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+ }
+ if(port)
+ *port = ntohs(sa.sin_port);
+ fdnoblock(cfd);
+ one = 1;
+ setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
+ taskstate("netaccept succeeded");
+ return cfd;
+}
+
+#define CLASS(p) ((*(unsigned char*)(p))>>6)
+static int
+parseip(char *name, uint32_t *ip)
+{
+ unsigned char addr[4];
+ char *p;
+ int i, x;
+
+ p = name;
+ for(i=0; i<4 && *p; i++){
+ x = strtoul(p, &p, 0);
+ if(x < 0 || x >= 256)
+ return -1;
+ if(*p != '.' && *p != 0)
+ return -1;
+ if(*p == '.')
+ p++;
+ addr[i] = x;
+ }
+
+ switch(CLASS(addr)){
+ case 0:
+ case 1:
+ if(i == 3){
+ addr[3] = addr[2];
+ addr[2] = addr[1];
+ addr[1] = 0;
+ }else if(i == 2){
+ addr[3] = addr[1];
+ addr[2] = 0;
+ addr[1] = 0;
+ }else if(i != 4)
+ return -1;
+ break;
+ case 2:
+ if(i == 3){
+ addr[3] = addr[2];
+ addr[2] = 0;
+ }else if(i != 4)
+ return -1;
+ break;
+ }
+ *ip = *(uint32_t*)addr;
+ return 0;
+}
+
+int
+netlookup(char *name, uint32_t *ip)
+{
+ struct hostent *he;
+
+ if(parseip(name, ip) >= 0)
+ return 0;
+
+ /* BUG - Name resolution blocks. Need a non-blocking DNS. */
+ taskstate("netlookup");
+ if((he = gethostbyname(name)) != 0){
+ *ip = *(uint32_t*)he->h_addr;
+ taskstate("netlookup succeeded");
+ return 0;
+ }
+
+ taskstate("netlookup failed");
+ return -1;
+}
+
+int
+netdial(int istcp, char *server, int port)
+{
+ int proto, fd, n;
+ uint32_t ip;
+ struct sockaddr_in sa;
+ socklen_t sn;
+
+ if(netlookup(server, &ip) < 0)
+ return -1;
+
+ taskstate("netdial");
+ proto = istcp ? SOCK_STREAM : SOCK_DGRAM;
+ if((fd = socket(AF_INET, proto, 0)) < 0){
+ taskstate("socket failed");
+ return -1;
+ }
+ fdnoblock(fd);
+
+ /* for udp */
+ if(!istcp){
+ n = 1;
+ setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof n);
+ }
+
+ /* start connecting */
+ memset(&sa, 0, sizeof sa);
+ memmove(&sa.sin_addr, &ip, 4);
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(port);
+ if(connect(fd, (struct sockaddr*)&sa, sizeof sa) < 0 && errno != EINPROGRESS){
+ taskstate("connect failed");
+ close(fd);
+ return -1;
+ }
+
+ /* wait for finish */
+ fdwait(fd, 'w');
+ sn = sizeof sa;
+ if(getpeername(fd, (struct sockaddr*)&sa, &sn) >= 0){
+ taskstate("connect succeeded");
+ return fd;
+ }
+
+ /* report error */
+ sn = sizeof n;
+ getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&n, &sn);
+ if(n == 0)
+ n = ECONNREFUSED;
+ close(fd);
+ taskstate("connect failed");
+ errno = n;
+ return -1;
+}
--- /dev/null
+#define setcontext(u) _setmcontext(&(u)->mc)
+#define getcontext(u) _getmcontext(&(u)->mc)
+typedef struct mcontext mcontext_t;
+typedef struct ucontext ucontext_t;
+struct mcontext
+{
+ ulong pc; /* lr */
+ ulong cr; /* mfcr */
+ ulong ctr; /* mfcr */
+ ulong xer; /* mfcr */
+ ulong sp; /* callee saved: r1 */
+ ulong toc; /* callee saved: r2 */
+ ulong r3; /* first arg to function, return register: r3 */
+ ulong gpr[19]; /* callee saved: r13-r31 */
+/*
+// XXX: currently do not save vector registers or floating-point state
+// ulong pad;
+// uvlong fpr[18]; / * callee saved: f14-f31 * /
+// ulong vr[4*12]; / * callee saved: v20-v31, 256-bits each * /
+*/
+};
+
+struct ucontext
+{
+ struct {
+ void *ss_sp;
+ uint ss_size;
+ } uc_stack;
+ sigset_t uc_sigmask;
+ mcontext_t mc;
+};
+
+void makecontext(ucontext_t*, void(*)(void), int, ...);
+int swapcontext(ucontext_t*, const ucontext_t*);
+int _getmcontext(mcontext_t*);
+void _setmcontext(const mcontext_t*);
--- /dev/null
+/* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <task.h>
+
+int quiet;
+int goal;
+int buffer;
+
+void
+primetask(void *arg)
+{
+ Channel *c, *nc;
+ int p, i;
+ c = arg;
+
+ p = chanrecvul(c);
+ if(p > goal)
+ taskexitall(0);
+ if(!quiet)
+ printf("%d\n", p);
+ nc = chancreate(sizeof(unsigned long), buffer);
+ taskcreate(primetask, nc, 32768);
+ for(;;){
+ i = chanrecvul(c);
+ if(i%p)
+ chansendul(nc, i);
+ }
+}
+
+void
+taskmain(int argc, char **argv)
+{
+ int i;
+ Channel *c;
+
+ if(argc>1)
+ goal = atoi(argv[1]);
+ else
+ goal = 100;
+ printf("goal=%d\n", goal);
+
+ c = chancreate(sizeof(unsigned long), buffer);
+ taskcreate(primetask, c, 32768);
+ for(i=2;; i++)
+ chansendul(c, i);
+}
+
+void*
+emalloc(unsigned long n)
+{
+ return calloc(n ,1);
+}
+
+long
+lrand(void)
+{
+ return rand();
+}
--- /dev/null
+/* Copyright (c) 2004 Russ Cox. See COPYRIGHT. */
+
+#include "taskimpl.h"
+#include <stdio.h> /* for strerror! */
+
+/*
+ * Stripped down print library. Plan 9 interface, new code.
+ */
+
+enum
+{
+ FlagLong = 1<<0,
+ FlagLongLong = 1<<1,
+ FlagUnsigned = 1<<2,
+};
+
+static char*
+printstr(char *dst, char *edst, char *s, int size)
+{
+ int l, n, sign;
+
+ sign = 1;
+ if(size < 0){
+ size = -size;
+ sign = -1;
+ }
+ if(dst >= edst)
+ return dst;
+ l = strlen(s);
+ n = l;
+ if(n < size)
+ n = size;
+ if(n >= edst-dst)
+ n = (edst-dst)-1;
+ if(l > n)
+ l = n;
+ if(sign < 0){
+ memmove(dst, s, l);
+ if(n-l)
+ memset(dst+l, ' ', n-l);
+ }else{
+ if(n-l)
+ memset(dst, ' ', n-l);
+ memmove(dst+n-l, s, l);
+ }
+ return dst+n;
+}
+
+char*
+vseprint(char *dst, char *edst, char *fmt, va_list arg)
+{
+ int fl, size, sign, base;
+ char *p, *w;
+ char cbuf[2];
+
+ w = dst;
+ for(p=fmt; *p && w<edst-1; p++){
+ switch(*p){
+ default:
+ *w++ = *p;
+ break;
+ case '%':
+ fl = 0;
+ size = 0;
+ sign = 1;
+ for(p++; *p; p++){
+ switch(*p){
+ case '-':
+ sign = -1;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ size = size*10 + *p-'0';
+ break;
+ case 'l':
+ if(fl&FlagLong)
+ fl |= FlagLongLong;
+ else
+ fl |= FlagLong;
+ break;
+ case 'u':
+ fl |= FlagUnsigned;
+ break;
+ case 'd':
+ base = 10;
+ goto num;
+ case 'o':
+ base = 8;
+ goto num;
+ case 'p':
+ case 'x':
+ base = 16;
+ goto num;
+ num:
+ {
+ static char digits[] = "0123456789abcdef";
+ char buf[30], *p;
+ int neg, zero;
+ uvlong luv;
+
+ if(fl&FlagLongLong){
+ if(fl&FlagUnsigned)
+ luv = va_arg(arg, uvlong);
+ else
+ luv = va_arg(arg, vlong);
+ }else{
+ if(fl&FlagLong){
+ if(fl&FlagUnsigned)
+ luv = va_arg(arg, ulong);
+ else
+ luv = va_arg(arg, long);
+ }else{
+ if(fl&FlagUnsigned)
+ luv = va_arg(arg, uint);
+ else
+ luv = va_arg(arg, int);
+ }
+ }
+
+ p = buf+sizeof buf;
+ neg = 0;
+ zero = 0;
+ if(!(fl&FlagUnsigned) && (vlong)luv < 0){
+ neg = 1;
+ luv = -luv;
+ }
+ if(luv == 0)
+ zero = 1;
+ *--p = 0;
+ while(luv){
+ *--p = digits[luv%base];
+ luv /= base;
+ }
+ if(base == 16){
+ *--p = 'x';
+ *--p = '0';
+ }
+ if(base == 8 || zero)
+ *--p = '0';
+ w = printstr(w, edst, p, size*sign);
+ goto break2;
+ }
+ case 'c':
+ cbuf[0] = va_arg(arg, int);
+ cbuf[1] = 0;
+ w = printstr(w, edst, cbuf, size*sign);
+ goto break2;
+ case 's':
+ w = printstr(w, edst, va_arg(arg, char*), size*sign);
+ goto break2;
+ case 'r':
+ w = printstr(w, edst, strerror(errno), size*sign);
+ goto break2;
+ default:
+ p = "X*verb*";
+ goto break2;
+ }
+ }
+ break2:
+ break;
+ }
+ }
+
+ assert(w < edst);
+ *w = 0;
+ return dst;
+}
+
+char*
+vsnprint(char *dst, uint n, char *fmt, va_list arg)
+{
+ return vseprint(dst, dst+n, fmt, arg);
+}
+
+char*
+snprint(char *dst, uint n, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ vsnprint(dst, n, fmt, arg);
+ va_end(arg);
+ return dst;
+}
+
+char*
+seprint(char *dst, char *edst, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(dst, edst, fmt, arg);
+ va_end(arg);
+ return dst;
+}
+
+int
+vfprint(int fd, char *fmt, va_list arg)
+{
+ char buf[256];
+
+ vseprint(buf, buf+sizeof buf, fmt, arg);
+ return write(fd, buf, strlen(buf));
+}
+
+int
+vprint(char *fmt, va_list arg)
+{
+ return vfprint(1, fmt, arg);
+}
+
+int
+fprint(int fd, char *fmt, ...)
+{
+ int n;
+ va_list arg;
+
+ va_start(arg, fmt);
+ n = vfprint(fd, fmt, arg);
+ va_end(arg);
+ return n;
+}
+
+int
+print(char *fmt, ...)
+{
+ int n;
+ va_list arg;
+
+ va_start(arg, fmt);
+ n = vprint(fmt, arg);
+ va_end(arg);
+ return n;
+}
+
+char*
+strecpy(char *dst, char *edst, char *src)
+{
+ *printstr(dst, edst, src, 0) = 0;
+ return dst;
+}
--- /dev/null
+#include "taskimpl.h"
+
+/*
+ * locking
+ */
+static int
+_qlock(QLock *l, int block)
+{
+ if(l->owner == nil){
+ l->owner = taskrunning;
+ return 1;
+ }
+ if(!block)
+ return 0;
+ addtask(&l->waiting, taskrunning);
+ taskstate("qlock");
+ taskswitch();
+ if(l->owner != taskrunning){
+ fprint(2, "qlock: owner=%p self=%p oops\n", l->owner, taskrunning);
+ abort();
+ }
+ return 1;
+}
+
+void
+qlock(QLock *l)
+{
+ _qlock(l, 1);
+}
+
+int
+canqlock(QLock *l)
+{
+ return _qlock(l, 0);
+}
+
+void
+qunlock(QLock *l)
+{
+ Task *ready;
+
+ if(l->owner == 0){
+ fprint(2, "qunlock: owner=0\n");
+ abort();
+ }
+ if((l->owner = ready = l->waiting.head) != nil){
+ deltask(&l->waiting, ready);
+ taskready(ready);
+ }
+}
+
+static int
+_rlock(RWLock *l, int block)
+{
+ if(l->writer == nil && l->wwaiting.head == nil){
+ l->readers++;
+ return 1;
+ }
+ if(!block)
+ return 0;
+ addtask(&l->rwaiting, taskrunning);
+ taskstate("rlock");
+ taskswitch();
+ return 1;
+}
+
+void
+rlock(RWLock *l)
+{
+ _rlock(l, 1);
+}
+
+int
+canrlock(RWLock *l)
+{
+ return _rlock(l, 0);
+}
+
+static int
+_wlock(RWLock *l, int block)
+{
+ if(l->writer == nil && l->readers == 0){
+ l->writer = taskrunning;
+ return 1;
+ }
+ if(!block)
+ return 0;
+ addtask(&l->wwaiting, taskrunning);
+ taskstate("wlock");
+ taskswitch();
+ return 1;
+}
+
+void
+wlock(RWLock *l)
+{
+ _wlock(l, 1);
+}
+
+int
+canwlock(RWLock *l)
+{
+ return _wlock(l, 0);
+}
+
+void
+runlock(RWLock *l)
+{
+ Task *t;
+
+ if(--l->readers == 0 && (t = l->wwaiting.head) != nil){
+ deltask(&l->wwaiting, t);
+ l->writer = t;
+ taskready(t);
+ }
+}
+
+void
+wunlock(RWLock *l)
+{
+ Task *t;
+
+ if(l->writer == nil){
+ fprint(2, "wunlock: not locked\n");
+ abort();
+ }
+ l->writer = nil;
+ if(l->readers != 0){
+ fprint(2, "wunlock: readers\n");
+ abort();
+ }
+ while((t = l->rwaiting.head) != nil){
+ deltask(&l->rwaiting, t);
+ l->readers++;
+ taskready(t);
+ }
+ if(l->readers == 0 && (t = l->wwaiting.head) != nil){
+ deltask(&l->wwaiting, t);
+ l->writer = t;
+ taskready(t);
+ }
+}
--- /dev/null
+#include "taskimpl.h"
+
+/*
+ * sleep and wakeup
+ */
+void
+tasksleep(Rendez *r)
+{
+ addtask(&r->waiting, taskrunning);
+ if(r->l)
+ qunlock(r->l);
+ taskstate("sleep");
+ taskswitch();
+ if(r->l)
+ qlock(r->l);
+}
+
+static int
+_taskwakeup(Rendez *r, int all)
+{
+ int i;
+ Task *t;
+
+ for(i=0;; i++){
+ if(i==1 && !all)
+ break;
+ if((t = r->waiting.head) == nil)
+ break;
+ deltask(&r->waiting, t);
+ taskready(t);
+ }
+ return i;
+}
+
+int
+taskwakeup(Rendez *r)
+{
+ return _taskwakeup(r, 0);
+}
+
+int
+taskwakeupall(Rendez *r)
+{
+ return _taskwakeup(r, 1);
+}
--- /dev/null
+/* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */
+
+#include "taskimpl.h"
+#include <fcntl.h>
+#include <stdio.h>
+
+int taskdebuglevel;
+int taskcount;
+int tasknswitch;
+int taskexitval;
+Task *taskrunning;
+
+Context taskschedcontext;
+Tasklist taskrunqueue;
+
+Task **alltask;
+int nalltask;
+
+static char *argv0;
+static void contextswitch(Context *from, Context *to);
+
+static void
+taskdebug(char *fmt, ...)
+{
+ va_list arg;
+ char buf[128];
+ Task *t;
+ char *p;
+ static int fd = -1;
+
+return;
+ va_start(arg, fmt);
+ vfprint(1, fmt, arg);
+ va_end(arg);
+return;
+
+ if(fd < 0){
+ p = strrchr(argv0, '/');
+ if(p)
+ p++;
+ else
+ p = argv0;
+ snprint(buf, sizeof buf, "/tmp/%s.tlog", p);
+ if((fd = open(buf, O_CREAT|O_WRONLY, 0666)) < 0)
+ fd = open("/dev/null", O_WRONLY);
+ }
+
+ va_start(arg, fmt);
+ vsnprint(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ t = taskrunning;
+ if(t)
+ fprint(fd, "%d.%d: %s\n", getpid(), t->id, buf);
+ else
+ fprint(fd, "%d._: %s\n", getpid(), buf);
+}
+
+static void
+taskstart(uint y, uint x)
+{
+ Task *t;
+ ulong z;
+
+ z = x<<16; /* hide undefined 32-bit shift from 32-bit compilers */
+ z <<= 16;
+ z |= y;
+ t = (Task*)z;
+
+//print("taskstart %p\n", t);
+ t->startfn(t->startarg);
+//print("taskexits %p\n", t);
+ taskexit(0);
+//print("not reacehd\n");
+}
+
+static int taskidgen;
+
+static Task*
+taskalloc(void (*fn)(void*), void *arg, uint stack)
+{
+ Task *t;
+ sigset_t zero;
+ uint x, y;
+ ulong z;
+
+ /* allocate the task and stack together */
+ t = malloc(sizeof *t+stack);
+ if(t == nil){
+ fprint(2, "taskalloc malloc: %r\n");
+ abort();
+ }
+ memset(t, 0, sizeof *t);
+ t->stk = (uchar*)(t+1);
+ t->stksize = stack;
+ t->id = ++taskidgen;
+ t->startfn = fn;
+ t->startarg = arg;
+
+#ifdef EMACS
+ init_emacs_lisp_context (t->id == 1, &t->context.ec);
+#endif
+
+ /* do a reasonable initialization */
+ memset(&t->context.uc, 0, sizeof t->context.uc);
+ sigemptyset(&zero);
+ sigprocmask(SIG_BLOCK, &zero, &t->context.uc.uc_sigmask);
+
+ /* must initialize with current context */
+ if(getcontext(&t->context.uc) < 0){
+ fprint(2, "getcontext: %r\n");
+ abort();
+ }
+
+ /* call makecontext to do the real work. */
+ /* leave a few words open on both ends */
+ t->context.uc.uc_stack.ss_sp = t->stk+8;
+ t->context.uc.uc_stack.ss_size = t->stksize-64;
+#if defined(__sun__) && !defined(__MAKECONTEXT_V2_SOURCE) /* sigh */
+#warning "doing sun thing"
+ /* can avoid this with __MAKECONTEXT_V2_SOURCE but only on SunOS 5.9 */
+ t->context.uc.uc_stack.ss_sp =
+ (char*)t->context.uc.uc_stack.ss_sp
+ +t->context.uc.uc_stack.ss_size;
+#endif
+ /*
+ * All this magic is because you have to pass makecontext a
+ * function that takes some number of word-sized variables,
+ * and on 64-bit machines pointers are bigger than words.
+ */
+//print("make %p\n", t);
+ z = (ulong)t;
+ y = z;
+ z >>= 16; /* hide undefined 32-bit shift from 32-bit compilers */
+ x = z>>16;
+ makecontext(&t->context.uc, (void(*)())taskstart, 2, y, x);
+
+ return t;
+}
+
+int
+taskcreate(void (*fn)(void*), void *arg, uint stack)
+{
+ int id;
+ Task *t;
+
+ t = taskalloc(fn, arg, stack);
+ taskcount++;
+ id = t->id;
+ if(nalltask%64 == 0){
+ alltask = realloc(alltask, (nalltask+64)*sizeof(alltask[0]));
+ if(alltask == nil){
+ fprint(2, "out of memory\n");
+ abort();
+ }
+ }
+ t->alltaskslot = nalltask;
+ alltask[nalltask++] = t;
+ taskready(t);
+ return id;
+}
+
+void
+tasksystem(void)
+{
+ if(!taskrunning->system){
+ taskrunning->system = 1;
+ --taskcount;
+ }
+}
+
+void
+taskswitch(void)
+{
+ needstack(0);
+ contextswitch(&taskrunning->context, &taskschedcontext);
+}
+
+void
+taskready(Task *t)
+{
+ t->ready = 1;
+ addtask(&taskrunqueue, t);
+}
+
+int
+taskyield(void)
+{
+ int n;
+
+ n = tasknswitch;
+ taskready(taskrunning);
+ taskstate("yield");
+ taskswitch();
+ return tasknswitch - n - 1;
+}
+
+int
+anyready(void)
+{
+ return taskrunqueue.head != nil;
+}
+
+void
+taskexitall(int val)
+{
+ exit(val);
+}
+
+void
+taskexit(int val)
+{
+ taskexitval = val;
+ taskrunning->exiting = 1;
+ taskswitch();
+}
+
+static void
+contextswitch(Context *from, Context *to)
+{
+#ifdef EMACS
+ switch_emacs_lisp_context (&from->ec, &to->ec);
+#endif
+ if(swapcontext(&from->uc, &to->uc) < 0){
+ fprint(2, "swapcontext failed: %r\n");
+ assert(0);
+ }
+}
+
+static void
+taskscheduler(void)
+{
+ int i;
+ Task *t;
+
+ taskdebug("scheduler enter");
+ for(;;){
+ if(taskcount == 0)
+ exit(taskexitval);
+ t = taskrunqueue.head;
+ if(t == nil){
+ fprint(2, "no runnable tasks! %d tasks stalled\n", taskcount);
+ exit(1);
+ }
+ deltask(&taskrunqueue, t);
+ t->ready = 0;
+ taskrunning = t;
+ tasknswitch++;
+ taskdebug("run %d (%s)", t->id, t->name);
+ contextswitch(&taskschedcontext, &t->context);
+//print("back in scheduler\n");
+ taskrunning = nil;
+ if(t->exiting){
+ if(!t->system)
+ taskcount--;
+ i = t->alltaskslot;
+ alltask[i] = alltask[--nalltask];
+ alltask[i]->alltaskslot = i;
+ free(t);
+ }
+ }
+}
+
+void**
+taskdata(void)
+{
+ return &taskrunning->udata;
+}
+
+/*
+ * debugging
+ */
+void
+taskname(char *fmt, ...)
+{
+ va_list arg;
+ Task *t;
+
+ t = taskrunning;
+ va_start(arg, fmt);
+ vsnprint(t->name, sizeof t->name, fmt, arg);
+ va_end(arg);
+}
+
+char*
+taskgetname(void)
+{
+ return taskrunning->name;
+}
+
+void
+taskstate(char *fmt, ...)
+{
+ va_list arg;
+ Task *t;
+
+ t = taskrunning;
+ va_start(arg, fmt);
+ vsnprint(t->state, sizeof t->name, fmt, arg);
+ va_end(arg);
+}
+
+char*
+taskgetstate(void)
+{
+ return taskrunning->state;
+}
+
+void
+needstack(int n)
+{
+ Task *t;
+
+ t = taskrunning;
+
+ if((char*)&t <= (char*)t->stk
+ || (char*)&t - (char*)t->stk < 256+n){
+ fprint(2, "task stack overflow: &t=%p tstk=%p n=%d\n", &t, t->stk, 256+n);
+ abort();
+ }
+}
+
+static void
+taskinfo(int s)
+{
+ int i;
+ Task *t;
+ char *extra;
+
+ fprint(2, "task list:\n");
+ for(i=0; i<nalltask; i++){
+ t = alltask[i];
+ if(t == taskrunning)
+ extra = " (running)";
+ else if(t->ready)
+ extra = " (ready)";
+ else
+ extra = "";
+ fprint(2, "%6d%c %-20s %s%s\n",
+ t->id, t->system ? 's' : ' ',
+ t->name, t->state, extra);
+ }
+}
+
+/*
+ * startup
+ */
+
+static int taskargc;
+static char **taskargv;
+int mainstacksize;
+
+static void
+taskmainstart(void *v)
+{
+ taskname("taskmain");
+ taskmain(taskargc, taskargv);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sigaction sa, osa;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_handler = taskinfo;
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGQUIT, &sa, &osa);
+
+#ifdef SIGINFO
+ sigaction(SIGINFO, &sa, &osa);
+#endif
+
+ argv0 = argv[0];
+ taskargc = argc;
+ taskargv = argv;
+
+ if(mainstacksize == 0)
+ mainstacksize = 256*1024;
+#ifdef EMACS
+ init_emacs_main_task ();
+ mainstacksize = 0x400000;
+#endif
+ taskcreate(taskmainstart, nil, mainstacksize);
+ taskscheduler();
+ fprint(2, "taskscheduler returned in main!\n");
+ abort();
+ return 0;
+}
+
+/*
+ * hooray for linked lists
+ */
+void
+addtask(Tasklist *l, Task *t)
+{
+ if(l->tail){
+ l->tail->next = t;
+ t->prev = l->tail;
+ }else{
+ l->head = t;
+ t->prev = nil;
+ }
+ l->tail = t;
+ t->next = nil;
+}
+
+void
+deltask(Tasklist *l, Task *t)
+{
+ if(t->prev)
+ t->prev->next = t->next;
+ else
+ l->head = t->next;
+ if(t->next)
+ t->next->prev = t->prev;
+ else
+ l->tail = t->prev;
+}
+
+unsigned int
+taskid(void)
+{
+ return taskrunning->id;
+}
--- /dev/null
+/* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */
+
+#ifndef _TASK_H_
+#define _TASK_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <inttypes.h>
+
+/*
+ * basic procs and threads
+ */
+
+typedef struct Task Task;
+typedef struct Tasklist Tasklist;
+
+int anyready(void);
+int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize);
+void taskexit(int);
+void taskexitall(int);
+void taskmain(int argc, char *argv[]);
+int taskyield(void);
+void** taskdata(void);
+void needstack(int);
+void taskname(char*, ...);
+void taskstate(char*, ...);
+char* taskgetname(void);
+char* taskgetstate(void);
+void tasksystem(void);
+unsigned int taskdelay(unsigned int);
+unsigned int taskid(void);
+
+struct Tasklist /* used internally */
+{
+ Task *head;
+ Task *tail;
+};
+
+/*
+ * queuing locks
+ */
+typedef struct QLock QLock;
+struct QLock
+{
+ Task *owner;
+ Tasklist waiting;
+};
+
+void qlock(QLock*);
+int canqlock(QLock*);
+void qunlock(QLock*);
+
+/*
+ * reader-writer locks
+ */
+typedef struct RWLock RWLock;
+struct RWLock
+{
+ int readers;
+ Task *writer;
+ Tasklist rwaiting;
+ Tasklist wwaiting;
+};
+
+void rlock(RWLock*);
+int canrlock(RWLock*);
+void runlock(RWLock*);
+
+void wlock(RWLock*);
+int canwlock(RWLock*);
+void wunlock(RWLock*);
+
+/*
+ * sleep and wakeup (condition variables)
+ */
+typedef struct Rendez Rendez;
+
+struct Rendez
+{
+ QLock *l;
+ Tasklist waiting;
+};
+
+void tasksleep(Rendez*);
+int taskwakeup(Rendez*);
+int taskwakeupall(Rendez*);
+
+/*
+ * channel communication
+ */
+typedef struct Alt Alt;
+typedef struct Altarray Altarray;
+typedef struct Channel Channel;
+
+enum
+{
+ CHANEND,
+ CHANSND,
+ CHANRCV,
+ CHANNOP,
+ CHANNOBLK,
+};
+
+struct Alt
+{
+ Channel *c;
+ void *v;
+ unsigned int op;
+ Task *task;
+ Alt *xalt;
+};
+
+struct Altarray
+{
+ Alt **a;
+ unsigned int n;
+ unsigned int m;
+};
+
+struct Channel
+{
+ unsigned int bufsize;
+ unsigned int elemsize;
+ unsigned char *buf;
+ unsigned int nbuf;
+ unsigned int off;
+ Altarray asend;
+ Altarray arecv;
+ char *name;
+};
+
+int chanalt(Alt *alts);
+Channel* chancreate(int elemsize, int elemcnt);
+void chanfree(Channel *c);
+int chaninit(Channel *c, int elemsize, int elemcnt);
+int channbrecv(Channel *c, void *v);
+void* channbrecvp(Channel *c);
+unsigned long channbrecvul(Channel *c);
+int channbsend(Channel *c, void *v);
+int channbsendp(Channel *c, void *v);
+int channbsendul(Channel *c, unsigned long v);
+int chanrecv(Channel *c, void *v);
+void* chanrecvp(Channel *c);
+unsigned long chanrecvul(Channel *c);
+int chansend(Channel *c, void *v);
+int chansendp(Channel *c, void *v);
+int chansendul(Channel *c, unsigned long v);
+
+/*
+ * Threaded I/O.
+ */
+int fdread(int, void*, int);
+int fdread1(int, void*, int); /* always uses fdwait */
+int fdwrite(int, void*, int);
+void fdwait(int, int);
+int fdnoblock(int);
+
+void fdtask(void*);
+
+/*
+ * Network dialing - sets non-blocking automatically
+ */
+enum
+{
+ UDP = 0,
+ TCP = 1,
+};
+
+int netannounce(int, char*, int);
+int netaccept(int, char*, int*);
+int netdial(int, char*, int);
+int netlookup(char*, uint32_t*); /* blocks entire program! */
+int netdial(int, char*, int);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--- /dev/null
+/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */
+
+#if defined(__sun__)
+# define __EXTENSIONS__ 1 /* SunOS */
+# if defined(__SunOS5_6__) || defined(__SunOS5_7__) || defined(__SunOS5_8__)
+ /* NOT USING #define __MAKECONTEXT_V2_SOURCE 1 / * SunOS */
+# else
+# define __MAKECONTEXT_V2_SOURCE 1
+# endif
+#endif
+
+#define USE_UCONTEXT 1
+
+#if defined(__OpenBSD__) || defined(__mips__)
+#undef USE_UCONTEXT
+#define USE_UCONTEXT 0
+#endif
+
+#if defined(__APPLE__)
+#include <AvailabilityMacros.h>
+#if defined(MAC_OS_X_VERSION_10_5)
+#undef USE_UCONTEXT
+#define USE_UCONTEXT 0
+#endif
+#endif
+
+#ifdef EMACS
+#include <config.h>
+#include "lisp.h"
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sched.h>
+#include <signal.h>
+#if USE_UCONTEXT
+#include <ucontext.h>
+#endif
+#include <sys/utsname.h>
+#include <inttypes.h>
+#include "task.h"
+
+#define nil ((void*)0)
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+
+#define ulong task_ulong
+#define uint task_uint
+#define uchar task_uchar
+#define ushort task_ushort
+#define uvlong task_uvlong
+#define vlong task_vlong
+
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned long long uvlong;
+typedef long long vlong;
+
+#define print task_print
+#define fprint task_fprint
+#define snprint task_snprint
+#define seprint task_seprint
+#define vprint task_vprint
+#define vfprint task_vfprint
+#define vsnprint task_vsnprint
+#define vseprint task_vseprint
+#define strecpy task_strecpy
+
+int print(char*, ...);
+int fprint(int, char*, ...);
+char *snprint(char*, uint, char*, ...);
+char *seprint(char*, char*, char*, ...);
+int vprint(char*, va_list);
+int vfprint(int, char*, va_list);
+char *vsnprint(char*, uint, char*, va_list);
+char *vseprint(char*, char*, char*, va_list);
+char *strecpy(char*, char*, char*);
+
+#if defined(__FreeBSD__) && __FreeBSD__ < 5
+extern int getmcontext(mcontext_t*);
+extern void setmcontext(const mcontext_t*);
+#define setcontext(u) setmcontext(&(u)->uc_mcontext)
+#define getcontext(u) getmcontext(&(u)->uc_mcontext)
+extern int swapcontext(ucontext_t*, const ucontext_t*);
+extern void makecontext(ucontext_t*, void(*)(), int, ...);
+#endif
+
+#if defined(__APPLE__)
+# define mcontext libthread_mcontext
+# define mcontext_t libthread_mcontext_t
+# define ucontext libthread_ucontext
+# define ucontext_t libthread_ucontext_t
+# if defined(__i386__)
+# include "386-ucontext.h"
+# elif defined(__x86_64__)
+# include "amd64-ucontext.h"
+# else
+# include "power-ucontext.h"
+# endif
+#endif
+
+#if defined(__OpenBSD__)
+# define mcontext libthread_mcontext
+# define mcontext_t libthread_mcontext_t
+# define ucontext libthread_ucontext
+# define ucontext_t libthread_ucontext_t
+# if defined __i386__
+# include "386-ucontext.h"
+# else
+# include "power-ucontext.h"
+# endif
+extern pid_t rfork_thread(int, void*, int(*)(void*), void*);
+#endif
+
+#if 0 && defined(__sun__)
+# define mcontext libthread_mcontext
+# define mcontext_t libthread_mcontext_t
+# define ucontext libthread_ucontext
+# define ucontext_t libthread_ucontext_t
+# include "sparc-ucontext.h"
+#endif
+
+#if defined(__arm__)
+int getmcontext(mcontext_t*);
+void setmcontext(const mcontext_t*);
+#define setcontext(u) setmcontext(&(u)->uc_mcontext)
+#define getcontext(u) getmcontext(&(u)->uc_mcontext)
+#endif
+
+#if defined(__mips__)
+#include "mips-ucontext.h"
+int getmcontext(mcontext_t*);
+void setmcontext(const mcontext_t*);
+#define setcontext(u) setmcontext(&(u)->uc_mcontext)
+#define getcontext(u) getmcontext(&(u)->uc_mcontext)
+#endif
+
+typedef struct Context Context;
+
+enum
+{
+ STACK = 8192
+};
+
+struct Context
+{
+ ucontext_t uc;
+#ifdef EMACS
+ struct emacs_lisp_task_context ec;
+#endif
+};
+
+struct Task
+{
+ char name[256]; // offset known to acid
+ char state[256];
+ Task *next;
+ Task *prev;
+ Task *allnext;
+ Task *allprev;
+ Context context;
+ uvlong alarmtime;
+ uint id;
+ uchar *stk;
+ uint stksize;
+ int exiting;
+ int alltaskslot;
+ int system;
+ int ready;
+ void (*startfn)(void*);
+ void *startarg;
+ void *udata;
+};
+
+void taskready(Task*);
+void taskswitch(void);
+
+void addtask(Tasklist*, Task*);
+void deltask(Tasklist*, Task*);
+
+extern Task *taskrunning;
+extern int taskcount;
+
+
+#ifdef EMACS
+#define malloc unexec_malloc
+#define realloc unexec_realloc
+#define free unexec_free
+void *unexec_malloc (size_t size);
+void *unexec_realloc (void *old_ptr, size_t new_size);
+void unexec_free (void *ptr);
+#endif
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <task.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+enum
+{
+ STACK = 32768
+};
+
+char *server;
+int port;
+void proxytask(void*);
+void rwtask(void*);
+
+int*
+mkfd2(int fd1, int fd2)
+{
+ int *a;
+
+ a = malloc(2*sizeof a[0]);
+ if(a == 0){
+ fprintf(stderr, "out of memory\n");
+ abort();
+ }
+ a[0] = fd1;
+ a[1] = fd2;
+ return a;
+}
+
+void
+taskmain(int argc, char **argv)
+{
+ int cfd, fd;
+ int rport;
+ char remote[16];
+
+ if(argc != 4){
+ fprintf(stderr, "usage: tcpproxy localport server remoteport\n");
+ taskexitall(1);
+ }
+ server = argv[2];
+ port = atoi(argv[3]);
+
+ if((fd = netannounce(TCP, 0, atoi(argv[1]))) < 0){
+ fprintf(stderr, "cannot announce on tcp port %d: %s\n", atoi(argv[1]), strerror(errno));
+ taskexitall(1);
+ }
+ fdnoblock(fd);
+ while((cfd = netaccept(fd, remote, &rport)) >= 0){
+ fprintf(stderr, "connection from %s:%d\n", remote, rport);
+ taskcreate(proxytask, (void*)cfd, STACK);
+ }
+}
+
+void
+proxytask(void *v)
+{
+ int fd, remotefd;
+
+ fd = (int)v;
+ if((remotefd = netdial(TCP, server, port)) < 0){
+ close(fd);
+ return;
+ }
+
+ fprintf(stderr, "connected to %s:%d\n", server, port);
+
+ taskcreate(rwtask, mkfd2(fd, remotefd), STACK);
+ taskcreate(rwtask, mkfd2(remotefd, fd), STACK);
+}
+
+void
+rwtask(void *v)
+{
+ int *a, rfd, wfd, n;
+ char buf[2048];
+
+ a = v;
+ rfd = a[0];
+ wfd = a[1];
+ free(a);
+
+ while((n = fdread(rfd, buf, sizeof buf)) > 0)
+ fdwrite(wfd, buf, n);
+ shutdown(wfd, SHUT_WR);
+ close(rfd);
+}
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <task.h>
+
+enum { STACK = 32768 };
+
+Channel *c;
+
+void
+delaytask(void *v)
+{
+ taskdelay((int)v);
+ printf("awake after %d ms\n", (int)v);
+ chansendul(c, 0);
+}
+
+void
+taskmain(int argc, char **argv)
+{
+ int i, n;
+
+ c = chancreate(sizeof(unsigned long), 0);
+
+ n = 0;
+ for(i=1; i<argc; i++){
+ n++;
+ printf("x");
+ taskcreate(delaytask, (void*)atoi(argv[i]), STACK);
+ }
+
+ /* wait for n tasks to finish */
+ for(i=0; i<n; i++){
+ printf("y");
+ chanrecvul(c);
+ }
+ taskexitall(0);
+}
--- /dev/null
+#include <task.h>
+
+void
+taskmain(int argc, char *argv[])
+{
+ taskdelay(1000);
+}
(cdr (cdr form))
(byte-optimize-body (cdr form) for-effect)))))
+ ((eq fn 'select)
+ (cons fn
+ (mapcar
+ (lambda (alt)
+ (cons
+ (pcase (car alt)
+ (`(receive ,chan . ,rest)
+ `(receive ,(byte-optimize-form chan) ,@rest))
+ (`(send ,chan ,val)
+ `(send ,(byte-optimize-form chan)
+ ,(byte-optimize-form val)))
+ ('default 'default)
+ (invalid
+ (byte-compile-report-error
+ (format-message "invalid `select' alternative %S"
+ invalid))))
+ (byte-optimize-body (cdr alt) for-effect)))
+ (cdr form))))
+
((eq fn 'ignore)
;; Don't treat the args to `ignore' as being
;; computed for effect. We want to avoid the warnings
;; seem worth the trouble.
(dolist (form forms) (cconv-analyze-form form nil)))
+ (`(select . ,alternatives)
+ (dolist (alternative alternatives)
+ (let ((env env)
+ (varstruct nil))
+ (pcase (car alternative)
+ (`(receive ,chan)
+ (cconv-analyze-form chan env))
+ (`(receive ,chan ,var)
+ (cconv-analyze-form chan env)
+ (setq varstruct (list var nil nil nil nil))
+ (push varstruct env))
+ (`(send ,chan ,val)
+ (cconv-analyze-form chan env)
+ (cconv-analyze-form val env))
+ ('default)
+ (invalid
+ (byte-compile-report-error
+ (format-message "invalid `select' alternative %S" invalid))))
+ (dolist (form (cdr alternative))
+ (cconv-analyze-form form env))
+ (when varstruct
+ (cconv--analyze-use varstruct form "variable")))))
+
;; `declare' should now be macro-expanded away (and if they're not, we're
;; in trouble because they *can* contain code nowadays).
;; (`(declare . ,_) nil) ;The args don't contain code.
buffer.o filelock.o insdel.o marker.o \
minibuf.o fileio.o dired.o \
cmds.o casetab.o casefiddle.o indent.o search.o regex.o undo.o \
+ coroutine.o \
alloc.o data.o doc.o editfns.o callint.o \
eval.o floatfns.o fns.o font.o print.o lread.o $(MODULES_OBJ) \
syntax.o $(UNEXEC_OBJ) bytecode.o \
$(LIBEGNU_ARCHIVE): $(config_h)
$(MAKE) -C $(lib) all
+libtask = \
+ $(lib)/libtask/asm.o \
+ $(lib)/libtask/channel.o \
+ $(lib)/libtask/context.o \
+ $(lib)/libtask/print.o \
+ $(lib)/libtask/qlock.o \
+ $(lib)/libtask/rendez.o \
+ $(lib)/libtask/task.o
+
+$(libtask): $(lib)/libtask/*.h
+
+$(lib)/libtask/%.o: $(lib)/libtask/%.c $(lib)/libtask/*.h lisp.h config.h globals.h
+ $(AM_V_CC)$(CC) -c -DEMACS -I. -I$(lib) $(CPPFLAGS) $(CFLAGS) -o $@ $<
+
## We have to create $(etc) here because init_cmdargs tests its
## existence when setting Vinstallation_directory (FIXME?).
## This goes on to affect various things, and the emacs binary fails
## to start if Vinstallation_directory has the wrong value.
temacs$(EXEEXT): $(LIBXMENU) $(ALLOBJS) \
- $(LIBEGNU_ARCHIVE) $(EMACSRES) ${charsets} ${charscript}
+ $(LIBEGNU_ARCHIVE) $(EMACSRES) ${charsets} ${charscript} \
+ $(libtask)
$(AM_V_CCLD)$(CC) $(ALL_CFLAGS) $(TEMACS_LDFLAGS) $(LDFLAGS) \
- -o temacs $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(W32_RES_LINK) $(LIBES)
+ -o temacs $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(W32_RES_LINK) $(libtask) $(LIBES)
$(MKDIR_P) $(etc)
ifneq ($(CANNOT_DUMP),yes)
ifneq ($(PAXCTL_notdumped),)
#include <verify.h>
#include <execinfo.h> /* For backtrace. */
+#include "libtask/task.h"
+
#ifdef HAVE_LINUX_SYSINFO
#include <sys/sysinfo.h>
#endif
free_misc (marker);
}
+DEFUN ("make-channel", Fmake_channel, Smake_channel, 0, 1, 0,
+ doc: /* Return a newly allocated communication channel.
+BUFFER-SIZE, if provided, must be a non-negative integer specifying the
+number of object that can be buffered in the channel. If BUFFER-SIZE
+is not provided, nil, or 0, the channel is unbuffered. */)
+ (Lisp_Object buffer_size)
+{
+ int buf_size;
+ if (NILP (buffer_size))
+ buf_size = 0;
+ else
+ {
+ CHECK_RANGED_INTEGER (buffer_size, 0, INT_MAX);
+ buf_size = XFASTINT (buffer_size);
+ }
+
+ Lisp_Object obj = allocate_misc (Lisp_Misc_Channel);
+ struct Lisp_Channel *chan = XCHANNEL (obj);
+ chan->channel = chancreate (sizeof (Lisp_Object), buf_size);
+ return obj;
+}
+
\f
/* Return a newly created vector or string with specified arguments as
elements. If all the arguments are characters that can fit
mark_object (XFINALIZER (obj)->function);
break;
+ case Lisp_Misc_Channel:
+ {
+ XMISCANY (obj)->gcmarkbit = 1;
+ Channel *channel = XCHANNEL (obj)->channel;
+ eassert (channel->elemsize == sizeof (Lisp_Object));
+ Lisp_Object *buffer_contents = (Lisp_Object *) channel->buf;
+ for (unsigned int i = 0; i < channel->nbuf; ++i)
+ mark_object (buffer_contents[channel->off + i]);
+ }
+ break;
+
#ifdef HAVE_MODULES
case Lisp_Misc_User_Ptr:
XMISCANY (obj)->gcmarkbit = true;
unchain_marker (&mblk->markers[i].m.u_marker);
else if (mblk->markers[i].m.u_any.type == Lisp_Misc_Finalizer)
unchain_finalizer (&mblk->markers[i].m.u_finalizer);
+ else if (mblk->markers[i].m.u_any.type == Lisp_Misc_Channel)
+ {
+ Channel *channel = mblk->markers[i].m.u_channel.channel;
+ eassert (channel != NULL);
+ eassert (channel->nbuf == 0);
+ eassert (channel->asend.n == 0);
+ eassert (channel->arecv.n == 0);
+ chanfree (channel);
+ }
#ifdef HAVE_MODULES
else if (mblk->markers[i].m.u_any.type == Lisp_Misc_User_Ptr)
{
defsubr (&Smake_symbol);
defsubr (&Smake_marker);
defsubr (&Smake_finalizer);
+ defsubr (&Smake_channel);
defsubr (&Spurecopy);
defsubr (&Sgarbage_collect);
defsubr (&Smemory_limit);
--- /dev/null
+/* Coroutine support for GNU Emacs Lisp.
+
+Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
+
+
+#include <config.h>
+
+#include <limits.h>
+#include <stdalign.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "lisp.h"
+
+#include "verify.h"
+#include "sysselect.h"
+#include "libtask/task.h"
+
+#if __has_attribute (cleanup)
+enum { coroutine_has_cleanup = true };
+#else
+enum { coroutine_has_cleanup = false };
+#endif
+
+#define COROUTINE_HANDLE_NONLOCAL_EXIT(retval) \
+ COROUTINE_SETJMP (CONDITION_CASE, coroutine_handle_signal, retval); \
+ COROUTINE_SETJMP (CATCHER_ALL, coroutine_handle_throw, retval)
+
+#define COROUTINE_SETJMP(handlertype, handlerfunc, retval) \
+ COROUTINE_SETJMP_1 (handlertype, handlerfunc, retval, \
+ internal_handler_##handlertype, \
+ internal_cleanup_##handlertype)
+
+#define COROUTINE_SETJMP_1(handlertype, handlerfunc, retval, c, dummy) \
+ struct handler *c = push_handler_nosignal (Qt, handlertype); \
+ if (!c) \
+ { \
+ coroutine_out_of_memory (); \
+ return retval; \
+ } \
+ verify (coroutine_has_cleanup); \
+ int dummy __attribute__ ((cleanup (coroutine_reset_handlerlist))); \
+ if (sys_setjmp (c->jmp)) \
+ { \
+ (handlerfunc) (c->val); \
+ return retval; \
+ } \
+ do { } while (false)
+
+/* Must be called after setting up a handler immediately before
+ returning from the function. See the comments in lisp.h and the
+ code in eval.c for details. The macros below arrange for this
+ function to be called automatically. DUMMY is ignored. */
+static void
+coroutine_reset_handlerlist (const int *dummy)
+{
+ handlerlist = handlerlist->next;
+}
+
+/* Called on `signal'. ERR is a pair (SYMBOL . DATA). */
+static void
+coroutine_handle_signal (Lisp_Object err)
+{
+ print_error_message (err, Qnil, "signal in coroutine caught: ", Qnil);
+ taskexit (1);
+}
+
+/* Called on `throw'. TAG_VAL is a pair (TAG . VALUE). */
+static void
+coroutine_handle_throw (Lisp_Object tag_val)
+{
+ print_error_message (tag_val, Qnil, "throw in coroutine caught: ", Qnil);
+ taskexit (1);
+}
+
+static void
+coroutine_out_of_memory (void)
+{
+ fputs ("OOM in coroutine\n", stderr);
+ taskexit (1);
+}
+
+struct task_arguments
+{
+ Lisp_Object function;
+};
+
+static void
+task_function (void *arg)
+{
+ struct task_arguments args = *(struct task_arguments *) arg;
+ xfree (arg);
+ COROUTINE_HANDLE_NONLOCAL_EXIT ();
+ ptrdiff_t count = SPECPDL_INDEX ();
+ specbind (Qinternal_interpreter_environment, Vinternal_interpreter_environment);
+ call0 (args.function);
+ unbind_to (count, Qnil);
+}
+
+DEFUN ("start-coroutine", Fstart_coroutine, Sstart_coroutine, 1, 1, 0,
+ doc: /* Start FUNCTION in a background coroutine. */)
+ (Lisp_Object function)
+{
+ // args will be freed in task_function.
+ struct task_arguments *args = xmalloc (sizeof *args);
+ args->function = function;
+ taskcreate (task_function, args, 0x400000);
+ return Qnil;
+}
+
+static void
+CHECK_CHANNEL (Lisp_Object x)
+{
+ CHECK_TYPE (CHANNELP (x), Qchannelp, x);
+}
+
+DEFUN ("channelp", Fchannelp, Schannelp, 1, 1, 0,
+ doc: /* Return t if OBJECT is a communication channel, nil otherwise. */)
+ (Lisp_Object object)
+{
+ return CHANNELP (object) ? Qt : Qnil;
+}
+
+DEFUN ("receive-from-channel", Freceive_from_channel, Sreceive_from_channel,
+ 1, 1, 0,
+ doc: /* Receive an object from CHANNEL.
+Block the current coroutine until an object is available on CHANNEL,
+then return that object and remove it from CHANNEL.
+CHANNEL must be a communication channel created by `make-channel'. */)
+ (Lisp_Object channel)
+{
+ CHECK_CHANNEL (channel);
+ Lisp_Object result;
+ int status = chanrecv (XCHANNEL (channel)->channel, &result);
+ eassert (status == 1);
+ return result;
+}
+
+DEFUN ("try-receive-from-channel", Ftry_receive_from_channel, Stry_receive_from_channel,
+ 1, 1, 0,
+ doc: /* Attempt to receive an object from CHANNEL.
+If an object can be read from CHANNEL, return a singleton list containing
+the object and remove it from CHANNEL. Otherwise, return nil.
+CHANNEL must be a communication channel created by `make-channel'. */)
+ (Lisp_Object channel)
+{
+ CHECK_CHANNEL (channel);
+ Lisp_Object result;
+ int status = channbrecv (XCHANNEL (channel)->channel, &result);
+ return (status == 1) ? list1 (result) : Qnil;
+}
+
+DEFUN ("send-to-channel", Fsend_to_channel, Ssend_to_channel,
+ 2, 2, 0,
+ doc: /* Send an object to CHANNEL.
+Block the current coroutine until an object can be put into CHANNEL,
+then put VALUE into CHANNEL. CHANNEL must be a communication channel
+created by `make-channel'. */)
+ (Lisp_Object channel, Lisp_Object value)
+{
+ CHECK_CHANNEL (channel);
+ int status = chansend (XCHANNEL (channel)->channel, &value);
+ eassert (status == 1);
+ return Qnil;
+}
+
+DEFUN ("try-send-to-channel", Ftry_send_to_channel, Stry_send_to_channel,
+ 2, 2, 0,
+ doc: /* Attempt to send an object to CHANNEL.
+If an object can be sent to channel CHANNEL, put VALUE into CHANNEL
+and return t, otherwise return nil. CHANNEL must be a communication
+channel created by `make-channel'. */)
+ (Lisp_Object channel, Lisp_Object value)
+{
+ CHECK_CHANNEL (channel);
+ int status = channbsend (XCHANNEL (channel)->channel, &value);
+ return (status == 1) ? Qt : Qnil;
+}
+
+DEFUN ("select", Fselect, Sselect, 0, UNEVALLED, 0,
+ doc: /* Synchronously multiplex between ALTERNATIVES.
+ALTERNATIVES must be a list of communication alternatives.
+Each alternative must be of one of the four following forms:
+1. ((receive CHANNEL) BODY...)
+2. ((receive CHANNEL SYMBOL) BODY...)
+3. ((send CHANNEL VALUE) BODY...)
+4. (default BODY...)
+At most one `default' form may be present.
+
+`select' checks for each of the alternative whether the respective
+communication operation can progress. If there is at least one such
+operation, one of the available ones is randomly selected and the
+respective BODY forms are evaluated. If there is no such operation
+(all operations would block), `select' either blocks until at least
+one operation can make progress (if no `default' alternative is
+given), or evaluates the BODY of the `default' alternative.
+
+The first two forms attempt to receive a value from CHANNEL (which is
+evaluated), as if by `receive-from-channel'. If SYMBOL is given and
+not nil, it is interpreted as an unevaluated symbol and bound to the
+value that has been received from CHANNEL within BODY, otherwise the
+value from CHANNEL is ignored.
+
+The third form attempts to send VALUE to CHANNEL, as if by
+`send-to-channel'. Both VALUE and CHANNEL are evaluated.
+
+The value of the last form of the BODY of the alternative being chosen
+is returned. */)
+ (Lisp_Object alternatives)
+{
+ CHECK_LIST (alternatives);
+ Lisp_Object length = Flength (alternatives);
+
+ // libtask uses signed integers for counts, therefore we restrict
+ // the number of alternatives to INT_MAX. Because we allocate one
+ // more Alt object for the delimiter, actually use INT_MAX − 1.
+ CHECK_RANGED_INTEGER (length, 0, INT_MAX - 1);
+ int num_alts = XINT (length);
+
+ // Allocate array of alternatives given to chanalt below.
+ // alts[num_alts] is the delimiter (CHANEND or CHANNOBLK, depending
+ // on whether a default alternative is present).
+ Alt *alts = xmalloc ((num_alts + 1) * sizeof *alts);
+
+ // The objects in this structure correspond to VALUE, SYMBOL, and
+ // BODY in the docstring, possibly evaluated. The body for a
+ // default alternative, if present, is stored elsewhere.
+ struct {
+ Lisp_Object value;
+ Lisp_Object symbol;
+ Lisp_Object body;
+ } *comms = xmalloc (num_alts * sizeof *comms);
+
+ bool has_default = false;
+ Lisp_Object default_body;
+
+ // Fill in the contents of alts and comms.
+ int i = 0;
+ for (Lisp_Object rest = alternatives; ! NILP (rest); rest = CDR (rest))
+ {
+ eassert (i < num_alts);
+ CHECK_CONS (rest);
+ Lisp_Object alternative = XCAR (rest);
+ CHECK_CONS (alternative);
+ Lisp_Object comm = XCAR (alternative);
+ Lisp_Object body = XCDR (alternative);
+ CHECK_LIST (body);
+ if (EQ (comm, Qdefault))
+ {
+ if (has_default)
+ signal_error ("Cannot have two default alternatives", alternatives);
+ has_default = true;
+ default_body = body;
+ // As a default alternative is allowed anywhere, simply skip
+ // it here.
+ alts[i].op = CHANNOP;
+ }
+ else
+ {
+ CHECK_CONS (comm);
+ Lisp_Object operation = XCAR (comm);
+ Lisp_Object channel_arg = XCDR (comm);
+ CHECK_CONS (channel_arg);
+ Lisp_Object channel = eval_sub (XCAR (channel_arg));
+ CHECK_CHANNEL (channel);
+ alts[i].c = XCHANNEL (channel)->channel;
+ Lisp_Object rest = XCDR (channel_arg);
+ CHECK_LIST (rest);
+ if (CONSP (rest) && ! NILP (XCDR (rest)))
+ signal_error ("Communication specification must have at most three elements", comm);
+ if (EQ (operation, Qreceive))
+ {
+ alts[i].op = CHANRCV;
+ Lisp_Object var = NILP (rest) ? Qnil : XCAR (rest);
+ CHECK_SYMBOL (var);
+ // We don’t care whether VAR refers to a constant here,
+ // `let' below will do it.
+ comms[i].value = Qnil;
+ comms[i].symbol = var;
+ // If alts[i].v is NULL, chanalt will throw away the
+ // received value.
+ alts[i].v = NILP (var) ? NULL : &comms[i].value;
+ }
+ else if (EQ (operation, Qsend))
+ {
+ alts[i].op = CHANSND;
+ CHECK_CONS (rest);
+ comms[i].value = eval_sub (XCAR (rest));
+ alts[i].v = &comms[i].value;
+ }
+ else
+ wrong_choice (list2 (Qreceive, Qsend), operation);
+ comms[i].body = body;
+ }
+ i++;
+ }
+ eassert (i == num_alts);
+ alts[num_alts].op = has_default ? CHANNOBLK : CHANEND;
+
+ // Actually perform the select operation.
+ int choice = chanalt (alts);
+
+ if (choice == -1)
+ {
+ // Default alternative has been chosen.
+ eassert (has_default);
+ eassert (alts[num_alts].op == CHANNOBLK);
+ return Fprogn (default_body);
+ }
+ else
+ {
+ // Non-default alternative has been chosen.
+ eassert (choice >= 0 && choice < num_alts);
+ Lisp_Object body = comms[choice].body;
+ if (alts[choice].op == CHANRCV && ! NILP (comms[choice].symbol))
+ {
+ Lisp_Object symbol = comms[choice].symbol;
+ Lisp_Object value = comms[choice].value;
+ return Flet (Fcons (list1 (list2 (symbol, value)), body));
+ }
+ else
+ return Fprogn (body);
+ }
+}
+
+struct pselect_args {
+ int nfds;
+ fd_set *readfds;
+ fd_set *writefds;
+ fd_set *errorfds;
+ const struct timespec *timeout;
+ const sigset_t *sigmask;
+ Rendez rendez;
+ int result;
+};
+
+static void
+do_pselect (void *arg)
+{
+ struct pselect_args *args = arg;
+ args->result = pselect (args->nfds,
+ args->readfds, args->writefds, args->errorfds,
+ args->timeout, args->sigmask);
+ int woken = taskwakeup (&args->rendez);
+ eassert (woken == 1);
+}
+
+// Non-blocking variant of pselect. Uses a task to run in the
+// background.
+int
+pselect_noblock (int nfds,
+ fd_set *restrict readfds,
+ fd_set *restrict writefds,
+ fd_set *restrict errorfds,
+ const struct timespec *restrict timeout,
+ const sigset_t *restrict sigmask)
+{
+ struct pselect_args args = {
+ .nfds = nfds,
+ .readfds = readfds,
+ .writefds = writefds,
+ .errorfds = errorfds,
+ .timeout = timeout,
+ .sigmask = sigmask,
+ };
+ taskcreate (do_pselect, &args, 0x1000);
+ tasksleep (&args.rendez);
+ return args.result;
+}
+
+void
+syms_of_coroutine (void)
+{
+ DEFSYM (Qchannelp, "channelp");
+ DEFSYM (Qreceive, "receive");
+ DEFSYM (Qsend, "send");
+
+ defsubr (&Sstart_coroutine);
+ defsubr (&Sreceive_from_channel);
+ defsubr (&Stry_receive_from_channel);
+ defsubr (&Ssend_to_channel);
+ defsubr (&Stry_send_to_channel);
+ defsubr (&Sselect);
+}
#include "systime.h"
#include "puresize.h"
+#include "libtask/task.h"
+
#include "getpagesize.h"
#include "gnutls.h"
}
/* ARGSUSED */
-int
-main (int argc, char **argv)
+void
+taskmain (int argc, char **argv)
{
Lisp_Object dummy;
char stack_bottom_variable;
run_time_remap (argv[0]);
#endif
-/* If using unexmacosx.c (set by s/darwin.h), we must do this. */
-#ifdef DARWIN_OS
- if (!initialized)
- unexec_init_emacs_zone ();
-#endif
-
init_standard_fds ();
atexit (close_output_streams);
syms_of_ccl ();
syms_of_character ();
syms_of_cmds ();
+ syms_of_coroutine ();
syms_of_dired ();
syms_of_display ();
syms_of_doc ();
/* Enter editor command loop. This never returns. */
Frecursive_edit ();
- /* NOTREACHED */
- return 0;
}
\f
/* Sort the args so we can find the most important ones
\f
+/* Called by libtask before allocating the main task. Use this
+ function to initialize allocation. After this, alloc should be
+ usable. */
+void
+init_emacs_main_task (void)
+{
+ /* If using unexmacosx.c (set by s/darwin.h), we must do this. */
+#ifdef DARWIN_OS
+ if (!initialized)
+ unexec_init_emacs_zone ();
+#endif
+}
+
#ifndef CANNOT_DUMP
#include "unexec.h"
return (backtrace_p (pdl) ? backtrace_function (pdl) : Qnil);
}
+void
+init_emacs_lisp_context (bool main_task,
+ struct emacs_lisp_task_context *context)
+{
+ if (main_task)
+ {
+#define SET(var) do { context->var = (var); } while (false)
+ SET(handlerlist);
+ SET(handlerlist_sentinel);
+ SET(specpdl_size);
+ SET(specpdl);
+ SET(specpdl_ptr);
+ SET(lisp_eval_depth);
+ SET(Vinternal_interpreter_environment);
+#undef SET
+ }
+ else
+ {
+ context->handlerlist = context->handlerlist_sentinel.nextfree = &context->handlerlist_sentinel;
+ context->specpdl_size = 50;
+ context->specpdl = xmalloc ((1 + context->specpdl_size) * sizeof *context->specpdl);
+ ++context->specpdl;
+ context->specpdl_ptr = context->specpdl;
+ struct handler *prev = handlerlist;
+ handlerlist = context->handlerlist;
+ struct handler *c = push_handler_nosignal (Qunbound, CATCHER);
+ handlerlist = prev;
+ eassert (c == &context->handlerlist_sentinel);
+ context->handlerlist_sentinel.nextfree = NULL;
+ context->handlerlist_sentinel.next = NULL;
+ context->lisp_eval_depth = 0;
+ context->Vinternal_interpreter_environment = CONSP (Vinternal_interpreter_environment) ? list1 (Qt) : Qnil;
+ }
+}
+
+void
+switch_emacs_lisp_context (struct emacs_lisp_task_context *from,
+ const struct emacs_lisp_task_context *to)
+{
+#define SWAP(var) do { from->var = (var); (var) = to->var; } while (false)
+ SWAP(handlerlist);
+ SWAP(handlerlist_sentinel);
+ SWAP(specpdl_size);
+ SWAP(specpdl);
+ SWAP(specpdl_ptr);
+ SWAP(lisp_eval_depth);
+ SWAP(Vinternal_interpreter_environment);
+#undef SWAP
+}
+
void
syms_of_eval (void)
{
Lisp_Misc_Overlay,
Lisp_Misc_Save_Value,
Lisp_Misc_Finalizer,
+ Lisp_Misc_Channel,
#ifdef HAVE_MODULES
Lisp_Misc_User_Ptr,
#endif
Used during startup to detect startup of dumped Emacs. */
extern bool initialized;
+void init_emacs_main_task (void);
+
/* Defined in floatfns.c. */
extern double extract_float (Lisp_Object);
return XSAVE_VALUE (obj)->data[n].object;
}
+struct Lisp_Channel
+{
+ struct Lisp_Misc_Any base;
+ struct Channel* channel;
+};
+
#ifdef HAVE_MODULES
struct Lisp_User_Ptr
{
struct Lisp_Overlay u_overlay;
struct Lisp_Save_Value u_save_value;
struct Lisp_Finalizer u_finalizer;
+ struct Lisp_Channel u_channel;
#ifdef HAVE_MODULES
struct Lisp_User_Ptr u_user_ptr;
#endif
return & XMISC (a)->u_finalizer;
}
+INLINE bool
+CHANNELP (Lisp_Object a)
+{
+ return MISCP (a) && XMISCTYPE (a) == Lisp_Misc_Channel;
+}
+
+INLINE struct Lisp_Channel *
+XCHANNEL (Lisp_Object a)
+{
+ eassert (CHANNELP (a));
+ return & XMISC (a)->u_channel;
+}
+
#ifdef HAVE_MODULES
INLINE struct Lisp_User_Ptr *
XUSER_PTR (Lisp_Object a)
extern void init_coding_once (void);
extern void syms_of_coding (void);
+/* Defined in coroutine.c. */
+extern void syms_of_coroutine (void);
+
/* Defined in character.c. */
extern ptrdiff_t chars_in_text (const unsigned char *, ptrdiff_t);
extern ptrdiff_t multibyte_chars_in_text (const unsigned char *, ptrdiff_t);
extern bool let_shadows_buffer_binding_p (struct Lisp_Symbol *symbol);
extern bool let_shadows_global_binding_p (Lisp_Object symbol);
+struct emacs_lisp_task_context {
+ struct handler *handlerlist;
+ struct handler handlerlist_sentinel;
+ ptrdiff_t specpdl_size;
+ union specbinding *specpdl;
+ union specbinding *specpdl_ptr;
+ EMACS_INT lisp_eval_depth;
+ struct emacs_globals globals;
+};
+
+void init_emacs_lisp_context (bool main_task,
+ struct emacs_lisp_task_context *context);
+
+void switch_emacs_lisp_context (struct emacs_lisp_task_context *from,
+ const struct emacs_lisp_task_context *to);
+
#ifdef HAVE_MODULES
/* Defined in alloc.c. */
extern Lisp_Object make_user_ptr (void (*finalizer) (void *), void *p);
if (NSApp == nil
|| (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
- return pselect (nfds, readfds, writefds, exceptfds, timeout, sigmask);
+ return pselect_noblock (nfds, readfds, writefds, exceptfds, timeout, sigmask);
[outerpool release];
outerpool = [[NSAutoreleasePool alloc] init];
fd_set fds;
FD_ZERO (&fds);
FD_SET (selfds[0], &fds);
- result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
+ result = pselect_noblock (selfds[0]+1, &fds, NULL, NULL, NULL, NULL);
if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
waiting = 0;
}
FD_SET (selfds[0], &readfds);
if (selfds[0] >= nfds) nfds = selfds[0]+1;
- result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
+ result = pselect_noblock (nfds, &readfds, wfds, NULL, tmo, NULL);
if (result == 0)
ns_send_appdefined (-2);
#include <float.h>
#include <ftoastr.h>
+#include "libtask/task.h"
+
#ifdef WINDOWSNT
# include <sys/socket.h> /* for F_DUPFD_CLOEXEC */
#endif
printchar ('>', printcharfun);
break;
+ case Lisp_Misc_Channel:
+ {
+ print_c_string ("#<channel", printcharfun);
+ const Channel *channel = XCHANNEL (obj)->channel;
+ {
+ int n = snprintf (buf, sizeof buf, " at %p", channel);
+ strout (buf, n, n, printcharfun);
+ }
+ if (channel->bufsize != 0)
+ {
+ int n = snprintf (buf, sizeof buf, " with a buffer size of %d", channel->bufsize);
+ strout (buf, n, n, printcharfun);
+ }
+ if (channel->nbuf != 0)
+ {
+ int n = snprintf (buf, sizeof buf, " with %d buffered objects", channel->nbuf);
+ strout (buf, n, n, printcharfun);
+ }
+ if (channel->arecv.n != 0)
+ {
+ int n = snprintf (buf, sizeof buf, " with %d queued receivers", channel->arecv.n);
+ strout (buf, n, n, printcharfun);
+ }
+ if (channel->asend.n != 0)
+ {
+ int n = snprintf (buf, sizeof buf, " with %d queued senders", channel->asend.n);
+ strout (buf, n, n, printcharfun);
+ }
+ printchar ('>', printcharfun);
+ }
+ break;
+
#ifdef HAVE_MODULES
case Lisp_Misc_User_Ptr:
{
FD_ZERO (&fdset);
FD_SET (s, &fdset);
QUIT;
- sc = pselect (s + 1, NULL, &fdset, NULL, NULL, NULL);
+ sc = pselect_noblock (s + 1, NULL, &fdset, NULL, NULL, NULL);
if (sc == -1)
{
if (errno == EINTR)
Ctemp = write_mask;
timeout = make_timespec (0, 0);
- if ((pselect (max (max_process_desc, max_input_desc) + 1,
- &Atemp,
- (num_pending_connects > 0 ? &Ctemp : NULL),
- NULL, &timeout, NULL)
+ if ((pselect_noblock (max (max_process_desc, max_input_desc) + 1,
+ &Atemp,
+ (num_pending_connects > 0 ? &Ctemp : NULL),
+ NULL, &timeout, NULL)
<= 0))
{
/* It's okay for us to do this and then continue with
#elif defined (HAVE_GLIB)
nfds = xg_select
#else
- nfds = pselect
+ nfds = pselect_noblock
#endif
(max (max_process_desc, max_input_desc) + 1,
&Available,
{
if (read_kbd || !NILP (wait_for_cell))
FD_SET (0, &waitchannels);
- nfds = pselect (1, &waitchannels, NULL, NULL, &timeout, NULL);
+ nfds = pselect_noblock (1, &waitchannels, NULL, NULL, &timeout, NULL);
}
xerrno = errno;
#endif /* !WINDOWSNT */
+// Defined in coroutine.c.
+int pselect_noblock (int nfds,
+ fd_set *restrict readfds,
+ fd_set *restrict writefds,
+ fd_set *restrict errorfds,
+ const struct timespec *restrict timeout,
+ const sigset_t *restrict sigmask);
+
#endif
void *
unexec_realloc (void *old_ptr, size_t new_size)
{
+ if (old_ptr == NULL)
+ return unexec_malloc (new_size);
if (in_dumped_exec)
{
void *p;
}
fds_lim = max_fds + 1;
- nfds = pselect (fds_lim, &all_rfds, have_wfds ? &all_wfds : NULL,
- efds, tmop, sigmask);
+ nfds = pselect_noblock (fds_lim, &all_rfds, have_wfds ? &all_wfds : NULL,
+ efds, tmop, sigmask);
if (nfds < 0)
retval = nfds;
xg_select so that timeout gets triggered. */
xg_select (n + 1, &read_fds, NULL, NULL, ntp, NULL);
#else
- pselect (n + 1, &read_fds, NULL, NULL, ntp, NULL);
+ pselect_noblock (n + 1, &read_fds, NULL, NULL, ntp, NULL);
#endif
}
}
timeout = make_timespec (0, 10 * 1000 * 1000);
/* Try to wait that long--but we might wake up sooner. */
- pselect (0, NULL, NULL, NULL, &timeout, NULL);
+ pselect_noblock (0, NULL, NULL, NULL, &timeout, NULL);
}
}
break;
tmo = timespec_sub (tmo_at, time_now);
- if (pselect (fd + 1, &fds, NULL, NULL, &tmo, NULL) == 0)
+ if (pselect_noblock (fd + 1, &fds, NULL, NULL, &tmo, NULL) == 0)
break; /* Timeout */
}
--- /dev/null
+;;; coroutine-tests.el --- coroutine tests -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2016 Free Software Foundation, Inc.
+
+;; Author: Philipp Stephani <p.stephani2@gmail.com>
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Unit tests for src/coroutine.c.
+
+;;; Code:
+
+(defun coroutine-tests--prime-task (channel)
+ (let ((prime (receive-from-channel channel)))
+ (message "%d" prime)
+ (let ((new-channel (make-channel)))
+ (start-coroutine (lambda () (coroutine-tests--prime-task new-channel)))
+ (while t
+ (let ((i (receive-from-channel channel)))
+ (unless (zerop (mod i prime))
+ (send-to-channel new-channel i)))))))
+
+(ert-deftest coroutines-tests--prime ()
+ (let ((channel (make-channel)))
+ (start-coroutine (lambda () (coroutine-tests--prime-task channel)))
+ (cl-loop for i from 2 to 100
+ do (send-to-channel channel i))))
+
+(ert-deftest coroutines-test--go-tour-1 ()
+ (cl-flet ((say (s)
+ (dotimes (_ 5)
+ (sleep-for 0.1)
+ (message "%s" s))))
+ (start-coroutine (lambda () (say "world")))
+ (say "hello")))
+
+(ert-deftest coroutines-test--go-tour-2 ()
+ (cl-flet ((sum (s c)
+ (let ((sum 0))
+ (dolist (v s) (cl-incf sum v))
+ (send-to-channel c sum))))
+ (let ((s '(7 2 8 -9 4 0))
+ (c (make-channel)))
+ (start-coroutine (lambda () (sum (butlast s (/ (length s) 2)) c)))
+ (start-coroutine (lambda () (sum (nthcdr (/ (length s) 2) s) c)))
+ (let ((x (receive-from-channel c))
+ (y (receive-from-channel c)))
+ (message "%d" (+ x y))
+ (should (equal (+ x y) (apply #'+ s)))))))
+
+(ert-deftest coroutines-test--go-tour-3 ()
+ (let ((ch (make-channel 2)))
+ (send-to-channel ch 1)
+ (send-to-channel ch 2)
+ (should (equal (receive-from-channel ch) 1))
+ (should (equal (receive-from-channel ch) 2))))
+
+(ert-deftest coroutines-test--go-tour-4 ()
+ (cl-flet ((fibonacci (n c close)
+ (let ((x 0) (y 1))
+ (dotimes (_ n)
+ (send-to-channel c x)
+ (cl-psetq x y
+ y (+ x y))))
+ (send-to-channel close nil)))
+ (let ((c (make-channel 10))
+ (close (make-channel 1)))
+ (start-coroutine (lambda () (fibonacci 10 c close)))
+ (cl-block nil
+ (while t
+ (select
+ ((receive c i)
+ (message "%d" i))
+ ((receive close)
+ (cl-return))))))))
+
+(ert-deftest coroutines-test--go-tour-5 ()
+ (cl-flet ((fibonacci (c quit)
+ (cl-block nil
+ (let ((x 0) (y 1))
+ (while t
+ (select
+ ((send c x)
+ (cl-psetq x y
+ y (+ x y)))
+ ((receive quit)
+ (message "quit")
+ (cl-return))))))))
+ (let ((c (make-channel))
+ (quit (make-channel)))
+ (start-coroutine
+ (lambda ()
+ (dotimes (_ 10)
+ (message "%S" (receive-from-channel c)))
+ (send-to-channel quit nil)))
+ (fibonacci c quit))))
+
+(defun coroutines-test--time-tick (d)
+ (cl-check-type d (and number cl-plus))
+ (let ((c (make-channel 1)))
+ (run-at-time nil d (lambda ()
+ (try-send-to-channel c (current-time))))
+ c))
+
+(defun coroutines-test--time-after (d)
+ (cl-check-type d (and number cl-plus))
+ (let ((c (make-channel 1)))
+ (run-at-time d nil (lambda ()
+ (try-send-to-channel c (current-time))))
+ c))
+
+(ert-deftest coroutines-test--go-tour-6 ()
+ (cl-block nil
+ (let ((tick (coroutines-test--time-tick 0.1))
+ (boom (coroutines-test--time-after 0.5)))
+ (while t
+ (select
+ ((receive tick)
+ (message "tick."))
+ ((receive boom)
+ (message "BOOM!")
+ (cl-return))
+ (default
+ (message " .")
+ (sleep-for 0.05)))))))
+
+(defvar coroutines-test--var nil)
+
+(ert-deftest coroutines-test--specbind ()
+ (let ((coroutines-test--var 'main)
+ (channel (make-channel)))
+ (start-coroutine
+ (lambda ()
+ (let ((coroutines-test--var 'child))
+ (setq coroutines-test--var 'child-setq)
+ (receive-from-channel channel)
+ (should (equal coroutines-test--var 'child-setq)))))
+ (should (equal coroutines-test--var 'main))
+ (setq coroutines-test--var 'main-setq)
+ (send-to-channel channel nil)
+ (should (equal coroutines-test--var 'main-setq))))
+
+;; (ert-deftest coroutines-test--deadlock ()
+;; (let ((ch-1 (make-channel))
+;; (ch-2 (make-channel)))
+;; (start-coroutine
+;; (lambda ()
+;; (receive-from-channel ch-1)
+;; (send-to-channel ch-2 nil)))
+;; (receive-from-channel ch-2)
+;; (send-to-channel ch-1 nil)))
+
+;;; coroutine-tests.el ends here