]> git.eshelyaron.com Git - emacs.git/commitdiff
WIP: CSP based on libtask
authorPhilipp Stephani <phst@google.com>
Sat, 22 Oct 2016 16:27:16 +0000 (18:27 +0200)
committerPhilipp Stephani <phst@google.com>
Wed, 26 Oct 2016 21:31:41 +0000 (23:31 +0200)
40 files changed:
lib/libtask/386-ucontext.h [new file with mode: 0644]
lib/libtask/COPYRIGHT [new file with mode: 0644]
lib/libtask/README [new file with mode: 0644]
lib/libtask/amd64-ucontext.h [new file with mode: 0644]
lib/libtask/asm.S [new file with mode: 0644]
lib/libtask/channel.c [new file with mode: 0644]
lib/libtask/context.c [new file with mode: 0644]
lib/libtask/fd.c [new file with mode: 0644]
lib/libtask/httpload.c [new file with mode: 0644]
lib/libtask/makesun [new file with mode: 0755]
lib/libtask/mips-ucontext.h [new file with mode: 0644]
lib/libtask/net.c [new file with mode: 0644]
lib/libtask/power-ucontext.h [new file with mode: 0644]
lib/libtask/primes.c [new file with mode: 0644]
lib/libtask/print.c [new file with mode: 0644]
lib/libtask/qlock.c [new file with mode: 0644]
lib/libtask/rendez.c [new file with mode: 0644]
lib/libtask/task.c [new file with mode: 0644]
lib/libtask/task.h [new file with mode: 0644]
lib/libtask/taskimpl.h [new file with mode: 0644]
lib/libtask/tcpproxy.c [new file with mode: 0644]
lib/libtask/testdelay.c [new file with mode: 0644]
lib/libtask/testdelay1.c [new file with mode: 0644]
lisp/emacs-lisp/byte-opt.el
lisp/emacs-lisp/cconv.el
src/Makefile.in
src/alloc.c
src/coroutine.c [new file with mode: 0644]
src/emacs.c
src/eval.c
src/lisp.h
src/nsterm.m
src/print.c
src/process.c
src/sysselect.h
src/unexmacosx.c
src/xgselect.c
src/xmenu.c
src/xterm.c
test/src/coroutine-tests.el [new file with mode: 0644]

diff --git a/lib/libtask/386-ucontext.h b/lib/libtask/386-ucontext.h
new file mode 100644 (file)
index 0000000..b840f9c
--- /dev/null
@@ -0,0 +1,119 @@
+#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];
+};
diff --git a/lib/libtask/COPYRIGHT b/lib/libtask/COPYRIGHT
new file mode 100644 (file)
index 0000000..c1aedca
--- /dev/null
@@ -0,0 +1,42 @@
+
+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.
+*/
diff --git a/lib/libtask/README b/lib/libtask/README
new file mode 100644 (file)
index 0000000..191ead4
--- /dev/null
@@ -0,0 +1,244 @@
+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.
diff --git a/lib/libtask/amd64-ucontext.h b/lib/libtask/amd64-ucontext.h
new file mode 100644 (file)
index 0000000..e4560e5
--- /dev/null
@@ -0,0 +1,135 @@
+#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];
+};
diff --git a/lib/libtask/asm.S b/lib/libtask/asm.S
new file mode 100644 (file)
index 0000000..647dba5
--- /dev/null
@@ -0,0 +1,326 @@
+/* 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
diff --git a/lib/libtask/channel.c b/lib/libtask/channel.c
new file mode 100644 (file)
index 0000000..5d9d8cb
--- /dev/null
@@ -0,0 +1,384 @@
+/* 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;
+}
diff --git a/lib/libtask/context.c b/lib/libtask/context.c
new file mode 100644 (file)
index 0000000..9571996
--- /dev/null
@@ -0,0 +1,141 @@
+/* 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
diff --git a/lib/libtask/fd.c b/lib/libtask/fd.c
new file mode 100644 (file)
index 0000000..8be99b9
--- /dev/null
@@ -0,0 +1,201 @@
+#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;
+}
diff --git a/lib/libtask/httpload.c b/lib/libtask/httpload.c
new file mode 100644 (file)
index 0000000..6cf1932
--- /dev/null
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <task.h>
+#include <stdlib.h>
+
+enum
+{
+       STACK = 32768
+};
+
+char *server;
+char *url;
+
+void fetchtask(void*);
+
+void
+taskmain(int argc, char **argv)
+{
+       int i, n;
+
+       if(argc != 4){
+               fprintf(stderr, "usage: httpload n server url\n");
+               taskexitall(1);
+       }
+       n = atoi(argv[1]);
+       server = argv[2];
+       url = argv[3];
+
+       for(i=0; i<n; i++){
+               taskcreate(fetchtask, 0, STACK);
+               while(taskyield() > 1)
+                       ;
+               sleep(1);
+       }
+}
+
+void
+fetchtask(void *v)
+{
+       int fd, n;
+       char buf[512];
+
+       fprintf(stderr, "starting...\n");
+       for(;;){
+               if((fd = netdial(TCP, server, 80)) < 0){
+                       fprintf(stderr, "dial %s: %s (%s)\n", server, strerror(errno), taskgetstate());
+                       continue;
+               }
+               snprintf(buf, sizeof buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", url, server);
+               fdwrite(fd, buf, strlen(buf));
+               while((n = fdread(fd, buf, sizeof buf)) > 0)
+                       ;
+               close(fd);
+               write(1, ".", 1);
+       }
+}
diff --git a/lib/libtask/makesun b/lib/libtask/makesun
new file mode 100755 (executable)
index 0000000..6243048
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+case "x$CC" in
+x|xcc)
+       CC=cc
+       CFLAGS="-mt -g -O -c -xCC -D__sun__ -I."
+       ;;
+xgcc)
+       CC=gcc
+       CFLAGS="-Wall -c -I."
+       ;;
+*)
+       echo 'unknown $CC'
+       exit 1
+esac
+
+u=`uname`
+v=`uname -r`
+s=`echo $u$v | tr '. ' '__'`
+CFLAGS="$CFLAGS -D__${s}__"
+
+make "CC=$CC" "CFLAGS=$CFLAGS" "ASM=" "TCPLIBS=-lsocket -lnsl"
diff --git a/lib/libtask/mips-ucontext.h b/lib/libtask/mips-ucontext.h
new file mode 100644 (file)
index 0000000..aa6f41e
--- /dev/null
@@ -0,0 +1,77 @@
+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];
+};
diff --git a/lib/libtask/net.c b/lib/libtask/net.c
new file mode 100644 (file)
index 0000000..179b9b0
--- /dev/null
@@ -0,0 +1,202 @@
+#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;
+}
diff --git a/lib/libtask/power-ucontext.h b/lib/libtask/power-ucontext.h
new file mode 100644 (file)
index 0000000..ad6cdb3
--- /dev/null
@@ -0,0 +1,36 @@
+#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*);
diff --git a/lib/libtask/primes.c b/lib/libtask/primes.c
new file mode 100644 (file)
index 0000000..e9aa5f5
--- /dev/null
@@ -0,0 +1,61 @@
+/* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <task.h>
+
+int quiet;
+int goal;
+int buffer;
+
+void
+primetask(void *arg)
+{
+       Channel *c, *nc;
+       int p, i;
+       c = arg;
+
+       p = chanrecvul(c);
+       if(p > goal)
+               taskexitall(0);
+       if(!quiet)
+               printf("%d\n", p);
+       nc = chancreate(sizeof(unsigned long), buffer);
+       taskcreate(primetask, nc, 32768);
+       for(;;){
+               i = chanrecvul(c);
+               if(i%p)
+                       chansendul(nc, i);
+       }
+}
+
+void
+taskmain(int argc, char **argv)
+{
+       int i;
+       Channel *c;
+
+       if(argc>1)
+               goal = atoi(argv[1]);
+       else
+               goal = 100;
+       printf("goal=%d\n", goal);
+
+       c = chancreate(sizeof(unsigned long), buffer);
+       taskcreate(primetask, c, 32768);
+       for(i=2;; i++)
+               chansendul(c, i);
+}
+
+void*
+emalloc(unsigned long n)
+{
+       return calloc(n ,1);
+}
+
+long
+lrand(void)
+{
+       return rand();
+}
diff --git a/lib/libtask/print.c b/lib/libtask/print.c
new file mode 100644 (file)
index 0000000..485bb84
--- /dev/null
@@ -0,0 +1,249 @@
+/* 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;
+}
diff --git a/lib/libtask/qlock.c b/lib/libtask/qlock.c
new file mode 100644 (file)
index 0000000..959784f
--- /dev/null
@@ -0,0 +1,142 @@
+#include "taskimpl.h"
+
+/*
+ * locking
+ */
+static int
+_qlock(QLock *l, int block)
+{
+       if(l->owner == nil){
+               l->owner = taskrunning;
+               return 1;
+       }
+       if(!block)
+               return 0;
+       addtask(&l->waiting, taskrunning);
+       taskstate("qlock");
+       taskswitch();
+       if(l->owner != taskrunning){
+               fprint(2, "qlock: owner=%p self=%p oops\n", l->owner, taskrunning);
+               abort();
+       }
+       return 1;
+}
+
+void
+qlock(QLock *l)
+{
+       _qlock(l, 1);
+}
+
+int
+canqlock(QLock *l)
+{
+       return _qlock(l, 0);
+}
+
+void
+qunlock(QLock *l)
+{
+       Task *ready;
+
+       if(l->owner == 0){
+               fprint(2, "qunlock: owner=0\n");
+               abort();
+       }
+       if((l->owner = ready = l->waiting.head) != nil){
+               deltask(&l->waiting, ready);
+               taskready(ready);
+       }
+}
+
+static int
+_rlock(RWLock *l, int block)
+{
+       if(l->writer == nil && l->wwaiting.head == nil){
+               l->readers++;
+               return 1;
+       }
+       if(!block)
+               return 0;
+       addtask(&l->rwaiting, taskrunning);
+       taskstate("rlock");
+       taskswitch();
+       return 1;
+}
+
+void
+rlock(RWLock *l)
+{
+       _rlock(l, 1);
+}
+
+int
+canrlock(RWLock *l)
+{
+       return _rlock(l, 0);
+}
+
+static int
+_wlock(RWLock *l, int block)
+{
+       if(l->writer == nil && l->readers == 0){
+               l->writer = taskrunning;
+               return 1;
+       }
+       if(!block)
+               return 0;
+       addtask(&l->wwaiting, taskrunning);
+       taskstate("wlock");
+       taskswitch();
+       return 1;
+}
+
+void
+wlock(RWLock *l)
+{
+       _wlock(l, 1);
+}
+
+int
+canwlock(RWLock *l)
+{
+       return _wlock(l, 0);
+}
+
+void
+runlock(RWLock *l)
+{
+       Task *t;
+
+       if(--l->readers == 0 && (t = l->wwaiting.head) != nil){
+               deltask(&l->wwaiting, t);
+               l->writer = t;
+               taskready(t);
+       }
+}
+
+void
+wunlock(RWLock *l)
+{
+       Task *t;
+
+       if(l->writer == nil){
+               fprint(2, "wunlock: not locked\n");
+               abort();
+       }
+       l->writer = nil;
+       if(l->readers != 0){
+               fprint(2, "wunlock: readers\n");
+               abort();
+       }
+       while((t = l->rwaiting.head) != nil){
+               deltask(&l->rwaiting, t);
+               l->readers++;
+               taskready(t);
+       }
+       if(l->readers == 0 && (t = l->wwaiting.head) != nil){
+               deltask(&l->wwaiting, t);
+               l->writer = t;
+               taskready(t);
+       }
+}
diff --git a/lib/libtask/rendez.c b/lib/libtask/rendez.c
new file mode 100644 (file)
index 0000000..d27fde7
--- /dev/null
@@ -0,0 +1,45 @@
+#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);
+}
diff --git a/lib/libtask/task.c b/lib/libtask/task.c
new file mode 100644 (file)
index 0000000..ebb8681
--- /dev/null
@@ -0,0 +1,424 @@
+/* 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;
+}
diff --git a/lib/libtask/task.h b/lib/libtask/task.h
new file mode 100644 (file)
index 0000000..d7fd4ff
--- /dev/null
@@ -0,0 +1,181 @@
+/* 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
diff --git a/lib/libtask/taskimpl.h b/lib/libtask/taskimpl.h
new file mode 100644 (file)
index 0000000..03a26c2
--- /dev/null
@@ -0,0 +1,200 @@
+/* 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
diff --git a/lib/libtask/tcpproxy.c b/lib/libtask/tcpproxy.c
new file mode 100644 (file)
index 0000000..114f39c
--- /dev/null
@@ -0,0 +1,91 @@
+#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);
+}
diff --git a/lib/libtask/testdelay.c b/lib/libtask/testdelay.c
new file mode 100644 (file)
index 0000000..7ebf70d
--- /dev/null
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <task.h>
+
+enum { STACK = 32768 };
+
+Channel *c;
+
+void
+delaytask(void *v)
+{
+       taskdelay((int)v);
+       printf("awake after %d ms\n", (int)v);
+       chansendul(c, 0);
+}
+
+void
+taskmain(int argc, char **argv)
+{
+       int i, n;
+
+       c = chancreate(sizeof(unsigned long), 0);
+
+       n = 0;
+       for(i=1; i<argc; i++){
+               n++;
+               printf("x");
+               taskcreate(delaytask, (void*)atoi(argv[i]), STACK);
+       }
+
+       /* wait for n tasks to finish */
+       for(i=0; i<n; i++){
+               printf("y");
+               chanrecvul(c);
+       }
+       taskexitall(0);
+}
diff --git a/lib/libtask/testdelay1.c b/lib/libtask/testdelay1.c
new file mode 100644 (file)
index 0000000..b762f80
--- /dev/null
@@ -0,0 +1,7 @@
+#include <task.h>
+
+void
+taskmain(int argc, char *argv[])
+{
+       taskdelay(1000);
+}
index 610c3b6c190e3b2d54d47b395495f242cc6bb17a..ab9354482b106eb05049986c207492ccc5d0c71d 100644 (file)
                            (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
index 46b5a7f342cfcd47558146d5cfbd6a2b2983d9b4..598436f647c9e3793905448e6c764d8905168cd4 100644 (file)
@@ -759,6 +759,29 @@ and updates the data stored in ENV."
      ;; 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.
index 89f7a921faa24ebc982e28b69e5ed25f16b9a72c..aa2db118d2c118f3cf00919a77d04a32f20c10de 100644 (file)
@@ -398,6 +398,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \
        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 \
@@ -598,14 +599,29 @@ LIBEGNU_ARCHIVE = $(lib)/lib$(if $(HYBRID_MALLOC),e)gnu.a
 $(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),)
index a58dc13cbd7dc5b84ea992278417e1609d9149e6..10b6c20e3e44da4810f59ff4fb0d185a7e5aa5c2 100644 (file)
@@ -51,6 +51,8 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <verify.h>
 #include <execinfo.h>           /* For backtrace.  */
 
+#include "libtask/task.h"
+
 #ifdef HAVE_LINUX_SYSINFO
 #include <sys/sysinfo.h>
 #endif
@@ -3865,6 +3867,28 @@ free_marker (Lisp_Object marker)
   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
@@ -6524,6 +6548,17 @@ mark_object (Lisp_Object arg)
           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;
@@ -6908,6 +6943,15 @@ sweep_misc (void)
                 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)
                {
@@ -7419,6 +7463,7 @@ The time is in seconds as a floating point value.  */);
   defsubr (&Smake_symbol);
   defsubr (&Smake_marker);
   defsubr (&Smake_finalizer);
+  defsubr (&Smake_channel);
   defsubr (&Spurecopy);
   defsubr (&Sgarbage_collect);
   defsubr (&Smemory_limit);
diff --git a/src/coroutine.c b/src/coroutine.c
new file mode 100644 (file)
index 0000000..601773a
--- /dev/null
@@ -0,0 +1,400 @@
+/* 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);
+}
index 13378c4c3b04d8f590c41088a258aecae32bc61d..adb972e1db83b57a8a68ffa9b53d3315b9ee9da8 100644 (file)
@@ -91,6 +91,8 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "systime.h"
 #include "puresize.h"
 
+#include "libtask/task.h"
+
 #include "getpagesize.h"
 #include "gnutls.h"
 
@@ -661,8 +663,8 @@ close_output_streams (void)
 }
 
 /* ARGSUSED */
-int
-main (int argc, char **argv)
+void
+taskmain (int argc, char **argv)
 {
   Lisp_Object dummy;
   char stack_bottom_variable;
@@ -739,12 +741,6 @@ main (int argc, char **argv)
     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);
 
@@ -1436,6 +1432,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
       syms_of_ccl ();
       syms_of_character ();
       syms_of_cmds ();
+      syms_of_coroutine ();
       syms_of_dired ();
       syms_of_display ();
       syms_of_doc ();
@@ -1657,8 +1654,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
 
   /* Enter editor command loop.  This never returns.  */
   Frecursive_edit ();
-  /* NOTREACHED */
-  return 0;
 }
 \f
 /* Sort the args so we can find the most important ones
@@ -2066,6 +2061,19 @@ shut_down_emacs (int sig, Lisp_Object stuff)
 
 
 \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"
index a9bad2491fae6885ef0b0485c10bc05e979038c0..98abce84196a69f58a147a2f38b1ae39e4a88d05 100644 (file)
@@ -3759,6 +3759,56 @@ Lisp_Object backtrace_top_function (void)
   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)
 {
index 2e46592c3d4fcc6c0b61ded879ca35bd42f32ae9..7efdd4573e1bdddb06f3e2f5e7c6f0154b314ad2 100644 (file)
@@ -459,6 +459,7 @@ enum Lisp_Misc_Type
     Lisp_Misc_Overlay,
     Lisp_Misc_Save_Value,
     Lisp_Misc_Finalizer,
+    Lisp_Misc_Channel,
 #ifdef HAVE_MODULES
     Lisp_Misc_User_Ptr,
 #endif
@@ -611,6 +612,8 @@ extern bool might_dump;
    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);
 
@@ -2252,6 +2255,12 @@ XSAVE_OBJECT (Lisp_Object obj, int n)
   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
 {
@@ -2299,6 +2308,7 @@ union Lisp_Misc
     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
@@ -2351,6 +2361,19 @@ XFINALIZER (Lisp_Object a)
   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)
@@ -3412,6 +3435,9 @@ extern void init_coding (void);
 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);
@@ -3932,6 +3958,22 @@ Lisp_Object backtrace_top_function (void);
 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);
index 1b44a73cd8bffbe77d68bee842cb72264bb16cac..b0883f3140b122b2d4114cb45ac536bf441ca027 100644 (file)
@@ -4143,7 +4143,7 @@ ns_select (int nfds, fd_set *readfds, fd_set *writefds,
 
   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];
@@ -5509,7 +5509,7 @@ not_in_argv (NSString *arg)
           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;
         }
@@ -5543,7 +5543,7 @@ not_in_argv (NSString *arg)
           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);
index f3db6748d031570f33e401e38445e466014e12f8..bb38b3d67e38c2edb746806bb51e7d2eba676964 100644 (file)
@@ -38,6 +38,8 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <float.h>
 #include <ftoastr.h>
 
+#include "libtask/task.h"
+
 #ifdef WINDOWSNT
 # include <sys/socket.h> /* for F_DUPFD_CLOEXEC */
 #endif
@@ -2006,6 +2008,38 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag)
          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:
          {
index 8cf045ca9c2549a7dde430676b032e1b383af32d..f6266686962fbc2f64b250d8fa245cac804e6e14 100644 (file)
@@ -3279,7 +3279,7 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
          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)
@@ -5003,10 +5003,10 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
          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
@@ -5187,7 +5187,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 #elif defined (HAVE_GLIB)
          nfds = xg_select
 #else
-         nfds = pselect
+         nfds = pselect_noblock
 #endif
             (max (max_process_desc, max_input_desc) + 1,
              &Available,
@@ -7410,7 +7410,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
        {
          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;
index 0bf9b40a3cb115ddd1d51e70a61804424c463aaf..21a3f59666289b6ff533dc3256f770962bdd5b70 100644 (file)
@@ -90,4 +90,12 @@ INLINE_HEADER_END
 
 #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
index ea8e884f177f55b4dbef5fa0f0d15cd71a2d29be..48a302c2e3fe3594614d6cdd60b9aad85971c466 100644 (file)
@@ -1377,6 +1377,8 @@ unexec_malloc (size_t size)
 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;
index 7850a16e9c024b08777281b1236369d0c2f931a5..02e33cc0902b8d273af7ea087053a56ab34dac8f 100644 (file)
@@ -109,8 +109,8 @@ xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds,
     }
 
   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;
index 9ab7bdf971f49401a64cfb0e3a2f928bfae15662..d5577bb08316725cfd0195950d30346ae77c4a4c 100644 (file)
@@ -195,7 +195,7 @@ x_menu_wait_for_event (void *data)
         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
     }
 }
index 747669446f57bb51dcd1642f94106cee33a6b0bc..6f57734a4225131bd80a38745d0408fc95bc4742 100644 (file)
@@ -3976,7 +3976,7 @@ XTflash (struct frame *f)
            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);
          }
       }
 
@@ -10564,7 +10564,7 @@ x_wait_for_event (struct frame *f, int eventtype)
        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 */
     }
 
diff --git a/test/src/coroutine-tests.el b/test/src/coroutine-tests.el
new file mode 100644 (file)
index 0000000..d86fe6c
--- /dev/null
@@ -0,0 +1,166 @@
+;;; 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