├── .gitignore ├── Make.common ├── Makefile ├── README.md ├── deps ├── .gitignore ├── libev-configure.sh └── libev │ ├── Changes │ ├── LICENSE │ ├── Makefile.am │ ├── Makefile.in │ ├── README │ ├── Symbols.ev │ ├── Symbols.event │ ├── aclocal.m4 │ ├── autogen.sh │ ├── config.guess │ ├── config.h.in │ ├── config.sub │ ├── configure │ ├── configure.ac │ ├── depcomp │ ├── ev++.h │ ├── ev.3 │ ├── ev.c │ ├── ev.h │ ├── ev.pod │ ├── ev_epoll.c │ ├── ev_kqueue.c │ ├── ev_poll.c │ ├── ev_port.c │ ├── ev_select.c │ ├── ev_vars.h │ ├── ev_win32.c │ ├── ev_wrap.h │ ├── event.c │ ├── event.h │ ├── install-sh │ ├── libev.m4 │ ├── ltmain.sh │ ├── missing │ └── mkinstalldirs ├── lab ├── debug.py ├── functional.mv ├── notes-on-task-hierarchy.md ├── notes.md ├── register-and-AR-thoughts.mv ├── sol-functions.mv ├── task-message-passing-take2.md ├── task-message-passing.md └── task-structure-overview.md ├── sol ├── Makefile ├── arec.h ├── common.h ├── common_atomic.h ├── common_stdint.h ├── common_target.h ├── debug.h ├── func.c ├── func.h ├── host.c ├── host.h ├── instr.h ├── log.c ├── log.h ├── main.c ├── msg.c ├── msg.h ├── runq.h ├── sched.c ├── sched.h ├── sched_exec.h ├── sched_exec_debug.h ├── sol.h ├── task.c ├── task.h ├── value.c ├── value.h └── vm.h └── test ├── Makefile ├── bench.h ├── test.h ├── test_instr.c ├── test_msg.c ├── test_prog_basics.c └── test_prog_timer.c /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | lab 4 | -------------------------------------------------------------------------------- /Make.common: -------------------------------------------------------------------------------- 1 | SRCROOT := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) 2 | HOST_ARCH = $(shell uname -m | sed s,i[3456789]86,ia32,) 3 | TARGET_ARCH := $(shell uname -m | sed s,i[3456789]86,ia32,) 4 | HOST_OS = $(shell uname -s) 5 | 6 | # Dependencies 7 | DEPS_PREFIX := $(SRCROOT)/deps 8 | 9 | # Build target type 10 | ifeq ($(strip $(DEBUG)),1) 11 | BUILD_TARGET_TYPE = debug 12 | else 13 | BUILD_TARGET_TYPE = release 14 | endif 15 | 16 | # Build directories 17 | BASE_BUILD_PREFIX := $(SRCROOT)/build 18 | BUILD_PREFIX := $(BASE_BUILD_PREFIX)/$(BUILD_TARGET_TYPE) 19 | TESTS_BUILD_PREFIX := $(BASE_BUILD_PREFIX)/test 20 | INCLUDE_BUILD_PREFIX := $(BUILD_PREFIX)/include 21 | LIB_BUILD_PREFIX := $(BUILD_PREFIX)/lib 22 | BIN_BUILD_PREFIX := $(BUILD_PREFIX)/bin 23 | DEBUG_BUILD_PREFIX := $(BUILD_PREFIX)/debug 24 | 25 | OBJECT_DIR := $(BUILD_PREFIX)/.objs 26 | 27 | # Tools 28 | CC = clang 29 | CXXC = clang++ 30 | LD = clang 31 | AR = ar 32 | 33 | # $(call UpperCase,foo_bar) -> FOO_BAR 34 | UpperCase = $(shell echo $(1) | tr a-z A-Z) 35 | 36 | # Compiler and Linker flags for all targets 37 | CFLAGS += -Wall -g -std=c99 -I$(INCLUDE_BUILD_PREFIX) \ 38 | -arch $(TARGET_ARCH) 39 | CXXFLAGS += -std=c++11 -fno-rtti 40 | LDFLAGS += -arch $(TARGET_ARCH) 41 | XXLDFLAGS += -lc++ -lstdc++ 42 | 43 | # Compiler and Linker flags for release and debug targets (e.g. make DEBUG=1) 44 | ifeq ($(strip $(DEBUG)),1) 45 | CFLAGS += -O0 -DS_DEBUG=1 46 | #LDFLAGS += 47 | else 48 | CFLAGS += -O3 -DS_DEBUG=0 -DNDEBUG 49 | #LDFLAGS += 50 | endif 51 | 52 | # Functions 53 | 54 | # $(call PubHeaderNames,,) -> 55 | PubHeaderNames = $(patsubst %,$(1)/%,$(2)) 56 | 57 | # $(call FileDirs,) -> 58 | FileDirs = $(sort $(foreach fn,$(1),$(dir $(fn)))) 59 | 60 | # $(call SrcToObjects,,) -> 61 | SrcToObjects = $(patsubst %,$(1)/%,$(2)) 62 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include Make.common 2 | all: test sol 3 | 4 | clean: 5 | @$(MAKE) -C $(SRCROOT)/sol $@ 6 | @$(MAKE) -C $(SRCROOT)/test $@ 7 | 8 | sol: 9 | @$(MAKE) -C $(SRCROOT)/sol $@ 10 | 11 | # Shorthand for "make DEBUG=1 sol" 12 | debug: 13 | @$(MAKE) -C $(SRCROOT)/sol DEBUG=1 14 | 15 | test: 16 | @$(MAKE) -C $(SRCROOT)/test $@ 17 | 18 | .PHONY: clean sol debug test 19 | -------------------------------------------------------------------------------- /deps/.gitignore: -------------------------------------------------------------------------------- 1 | *.tar.gz 2 | libev/.* 3 | libev/Makefile 4 | libev/config.* 5 | libev/ev.*o 6 | libev/event.*o 7 | libev/libev.*a 8 | libev/libtool 9 | libev/stamp-* 10 | -------------------------------------------------------------------------------- /deps/libev-configure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | cd "$(dirname "$0")/libev" 4 | #LIBEV_DIR=$(pwd) 5 | ./configure --enable-shared=no --enable-static=yes 6 | # cd ../../ # now in ./ 7 | # mkdir -p src/ev 8 | # for srcfile in \ 9 | # ev.h ev.c ev_vars.h ev_wrap.h \ 10 | # ev_select.c ev_poll.c ev_kqueue.c ev_epoll.c ev_port.c ev_win32.c 11 | # do 12 | # cp -f "$LIBEV_DIR/$srcfile" src/ev/$srcfile 13 | # done 14 | -------------------------------------------------------------------------------- /deps/libev/LICENSE: -------------------------------------------------------------------------------- 1 | All files in libev are Copyright (C)2007,2008,2009 Marc Alexander Lehmann. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | Alternatively, the contents of this package may be used under the terms 28 | of the GNU General Public License ("GPL") version 2 or any later version, 29 | in which case the provisions of the GPL are applicable instead of the 30 | above. If you wish to allow the use of your version of this package only 31 | under the terms of the GPL and not to allow others to use your version of 32 | this file under the BSD license, indicate your decision by deleting the 33 | provisions above and replace them with the notice and other provisions 34 | required by the GPL in this and the other files of this package. If you do 35 | not delete the provisions above, a recipient may use your version of this 36 | file under either the BSD or the GPL. 37 | -------------------------------------------------------------------------------- /deps/libev/Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign 2 | 3 | VERSION_INFO = 4:0:0 4 | 5 | EXTRA_DIST = LICENSE Changes libev.m4 autogen.sh \ 6 | ev_vars.h ev_wrap.h \ 7 | ev_epoll.c ev_select.c ev_poll.c ev_kqueue.c ev_port.c ev_win32.c \ 8 | ev.3 ev.pod Symbols.ev Symbols.event 9 | 10 | man_MANS = ev.3 11 | 12 | include_HEADERS = ev.h ev++.h event.h 13 | 14 | lib_LTLIBRARIES = libev.la 15 | 16 | libev_la_SOURCES = ev.c event.c 17 | libev_la_LDFLAGS = -version-info $(VERSION_INFO) 18 | 19 | ev.3: ev.pod 20 | pod2man -n LIBEV -r "libev-$(VERSION)" -c "libev - high performance full featured event loop" -s3 <$< >$@ 21 | -------------------------------------------------------------------------------- /deps/libev/README: -------------------------------------------------------------------------------- 1 | libev is a high-performance event loop/event model with lots of features. 2 | (see benchmark at http://libev.schmorp.de/bench.html) 3 | 4 | 5 | ABOUT 6 | 7 | Homepage: http://software.schmorp.de/pkg/libev 8 | Mailinglist: libev@lists.schmorp.de 9 | http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev 10 | Library Documentation: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod 11 | 12 | Libev is modelled (very losely) after libevent and the Event perl 13 | module, but is faster, scales better and is more correct, and also more 14 | featureful. And also smaller. Yay. 15 | 16 | Some of the specialties of libev not commonly found elsewhere are: 17 | 18 | - extensive and detailed, readable documentation (not doxygen garbage). 19 | - fully supports fork, can detect fork in various ways and automatically 20 | re-arms kernel mechanisms that do not support fork. 21 | - highly optimised select, poll, epoll, kqueue and event ports backends. 22 | - filesystem object (path) watching (with optional linux inotify support). 23 | - wallclock-based times (using absolute time, cron-like). 24 | - relative timers/timeouts (handle time jumps). 25 | - fast intra-thread communication between multiple 26 | event loops (with optional fast linux eventfd backend). 27 | - extremely easy to embed. 28 | - very small codebase, no bloated library. 29 | - fully extensible by being able to plug into the event loop, 30 | integrate other event loops, integrate other event loop users. 31 | - very little memory use (small watchers, small event loop data). 32 | - optional C++ interface allowing method and function callbacks 33 | at no extra memory or runtime overhead. 34 | - optional Perl interface with similar characteristics (capable 35 | of running Glib/Gtk2 on libev, interfaces with Net::SNMP and 36 | libadns). 37 | - support for other languages (multiple C++ interfaces, D, Ruby, 38 | Python) available from third-parties. 39 | 40 | Examples of programs that embed libev: the EV perl module, 41 | rxvt-unicode, gvpe (GNU Virtual Private Ethernet), the Deliantra MMORPG 42 | server (http://www.deliantra.net/), Rubinius (a next-generation Ruby 43 | VM), the Ebb web server, the Rev event toolkit. 44 | 45 | 46 | CONTRIBUTORS 47 | 48 | libev was written and designed by Marc Lehmann and Emanuele Giaquinta. 49 | 50 | The following people sent in patches or made other noteworthy 51 | contributions to the design (for minor patches, see the Changes 52 | file. If I forgot to include you, please shout at me, it was an 53 | accident): 54 | 55 | W.C.A. Wijngaards 56 | Christopher Layne 57 | Chris Brody 58 | 59 | -------------------------------------------------------------------------------- /deps/libev/Symbols.ev: -------------------------------------------------------------------------------- 1 | ev_async_send 2 | ev_async_start 3 | ev_async_stop 4 | ev_backend 5 | ev_break 6 | ev_check_start 7 | ev_check_stop 8 | ev_child_start 9 | ev_child_stop 10 | ev_cleanup_start 11 | ev_cleanup_stop 12 | ev_clear_pending 13 | ev_default_loop 14 | ev_default_loop_ptr 15 | ev_depth 16 | ev_embed_start 17 | ev_embed_stop 18 | ev_embed_sweep 19 | ev_embeddable_backends 20 | ev_feed_event 21 | ev_feed_fd_event 22 | ev_feed_signal 23 | ev_feed_signal_event 24 | ev_fork_start 25 | ev_fork_stop 26 | ev_idle_start 27 | ev_idle_stop 28 | ev_invoke 29 | ev_invoke_pending 30 | ev_io_start 31 | ev_io_stop 32 | ev_iteration 33 | ev_loop_destroy 34 | ev_loop_fork 35 | ev_loop_new 36 | ev_now 37 | ev_now_update 38 | ev_once 39 | ev_pending_count 40 | ev_periodic_again 41 | ev_periodic_start 42 | ev_periodic_stop 43 | ev_prepare_start 44 | ev_prepare_stop 45 | ev_recommended_backends 46 | ev_ref 47 | ev_resume 48 | ev_run 49 | ev_set_allocator 50 | ev_set_invoke_pending_cb 51 | ev_set_io_collect_interval 52 | ev_set_loop_release_cb 53 | ev_set_syserr_cb 54 | ev_set_timeout_collect_interval 55 | ev_set_userdata 56 | ev_signal_start 57 | ev_signal_stop 58 | ev_sleep 59 | ev_stat_start 60 | ev_stat_stat 61 | ev_stat_stop 62 | ev_supported_backends 63 | ev_suspend 64 | ev_time 65 | ev_timer_again 66 | ev_timer_remaining 67 | ev_timer_start 68 | ev_timer_stop 69 | ev_unref 70 | ev_userdata 71 | ev_verify 72 | ev_version_major 73 | ev_version_minor 74 | -------------------------------------------------------------------------------- /deps/libev/Symbols.event: -------------------------------------------------------------------------------- 1 | event_active 2 | event_add 3 | event_base_dispatch 4 | event_base_free 5 | event_base_loop 6 | event_base_loopexit 7 | event_base_once 8 | event_base_priority_init 9 | event_base_set 10 | event_del 11 | event_dispatch 12 | event_get_method 13 | event_get_version 14 | event_init 15 | event_loop 16 | event_loopexit 17 | event_once 18 | event_pending 19 | event_priority_init 20 | event_priority_set 21 | event_set 22 | -------------------------------------------------------------------------------- /deps/libev/autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf --install --symlink --force 4 | -------------------------------------------------------------------------------- /deps/libev/config.h.in: -------------------------------------------------------------------------------- 1 | /* config.h.in. Generated from configure.ac by autoheader. */ 2 | 3 | /* Define to 1 if you have the `clock_gettime' function. */ 4 | #undef HAVE_CLOCK_GETTIME 5 | 6 | /* Define to 1 to use the syscall interface for clock_gettime */ 7 | #undef HAVE_CLOCK_SYSCALL 8 | 9 | /* Define to 1 if you have the header file. */ 10 | #undef HAVE_DLFCN_H 11 | 12 | /* Define to 1 if you have the `epoll_ctl' function. */ 13 | #undef HAVE_EPOLL_CTL 14 | 15 | /* Define to 1 if you have the `eventfd' function. */ 16 | #undef HAVE_EVENTFD 17 | 18 | /* Define to 1 if the floor function is available */ 19 | #undef HAVE_FLOOR 20 | 21 | /* Define to 1 if you have the `inotify_init' function. */ 22 | #undef HAVE_INOTIFY_INIT 23 | 24 | /* Define to 1 if you have the header file. */ 25 | #undef HAVE_INTTYPES_H 26 | 27 | /* Define to 1 if you have the `kqueue' function. */ 28 | #undef HAVE_KQUEUE 29 | 30 | /* Define to 1 if you have the `rt' library (-lrt). */ 31 | #undef HAVE_LIBRT 32 | 33 | /* Define to 1 if you have the header file. */ 34 | #undef HAVE_MEMORY_H 35 | 36 | /* Define to 1 if you have the `nanosleep' function. */ 37 | #undef HAVE_NANOSLEEP 38 | 39 | /* Define to 1 if you have the `poll' function. */ 40 | #undef HAVE_POLL 41 | 42 | /* Define to 1 if you have the header file. */ 43 | #undef HAVE_POLL_H 44 | 45 | /* Define to 1 if you have the `port_create' function. */ 46 | #undef HAVE_PORT_CREATE 47 | 48 | /* Define to 1 if you have the header file. */ 49 | #undef HAVE_PORT_H 50 | 51 | /* Define to 1 if you have the `select' function. */ 52 | #undef HAVE_SELECT 53 | 54 | /* Define to 1 if you have the `signalfd' function. */ 55 | #undef HAVE_SIGNALFD 56 | 57 | /* Define to 1 if you have the header file. */ 58 | #undef HAVE_STDINT_H 59 | 60 | /* Define to 1 if you have the header file. */ 61 | #undef HAVE_STDLIB_H 62 | 63 | /* Define to 1 if you have the header file. */ 64 | #undef HAVE_STRINGS_H 65 | 66 | /* Define to 1 if you have the header file. */ 67 | #undef HAVE_STRING_H 68 | 69 | /* Define to 1 if you have the header file. */ 70 | #undef HAVE_SYS_EPOLL_H 71 | 72 | /* Define to 1 if you have the header file. */ 73 | #undef HAVE_SYS_EVENTFD_H 74 | 75 | /* Define to 1 if you have the header file. */ 76 | #undef HAVE_SYS_EVENT_H 77 | 78 | /* Define to 1 if you have the header file. */ 79 | #undef HAVE_SYS_INOTIFY_H 80 | 81 | /* Define to 1 if you have the header file. */ 82 | #undef HAVE_SYS_SELECT_H 83 | 84 | /* Define to 1 if you have the header file. */ 85 | #undef HAVE_SYS_SIGNALFD_H 86 | 87 | /* Define to 1 if you have the header file. */ 88 | #undef HAVE_SYS_STAT_H 89 | 90 | /* Define to 1 if you have the header file. */ 91 | #undef HAVE_SYS_TYPES_H 92 | 93 | /* Define to 1 if you have the header file. */ 94 | #undef HAVE_UNISTD_H 95 | 96 | /* Define to the sub-directory in which libtool stores uninstalled libraries. 97 | */ 98 | #undef LT_OBJDIR 99 | 100 | /* Name of package */ 101 | #undef PACKAGE 102 | 103 | /* Define to the address where bug reports for this package should be sent. */ 104 | #undef PACKAGE_BUGREPORT 105 | 106 | /* Define to the full name of this package. */ 107 | #undef PACKAGE_NAME 108 | 109 | /* Define to the full name and version of this package. */ 110 | #undef PACKAGE_STRING 111 | 112 | /* Define to the one symbol short name of this package. */ 113 | #undef PACKAGE_TARNAME 114 | 115 | /* Define to the home page for this package. */ 116 | #undef PACKAGE_URL 117 | 118 | /* Define to the version of this package. */ 119 | #undef PACKAGE_VERSION 120 | 121 | /* Define to 1 if you have the ANSI C header files. */ 122 | #undef STDC_HEADERS 123 | 124 | /* Version number of package */ 125 | #undef VERSION 126 | -------------------------------------------------------------------------------- /deps/libev/configure.ac: -------------------------------------------------------------------------------- 1 | orig_CFLAGS="$CFLAGS" 2 | 3 | AC_INIT 4 | AC_CONFIG_SRCDIR([ev_epoll.c]) 5 | 6 | AM_INIT_AUTOMAKE(libev,4.11) dnl also update ev.h! 7 | AC_CONFIG_HEADERS([config.h]) 8 | AM_MAINTAINER_MODE 9 | 10 | AC_PROG_CC 11 | 12 | dnl Supply default CFLAGS, if not specified 13 | if test -z "$orig_CFLAGS"; then 14 | if test x$GCC = xyes; then 15 | CFLAGS="-g -O3" 16 | fi 17 | fi 18 | 19 | AC_PROG_INSTALL 20 | AC_PROG_LIBTOOL 21 | 22 | m4_include([libev.m4]) 23 | 24 | AC_CONFIG_FILES([Makefile]) 25 | AC_OUTPUT 26 | -------------------------------------------------------------------------------- /deps/libev/ev_epoll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev epoll fd activity backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | /* 41 | * general notes about epoll: 42 | * 43 | * a) epoll silently removes fds from the fd set. as nothing tells us 44 | * that an fd has been removed otherwise, we have to continually 45 | * "rearm" fds that we suspect *might* have changed (same 46 | * problem with kqueue, but much less costly there). 47 | * b) the fact that ADD != MOD creates a lot of extra syscalls due to a) 48 | * and seems not to have any advantage. 49 | * c) the inability to handle fork or file descriptors (think dup) 50 | * limits the applicability over poll, so this is not a generic 51 | * poll replacement. 52 | * d) epoll doesn't work the same as select with many file descriptors 53 | * (such as files). while not critical, no other advanced interface 54 | * seems to share this (rather non-unixy) limitation. 55 | * e) epoll claims to be embeddable, but in practise you never get 56 | * a ready event for the epoll fd (broken: <=2.6.26, working: >=2.6.32). 57 | * f) epoll_ctl returning EPERM means the fd is always ready. 58 | * 59 | * lots of "weird code" and complication handling in this file is due 60 | * to these design problems with epoll, as we try very hard to avoid 61 | * epoll_ctl syscalls for common usage patterns and handle the breakage 62 | * ensuing from receiving events for closed and otherwise long gone 63 | * file descriptors. 64 | */ 65 | 66 | #include 67 | 68 | #define EV_EMASK_EPERM 0x80 69 | 70 | static void 71 | epoll_modify (EV_P_ int fd, int oev, int nev) 72 | { 73 | struct epoll_event ev; 74 | unsigned char oldmask; 75 | 76 | /* 77 | * we handle EPOLL_CTL_DEL by ignoring it here 78 | * on the assumption that the fd is gone anyways 79 | * if that is wrong, we have to handle the spurious 80 | * event in epoll_poll. 81 | * if the fd is added again, we try to ADD it, and, if that 82 | * fails, we assume it still has the same eventmask. 83 | */ 84 | if (!nev) 85 | return; 86 | 87 | oldmask = anfds [fd].emask; 88 | anfds [fd].emask = nev; 89 | 90 | /* store the generation counter in the upper 32 bits, the fd in the lower 32 bits */ 91 | ev.data.u64 = (uint64_t)(uint32_t)fd 92 | | ((uint64_t)(uint32_t)++anfds [fd].egen << 32); 93 | ev.events = (nev & EV_READ ? EPOLLIN : 0) 94 | | (nev & EV_WRITE ? EPOLLOUT : 0); 95 | 96 | if (expect_true (!epoll_ctl (backend_fd, oev && oldmask != nev ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev))) 97 | return; 98 | 99 | if (expect_true (errno == ENOENT)) 100 | { 101 | /* if ENOENT then the fd went away, so try to do the right thing */ 102 | if (!nev) 103 | goto dec_egen; 104 | 105 | if (!epoll_ctl (backend_fd, EPOLL_CTL_ADD, fd, &ev)) 106 | return; 107 | } 108 | else if (expect_true (errno == EEXIST)) 109 | { 110 | /* EEXIST means we ignored a previous DEL, but the fd is still active */ 111 | /* if the kernel mask is the same as the new mask, we assume it hasn't changed */ 112 | if (oldmask == nev) 113 | goto dec_egen; 114 | 115 | if (!epoll_ctl (backend_fd, EPOLL_CTL_MOD, fd, &ev)) 116 | return; 117 | } 118 | else if (expect_true (errno == EPERM)) 119 | { 120 | /* EPERM means the fd is always ready, but epoll is too snobbish */ 121 | /* to handle it, unlike select or poll. */ 122 | anfds [fd].emask = EV_EMASK_EPERM; 123 | 124 | /* add fd to epoll_eperms, if not already inside */ 125 | if (!(oldmask & EV_EMASK_EPERM)) 126 | { 127 | array_needsize (int, epoll_eperms, epoll_epermmax, epoll_epermcnt + 1, EMPTY2); 128 | epoll_eperms [epoll_epermcnt++] = fd; 129 | } 130 | 131 | return; 132 | } 133 | 134 | fd_kill (EV_A_ fd); 135 | 136 | dec_egen: 137 | /* we didn't successfully call epoll_ctl, so decrement the generation counter again */ 138 | --anfds [fd].egen; 139 | } 140 | 141 | static void 142 | epoll_poll (EV_P_ ev_tstamp timeout) 143 | { 144 | int i; 145 | int eventcnt; 146 | 147 | if (expect_false (epoll_epermcnt)) 148 | timeout = 0.; 149 | 150 | /* epoll wait times cannot be larger than (LONG_MAX - 999UL) / HZ msecs, which is below */ 151 | /* the default libev max wait time, however. */ 152 | EV_RELEASE_CB; 153 | eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax, timeout * 1e3); 154 | EV_ACQUIRE_CB; 155 | 156 | if (expect_false (eventcnt < 0)) 157 | { 158 | if (errno != EINTR) 159 | ev_syserr ("(libev) epoll_wait"); 160 | 161 | return; 162 | } 163 | 164 | for (i = 0; i < eventcnt; ++i) 165 | { 166 | struct epoll_event *ev = epoll_events + i; 167 | 168 | int fd = (uint32_t)ev->data.u64; /* mask out the lower 32 bits */ 169 | int want = anfds [fd].events; 170 | int got = (ev->events & (EPOLLOUT | EPOLLERR | EPOLLHUP) ? EV_WRITE : 0) 171 | | (ev->events & (EPOLLIN | EPOLLERR | EPOLLHUP) ? EV_READ : 0); 172 | 173 | /* 174 | * check for spurious notification. 175 | * this only finds spurious notifications on egen updates 176 | * other spurious notifications will be found by epoll_ctl, below 177 | * we assume that fd is always in range, as we never shrink the anfds array 178 | */ 179 | if (expect_false ((uint32_t)anfds [fd].egen != (uint32_t)(ev->data.u64 >> 32))) 180 | { 181 | /* recreate kernel state */ 182 | postfork = 1; 183 | continue; 184 | } 185 | 186 | if (expect_false (got & ~want)) 187 | { 188 | anfds [fd].emask = want; 189 | 190 | /* 191 | * we received an event but are not interested in it, try mod or del 192 | * this often happens because we optimistically do not unregister fds 193 | * when we are no longer interested in them, but also when we get spurious 194 | * notifications for fds from another process. this is partially handled 195 | * above with the gencounter check (== our fd is not the event fd), and 196 | * partially here, when epoll_ctl returns an error (== a child has the fd 197 | * but we closed it). 198 | */ 199 | ev->events = (want & EV_READ ? EPOLLIN : 0) 200 | | (want & EV_WRITE ? EPOLLOUT : 0); 201 | 202 | /* pre-2.6.9 kernels require a non-null pointer with EPOLL_CTL_DEL, */ 203 | /* which is fortunately easy to do for us. */ 204 | if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev)) 205 | { 206 | postfork = 1; /* an error occurred, recreate kernel state */ 207 | continue; 208 | } 209 | } 210 | 211 | fd_event (EV_A_ fd, got); 212 | } 213 | 214 | /* if the receive array was full, increase its size */ 215 | if (expect_false (eventcnt == epoll_eventmax)) 216 | { 217 | ev_free (epoll_events); 218 | epoll_eventmax = array_nextsize (sizeof (struct epoll_event), epoll_eventmax, epoll_eventmax + 1); 219 | epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax); 220 | } 221 | 222 | /* now synthesize events for all fds where epoll fails, while select works... */ 223 | for (i = epoll_epermcnt; i--; ) 224 | { 225 | int fd = epoll_eperms [i]; 226 | unsigned char events = anfds [fd].events & (EV_READ | EV_WRITE); 227 | 228 | if (anfds [fd].emask & EV_EMASK_EPERM && events) 229 | fd_event (EV_A_ fd, events); 230 | else 231 | epoll_eperms [i] = epoll_eperms [--epoll_epermcnt]; 232 | } 233 | } 234 | 235 | int inline_size 236 | epoll_init (EV_P_ int flags) 237 | { 238 | #ifdef EPOLL_CLOEXEC 239 | backend_fd = epoll_create1 (EPOLL_CLOEXEC); 240 | 241 | if (backend_fd < 0) 242 | #endif 243 | backend_fd = epoll_create (256); 244 | 245 | if (backend_fd < 0) 246 | return 0; 247 | 248 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 249 | 250 | backend_mintime = 1e-3; /* epoll does sometimes return early, this is just to avoid the worst */ 251 | backend_modify = epoll_modify; 252 | backend_poll = epoll_poll; 253 | 254 | epoll_eventmax = 64; /* initial number of events receivable per poll */ 255 | epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax); 256 | 257 | return EVBACKEND_EPOLL; 258 | } 259 | 260 | void inline_size 261 | epoll_destroy (EV_P) 262 | { 263 | ev_free (epoll_events); 264 | array_free (epoll_eperm, EMPTY); 265 | } 266 | 267 | void inline_size 268 | epoll_fork (EV_P) 269 | { 270 | close (backend_fd); 271 | 272 | while ((backend_fd = epoll_create (256)) < 0) 273 | ev_syserr ("(libev) epoll_create"); 274 | 275 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 276 | 277 | fd_rearm_all (EV_A); 278 | } 279 | 280 | -------------------------------------------------------------------------------- /deps/libev/ev_kqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev kqueue backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | void inline_speed 47 | kqueue_change (EV_P_ int fd, int filter, int flags, int fflags) 48 | { 49 | ++kqueue_changecnt; 50 | array_needsize (struct kevent, kqueue_changes, kqueue_changemax, kqueue_changecnt, EMPTY2); 51 | 52 | EV_SET (&kqueue_changes [kqueue_changecnt - 1], fd, filter, flags, fflags, 0, 0); 53 | } 54 | 55 | /* OS X at least needs this */ 56 | #ifndef EV_ENABLE 57 | # define EV_ENABLE 0 58 | #endif 59 | #ifndef NOTE_EOF 60 | # define NOTE_EOF 0 61 | #endif 62 | 63 | static void 64 | kqueue_modify (EV_P_ int fd, int oev, int nev) 65 | { 66 | if (oev != nev) 67 | { 68 | if (oev & EV_READ) 69 | kqueue_change (EV_A_ fd, EVFILT_READ , EV_DELETE, 0); 70 | 71 | if (oev & EV_WRITE) 72 | kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_DELETE, 0); 73 | } 74 | 75 | /* to detect close/reopen reliably, we have to re-add */ 76 | /* event requests even when oev == nev */ 77 | 78 | if (nev & EV_READ) 79 | kqueue_change (EV_A_ fd, EVFILT_READ , EV_ADD | EV_ENABLE, NOTE_EOF); 80 | 81 | if (nev & EV_WRITE) 82 | kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, NOTE_EOF); 83 | } 84 | 85 | static void 86 | kqueue_poll (EV_P_ ev_tstamp timeout) 87 | { 88 | int res, i; 89 | struct timespec ts; 90 | 91 | /* need to resize so there is enough space for errors */ 92 | if (kqueue_changecnt > kqueue_eventmax) 93 | { 94 | ev_free (kqueue_events); 95 | kqueue_eventmax = array_nextsize (sizeof (struct kevent), kqueue_eventmax, kqueue_changecnt); 96 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 97 | } 98 | 99 | EV_RELEASE_CB; 100 | EV_TS_SET (ts, timeout); 101 | res = kevent (backend_fd, kqueue_changes, kqueue_changecnt, kqueue_events, kqueue_eventmax, &ts); 102 | EV_ACQUIRE_CB; 103 | kqueue_changecnt = 0; 104 | 105 | if (expect_false (res < 0)) 106 | { 107 | if (errno != EINTR) 108 | ev_syserr ("(libev) kevent"); 109 | 110 | return; 111 | } 112 | 113 | for (i = 0; i < res; ++i) 114 | { 115 | int fd = kqueue_events [i].ident; 116 | 117 | if (expect_false (kqueue_events [i].flags & EV_ERROR)) 118 | { 119 | int err = kqueue_events [i].data; 120 | 121 | /* we are only interested in errors for fds that we are interested in :) */ 122 | if (anfds [fd].events) 123 | { 124 | if (err == ENOENT) /* resubmit changes on ENOENT */ 125 | kqueue_modify (EV_A_ fd, 0, anfds [fd].events); 126 | else if (err == EBADF) /* on EBADF, we re-check the fd */ 127 | { 128 | if (fd_valid (fd)) 129 | kqueue_modify (EV_A_ fd, 0, anfds [fd].events); 130 | else 131 | fd_kill (EV_A_ fd); 132 | } 133 | else /* on all other errors, we error out on the fd */ 134 | fd_kill (EV_A_ fd); 135 | } 136 | } 137 | else 138 | fd_event ( 139 | EV_A_ 140 | fd, 141 | kqueue_events [i].filter == EVFILT_READ ? EV_READ 142 | : kqueue_events [i].filter == EVFILT_WRITE ? EV_WRITE 143 | : 0 144 | ); 145 | } 146 | 147 | if (expect_false (res == kqueue_eventmax)) 148 | { 149 | ev_free (kqueue_events); 150 | kqueue_eventmax = array_nextsize (sizeof (struct kevent), kqueue_eventmax, kqueue_eventmax + 1); 151 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 152 | } 153 | } 154 | 155 | int inline_size 156 | kqueue_init (EV_P_ int flags) 157 | { 158 | /* Initialize the kernel queue */ 159 | if ((backend_fd = kqueue ()) < 0) 160 | return 0; 161 | 162 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); /* not sure if necessary, hopefully doesn't hurt */ 163 | 164 | backend_mintime = 1e-9; /* apparently, they did the right thing in freebsd */ 165 | backend_modify = kqueue_modify; 166 | backend_poll = kqueue_poll; 167 | 168 | kqueue_eventmax = 64; /* initial number of events receivable per poll */ 169 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 170 | 171 | kqueue_changes = 0; 172 | kqueue_changemax = 0; 173 | kqueue_changecnt = 0; 174 | 175 | return EVBACKEND_KQUEUE; 176 | } 177 | 178 | void inline_size 179 | kqueue_destroy (EV_P) 180 | { 181 | ev_free (kqueue_events); 182 | ev_free (kqueue_changes); 183 | } 184 | 185 | void inline_size 186 | kqueue_fork (EV_P) 187 | { 188 | close (backend_fd); 189 | 190 | while ((backend_fd = kqueue ()) < 0) 191 | ev_syserr ("(libev) kqueue"); 192 | 193 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 194 | 195 | /* re-register interest in fds */ 196 | fd_rearm_all (EV_A); 197 | } 198 | 199 | -------------------------------------------------------------------------------- /deps/libev/ev_poll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev poll fd activity backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #include 41 | 42 | void inline_size 43 | pollidx_init (int *base, int count) 44 | { 45 | /* consider using memset (.., -1, ...), which is practically guaranteed 46 | * to work on all systems implementing poll */ 47 | while (count--) 48 | *base++ = -1; 49 | } 50 | 51 | static void 52 | poll_modify (EV_P_ int fd, int oev, int nev) 53 | { 54 | int idx; 55 | 56 | if (oev == nev) 57 | return; 58 | 59 | array_needsize (int, pollidxs, pollidxmax, fd + 1, pollidx_init); 60 | 61 | idx = pollidxs [fd]; 62 | 63 | if (idx < 0) /* need to allocate a new pollfd */ 64 | { 65 | pollidxs [fd] = idx = pollcnt++; 66 | array_needsize (struct pollfd, polls, pollmax, pollcnt, EMPTY2); 67 | polls [idx].fd = fd; 68 | } 69 | 70 | assert (polls [idx].fd == fd); 71 | 72 | if (nev) 73 | polls [idx].events = 74 | (nev & EV_READ ? POLLIN : 0) 75 | | (nev & EV_WRITE ? POLLOUT : 0); 76 | else /* remove pollfd */ 77 | { 78 | pollidxs [fd] = -1; 79 | 80 | if (expect_true (idx < --pollcnt)) 81 | { 82 | polls [idx] = polls [pollcnt]; 83 | pollidxs [polls [idx].fd] = idx; 84 | } 85 | } 86 | } 87 | 88 | static void 89 | poll_poll (EV_P_ ev_tstamp timeout) 90 | { 91 | struct pollfd *p; 92 | int res; 93 | 94 | EV_RELEASE_CB; 95 | res = poll (polls, pollcnt, timeout * 1e3); 96 | EV_ACQUIRE_CB; 97 | 98 | if (expect_false (res < 0)) 99 | { 100 | if (errno == EBADF) 101 | fd_ebadf (EV_A); 102 | else if (errno == ENOMEM && !syserr_cb) 103 | fd_enomem (EV_A); 104 | else if (errno != EINTR) 105 | ev_syserr ("(libev) poll"); 106 | } 107 | else 108 | for (p = polls; res; ++p) 109 | { 110 | assert (("libev: poll() returned illegal result, broken BSD kernel?", p < polls + pollcnt)); 111 | 112 | if (expect_false (p->revents)) /* this expect is debatable */ 113 | { 114 | --res; 115 | 116 | if (expect_false (p->revents & POLLNVAL)) 117 | fd_kill (EV_A_ p->fd); 118 | else 119 | fd_event ( 120 | EV_A_ 121 | p->fd, 122 | (p->revents & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0) 123 | | (p->revents & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0) 124 | ); 125 | } 126 | } 127 | } 128 | 129 | int inline_size 130 | poll_init (EV_P_ int flags) 131 | { 132 | backend_mintime = 1e-3; 133 | backend_modify = poll_modify; 134 | backend_poll = poll_poll; 135 | 136 | pollidxs = 0; pollidxmax = 0; 137 | polls = 0; pollmax = 0; pollcnt = 0; 138 | 139 | return EVBACKEND_POLL; 140 | } 141 | 142 | void inline_size 143 | poll_destroy (EV_P) 144 | { 145 | ev_free (pollidxs); 146 | ev_free (polls); 147 | } 148 | 149 | -------------------------------------------------------------------------------- /deps/libev/ev_port.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev solaris event port backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | /* useful reading: 41 | * 42 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6268715 (random results) 43 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6455223 (just totally broken) 44 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6873782 (manpage ETIME) 45 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6874410 (implementation ETIME) 46 | * http://www.mail-archive.com/networking-discuss@opensolaris.org/msg11898.html ETIME vs. nget 47 | * http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/event_port.c (libc) 48 | * http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/fs/portfs/port.c#1325 (kernel) 49 | */ 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | void inline_speed 59 | port_associate_and_check (EV_P_ int fd, int ev) 60 | { 61 | if (0 > 62 | port_associate ( 63 | backend_fd, PORT_SOURCE_FD, fd, 64 | (ev & EV_READ ? POLLIN : 0) 65 | | (ev & EV_WRITE ? POLLOUT : 0), 66 | 0 67 | ) 68 | ) 69 | { 70 | if (errno == EBADFD) 71 | fd_kill (EV_A_ fd); 72 | else 73 | ev_syserr ("(libev) port_associate"); 74 | } 75 | } 76 | 77 | static void 78 | port_modify (EV_P_ int fd, int oev, int nev) 79 | { 80 | /* we need to reassociate no matter what, as closes are 81 | * once more silently being discarded. 82 | */ 83 | if (!nev) 84 | { 85 | if (oev) 86 | port_dissociate (backend_fd, PORT_SOURCE_FD, fd); 87 | } 88 | else 89 | port_associate_and_check (EV_A_ fd, nev); 90 | } 91 | 92 | static void 93 | port_poll (EV_P_ ev_tstamp timeout) 94 | { 95 | int res, i; 96 | struct timespec ts; 97 | uint_t nget = 1; 98 | 99 | /* we initialise this to something we will skip in the loop, as */ 100 | /* port_getn can return with nget unchanged, but no indication */ 101 | /* whether it was the original value or has been updated :/ */ 102 | port_events [0].portev_source = 0; 103 | 104 | EV_RELEASE_CB; 105 | EV_TS_SET (ts, timeout); 106 | res = port_getn (backend_fd, port_events, port_eventmax, &nget, &ts); 107 | EV_ACQUIRE_CB; 108 | 109 | /* port_getn may or may not set nget on error */ 110 | /* so we rely on port_events [0].portev_source not being updated */ 111 | if (res == -1 && errno != ETIME && errno != EINTR) 112 | ev_syserr ("(libev) port_getn (see http://bugs.opensolaris.org/view_bug.do?bug_id=6268715, try LIBEV_FLAGS=3 env variable)"); 113 | 114 | for (i = 0; i < nget; ++i) 115 | { 116 | if (port_events [i].portev_source == PORT_SOURCE_FD) 117 | { 118 | int fd = port_events [i].portev_object; 119 | 120 | fd_event ( 121 | EV_A_ 122 | fd, 123 | (port_events [i].portev_events & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0) 124 | | (port_events [i].portev_events & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0) 125 | ); 126 | 127 | fd_change (EV_A_ fd, EV__IOFDSET); 128 | } 129 | } 130 | 131 | if (expect_false (nget == port_eventmax)) 132 | { 133 | ev_free (port_events); 134 | port_eventmax = array_nextsize (sizeof (port_event_t), port_eventmax, port_eventmax + 1); 135 | port_events = (port_event_t *)ev_malloc (sizeof (port_event_t) * port_eventmax); 136 | } 137 | } 138 | 139 | int inline_size 140 | port_init (EV_P_ int flags) 141 | { 142 | /* Initialize the kernel queue */ 143 | if ((backend_fd = port_create ()) < 0) 144 | return 0; 145 | 146 | assert (("libev: PORT_SOURCE_FD must not be zero", PORT_SOURCE_FD)); 147 | 148 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); /* not sure if necessary, hopefully doesn't hurt */ 149 | 150 | /* if my reading of the opensolaris kernel sources are correct, then 151 | * opensolaris does something very stupid: it checks if the time has already 152 | * elapsed and doesn't round up if that is the case,m otherwise it DOES round 153 | * up. Since we can't know what the case is, we need to guess by using a 154 | * "large enough" timeout. Normally, 1e-9 would be correct. 155 | */ 156 | backend_mintime = 1e-3; /* needed to compensate for port_getn returning early */ 157 | backend_modify = port_modify; 158 | backend_poll = port_poll; 159 | 160 | port_eventmax = 64; /* initial number of events receivable per poll */ 161 | port_events = (port_event_t *)ev_malloc (sizeof (port_event_t) * port_eventmax); 162 | 163 | return EVBACKEND_PORT; 164 | } 165 | 166 | void inline_size 167 | port_destroy (EV_P) 168 | { 169 | ev_free (port_events); 170 | } 171 | 172 | void inline_size 173 | port_fork (EV_P) 174 | { 175 | close (backend_fd); 176 | 177 | while ((backend_fd = port_create ()) < 0) 178 | ev_syserr ("(libev) port"); 179 | 180 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 181 | 182 | /* re-register interest in fds */ 183 | fd_rearm_all (EV_A); 184 | } 185 | 186 | -------------------------------------------------------------------------------- /deps/libev/ev_select.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev select fd activity backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifndef _WIN32 41 | /* for unix systems */ 42 | # include 43 | # ifndef __hpux 44 | /* for REAL unix systems */ 45 | # include 46 | # endif 47 | #endif 48 | 49 | #ifndef EV_SELECT_USE_FD_SET 50 | # ifdef NFDBITS 51 | # define EV_SELECT_USE_FD_SET 0 52 | # else 53 | # define EV_SELECT_USE_FD_SET 1 54 | # endif 55 | #endif 56 | 57 | #if EV_SELECT_IS_WINSOCKET 58 | # undef EV_SELECT_USE_FD_SET 59 | # define EV_SELECT_USE_FD_SET 1 60 | # undef NFDBITS 61 | # define NFDBITS 0 62 | #endif 63 | 64 | #if !EV_SELECT_USE_FD_SET 65 | # define NFDBYTES (NFDBITS / 8) 66 | #endif 67 | 68 | #include 69 | 70 | static void 71 | select_modify (EV_P_ int fd, int oev, int nev) 72 | { 73 | if (oev == nev) 74 | return; 75 | 76 | { 77 | #if EV_SELECT_USE_FD_SET 78 | 79 | #if EV_SELECT_IS_WINSOCKET 80 | SOCKET handle = anfds [fd].handle; 81 | #else 82 | int handle = fd; 83 | #endif 84 | 85 | assert (("libev: fd >= FD_SETSIZE passed to fd_set-based select backend", fd < FD_SETSIZE)); 86 | 87 | /* FD_SET is broken on windows (it adds the fd to a set twice or more, 88 | * which eventually leads to overflows). Need to call it only on changes. 89 | */ 90 | #if EV_SELECT_IS_WINSOCKET 91 | if ((oev ^ nev) & EV_READ) 92 | #endif 93 | if (nev & EV_READ) 94 | FD_SET (handle, (fd_set *)vec_ri); 95 | else 96 | FD_CLR (handle, (fd_set *)vec_ri); 97 | 98 | #if EV_SELECT_IS_WINSOCKET 99 | if ((oev ^ nev) & EV_WRITE) 100 | #endif 101 | if (nev & EV_WRITE) 102 | FD_SET (handle, (fd_set *)vec_wi); 103 | else 104 | FD_CLR (handle, (fd_set *)vec_wi); 105 | 106 | #else 107 | 108 | int word = fd / NFDBITS; 109 | fd_mask mask = 1UL << (fd % NFDBITS); 110 | 111 | if (expect_false (vec_max <= word)) 112 | { 113 | int new_max = word + 1; 114 | 115 | vec_ri = ev_realloc (vec_ri, new_max * NFDBYTES); 116 | vec_ro = ev_realloc (vec_ro, new_max * NFDBYTES); /* could free/malloc */ 117 | vec_wi = ev_realloc (vec_wi, new_max * NFDBYTES); 118 | vec_wo = ev_realloc (vec_wo, new_max * NFDBYTES); /* could free/malloc */ 119 | #ifdef _WIN32 120 | vec_eo = ev_realloc (vec_eo, new_max * NFDBYTES); /* could free/malloc */ 121 | #endif 122 | 123 | for (; vec_max < new_max; ++vec_max) 124 | ((fd_mask *)vec_ri) [vec_max] = 125 | ((fd_mask *)vec_wi) [vec_max] = 0; 126 | } 127 | 128 | ((fd_mask *)vec_ri) [word] |= mask; 129 | if (!(nev & EV_READ)) 130 | ((fd_mask *)vec_ri) [word] &= ~mask; 131 | 132 | ((fd_mask *)vec_wi) [word] |= mask; 133 | if (!(nev & EV_WRITE)) 134 | ((fd_mask *)vec_wi) [word] &= ~mask; 135 | #endif 136 | } 137 | } 138 | 139 | static void 140 | select_poll (EV_P_ ev_tstamp timeout) 141 | { 142 | struct timeval tv; 143 | int res; 144 | int fd_setsize; 145 | 146 | EV_RELEASE_CB; 147 | EV_TV_SET (tv, timeout); 148 | 149 | #if EV_SELECT_USE_FD_SET 150 | fd_setsize = sizeof (fd_set); 151 | #else 152 | fd_setsize = vec_max * NFDBYTES; 153 | #endif 154 | 155 | memcpy (vec_ro, vec_ri, fd_setsize); 156 | memcpy (vec_wo, vec_wi, fd_setsize); 157 | 158 | #ifdef _WIN32 159 | /* pass in the write set as except set. 160 | * the idea behind this is to work around a windows bug that causes 161 | * errors to be reported as an exception and not by setting 162 | * the writable bit. this is so uncontrollably lame. 163 | */ 164 | memcpy (vec_eo, vec_wi, fd_setsize); 165 | res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, (fd_set *)vec_eo, &tv); 166 | #elif EV_SELECT_USE_FD_SET 167 | fd_setsize = anfdmax < FD_SETSIZE ? anfdmax : FD_SETSIZE; 168 | res = select (fd_setsize, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv); 169 | #else 170 | res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv); 171 | #endif 172 | EV_ACQUIRE_CB; 173 | 174 | if (expect_false (res < 0)) 175 | { 176 | #if EV_SELECT_IS_WINSOCKET 177 | errno = WSAGetLastError (); 178 | #endif 179 | #ifdef WSABASEERR 180 | /* on windows, select returns incompatible error codes, fix this */ 181 | if (errno >= WSABASEERR && errno < WSABASEERR + 1000) 182 | if (errno == WSAENOTSOCK) 183 | errno = EBADF; 184 | else 185 | errno -= WSABASEERR; 186 | #endif 187 | 188 | #ifdef _WIN32 189 | /* select on windows erroneously returns EINVAL when no fd sets have been 190 | * provided (this is documented). what microsoft doesn't tell you that this bug 191 | * exists even when the fd sets _are_ provided, so we have to check for this bug 192 | * here and emulate by sleeping manually. 193 | * we also get EINVAL when the timeout is invalid, but we ignore this case here 194 | * and assume that EINVAL always means: you have to wait manually. 195 | */ 196 | if (errno == EINVAL) 197 | { 198 | if (timeout) 199 | { 200 | unsigned long ms = timeout * 1e3; 201 | Sleep (ms ? ms : 1); 202 | } 203 | 204 | return; 205 | } 206 | #endif 207 | 208 | if (errno == EBADF) 209 | fd_ebadf (EV_A); 210 | else if (errno == ENOMEM && !syserr_cb) 211 | fd_enomem (EV_A); 212 | else if (errno != EINTR) 213 | ev_syserr ("(libev) select"); 214 | 215 | return; 216 | } 217 | 218 | #if EV_SELECT_USE_FD_SET 219 | 220 | { 221 | int fd; 222 | 223 | for (fd = 0; fd < anfdmax; ++fd) 224 | if (anfds [fd].events) 225 | { 226 | int events = 0; 227 | #if EV_SELECT_IS_WINSOCKET 228 | SOCKET handle = anfds [fd].handle; 229 | #else 230 | int handle = fd; 231 | #endif 232 | 233 | if (FD_ISSET (handle, (fd_set *)vec_ro)) events |= EV_READ; 234 | if (FD_ISSET (handle, (fd_set *)vec_wo)) events |= EV_WRITE; 235 | #ifdef _WIN32 236 | if (FD_ISSET (handle, (fd_set *)vec_eo)) events |= EV_WRITE; 237 | #endif 238 | 239 | if (expect_true (events)) 240 | fd_event (EV_A_ fd, events); 241 | } 242 | } 243 | 244 | #else 245 | 246 | { 247 | int word, bit; 248 | for (word = vec_max; word--; ) 249 | { 250 | fd_mask word_r = ((fd_mask *)vec_ro) [word]; 251 | fd_mask word_w = ((fd_mask *)vec_wo) [word]; 252 | #ifdef _WIN32 253 | word_w |= ((fd_mask *)vec_eo) [word]; 254 | #endif 255 | 256 | if (word_r || word_w) 257 | for (bit = NFDBITS; bit--; ) 258 | { 259 | fd_mask mask = 1UL << bit; 260 | int events = 0; 261 | 262 | events |= word_r & mask ? EV_READ : 0; 263 | events |= word_w & mask ? EV_WRITE : 0; 264 | 265 | if (expect_true (events)) 266 | fd_event (EV_A_ word * NFDBITS + bit, events); 267 | } 268 | } 269 | } 270 | 271 | #endif 272 | } 273 | 274 | int inline_size 275 | select_init (EV_P_ int flags) 276 | { 277 | backend_mintime = 1e-6; 278 | backend_modify = select_modify; 279 | backend_poll = select_poll; 280 | 281 | #if EV_SELECT_USE_FD_SET 282 | vec_ri = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri); 283 | vec_ro = ev_malloc (sizeof (fd_set)); 284 | vec_wi = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_wi); 285 | vec_wo = ev_malloc (sizeof (fd_set)); 286 | #ifdef _WIN32 287 | vec_eo = ev_malloc (sizeof (fd_set)); 288 | #endif 289 | #else 290 | vec_max = 0; 291 | vec_ri = 0; 292 | vec_ro = 0; 293 | vec_wi = 0; 294 | vec_wo = 0; 295 | #ifdef _WIN32 296 | vec_eo = 0; 297 | #endif 298 | #endif 299 | 300 | return EVBACKEND_SELECT; 301 | } 302 | 303 | void inline_size 304 | select_destroy (EV_P) 305 | { 306 | ev_free (vec_ri); 307 | ev_free (vec_ro); 308 | ev_free (vec_wi); 309 | ev_free (vec_wo); 310 | #ifdef _WIN32 311 | ev_free (vec_eo); 312 | #endif 313 | } 314 | 315 | -------------------------------------------------------------------------------- /deps/libev/ev_vars.h: -------------------------------------------------------------------------------- 1 | /* 2 | * loop member variable declarations 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #define VARx(type,name) VAR(name, type name) 41 | 42 | VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */ 43 | VARx(ev_tstamp, mn_now) /* monotonic clock "now" */ 44 | VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time */ 45 | 46 | VARx(ev_tstamp, io_blocktime) 47 | VARx(ev_tstamp, timeout_blocktime) 48 | 49 | VARx(int, backend) 50 | VARx(int, activecnt) /* total number of active events ("refcount") */ 51 | VARx(EV_ATOMIC_T, loop_done) /* signal by ev_break */ 52 | 53 | VARx(int, backend_fd) 54 | VARx(ev_tstamp, backend_mintime) /* assumed typical timer resolution */ 55 | VAR (backend_modify, void (*backend_modify)(EV_P_ int fd, int oev, int nev)) 56 | VAR (backend_poll , void (*backend_poll)(EV_P_ ev_tstamp timeout)) 57 | 58 | VARx(ANFD *, anfds) 59 | VARx(int, anfdmax) 60 | 61 | VAR (pendings, ANPENDING *pendings [NUMPRI]) 62 | VAR (pendingmax, int pendingmax [NUMPRI]) 63 | VAR (pendingcnt, int pendingcnt [NUMPRI]) 64 | VARx(ev_prepare, pending_w) /* dummy pending watcher */ 65 | 66 | /* for reverse feeding of events */ 67 | VARx(W *, rfeeds) 68 | VARx(int, rfeedmax) 69 | VARx(int, rfeedcnt) 70 | 71 | #if EV_USE_EVENTFD || EV_GENWRAP 72 | VARx(int, evfd) 73 | #endif 74 | VAR (evpipe, int evpipe [2]) 75 | VARx(ev_io, pipe_w) 76 | VARx(EV_ATOMIC_T, pipe_write_wanted) 77 | VARx(EV_ATOMIC_T, pipe_write_skipped) 78 | 79 | #if !defined(_WIN32) || EV_GENWRAP 80 | VARx(pid_t, curpid) 81 | #endif 82 | 83 | VARx(char, postfork) /* true if we need to recreate kernel state after fork */ 84 | 85 | #if EV_USE_SELECT || EV_GENWRAP 86 | VARx(void *, vec_ri) 87 | VARx(void *, vec_ro) 88 | VARx(void *, vec_wi) 89 | VARx(void *, vec_wo) 90 | #if defined(_WIN32) || EV_GENWRAP 91 | VARx(void *, vec_eo) 92 | #endif 93 | VARx(int, vec_max) 94 | #endif 95 | 96 | #if EV_USE_POLL || EV_GENWRAP 97 | VARx(struct pollfd *, polls) 98 | VARx(int, pollmax) 99 | VARx(int, pollcnt) 100 | VARx(int *, pollidxs) /* maps fds into structure indices */ 101 | VARx(int, pollidxmax) 102 | #endif 103 | 104 | #if EV_USE_EPOLL || EV_GENWRAP 105 | VARx(struct epoll_event *, epoll_events) 106 | VARx(int, epoll_eventmax) 107 | VARx(int *, epoll_eperms) 108 | VARx(int, epoll_epermcnt) 109 | VARx(int, epoll_epermmax) 110 | #endif 111 | 112 | #if EV_USE_KQUEUE || EV_GENWRAP 113 | VARx(struct kevent *, kqueue_changes) 114 | VARx(int, kqueue_changemax) 115 | VARx(int, kqueue_changecnt) 116 | VARx(struct kevent *, kqueue_events) 117 | VARx(int, kqueue_eventmax) 118 | #endif 119 | 120 | #if EV_USE_PORT || EV_GENWRAP 121 | VARx(struct port_event *, port_events) 122 | VARx(int, port_eventmax) 123 | #endif 124 | 125 | #if EV_USE_IOCP || EV_GENWRAP 126 | VARx(HANDLE, iocp) 127 | #endif 128 | 129 | VARx(int *, fdchanges) 130 | VARx(int, fdchangemax) 131 | VARx(int, fdchangecnt) 132 | 133 | VARx(ANHE *, timers) 134 | VARx(int, timermax) 135 | VARx(int, timercnt) 136 | 137 | #if EV_PERIODIC_ENABLE || EV_GENWRAP 138 | VARx(ANHE *, periodics) 139 | VARx(int, periodicmax) 140 | VARx(int, periodiccnt) 141 | #endif 142 | 143 | #if EV_IDLE_ENABLE || EV_GENWRAP 144 | VAR (idles, ev_idle **idles [NUMPRI]) 145 | VAR (idlemax, int idlemax [NUMPRI]) 146 | VAR (idlecnt, int idlecnt [NUMPRI]) 147 | #endif 148 | VARx(int, idleall) /* total number */ 149 | 150 | VARx(struct ev_prepare **, prepares) 151 | VARx(int, preparemax) 152 | VARx(int, preparecnt) 153 | 154 | VARx(struct ev_check **, checks) 155 | VARx(int, checkmax) 156 | VARx(int, checkcnt) 157 | 158 | #if EV_FORK_ENABLE || EV_GENWRAP 159 | VARx(struct ev_fork **, forks) 160 | VARx(int, forkmax) 161 | VARx(int, forkcnt) 162 | #endif 163 | 164 | #if EV_CLEANUP_ENABLE || EV_GENWRAP 165 | VARx(struct ev_cleanup **, cleanups) 166 | VARx(int, cleanupmax) 167 | VARx(int, cleanupcnt) 168 | #endif 169 | 170 | #if EV_ASYNC_ENABLE || EV_GENWRAP 171 | VARx(EV_ATOMIC_T, async_pending) 172 | VARx(struct ev_async **, asyncs) 173 | VARx(int, asyncmax) 174 | VARx(int, asynccnt) 175 | #endif 176 | 177 | #if EV_USE_INOTIFY || EV_GENWRAP 178 | VARx(int, fs_fd) 179 | VARx(ev_io, fs_w) 180 | VARx(char, fs_2625) /* whether we are running in linux 2.6.25 or newer */ 181 | VAR (fs_hash, ANFS fs_hash [EV_INOTIFY_HASHSIZE]) 182 | #endif 183 | 184 | VARx(EV_ATOMIC_T, sig_pending) 185 | #if EV_USE_SIGNALFD || EV_GENWRAP 186 | VARx(int, sigfd) 187 | VARx(ev_io, sigfd_w) 188 | VARx(sigset_t, sigfd_set) 189 | #endif 190 | 191 | VARx(unsigned int, origflags) /* original loop flags */ 192 | 193 | #if EV_FEATURE_API || EV_GENWRAP 194 | VARx(unsigned int, loop_count) /* total number of loop iterations/blocks */ 195 | VARx(unsigned int, loop_depth) /* #ev_run enters - #ev_run leaves */ 196 | 197 | VARx(void *, userdata) 198 | VAR (release_cb, void (*release_cb)(EV_P)) 199 | VAR (acquire_cb, void (*acquire_cb)(EV_P)) 200 | VAR (invoke_cb , void (*invoke_cb) (EV_P)) 201 | #endif 202 | 203 | #undef VARx 204 | 205 | -------------------------------------------------------------------------------- /deps/libev/ev_win32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev win32 compatibility cruft (_not_ a backend) 3 | * 4 | * Copyright (c) 2007,2008,2009 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifdef _WIN32 41 | 42 | /* timeb.h is actually xsi legacy functionality */ 43 | #include 44 | 45 | /* note: the comment below could not be substantiated, but what would I care */ 46 | /* MSDN says this is required to handle SIGFPE */ 47 | /* my wild guess would be that using something floating-pointy is required */ 48 | /* for the crt to do something about it */ 49 | volatile double SIGFPE_REQ = 0.0f; 50 | 51 | /* oh, the humanity! */ 52 | static int 53 | ev_pipe (int filedes [2]) 54 | { 55 | struct sockaddr_in addr = { 0 }; 56 | int addr_size = sizeof (addr); 57 | struct sockaddr_in adr2; 58 | int adr2_size = sizeof (adr2); 59 | SOCKET listener; 60 | SOCKET sock [2] = { -1, -1 }; 61 | 62 | if ((listener = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) 63 | return -1; 64 | 65 | addr.sin_family = AF_INET; 66 | addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 67 | addr.sin_port = 0; 68 | 69 | if (bind (listener, (struct sockaddr *)&addr, addr_size)) 70 | goto fail; 71 | 72 | if (getsockname (listener, (struct sockaddr *)&addr, &addr_size)) 73 | goto fail; 74 | 75 | if (listen (listener, 1)) 76 | goto fail; 77 | 78 | if ((sock [0] = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) 79 | goto fail; 80 | 81 | if (connect (sock [0], (struct sockaddr *)&addr, addr_size)) 82 | goto fail; 83 | 84 | if ((sock [1] = accept (listener, 0, 0)) < 0) 85 | goto fail; 86 | 87 | /* windows vista returns fantasy port numbers for sockets: 88 | * example for two interconnected tcp sockets: 89 | * 90 | * (Socket::unpack_sockaddr_in getsockname $sock0)[0] == 53364 91 | * (Socket::unpack_sockaddr_in getpeername $sock0)[0] == 53363 92 | * (Socket::unpack_sockaddr_in getsockname $sock1)[0] == 53363 93 | * (Socket::unpack_sockaddr_in getpeername $sock1)[0] == 53365 94 | * 95 | * wow! tridirectional sockets! 96 | * 97 | * this way of checking ports seems to work: 98 | */ 99 | if (getpeername (sock [0], (struct sockaddr *)&addr, &addr_size)) 100 | goto fail; 101 | 102 | if (getsockname (sock [1], (struct sockaddr *)&adr2, &adr2_size)) 103 | goto fail; 104 | 105 | errno = WSAEINVAL; 106 | if (addr_size != adr2_size 107 | || addr.sin_addr.s_addr != adr2.sin_addr.s_addr /* just to be sure, I mean, it's windows */ 108 | || addr.sin_port != adr2.sin_port) 109 | goto fail; 110 | 111 | closesocket (listener); 112 | 113 | #if EV_SELECT_IS_WINSOCKET 114 | filedes [0] = EV_WIN32_HANDLE_TO_FD (sock [0]); 115 | filedes [1] = EV_WIN32_HANDLE_TO_FD (sock [1]); 116 | #else 117 | /* when select isn't winsocket, we also expect socket, connect, accept etc. 118 | * to work on fds */ 119 | filedes [0] = sock [0]; 120 | filedes [1] = sock [1]; 121 | #endif 122 | 123 | return 0; 124 | 125 | fail: 126 | closesocket (listener); 127 | 128 | if (sock [0] != INVALID_SOCKET) closesocket (sock [0]); 129 | if (sock [1] != INVALID_SOCKET) closesocket (sock [1]); 130 | 131 | return -1; 132 | } 133 | 134 | #undef pipe 135 | #define pipe(filedes) ev_pipe (filedes) 136 | 137 | #define EV_HAVE_EV_TIME 1 138 | ev_tstamp 139 | ev_time (void) 140 | { 141 | FILETIME ft; 142 | ULARGE_INTEGER ui; 143 | 144 | GetSystemTimeAsFileTime (&ft); 145 | ui.u.LowPart = ft.dwLowDateTime; 146 | ui.u.HighPart = ft.dwHighDateTime; 147 | 148 | /* msvc cannot convert ulonglong to double... yes, it is that sucky */ 149 | return (LONGLONG)(ui.QuadPart - 116444736000000000) * 1e-7; 150 | } 151 | 152 | #endif 153 | 154 | -------------------------------------------------------------------------------- /deps/libev/ev_wrap.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT, automatically generated by update_ev_wrap */ 2 | #ifndef EV_WRAP_H 3 | #define EV_WRAP_H 4 | #define now_floor ((loop)->now_floor) 5 | #define mn_now ((loop)->mn_now) 6 | #define rtmn_diff ((loop)->rtmn_diff) 7 | #define io_blocktime ((loop)->io_blocktime) 8 | #define timeout_blocktime ((loop)->timeout_blocktime) 9 | #define backend ((loop)->backend) 10 | #define activecnt ((loop)->activecnt) 11 | #define loop_done ((loop)->loop_done) 12 | #define backend_fd ((loop)->backend_fd) 13 | #define backend_mintime ((loop)->backend_mintime) 14 | #define backend_modify ((loop)->backend_modify) 15 | #define backend_poll ((loop)->backend_poll) 16 | #define anfds ((loop)->anfds) 17 | #define anfdmax ((loop)->anfdmax) 18 | #define pendings ((loop)->pendings) 19 | #define pendingmax ((loop)->pendingmax) 20 | #define pendingcnt ((loop)->pendingcnt) 21 | #define pending_w ((loop)->pending_w) 22 | #define rfeeds ((loop)->rfeeds) 23 | #define rfeedmax ((loop)->rfeedmax) 24 | #define rfeedcnt ((loop)->rfeedcnt) 25 | #define evfd ((loop)->evfd) 26 | #define evpipe ((loop)->evpipe) 27 | #define pipe_w ((loop)->pipe_w) 28 | #define pipe_write_wanted ((loop)->pipe_write_wanted) 29 | #define pipe_write_skipped ((loop)->pipe_write_skipped) 30 | #define curpid ((loop)->curpid) 31 | #define postfork ((loop)->postfork) 32 | #define vec_ri ((loop)->vec_ri) 33 | #define vec_ro ((loop)->vec_ro) 34 | #define vec_wi ((loop)->vec_wi) 35 | #define vec_wo ((loop)->vec_wo) 36 | #define vec_eo ((loop)->vec_eo) 37 | #define vec_max ((loop)->vec_max) 38 | #define polls ((loop)->polls) 39 | #define pollmax ((loop)->pollmax) 40 | #define pollcnt ((loop)->pollcnt) 41 | #define pollidxs ((loop)->pollidxs) 42 | #define pollidxmax ((loop)->pollidxmax) 43 | #define epoll_events ((loop)->epoll_events) 44 | #define epoll_eventmax ((loop)->epoll_eventmax) 45 | #define epoll_eperms ((loop)->epoll_eperms) 46 | #define epoll_epermcnt ((loop)->epoll_epermcnt) 47 | #define epoll_epermmax ((loop)->epoll_epermmax) 48 | #define kqueue_changes ((loop)->kqueue_changes) 49 | #define kqueue_changemax ((loop)->kqueue_changemax) 50 | #define kqueue_changecnt ((loop)->kqueue_changecnt) 51 | #define kqueue_events ((loop)->kqueue_events) 52 | #define kqueue_eventmax ((loop)->kqueue_eventmax) 53 | #define port_events ((loop)->port_events) 54 | #define port_eventmax ((loop)->port_eventmax) 55 | #define iocp ((loop)->iocp) 56 | #define fdchanges ((loop)->fdchanges) 57 | #define fdchangemax ((loop)->fdchangemax) 58 | #define fdchangecnt ((loop)->fdchangecnt) 59 | #define timers ((loop)->timers) 60 | #define timermax ((loop)->timermax) 61 | #define timercnt ((loop)->timercnt) 62 | #define periodics ((loop)->periodics) 63 | #define periodicmax ((loop)->periodicmax) 64 | #define periodiccnt ((loop)->periodiccnt) 65 | #define idles ((loop)->idles) 66 | #define idlemax ((loop)->idlemax) 67 | #define idlecnt ((loop)->idlecnt) 68 | #define idleall ((loop)->idleall) 69 | #define prepares ((loop)->prepares) 70 | #define preparemax ((loop)->preparemax) 71 | #define preparecnt ((loop)->preparecnt) 72 | #define checks ((loop)->checks) 73 | #define checkmax ((loop)->checkmax) 74 | #define checkcnt ((loop)->checkcnt) 75 | #define forks ((loop)->forks) 76 | #define forkmax ((loop)->forkmax) 77 | #define forkcnt ((loop)->forkcnt) 78 | #define cleanups ((loop)->cleanups) 79 | #define cleanupmax ((loop)->cleanupmax) 80 | #define cleanupcnt ((loop)->cleanupcnt) 81 | #define async_pending ((loop)->async_pending) 82 | #define asyncs ((loop)->asyncs) 83 | #define asyncmax ((loop)->asyncmax) 84 | #define asynccnt ((loop)->asynccnt) 85 | #define fs_fd ((loop)->fs_fd) 86 | #define fs_w ((loop)->fs_w) 87 | #define fs_2625 ((loop)->fs_2625) 88 | #define fs_hash ((loop)->fs_hash) 89 | #define sig_pending ((loop)->sig_pending) 90 | #define sigfd ((loop)->sigfd) 91 | #define sigfd_w ((loop)->sigfd_w) 92 | #define sigfd_set ((loop)->sigfd_set) 93 | #define origflags ((loop)->origflags) 94 | #define loop_count ((loop)->loop_count) 95 | #define loop_depth ((loop)->loop_depth) 96 | #define userdata ((loop)->userdata) 97 | #define release_cb ((loop)->release_cb) 98 | #define acquire_cb ((loop)->acquire_cb) 99 | #define invoke_cb ((loop)->invoke_cb) 100 | #else 101 | #undef EV_WRAP_H 102 | #undef now_floor 103 | #undef mn_now 104 | #undef rtmn_diff 105 | #undef io_blocktime 106 | #undef timeout_blocktime 107 | #undef backend 108 | #undef activecnt 109 | #undef loop_done 110 | #undef backend_fd 111 | #undef backend_mintime 112 | #undef backend_modify 113 | #undef backend_poll 114 | #undef anfds 115 | #undef anfdmax 116 | #undef pendings 117 | #undef pendingmax 118 | #undef pendingcnt 119 | #undef pending_w 120 | #undef rfeeds 121 | #undef rfeedmax 122 | #undef rfeedcnt 123 | #undef evfd 124 | #undef evpipe 125 | #undef pipe_w 126 | #undef pipe_write_wanted 127 | #undef pipe_write_skipped 128 | #undef curpid 129 | #undef postfork 130 | #undef vec_ri 131 | #undef vec_ro 132 | #undef vec_wi 133 | #undef vec_wo 134 | #undef vec_eo 135 | #undef vec_max 136 | #undef polls 137 | #undef pollmax 138 | #undef pollcnt 139 | #undef pollidxs 140 | #undef pollidxmax 141 | #undef epoll_events 142 | #undef epoll_eventmax 143 | #undef epoll_eperms 144 | #undef epoll_epermcnt 145 | #undef epoll_epermmax 146 | #undef kqueue_changes 147 | #undef kqueue_changemax 148 | #undef kqueue_changecnt 149 | #undef kqueue_events 150 | #undef kqueue_eventmax 151 | #undef port_events 152 | #undef port_eventmax 153 | #undef iocp 154 | #undef fdchanges 155 | #undef fdchangemax 156 | #undef fdchangecnt 157 | #undef timers 158 | #undef timermax 159 | #undef timercnt 160 | #undef periodics 161 | #undef periodicmax 162 | #undef periodiccnt 163 | #undef idles 164 | #undef idlemax 165 | #undef idlecnt 166 | #undef idleall 167 | #undef prepares 168 | #undef preparemax 169 | #undef preparecnt 170 | #undef checks 171 | #undef checkmax 172 | #undef checkcnt 173 | #undef forks 174 | #undef forkmax 175 | #undef forkcnt 176 | #undef cleanups 177 | #undef cleanupmax 178 | #undef cleanupcnt 179 | #undef async_pending 180 | #undef asyncs 181 | #undef asyncmax 182 | #undef asynccnt 183 | #undef fs_fd 184 | #undef fs_w 185 | #undef fs_2625 186 | #undef fs_hash 187 | #undef sig_pending 188 | #undef sigfd 189 | #undef sigfd_w 190 | #undef sigfd_set 191 | #undef origflags 192 | #undef loop_count 193 | #undef loop_depth 194 | #undef userdata 195 | #undef release_cb 196 | #undef acquire_cb 197 | #undef invoke_cb 198 | #endif 199 | -------------------------------------------------------------------------------- /deps/libev/event.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libevent compatibility layer 3 | * 4 | * Copyright (c) 2007,2008,2009,2010 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | #ifdef EV_EVENT_H 45 | # include EV_EVENT_H 46 | #else 47 | # include "event.h" 48 | #endif 49 | 50 | #if EV_MULTIPLICITY 51 | # define dLOOPev struct ev_loop *loop = (struct ev_loop *)ev->ev_base 52 | # define dLOOPbase struct ev_loop *loop = (struct ev_loop *)base 53 | #else 54 | # define dLOOPev 55 | # define dLOOPbase 56 | #endif 57 | 58 | /* never accessed, will always be cast from/to ev_loop */ 59 | struct event_base 60 | { 61 | int dummy; 62 | }; 63 | 64 | static struct event_base *ev_x_cur; 65 | 66 | static ev_tstamp 67 | ev_tv_get (struct timeval *tv) 68 | { 69 | if (tv) 70 | { 71 | ev_tstamp after = tv->tv_sec + tv->tv_usec * 1e-6; 72 | return after ? after : 1e-6; 73 | } 74 | else 75 | return -1.; 76 | } 77 | 78 | #define EVENT_STRINGIFY(s) # s 79 | #define EVENT_VERSION(a,b) EVENT_STRINGIFY (a) "." EVENT_STRINGIFY (b) 80 | 81 | const char *event_get_version (void) 82 | { 83 | /* returns ABI, not API or library, version */ 84 | return EVENT_VERSION (EV_VERSION_MAJOR, EV_VERSION_MINOR); 85 | } 86 | 87 | const char *event_get_method (void) 88 | { 89 | return "libev"; 90 | } 91 | 92 | void *event_init (void) 93 | { 94 | #if EV_MULTIPLICITY 95 | if (ev_x_cur) 96 | ev_x_cur = (struct event_base *)ev_loop_new (EVFLAG_AUTO); 97 | else 98 | ev_x_cur = (struct event_base *)ev_default_loop (EVFLAG_AUTO); 99 | #else 100 | assert (("libev: multiple event bases not supported when not compiled with EV_MULTIPLICITY", !ev_x_cur)); 101 | 102 | ev_x_cur = (struct event_base *)(long)ev_default_loop (EVFLAG_AUTO); 103 | #endif 104 | 105 | return ev_x_cur; 106 | } 107 | 108 | void event_base_free (struct event_base *base) 109 | { 110 | dLOOPbase; 111 | 112 | #if EV_MULTIPLICITY 113 | if (!ev_is_default_loop (loop)) 114 | ev_loop_destroy (loop); 115 | #endif 116 | } 117 | 118 | int event_dispatch (void) 119 | { 120 | return event_base_dispatch (ev_x_cur); 121 | } 122 | 123 | #ifdef EV_STANDALONE 124 | void event_set_log_callback (event_log_cb cb) 125 | { 126 | /* nop */ 127 | } 128 | #endif 129 | 130 | int event_loop (int flags) 131 | { 132 | return event_base_loop (ev_x_cur, flags); 133 | } 134 | 135 | int event_loopexit (struct timeval *tv) 136 | { 137 | return event_base_loopexit (ev_x_cur, tv); 138 | } 139 | 140 | static void 141 | ev_x_cb (struct event *ev, int revents) 142 | { 143 | revents &= EV_READ | EV_WRITE | EV_TIMER | EV_SIGNAL; 144 | 145 | ev->ev_res = revents; 146 | ev->ev_callback (ev->ev_fd, (short)revents, ev->ev_arg); 147 | } 148 | 149 | static void 150 | ev_x_cb_sig (EV_P_ struct ev_signal *w, int revents) 151 | { 152 | struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, iosig.sig)); 153 | 154 | if (revents & EV_ERROR) 155 | event_del (ev); 156 | 157 | ev_x_cb (ev, revents); 158 | } 159 | 160 | static void 161 | ev_x_cb_io (EV_P_ struct ev_io *w, int revents) 162 | { 163 | struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, iosig.io)); 164 | 165 | if ((revents & EV_ERROR) || !(ev->ev_events & EV_PERSIST)) 166 | event_del (ev); 167 | 168 | ev_x_cb (ev, revents); 169 | } 170 | 171 | static void 172 | ev_x_cb_to (EV_P_ struct ev_timer *w, int revents) 173 | { 174 | struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, to)); 175 | 176 | event_del (ev); 177 | 178 | ev_x_cb (ev, revents); 179 | } 180 | 181 | void event_set (struct event *ev, int fd, short events, void (*cb)(int, short, void *), void *arg) 182 | { 183 | if (events & EV_SIGNAL) 184 | ev_init (&ev->iosig.sig, ev_x_cb_sig); 185 | else 186 | ev_init (&ev->iosig.io, ev_x_cb_io); 187 | 188 | ev_init (&ev->to, ev_x_cb_to); 189 | 190 | ev->ev_base = ev_x_cur; /* not threadsafe, but it's how libevent works */ 191 | ev->ev_fd = fd; 192 | ev->ev_events = events; 193 | ev->ev_pri = 0; 194 | ev->ev_callback = cb; 195 | ev->ev_arg = arg; 196 | ev->ev_res = 0; 197 | ev->ev_flags = EVLIST_INIT; 198 | } 199 | 200 | int event_once (int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv) 201 | { 202 | return event_base_once (ev_x_cur, fd, events, cb, arg, tv); 203 | } 204 | 205 | int event_add (struct event *ev, struct timeval *tv) 206 | { 207 | dLOOPev; 208 | 209 | if (ev->ev_events & EV_SIGNAL) 210 | { 211 | if (!ev_is_active (&ev->iosig.sig)) 212 | { 213 | ev_signal_set (&ev->iosig.sig, ev->ev_fd); 214 | ev_signal_start (EV_A_ &ev->iosig.sig); 215 | 216 | ev->ev_flags |= EVLIST_SIGNAL; 217 | } 218 | } 219 | else if (ev->ev_events & (EV_READ | EV_WRITE)) 220 | { 221 | if (!ev_is_active (&ev->iosig.io)) 222 | { 223 | ev_io_set (&ev->iosig.io, ev->ev_fd, ev->ev_events & (EV_READ | EV_WRITE)); 224 | ev_io_start (EV_A_ &ev->iosig.io); 225 | 226 | ev->ev_flags |= EVLIST_INSERTED; 227 | } 228 | } 229 | 230 | if (tv) 231 | { 232 | ev->to.repeat = ev_tv_get (tv); 233 | ev_timer_again (EV_A_ &ev->to); 234 | ev->ev_flags |= EVLIST_TIMEOUT; 235 | } 236 | else 237 | { 238 | ev_timer_stop (EV_A_ &ev->to); 239 | ev->ev_flags &= ~EVLIST_TIMEOUT; 240 | } 241 | 242 | ev->ev_flags |= EVLIST_ACTIVE; 243 | 244 | return 0; 245 | } 246 | 247 | int event_del (struct event *ev) 248 | { 249 | dLOOPev; 250 | 251 | if (ev->ev_events & EV_SIGNAL) 252 | ev_signal_stop (EV_A_ &ev->iosig.sig); 253 | else if (ev->ev_events & (EV_READ | EV_WRITE)) 254 | ev_io_stop (EV_A_ &ev->iosig.io); 255 | 256 | if (ev_is_active (&ev->to)) 257 | ev_timer_stop (EV_A_ &ev->to); 258 | 259 | ev->ev_flags = EVLIST_INIT; 260 | 261 | return 0; 262 | } 263 | 264 | void event_active (struct event *ev, int res, short ncalls) 265 | { 266 | dLOOPev; 267 | 268 | if (res & EV_TIMEOUT) 269 | ev_feed_event (EV_A_ &ev->to, res & EV_TIMEOUT); 270 | 271 | if (res & EV_SIGNAL) 272 | ev_feed_event (EV_A_ &ev->iosig.sig, res & EV_SIGNAL); 273 | 274 | if (res & (EV_READ | EV_WRITE)) 275 | ev_feed_event (EV_A_ &ev->iosig.io, res & (EV_READ | EV_WRITE)); 276 | } 277 | 278 | int event_pending (struct event *ev, short events, struct timeval *tv) 279 | { 280 | short revents = 0; 281 | dLOOPev; 282 | 283 | if (ev->ev_events & EV_SIGNAL) 284 | { 285 | /* sig */ 286 | if (ev_is_active (&ev->iosig.sig) || ev_is_pending (&ev->iosig.sig)) 287 | revents |= EV_SIGNAL; 288 | } 289 | else if (ev->ev_events & (EV_READ | EV_WRITE)) 290 | { 291 | /* io */ 292 | if (ev_is_active (&ev->iosig.io) || ev_is_pending (&ev->iosig.io)) 293 | revents |= ev->ev_events & (EV_READ | EV_WRITE); 294 | } 295 | 296 | if (ev->ev_events & EV_TIMEOUT || ev_is_active (&ev->to) || ev_is_pending (&ev->to)) 297 | { 298 | revents |= EV_TIMEOUT; 299 | 300 | if (tv) 301 | { 302 | ev_tstamp at = ev_now (EV_A); 303 | 304 | tv->tv_sec = (long)at; 305 | tv->tv_usec = (long)((at - (ev_tstamp)tv->tv_sec) * 1e6); 306 | } 307 | } 308 | 309 | return events & revents; 310 | } 311 | 312 | int event_priority_init (int npri) 313 | { 314 | return event_base_priority_init (ev_x_cur, npri); 315 | } 316 | 317 | int event_priority_set (struct event *ev, int pri) 318 | { 319 | ev->ev_pri = pri; 320 | 321 | return 0; 322 | } 323 | 324 | int event_base_set (struct event_base *base, struct event *ev) 325 | { 326 | ev->ev_base = base; 327 | 328 | return 0; 329 | } 330 | 331 | int event_base_loop (struct event_base *base, int flags) 332 | { 333 | dLOOPbase; 334 | 335 | ev_run (EV_A_ flags); 336 | 337 | return 0; 338 | } 339 | 340 | int event_base_dispatch (struct event_base *base) 341 | { 342 | return event_base_loop (base, 0); 343 | } 344 | 345 | static void 346 | ev_x_loopexit_cb (int revents, void *base) 347 | { 348 | dLOOPbase; 349 | 350 | ev_break (EV_A_ EVBREAK_ONE); 351 | } 352 | 353 | int event_base_loopexit (struct event_base *base, struct timeval *tv) 354 | { 355 | ev_tstamp after = ev_tv_get (tv); 356 | dLOOPbase; 357 | 358 | ev_once (EV_A_ -1, 0, after >= 0. ? after : 0., ev_x_loopexit_cb, (void *)base); 359 | 360 | return 0; 361 | } 362 | 363 | struct ev_x_once 364 | { 365 | int fd; 366 | void (*cb)(int, short, void *); 367 | void *arg; 368 | }; 369 | 370 | static void 371 | ev_x_once_cb (int revents, void *arg) 372 | { 373 | struct ev_x_once *once = (struct ev_x_once *)arg; 374 | 375 | once->cb (once->fd, (short)revents, once->arg); 376 | free (once); 377 | } 378 | 379 | int event_base_once (struct event_base *base, int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv) 380 | { 381 | struct ev_x_once *once = (struct ev_x_once *)malloc (sizeof (struct ev_x_once)); 382 | dLOOPbase; 383 | 384 | if (!once) 385 | return -1; 386 | 387 | once->fd = fd; 388 | once->cb = cb; 389 | once->arg = arg; 390 | 391 | ev_once (EV_A_ fd, events & (EV_READ | EV_WRITE), ev_tv_get (tv), ev_x_once_cb, (void *)once); 392 | 393 | return 0; 394 | } 395 | 396 | int event_base_priority_init (struct event_base *base, int npri) 397 | { 398 | /*dLOOPbase;*/ 399 | 400 | return 0; 401 | } 402 | 403 | -------------------------------------------------------------------------------- /deps/libev/event.h: -------------------------------------------------------------------------------- 1 | /* 2 | * libevent compatibility header, only core events supported 3 | * 4 | * Copyright (c) 2007,2008,2010 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifndef EVENT_H_ 41 | #define EVENT_H_ 42 | 43 | #ifdef EV_H 44 | # include EV_H 45 | #else 46 | # include "ev.h" 47 | #endif 48 | 49 | #ifndef EVLOOP_NONBLOCK 50 | # define EVLOOP_NONBLOCK EVRUN_NOWAIT 51 | #endif 52 | #ifndef EVLOOP_ONESHOT 53 | # define EVLOOP_ONESHOT EVRUN_ONCE 54 | #endif 55 | #ifndef EV_TIMEOUT 56 | # define EV_TIMEOUT EV_TIMER 57 | #endif 58 | 59 | #ifdef __cplusplus 60 | extern "C" { 61 | #endif 62 | 63 | /* we need sys/time.h for struct timeval only */ 64 | #if !defined (WIN32) || defined (__MINGW32__) 65 | # include /* mingw seems to need this, for whatever reason */ 66 | # include 67 | #endif 68 | 69 | struct event_base; 70 | 71 | #define EVLIST_TIMEOUT 0x01 72 | #define EVLIST_INSERTED 0x02 73 | #define EVLIST_SIGNAL 0x04 74 | #define EVLIST_ACTIVE 0x08 75 | #define EVLIST_INTERNAL 0x10 76 | #define EVLIST_INIT 0x80 77 | 78 | struct event 79 | { 80 | /* libev watchers we map onto */ 81 | union { 82 | struct ev_io io; 83 | struct ev_signal sig; 84 | } iosig; 85 | struct ev_timer to; 86 | 87 | /* compatibility slots */ 88 | struct event_base *ev_base; 89 | void (*ev_callback)(int, short, void *arg); 90 | void *ev_arg; 91 | int ev_fd; 92 | int ev_pri; 93 | int ev_res; 94 | int ev_flags; 95 | short ev_events; 96 | }; 97 | 98 | #define EV_READ EV_READ 99 | #define EV_WRITE EV_WRITE 100 | #define EV_PERSIST 0x10 101 | 102 | #define EVENT_SIGNAL(ev) ((int) (ev)->ev_fd) 103 | #define EVENT_FD(ev) ((int) (ev)->ev_fd) 104 | 105 | #define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) 106 | 107 | #define evtimer_add(ev,tv) event_add (ev, tv) 108 | #define evtimer_set(ev,cb,data) event_set (ev, -1, 0, cb, data) 109 | #define evtimer_del(ev) event_del (ev) 110 | #define evtimer_pending(ev,tv) event_pending (ev, EV_TIMEOUT, tv) 111 | #define evtimer_initialized(ev) event_initialized (ev) 112 | 113 | #define timeout_add(ev,tv) evtimer_add (ev, tv) 114 | #define timeout_set(ev,cb,data) evtimer_set (ev, cb, data) 115 | #define timeout_del(ev) evtimer_del (ev) 116 | #define timeout_pending(ev,tv) evtimer_pending (ev, tv) 117 | #define timeout_initialized(ev) evtimer_initialized (ev) 118 | 119 | #define signal_add(ev,tv) event_add (ev, tv) 120 | #define signal_set(ev,sig,cb,data) event_set (ev, sig, EV_SIGNAL | EV_PERSIST, cb, data) 121 | #define signal_del(ev) event_del (ev) 122 | #define signal_pending(ev,tv) event_pending (ev, EV_SIGNAL, tv) 123 | #define signal_initialized(ev) event_initialized (ev) 124 | 125 | const char *event_get_version (void); 126 | const char *event_get_method (void); 127 | 128 | void *event_init (void); 129 | void event_base_free (struct event_base *base); 130 | 131 | #define EVLOOP_ONCE EVLOOP_ONESHOT 132 | int event_loop (int); 133 | int event_loopexit (struct timeval *tv); 134 | int event_dispatch (void); 135 | 136 | #define _EVENT_LOG_DEBUG 0 137 | #define _EVENT_LOG_MSG 1 138 | #define _EVENT_LOG_WARN 2 139 | #define _EVENT_LOG_ERR 3 140 | typedef void (*event_log_cb)(int severity, const char *msg); 141 | void event_set_log_callback(event_log_cb cb); 142 | 143 | void event_set (struct event *ev, int fd, short events, void (*cb)(int, short, void *), void *arg); 144 | int event_once (int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv); 145 | 146 | int event_add (struct event *ev, struct timeval *tv); 147 | int event_del (struct event *ev); 148 | void event_active (struct event *ev, int res, short ncalls); /* ncalls is being ignored */ 149 | 150 | int event_pending (struct event *ev, short, struct timeval *tv); 151 | 152 | int event_priority_init (int npri); 153 | int event_priority_set (struct event *ev, int pri); 154 | 155 | int event_base_set (struct event_base *base, struct event *ev); 156 | int event_base_loop (struct event_base *base, int); 157 | int event_base_loopexit (struct event_base *base, struct timeval *tv); 158 | int event_base_dispatch (struct event_base *base); 159 | int event_base_once (struct event_base *base, int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv); 160 | int event_base_priority_init (struct event_base *base, int fd); 161 | 162 | /* next line is different in the libevent+libev version */ 163 | /*libevent-include*/ 164 | 165 | #ifdef __cplusplus 166 | } 167 | #endif 168 | 169 | #endif 170 | 171 | -------------------------------------------------------------------------------- /deps/libev/install-sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # install - install a program, script, or datafile 4 | # 5 | # This originates from X11R5 (mit/util/scripts/install.sh), which was 6 | # later released in X11R6 (xc/config/util/install.sh) with the 7 | # following copyright and license. 8 | # 9 | # Copyright (C) 1994 X Consortium 10 | # 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documentation files (the "Software"), to 13 | # deal in the Software without restriction, including without limitation the 14 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 15 | # sell copies of the Software, and to permit persons to whom the Software is 16 | # furnished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 25 | # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- 26 | # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | # 28 | # Except as contained in this notice, the name of the X Consortium shall not 29 | # be used in advertising or otherwise to promote the sale, use or other deal- 30 | # ings in this Software without prior written authorization from the X Consor- 31 | # tium. 32 | # 33 | # 34 | # FSF changes to this file are in the public domain. 35 | # 36 | # Calling this script install-sh is preferred over install.sh, to prevent 37 | # `make' implicit rules from creating a file called install from it 38 | # when there is no Makefile. 39 | # 40 | # This script is compatible with the BSD install script, but was written 41 | # from scratch. It can only install one file at a time, a restriction 42 | # shared with many OS's install programs. 43 | 44 | 45 | # set DOITPROG to echo to test this script 46 | 47 | # Don't use :- since 4.3BSD and earlier shells don't like it. 48 | doit="${DOITPROG-}" 49 | 50 | 51 | # put in absolute paths if you don't have them in your path; or use env. vars. 52 | 53 | mvprog="${MVPROG-mv}" 54 | cpprog="${CPPROG-cp}" 55 | chmodprog="${CHMODPROG-chmod}" 56 | chownprog="${CHOWNPROG-chown}" 57 | chgrpprog="${CHGRPPROG-chgrp}" 58 | stripprog="${STRIPPROG-strip}" 59 | rmprog="${RMPROG-rm}" 60 | mkdirprog="${MKDIRPROG-mkdir}" 61 | 62 | transformbasename="" 63 | transform_arg="" 64 | instcmd="$mvprog" 65 | chmodcmd="$chmodprog 0755" 66 | chowncmd="" 67 | chgrpcmd="" 68 | stripcmd="" 69 | rmcmd="$rmprog -f" 70 | mvcmd="$mvprog" 71 | src="" 72 | dst="" 73 | dir_arg="" 74 | 75 | while [ x"$1" != x ]; do 76 | case $1 in 77 | -c) instcmd=$cpprog 78 | shift 79 | continue;; 80 | 81 | -d) dir_arg=true 82 | shift 83 | continue;; 84 | 85 | -m) chmodcmd="$chmodprog $2" 86 | shift 87 | shift 88 | continue;; 89 | 90 | -o) chowncmd="$chownprog $2" 91 | shift 92 | shift 93 | continue;; 94 | 95 | -g) chgrpcmd="$chgrpprog $2" 96 | shift 97 | shift 98 | continue;; 99 | 100 | -s) stripcmd=$stripprog 101 | shift 102 | continue;; 103 | 104 | -t=*) transformarg=`echo $1 | sed 's/-t=//'` 105 | shift 106 | continue;; 107 | 108 | -b=*) transformbasename=`echo $1 | sed 's/-b=//'` 109 | shift 110 | continue;; 111 | 112 | *) if [ x"$src" = x ] 113 | then 114 | src=$1 115 | else 116 | # this colon is to work around a 386BSD /bin/sh bug 117 | : 118 | dst=$1 119 | fi 120 | shift 121 | continue;; 122 | esac 123 | done 124 | 125 | if [ x"$src" = x ] 126 | then 127 | echo "$0: no input file specified" >&2 128 | exit 1 129 | else 130 | : 131 | fi 132 | 133 | if [ x"$dir_arg" != x ]; then 134 | dst=$src 135 | src="" 136 | 137 | if [ -d "$dst" ]; then 138 | instcmd=: 139 | chmodcmd="" 140 | else 141 | instcmd=$mkdirprog 142 | fi 143 | else 144 | 145 | # Waiting for this to be detected by the "$instcmd $src $dsttmp" command 146 | # might cause directories to be created, which would be especially bad 147 | # if $src (and thus $dsttmp) contains '*'. 148 | 149 | if [ -f "$src" ] || [ -d "$src" ] 150 | then 151 | : 152 | else 153 | echo "$0: $src does not exist" >&2 154 | exit 1 155 | fi 156 | 157 | if [ x"$dst" = x ] 158 | then 159 | echo "$0: no destination specified" >&2 160 | exit 1 161 | else 162 | : 163 | fi 164 | 165 | # If destination is a directory, append the input filename; if your system 166 | # does not like double slashes in filenames, you may need to add some logic 167 | 168 | if [ -d "$dst" ] 169 | then 170 | dst=$dst/`basename "$src"` 171 | else 172 | : 173 | fi 174 | fi 175 | 176 | ## this sed command emulates the dirname command 177 | dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` 178 | 179 | # Make sure that the destination directory exists. 180 | # this part is taken from Noah Friedman's mkinstalldirs script 181 | 182 | # Skip lots of stat calls in the usual case. 183 | if [ ! -d "$dstdir" ]; then 184 | defaultIFS=' 185 | ' 186 | IFS="${IFS-$defaultIFS}" 187 | 188 | oIFS=$IFS 189 | # Some sh's can't handle IFS=/ for some reason. 190 | IFS='%' 191 | set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` 192 | IFS=$oIFS 193 | 194 | pathcomp='' 195 | 196 | while [ $# -ne 0 ] ; do 197 | pathcomp=$pathcomp$1 198 | shift 199 | 200 | if [ ! -d "$pathcomp" ] ; 201 | then 202 | $mkdirprog "$pathcomp" 203 | else 204 | : 205 | fi 206 | 207 | pathcomp=$pathcomp/ 208 | done 209 | fi 210 | 211 | if [ x"$dir_arg" != x ] 212 | then 213 | $doit $instcmd "$dst" && 214 | 215 | if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dst"; else : ; fi && 216 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dst"; else : ; fi && 217 | if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dst"; else : ; fi && 218 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dst"; else : ; fi 219 | else 220 | 221 | # If we're going to rename the final executable, determine the name now. 222 | 223 | if [ x"$transformarg" = x ] 224 | then 225 | dstfile=`basename "$dst"` 226 | else 227 | dstfile=`basename "$dst" $transformbasename | 228 | sed $transformarg`$transformbasename 229 | fi 230 | 231 | # don't allow the sed command to completely eliminate the filename 232 | 233 | if [ x"$dstfile" = x ] 234 | then 235 | dstfile=`basename "$dst"` 236 | else 237 | : 238 | fi 239 | 240 | # Make a couple of temp file names in the proper directory. 241 | 242 | dsttmp=$dstdir/_inst.$$_ 243 | rmtmp=$dstdir/_rm.$$_ 244 | 245 | # Trap to clean up temp files at exit. 246 | 247 | trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0 248 | trap '(exit $?); exit' 1 2 13 15 249 | 250 | # Move or copy the file name to the temp name 251 | 252 | $doit $instcmd "$src" "$dsttmp" && 253 | 254 | # and set any options; do chmod last to preserve setuid bits 255 | 256 | # If any of these fail, we abort the whole thing. If we want to 257 | # ignore errors from any of these, just make sure not to ignore 258 | # errors from the above "$doit $instcmd $src $dsttmp" command. 259 | 260 | if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp"; else :;fi && 261 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp"; else :;fi && 262 | if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dsttmp"; else :;fi && 263 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; else :;fi && 264 | 265 | # Now remove or move aside any old file at destination location. We try this 266 | # two ways since rm can't unlink itself on some systems and the destination 267 | # file might be busy for other reasons. In this case, the final cleanup 268 | # might fail but the new file should still install successfully. 269 | 270 | { 271 | if [ -f "$dstdir/$dstfile" ] 272 | then 273 | $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null || 274 | $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null || 275 | { 276 | echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 277 | (exit 1); exit 278 | } 279 | else 280 | : 281 | fi 282 | } && 283 | 284 | # Now rename the file to the real destination. 285 | 286 | $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" 287 | 288 | fi && 289 | 290 | # The final little trick to "correctly" pass the exit status to the exit trap. 291 | 292 | { 293 | (exit 0); exit 294 | } 295 | -------------------------------------------------------------------------------- /deps/libev/libev.m4: -------------------------------------------------------------------------------- 1 | dnl this file is part of libev, do not make local modifications 2 | dnl http://software.schmorp.de/pkg/libev 3 | 4 | dnl libev support 5 | AC_CHECK_HEADERS(sys/inotify.h sys/epoll.h sys/event.h port.h poll.h sys/select.h sys/eventfd.h sys/signalfd.h) 6 | 7 | AC_CHECK_FUNCS(inotify_init epoll_ctl kqueue port_create poll select eventfd signalfd) 8 | 9 | AC_CHECK_FUNCS(clock_gettime, [], [ 10 | dnl on linux, try syscall wrapper first 11 | if test $(uname) = Linux; then 12 | AC_MSG_CHECKING(for clock_gettime syscall) 13 | AC_LINK_IFELSE([AC_LANG_PROGRAM( 14 | [#include 15 | #include 16 | #include ], 17 | [struct timespec ts; int status = syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts)])], 18 | [ac_have_clock_syscall=1 19 | AC_DEFINE(HAVE_CLOCK_SYSCALL, 1, Define to 1 to use the syscall interface for clock_gettime) 20 | AC_MSG_RESULT(yes)], 21 | [AC_MSG_RESULT(no)]) 22 | fi 23 | if test -z "$LIBEV_M4_AVOID_LIBRT" && test -z "$ac_have_clock_syscall"; then 24 | AC_CHECK_LIB(rt, clock_gettime) 25 | unset ac_cv_func_clock_gettime 26 | AC_CHECK_FUNCS(clock_gettime) 27 | fi 28 | ]) 29 | 30 | AC_CHECK_FUNCS(nanosleep, [], [ 31 | if test -z "$LIBEV_M4_AVOID_LIBRT"; then 32 | AC_CHECK_LIB(rt, nanosleep) 33 | unset ac_cv_func_nanosleep 34 | AC_CHECK_FUNCS(nanosleep) 35 | fi 36 | ]) 37 | 38 | if test -z "$LIBEV_M4_AVOID_LIBM"; then 39 | LIBM=m 40 | fi 41 | AC_SEARCH_LIBS(floor, $LIBM, [AC_DEFINE(HAVE_FLOOR, 1, Define to 1 if the floor function is available)]) 42 | 43 | -------------------------------------------------------------------------------- /deps/libev/mkinstalldirs: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # mkinstalldirs --- make directory hierarchy 3 | # Author: Noah Friedman 4 | # Created: 1993-05-16 5 | # Public domain 6 | 7 | errstatus=0 8 | dirmode="" 9 | 10 | usage="\ 11 | Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..." 12 | 13 | # process command line arguments 14 | while test $# -gt 0 ; do 15 | case $1 in 16 | -h | --help | --h*) # -h for help 17 | echo "$usage" 1>&2 18 | exit 0 19 | ;; 20 | -m) # -m PERM arg 21 | shift 22 | test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } 23 | dirmode=$1 24 | shift 25 | ;; 26 | --) # stop option processing 27 | shift 28 | break 29 | ;; 30 | -*) # unknown option 31 | echo "$usage" 1>&2 32 | exit 1 33 | ;; 34 | *) # first non-opt arg 35 | break 36 | ;; 37 | esac 38 | done 39 | 40 | for file 41 | do 42 | if test -d "$file"; then 43 | shift 44 | else 45 | break 46 | fi 47 | done 48 | 49 | case $# in 50 | 0) exit 0 ;; 51 | esac 52 | 53 | case $dirmode in 54 | '') 55 | if mkdir -p -- . 2>/dev/null; then 56 | echo "mkdir -p -- $*" 57 | exec mkdir -p -- "$@" 58 | fi 59 | ;; 60 | *) 61 | if mkdir -m "$dirmode" -p -- . 2>/dev/null; then 62 | echo "mkdir -m $dirmode -p -- $*" 63 | exec mkdir -m "$dirmode" -p -- "$@" 64 | fi 65 | ;; 66 | esac 67 | 68 | for file 69 | do 70 | set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` 71 | shift 72 | 73 | pathcomp= 74 | for d 75 | do 76 | pathcomp="$pathcomp$d" 77 | case $pathcomp in 78 | -*) pathcomp=./$pathcomp ;; 79 | esac 80 | 81 | if test ! -d "$pathcomp"; then 82 | echo "mkdir $pathcomp" 83 | 84 | mkdir "$pathcomp" || lasterr=$? 85 | 86 | if test ! -d "$pathcomp"; then 87 | errstatus=$lasterr 88 | else 89 | if test ! -z "$dirmode"; then 90 | echo "chmod $dirmode $pathcomp" 91 | lasterr="" 92 | chmod "$dirmode" "$pathcomp" || lasterr=$? 93 | 94 | if test ! -z "$lasterr"; then 95 | errstatus=$lasterr 96 | fi 97 | fi 98 | fi 99 | fi 100 | 101 | pathcomp="$pathcomp/" 102 | done 103 | done 104 | 105 | exit $errstatus 106 | 107 | # Local Variables: 108 | # mode: shell-script 109 | # sh-indentation: 2 110 | # End: 111 | # mkinstalldirs ends here 112 | -------------------------------------------------------------------------------- /lab/debug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, sys, readline 3 | 4 | sys.path.insert(0, 5 | '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 6 | #'/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python') 7 | import lldb 8 | 9 | debugger = None 10 | cli = None 11 | 12 | def breakpoint_function_wrapper(frame, bp_loc, dict): 13 | print 'breakpoint_function_wrapper' 14 | 15 | def disassemble_instructions(insts): 16 | for i in insts: 17 | print i 18 | 19 | def get_stopped_threads(process, reason): 20 | '''Returns the thread(s) with the specified stop reason in a list. 21 | The list can be empty if no such thread exists. 22 | ''' 23 | threads = [] 24 | for t in process: 25 | if t.GetStopReason() == reason: 26 | threads.append(t) 27 | return threads 28 | 29 | def rl_completer(): 30 | # cli.HandleCompletion(self, str current_line, uint32_t cursor_pos, int match_start_point, 31 | # int max_return_elements, SBStringList matches) -> int 32 | pass 33 | 34 | def init_cli(): 35 | readline.set_completer(rl_completer) 36 | readline.parse_and_bind('tab: complete') 37 | readline.parse_and_bind('set editing-mode emacs') 38 | readline.parse_and_bind('set bind-tty-special-chars on') 39 | readline.parse_and_bind('set print-completions-horizontally on') 40 | # Get the SBCommandInterpreter 41 | global debugger, cli 42 | cli = debugger.GetCommandInterpreter() 43 | 44 | def cli_run_command(command): 45 | global cli 46 | res = lldb.SBCommandReturnObject() 47 | cli.HandleCommand(command, res) 48 | if not res.Succeeded(): 49 | print res.GetError() 50 | else: 51 | print res.GetOutput() 52 | return res 53 | 54 | def enter_interactive_mode(): 55 | global cli 56 | while True: 57 | try: 58 | line = raw_input('(lldb) ') 59 | except EOFError: 60 | print '' 61 | break 62 | except KeyboardInterrupt: 63 | print '' 64 | os._exit(1) # instant death 65 | if line == 'quit' or line == 'q': 66 | break 67 | cli_run_command(line) 68 | 69 | if __name__ == '__main__': 70 | # Create a new debugger instance in your module if your module 71 | # can be run from the command line. When we run a script from 72 | # the command line, we won't have any debugger object in 73 | # lldb.debugger, so we can just create it if it will be needed 74 | debugger = lldb.SBDebugger.Create() 75 | 76 | # When we step or continue, don't return from the function until the process 77 | # stops. Otherwise we would have to handle the process events ourselves which, 78 | # while doable is a little tricky. We do this by setting the async mode to 79 | # false. 80 | debugger.SetAsync(False) 81 | 82 | # Create a target from a file and arch 83 | target_path = sys.argv[1] 84 | target_args = sys.argv[2:] 85 | target = debugger.CreateTargetWithFileAndArch(target_path, 86 | lldb.LLDB_ARCH_DEFAULT) 87 | if not target: 88 | print 'Failed to create target' 89 | sys.exit(1) 90 | else: 91 | # If the target is valid set a breakpoint at main 92 | #main_bp = target.BreakpointCreateByName("main", target.GetExecutable().GetFilename()); 93 | #print main_bp 94 | 95 | # Build env list (LaunchSimple needs env to be a list, not a map) 96 | env = [] 97 | for k in os.environ: 98 | env.append(k + '=' + os.environ[k]) 99 | 100 | # Find TTY name 101 | import subprocess 102 | ttyname = subprocess.check_output('tty').strip() 103 | 104 | # Launch the process. Since we specified synchronous mode, we won't return 105 | # from this function until we hit the breakpoint at main 106 | # process = target.LaunchSimple(target_args, env, os.getcwd()) 107 | launch_error = lldb.SBError() 108 | process = target.Launch(debugger.GetListener(), None, None, 109 | ttyname, ttyname, ttyname, 110 | os.getcwd(), 0, False, launch_error) 111 | 112 | # Make sure the launch went ok 113 | if process: 114 | # Print some simple process info 115 | state = process.GetState() 116 | if state == lldb.eStateStopped or state == lldb.eStateCrashed: 117 | print process 118 | 119 | # Get the (first) thread which faulted 120 | thread = None 121 | for t in process: 122 | if t.GetStopReason() == lldb.eStopReasonException: 123 | thread = t 124 | break 125 | 126 | if thread: 127 | # Print some simple thread info 128 | print thread 129 | # Get the first frame 130 | frame = thread.GetFrameAtIndex(0) 131 | if frame: 132 | # Print some simple frame info 133 | #print frame 134 | function = frame.GetFunction() 135 | # See if we have debug info (a function) 136 | if function: 137 | pass 138 | # We do have a function, print some info for the function 139 | #print function 140 | # Now get all instructions for this function and print them 141 | #insts = function.GetInstructions(target) 142 | #disassemble_instructions(insts) 143 | else: 144 | # See if we have a symbol in the symbol table for where we stopped 145 | symbol = frame.GetSymbol(); 146 | if symbol: 147 | # We do have a symbol, print some info for the symbol 148 | print symbol 149 | 150 | init_cli() 151 | cli_run_command('disassemble --pc -m -C 3') 152 | enter_interactive_mode() 153 | 154 | # PYTHONPATH=/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python 155 | -------------------------------------------------------------------------------- /lab/functional.mv: -------------------------------------------------------------------------------- 1 | sub = function x y: 2 | return x - y 3 | 4 | subs = function len: 5 | each x in 0..len: 6 | y = if (x == 2) then 10 else 1 7 | sub(x y) 8 | 9 | subs 5 -> [-1, 0, -8, 2, 3] 10 | 11 | subs: 12 | # Generator that starts at 0 and ends at len in increments of 1 13 | ngen = 0..len 14 | # Application function 15 | f0 = function x: 16 | if x == 2 17 | y = 10 18 | else 19 | y = 1 20 | sub(x y) 21 | # Map f0 to ngen 22 | mapper ngen f0 23 | 24 | # VM instructions 25 | function sub # args: (x=R(0), y=R(1)) 26 | SUB 0 0 1 # R(0) = R(0) - R(1) 27 | RETURN 0 1 # <- R(0)...R(0+1-1) = R(0)..R(0) = R(0) 28 | function subs/f0 # args: (x=R(0)), k: (2,10,1,"sub") 29 | EQ 0 0 256 # (0 == R(0) == K(0)) ? cont else PC++ 30 | JUMP 2 # PC += 2 31 | LOADK 2 1 # R(1) = K(1) = 10 32 | JUMP 1 # PC += 1 33 | LOADK 2 2 # R(1) = K(2) = 1 34 | MOVE 1 0 # R(1) = R(0) 35 | GETGLOBAL 0 3 # R(0) = G[K(3)] = G["sub"] = 36 | CALL 0 2 1 # R(0)(R(1), R(2)) = sub(x y) 37 | RETURN 0 1 # <- R(0)..R(0+1-1) = R(0)..R(0) = R(0) 38 | function subs # args: (len=R(0)), k: (0,) 39 | MKIGEN 0 256 0 # R(0) = mkigen(K(0), R(0)) = mkigen(0, len) 40 | MAP 0 0 257 # R(0) = MAP(R(0), K(1)) = MAP(ngen, ) 41 | RETURN 0 1 # <- R(0)..R(0+1-1) = R(0)..R(0) = R(0) 42 | 43 | -------------------------------------------------------------------------------- /lab/notes-on-task-hierarchy.md: -------------------------------------------------------------------------------- 1 | ## Hierarchical tasks 2 | 3 | A spawns B 4 | B spawns C and D 5 | C 6 | D 7 | E spawns F 8 | F 9 | ... 10 | 11 | When B terminates, C and any other tasks spawned by B must also be terminated (`C` and `D` in the above case). When a task is terminated its parent will receive a message `(TaskInfo, tid, Ended)` about the task having terminated. 12 | 13 | Common scenario: 14 | 15 | A -> B 16 | B -> x 17 | A 18 | 19 | 1. Task A spawns task B 20 | 2. Task A waits for a message from task B 21 | 3. Task B produces a message X for A 22 | 4. Task B terminates 23 | 5. Task A receives message X 24 | 6. Task A receives message "task B ended" 25 | 26 | 27 | ### Approach 1: 28 | 29 | Task { Task* parent } 30 | 31 | + Small memory footprint 32 | - Expensive worst-case scenario where the entire tree of tasks need to be traversed 33 | 34 | ### Approach 1.1: 35 | 36 | TaskRef { Task* task } 37 | Task { TaskRef* parent } 38 | 39 | Before a task is scheduled, the scheduler checks if its parent is still alive (a TaskRef structure is used to enable setting the value of `parent` to NULL). If the parent is NULL, then the task is imemdiately terminated. Question is, how expensive is this to do every time a task is scheduled in? Perhaps this logic is only executed every N per-task schedule-ins? Also note that a `TaskRef` approach cost *two* pointers of memory. 40 | 41 | ### Approach 2: 42 | 43 | Task { Task* children[] } 44 | 45 | + Good worst-case scenario where a subtree is traversed top-down. 46 | - Requires memory reallocation of `children` for each task spawned. 47 | 48 | ### Ideas: 49 | 50 | Since when a task (e.g. `A` above) terminates, we know that _all_ it's children also needs terminating. Traversing a tree downwards in this case would less efficient that for instance accessing a set of all tasks spanwed by `A`. Perhaps we can find a task identifier scheme that allows this, i.e. identifiers are hierarchical. 51 | 52 | A sparse array as a set for active tasks where the index is the task id? 53 | 54 | --- 55 | 56 | What the run queue is the task graph where we have one magical "root" task? 57 | 58 | Imagine the following task hierarchy: 59 | 60 | A 61 | B 62 | C 63 | D 64 | E 65 | F 66 | G 67 | H 68 | 69 | B terminates: 70 | 71 | 1. A -> B -> C -> D -> E -> F -> G -> H 72 | 2. A -> (B -> C -> D ->) E -> F -> G -> H 73 | 3. A -> E -> F -> G -> H 74 | 75 | Problem: How can a task discern from it's children and other tasks? 76 | Possible solution: A task can have a `tail` member in addition to it's next pointer: 77 | 78 | Task { 79 | Task* next 80 | Task* tail 81 | } 82 | 83 | When a task is spawned: 84 | 85 | if parentT.tail != 0: 86 | newT.next = parentT.tail.next 87 | else 88 | newT.next = parentT.next 89 | parentT.next = newT 90 | parentT.tail = newT 91 | 92 | Eventually: 93 | 94 | A{ 95 | tail = *E 96 | next = B{ 97 | tail = *D 98 | next = C{ 99 | next = D{ 100 | next = E{ 101 | next = F{ 102 | tail = *H 103 | next = G{ 104 | next = H{ 105 | } 106 | ... 107 | } 108 | 109 | A{ 110 | tail = 0 111 | next = F{ 112 | next = 0 113 | } 114 | } 115 | 116 | --> A spawns B --> 117 | 118 | A{ 119 | tail = *B 120 | next = B{ 121 | next = F{ 122 | next = 0 123 | } 124 | } 125 | 126 | To save room, the `tail` could be implemented as an offset from `next` rather than an absolute address. In a setupd like this, where `next` and `tail` must not be nil for live tasks, the scheduler can no longer test the `next` member for nil to decide wether to execute a task or not. Instead, we add a member `status` to the task type: 127 | 128 | Task { 129 | Task* next 130 | Task* tail 131 | enum { Running, Suspended } status; 132 | } 133 | 134 | The scheduler will then conditionally execute a task depending on its status: 135 | 136 | SSchedRun(SSched* s) { 137 | Task* t = s->task_head; 138 | while (t) { 139 | if (t->status == Running) { 140 | SSchedExec(t) 141 | // check if task ended and if so, unlink etc 142 | } 143 | if (t->next == 0) { 144 | t = s->task_head; 145 | } else { 146 | t = t->next; 147 | } 148 | } 149 | } 150 | 151 | Problem with this approach: When a task spawns a new task, the new task is scheduled to run _before_ existing tasks. This is dangerous as given a scenario where many tasks spawn new tasks, preexisting tasks might never run! 152 | 153 | Let's explore an alternative: 154 | 155 | Task { 156 | Task* parent; // Task that owns us 157 | Task* tail; // Last task we own 158 | Task* next; // Next scheduled task (cyclic) 159 | } 160 | 161 | Scheduler routines: 162 | 163 | SSchedTask(SSched* s, STask* t, STask* pt): 164 | if s.head == 0: 165 | s.head = t 166 | else: 167 | s.tail.next = t 168 | s.tail = t 169 | if (pt != 0): 170 | t.parent = pt 171 | pt.tail = t 172 | 173 | Runloop: 174 | 175 | prev_task = FindTaskJustBeforeTail() # called seldomly 176 | t = s.head 177 | while (t != 0): 178 | status = SSchedExec(t) 179 | 180 | if (status != Yield): 181 | # Here status is either Suspend or End 182 | if (s.head == s.tail): 183 | # t is the only task scheduled 184 | s.head = 0 185 | s.tail = 0 186 | else: 187 | assert(prev_task.next == t) 188 | prev_task.next = t.next 189 | if (t == s.head): 190 | s.head = t.next 191 | assert(s.tail.next == t) 192 | s.tail.next = s.head 193 | else if (t == s.tail): 194 | s.tail = prev_task 195 | 196 | if (status == End): 197 | if (t.parent != 0) 198 | NotifyParentOfSubtaskExit(t, End) 199 | if (t.tail != 0) 200 | TerminateAllSubtasks(t) 201 | STaskDestroy(t) 202 | 203 | else: 204 | prev_task = t 205 | 206 | t = t.next 207 | 208 | --- 209 | 210 | Initial state of scheduler `s`: () 211 | 212 | s.tail = 0 213 | s.head = 0 214 | 215 | A is scheduled: (A) 216 | 217 | s.tail = *A 218 | s.head = A{ 219 | next = *A } 220 | 221 | F is scheduled: (A -> F) 222 | 223 | s.tail = *F 224 | s.head = A{ 225 | next = F{ 226 | next = *A }} 227 | 228 | A spawns B: (A -> F -> B) 229 | 230 | s.tail = *B 231 | s.head = A{ 232 | tail = *B 233 | next = F{ 234 | next = B{ 235 | parent = *A 236 | next = *A }}} 237 | 238 | A spawns C: (A -> F -> B -> C) 239 | 240 | s.tail = *C 241 | s.head = A{ 242 | tail = *C 243 | next = F{ 244 | next = B{ 245 | parent = *A 246 | next = C{ 247 | parent = *A 248 | next = *A }}}} 249 | 250 | F spawns G: (A -> F -> B -> C -> G) 251 | 252 | s.tail = *G 253 | s.head = A{ 254 | tail = *C 255 | next = F{ 256 | tail = *G 257 | next = B{ 258 | parent = *A 259 | next = C{ 260 | parent = *A 261 | next = G{ 262 | parent = *F 263 | next = *A }}}}} 264 | 265 | B is supended (e.g. waiting for a timer): 266 | 267 | s.tail = *G 268 | s.head = A{ 269 | tail = *C 270 | next = F{ 271 | tail = *G 272 | next = C{ 273 | parent = *A 274 | next = G{ 275 | parent = *F 276 | next = *A }}}} 277 | 278 | B is resumed (e.g. from a timer triggering): 279 | 280 | s.tail = *B 281 | s.head = A{ 282 | tail = *C 283 | next = F{ 284 | tail = *G 285 | next = C{ 286 | parent = *A 287 | next = G{ 288 | parent = *F 289 | next = B{ 290 | parent = *A 291 | next = *A }}}}} 292 | 293 | A is suspended: 294 | 295 | s.tail = *B 296 | s.head = F{ 297 | tail = *G 298 | next = C{ 299 | parent = *A 300 | next = G{ 301 | parent = *F 302 | next = B{ 303 | parent = *A 304 | next = *F }}}} 305 | 306 | A is resumed: 307 | 308 | s.tail = *A 309 | s.head = F{ 310 | tail = *G 311 | next = C{ 312 | parent = *A 313 | next = G{ 314 | parent = *F 315 | next = B{ 316 | parent = *A 317 | next = A{ 318 | tail = *C 319 | next = *F }}}}} 320 | 321 | C ends: 322 | 323 | s.tail = *A 324 | s.head = F{ 325 | tail = *G 326 | next = G{ 327 | parent = *F 328 | next = B{ 329 | parent = *A 330 | next = A{ 331 | tail = *B 332 | next = *F }}}} 333 | 334 | A ends, taking subtasks with it: 335 | 336 | s.tail = *G 337 | s.head = F{ 338 | tail = *G 339 | next = G{ 340 | parent = *F 341 | next = *F }} 342 | 343 | 344 | 345 | - unschedule a task 346 | - suspend a task 347 | - a task with children dies, causing children to die too 348 | - a task with a parent dies, informs the parent 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | -------------------------------------------------------------------------------- /lab/notes.md: -------------------------------------------------------------------------------- 1 | notes 2 | 3 | variables are just names for program fragments 4 | ns rsms.datastore 5 | 6 | The actor model consists of a few key principles: 7 | 8 | - No shared state 9 | - Lightweight processes 10 | - Asynchronous message-passing 11 | - Mailboxes to buffer incoming messages 12 | - Mailbox processing with pattern matching 13 | 14 | Some key assumptions are built into this model: 15 | 16 | - Processes are cheap (in memory) and fast to create 17 | - Processes are small and thus large numbers of processes can be created 18 | - Processes are not bound to kernel threads and can be efficiently scheduled in user space 19 | - The scheduler requires the ability to pause and continue process execution 20 | - Messages can be retrieved by efficiently pattern-matching messages in the mailbox 21 | 22 | 23 | The Erlang view of the world can be summarized in the following statements: 24 | 25 | - Everything is a process. 26 | - Processes are strongly isolated. 27 | - Process creation and destruction is a lightweight operation. 28 | - Message passing is the only way for processes to interact. 29 | - Processes have unique names. 30 | - If you know the name of a process you can send it a message. • Processes share no resources. 31 | - Error handling is non-local. 32 | - Processes do what they are supposed to do or fail. 33 | 34 | Erlang has three ways of spawning processes: 35 | 36 | 1. I don't care if my child process dies: 37 | spawn(...) 38 | 39 | 2. I want to crash if my child process crashes: 40 | spawn_link(...) 41 | 42 | 3. I want to receive a message if my child process terminates (normally or not): 43 | process_flag(trap_exit, true), 44 | spawn_link(...) 45 | 46 | 47 | 48 | local linda = lanes.linda() 49 | 50 | local function loop( max ) 51 | for i=1,max do 52 | print( "sending: "..i ) 53 | linda:send( "x", i ) -- linda as upvalue 54 | end 55 | end 56 | 57 | a= lanes.gen("",loop)( 10000 ) 58 | 59 | while true do 60 | local val= linda:receive( 3.0, "x" ) -- timeout in seconds 61 | if val==nil then 62 | print( "timed out" ) 63 | break 64 | end 65 | print( "received: "..val ) 66 | end 67 | 68 | producer count = 69 | for n in [1..count] 70 | number n -> parent 71 | 72 | main = 73 | producer_task = spawn producer 1000 74 | receive 75 | producer_task -> 76 | | number n 77 | print "Received number: " n 78 | receive 79 | -------------------------------------------------------------------------------- /lab/register-and-AR-thoughts.mv: -------------------------------------------------------------------------------- 1 | sub = function x y: 2 | return x - y 3 | 4 | main = function: 5 | x = 10 6 | while x > 0: 7 | x = sub(x 1) 8 | 9 | # VM instructions 10 | function sub # args: x=R(0), y=R(1) 11 | SUB 0 0 1 # R(0) = R(0) - R(1) 12 | RETURN 0 1 # <- R(0)...R(0+1-1) = R(0)..R(0) = R(0) 13 | function main # k=10,0,1,"sub" 14 | LOADK 0 0 # R(0) = K(0) = 15 | LT 0 0 257 # (0 == R(0) < K(1)) ? ; (r0 >= 0) cont else PC++ 16 | JUMP 5 # PC += 5 to RETURN 17 | MOVE 1 0 # R(1) = R(0) = 18 | GETGLOBAL 0 3 # R(0) = G[K(3)] = G["sub"] = 19 | LOADK 2 2 # R(2) = K(2) = 20 | CALL 0 2 1 # R(0)(R(1), R(2)) = sub(x 1) 21 | # Note: R(0) is now (return value from call) 22 | JUMP -7 # PC += -7 to LT 23 | RETURN 0 1 # <- R(0)..R(0+1-1) = R(0)..R(0) = R(0) 24 | 25 | CALL A B C -> R(A), ... ,R(A+C-1) := R(A)(R(A+1), ... ,R(A+B)) 26 | # Start, ... Length = fun( Start, ... Length ) 27 | # CALL 3 0 0 = 3(4..3) = 3() -> 3..2 = () 28 | # CALL 3 3 1 = 3(4..6) = 3(4, 5, 6) -> 3..3 = (v0) 29 | # CALL 3 1 3 = 3(4..4) = 3(4) -> 3..5 = (v0, v1, v2) 30 | 31 | STask 32 | STask* next 33 | SActivationRecord* stack_top 34 | 35 | SActivationRecord 36 | SActivationRecord* parent 37 | SFunc func 38 | SInstr* pc 39 | SValue registry[] 40 | 41 | SFunc 42 | SValue constants[] 43 | SInstr instructions[] 44 | 45 | 46 | 47 | # Idea: What if there's no RETURN instruction, but a function implicitly returns 48 | # all its registers? It would be cheap. There could instead be a END instruction 49 | # which marks the end of a program (so sched_exec knows when to stop). Any 50 | # downsides? Maybe the upsides are none? 51 | 52 | -------------------------------------------------------------------------------- /lab/sol-functions.mv: -------------------------------------------------------------------------------- 1 | # Figure 1. 2 | # File: Function Line # Control flow> Instruction 3 | # ----------------------------- ------------------------------------------------------------- 4 | foo.sol: # sched> T1 = Task(bar/main) 5 | three = ^{ # T1/main:1> MOVEK R(0) = K(0) = V(1200.0) 6 | 1 print "Timeout completed" # T1/main:2.1> SLEEP delay=RK(0) retreg=1 7 | 2 return # sched> suspend and unschedule T1, start_timer(T1, delay) 8 | } # sched> schedule other tasks ...timer_expired: resume(T1) 9 | bar.sol: # T1/main:2.2> EQ RK(1) RK(255+1) PC++ else continue 10 | import foo # T1/main:2.3> JUMP 0 11 | main = ^{ # T1/main:3.1> MOVEK R(1) = K(1) (value bar/two) 12 | 1 delay = 1200 # T1/main:3.2> CALL 1 0 0 ; R(1)(R(2)..R(0)) 13 | 2 if ((sleep delay) == 0) # T1/two:1.1> MOVEK R(1) = K(2) (value foo) 14 | 3 two() # T1/two:1.2> GETTABLE R(1) = R(1)[RK(2)] where RK(2) = "three" 15 | 4 sleep 500 # T1/two:1.3> CALL 1 0 0 ; R(1)(R(2)..R(0)) 16 | 5 return # T1/three:1.1> MOVEK R(0) = K(0) (value print) 17 | } # T1/three:1.1> MOVEK R(1) = K(1) ("Timeout completed") 18 | two = ^{ # T1/three:1.2> CALL 0 2 0 ; R(0)(R(1)..R(1)) 19 | 1 foo.three() # print> ... eventually print returns 20 | 2 return # T1/three:2> RETURN 21 | } # T1/two:2> RETURN 22 | # T1/main:4.1> MOVEK R(0) = K(2) = V(500.0) 23 | # T1/main:4.2> SLEEP delay=RK(0) retreg=1 24 | # sched> suspend and unschedule T1, start_timer(T1, delay) 25 | # sched> schedule other tasks ...timer_expired: resume(T1) 26 | # T1/main:5> RETURN 27 | # sched> idle trap 28 | 29 | # Figure 2.1. Skip the next instruction when the test succeeds 30 | 1 if x == y # 1 if (RK(B) == RK(C)) PC++ (to 3) else continue (to 2) 31 | # 2 JUMP 3 (to 6) 32 | 2 do a # 3 do a 33 | 3 do b # 4 do b 34 | 4 do c # 5 do c 35 | 5 else # 6 JUMP 2 (to 9) 36 | 6 do e # 7 do e 37 | 7 do f # 8 do f 38 | 8 return # 9 RETURN 39 | 40 | # Figure 2.2. Skip the next instruction when the test fails 41 | 1 if x == y # 1 if (RK(B) != RK(C)) continue (to 2) else PC++ (to 3) 42 | # 2 JUMP 3 (to 6) 43 | 2 do a # 3 do a 44 | 3 do b # 4 do b 45 | 4 do c # 5 do c 46 | 5 else # 6 JUMP 2 (to 9) 47 | 6 do e # 7 do e 48 | 7 do f # 8 do f 49 | 8 return # 9 RETURN 50 | 51 | # Figure 3. Lua-style CALL instruction 52 | CALL A B C -> R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) 53 | 54 | # Figure 4.1. As "sleep" returns the time remaining to sleep when the timer was 55 | # interrupted, we can implement "retry" just like in regular C: 56 | delay = 2000 57 | while delay > 0 58 | if ((delay = sleep delay) > 0) 59 | print "Sleep was interrupted. Sleeping again..." 60 | 61 | # Figure 4.2. Figure 4.1 Expands to: 62 | MOVEK R(0) = K(0) # where K(0) = 2000.0 63 | SLEEP delay=RK(0) retreg=1 # sleep for RK(0) and put remaining time in R(1) 64 | 65 | LT RK(255+1) RK(1) # if (K(1) < R(1)) continue else PC++ 66 | JUMP 3 # to RETURN 67 | MOVEK R(2) = K(2) # R(2) = function "print" 68 | CALL R(2) RK(255+3) RK(255+3) # print K(3)..K(3) where K3() = "Sleep was i..." 69 | JUMP -6 # to SLEEP 70 | RETURN 71 | 72 | -------------------------------------------------------------------------------- /lab/task-message-passing-take2.md: -------------------------------------------------------------------------------- 1 | Erlang snippet re pids 2 | 3 | erts_smp_atomic_inc(&process_count); 4 | p->id = make_internal_pid(p_serial << p_serial_shift | p_next); 5 | if (p->id == ERTS_INVALID_PID) { 6 | /* Do not use the invalid pid; change serial */ 7 | p_serial++; 8 | p_serial &= p_serial_mask; 9 | p->id = make_internal_pid(p_serial << p_serial_shift | p_next); 10 | ASSERT(p->id != ERTS_INVALID_PID); 11 | } 12 | ASSERT(internal_pid_serial(p->id) <= (erts_use_r9_pids_ports 13 | ? ERTS_MAX_PID_R9_SERIAL 14 | : ERTS_MAX_PID_SERIAL)); 15 | 16 | ## 1. send msg to tid 17 | 18 | + Easily parallelizable (no dangling refs to tasks). 19 | + Efficient implementation a swe know there's only one consumer. 20 | + Simple programming, as there's no need for channel creation. 21 | - TIDs must either be secure (that is hard) or any task might send a message to any other task 22 | - Either every task must have an inbox (uses ~100 bytes or memory), or there needs to be a lazy instantiation of an inbox. 23 | 24 | y = fun: 25 | Connection c = recv 26 | send "ok" to __parent 27 | x = fun: 28 | tid = spawn y 29 | Connection c = accept 30 | send c to tid 31 | Text status = recv 32 | 33 | 34 | ## 2. send msg to T 35 | 36 | + Secure as T is explicitly handed over to non-family tasks. 37 | + Efficient implementation a swe know there's only one consumer. 38 | + Simple programming (any task can be sent a message.) 39 | - Hard to parallelize as a reference to a task would be… actually this is equally hard as alternative 3. 40 | - Either every task must have an inbox (uses ~100 bytes or memory), or there needs to be a lazy instantiation of an inbox. 41 | 42 | y = fun: 43 | Connection c = recv 44 | send "ok" to __parent 45 | x = fun: 46 | t = spawn y 47 | Connection c = accept 48 | send c to t 49 | Text status = recv 50 | 51 | ## 3 send msg over chan 52 | 53 | + Simpler implementation as a task has no inbox. 54 | + More flexible as a single channel can be used across many processes. In the case where there's one producer (e.g. "import jobs, preprocess and then have the first available worker take on the job" could be efficiently implemented with a MP/MC queue, but with a SP/MC queue, the producer would need to decide which task to give the job). 55 | - If the common case is for tasks to communicate, this requires boiler-plate code (making a channel, passing it, etc). 56 | - Less effective queue implementation as single-consumer can not be assumed (must assume multiple-consumers). 57 | - More complex queue and task implementation as a task can be waiting for a specific queue (with an inbox model, the queue is implied by the "wait_msg" flag). 58 | - There would be no "cascading errors" of task upon errors, where a subtask sends "error" to its parent when an error occurs and the parent will subsequently propagate that error by exiting and so on. 59 | 60 | y = fun chan: 61 | Connection c = recv from chan 62 | send "ok" over chan 63 | x = fun: 64 | chan = Chan{} 65 | t = spawn y with args chan 66 | Connection c = accept 67 | send c over chan 68 | Text status = recv from chan 69 | 70 | ## Questions 71 | 72 | 1. Can we efficiently and simplisticly implement a variable-channel listener? 73 | 2. Can we implement a MP/MC queue that comes close to the efficiency of the current MP/SC queue? 74 | 3. Are we pursuing a mental model of "task hierarchy" or "sea of potentially communicating tasks"? 75 | 76 | A "task group" (aka Erlang-style) model might be favorable as it is likely to be easier to understand. We could provide a library function for MP/MC queues ("channel") to solve the use-case of "let the first available subtask receive this message", but retain "cascading errors" and "task hierarchy". 77 | 78 | Error handling is important as we assume these systems will inherently deal with errors. 79 | 80 | Erlang has two "types" of errors: 81 | 82 | - _Exceptions_ occur when the run-time system does not know what to do. E.g. when asked to open a file which does not exist. 83 | - _Errors_ occur when the programmer doesn’t know what to do. I.e. when there's no way to deal with an unexpected result (e.g. status code returned from a subtask is unknown to the receiving task and so the task has no way of dealing with it). 84 | 85 | This is a very sensible error model. An _Error_ would cause a task to exit with that error as the return argument. An _Exception_ would be an error that is trapped by user code. Thus, an _Exception_ which is not trapped in a task is an _Error_. 86 | 87 | ### Alternatives for trapping errors 88 | 89 | A: 90 | 91 | f, error = fs:open "bar" 92 | # do something about err ... 93 | 94 | B: 95 | 96 | try: 97 | f = fs:open "bar" 98 | catch error: 99 | # do something about err ... 100 | 101 | For any case, if error is not trapped: 102 | 103 | f = fs:open "bar" 104 | # crash on error 105 | 106 | I lean toward alternative A as the high-level protocol could be very simple and easy to understand. BUT! This needs to be universally applicable to any function. How about this case: 107 | 108 | read = fun fn: 109 | f = fs:open fn 110 | data = (fs:read f) 111 | if (len data) > 100: 112 | return data, "large" 113 | else: 114 | return data 115 | main = fun: 116 | _, data = read "bar" 117 | 118 | #### Hurdle 119 | 120 | What happens when `fs:open` fails in the `read` function? The `read` function has a variable number of return values, so we can't simply return prematurely with `nil, nil, error`. Or perhaps we can. We could make a rule that any function in Sol must declare their return types, and so the number of return values are constant. 121 | 122 | Our `read` function would be defined as: 123 | 124 | read = fun fn -> Data Text: 125 | ... 126 | 127 | And so when an error occurs (e.g. in `fs:open`) we simply return N number of `nil`s followed by the error, where N is the number of declared return values. Then, the landing code (after a function call) would inspect the number of landing values; R. If R is <= to N, then check if there's an error set. If there is, propagate the error. If R is == N+1, then set landing value R-1 to any error, or `nil` if no error was set. 128 | 129 | In this scenario a function that wishes to return variable arguments could be done so with syntactic sugar 130 | 131 | M = fun -> Text… Int: 132 | return "a", "b", "c", 9 133 | main = fun: 134 | names…, y, error = M() 135 | name1, name2 = M() 136 | 137 | In the second case of calling `M`, trapping of errors would not be possible, which is OK as trapping of errors would be possible as illustrated by the first case (`names…`). 138 | 139 | 140 | ## Scheduler load balancing 141 | 142 | volatile SSched* least_loaded_sched 143 | 144 | S1: 145 | 146 | runloop: 147 | exec()... 148 | for each S in all_schedulers: 149 | if S.load <= my_load: 150 | goto runloop 151 | least_loaded_sched = S1 152 | S2: 153 | 154 | runloop: 155 | exec()... 156 | for each S in all_schedulers: 157 | if S.load <= my_load: 158 | goto runloop 159 | least_loaded_sched = S1 160 | -------------------------------------------------------------------------------- /lab/task-message-passing.md: -------------------------------------------------------------------------------- 1 | Task message passing. Simple FIFO should do. 2 | 3 | A: B = spawn bfun 4 | B: x = recv() 5 | A: "hello" -> B 6 | B: print x # "hello" 7 | 8 | ### Approach 1: 9 | 10 | Erlang-style, each task has an inbox. 11 | 12 | Task { 13 | FIFO inbox 14 | Flag msg_wait 15 | } 16 | 17 | - Must be a way for a task to wait for input to arrive on it's inbox. We could solve this with a YIELD instruction type "wait-inbox", or simply a SEND and RECV instruction pair with yield similarities, avoiding higher-level locking or semaphores. 18 | 19 | task A> SPAWN ... 20 | sched> runqueue_push(new_task(...)) 21 | task B> RECV 0 1 0 22 | sched> if (T->inbox is empty): 23 | T->msg_wait = 1 24 | runqueue_pop(T) 25 | task A> MOVEK 0 0 ; R(0) = 26 | task A> MOVEK 1 1 ; R(1) = "hello" 27 | task A> SEND 0 1 1 28 | sched> T = find_task(R(A)) = 29 | sched> inbox_push(T->inbox, R(B)) 30 | sched> if (T->msg_wait): 31 | runqueue_push(T) 32 | task B> CALL … print R(0) ; "hello" is in R(0) 33 | 34 | -------------------------------------------------------------------------------- /lab/task-structure-overview.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | - A task has an inbox (MP/SC queue) 4 | - When sending messages, tasks are referenced by themselves (not a "TID") 5 | - A future library function might provide a "channel" (MP/MS queue) for cases where multiple consumers is desired 6 | - A task maintains a reference to its supertask (which spawned it) 7 | - When tasks are spawned, arguments can be passed to the entry function 8 | - Errors cascade — when a subtask end-with-error, the supertask propagates that error, unless the supertask is trapping subtask erros 9 | - Tasks can set a flag to trap subprocess end-with-errors, e.g. to perform clean up or restart subtasks 10 | - Functions can return multiple results and an untrapped error is always returned as the last+1 result 11 | - As an incremental improvement, schedulers could perform "zombie task collection" when idle or lightly loaded by traversing all suspended tasks to see if they are waiting for a message from a dead supertask (and if so cause them to end with an error "task dead" or "task inbox closed"). That should be sufficient to avoid fatal leaks. -------------------------------------------------------------------------------- /sol/Makefile: -------------------------------------------------------------------------------- 1 | include ../Make.common 2 | 3 | # Sources 4 | cxx_sources := 5 | 6 | c_sources := log.c host.c msg.c \ 7 | sched.c task.c func.c \ 8 | value.c 9 | 10 | headers_pub := sol.h common.h common_target.h common_stdint.h common_atomic.h \ 11 | debug.h log.h host.h msg.h \ 12 | vm.h sched.h runq.h task.h func.h arec.h instr.h \ 13 | value.h 14 | 15 | main_c_sources := main.c 16 | 17 | # --- conf --------------------------------------------------------------------- 18 | 19 | project_id = sol 20 | all: $(project_id) 21 | 22 | # Source files to Object files 23 | object_dir = $(OBJECT_DIR)-$(project_id) 24 | objects = $(call SrcToObjects,$(object_dir),${cxx_sources:.cc=.o} ${c_sources:.c=.o}) 25 | main_objects = $(call SrcToObjects,$(object_dir),${main_c_sources:.c=.o}) 26 | object_dirs = $(call FileDirs,$(objects)) $(call FileDirs,$(main_objects)) 27 | 28 | # For LLVM IR and Assembly output 29 | asmout_dir = $(DEBUG_BUILD_PREFIX)/$(project_id)-asm 30 | asmout_ll = $(call SrcToObjects,$(asmout_dir),${cxx_sources:.cc=.ll} ${c_sources:.c=.ll}) 31 | asmout_ll_dirs = $(call FileDirs,$(asmout_ll)) 32 | asmout_s = $(call SrcToObjects,$(asmout_dir),${cxx_sources:.cc=.s} ${c_sources:.c=.s}) 33 | asmout_s_dirs = $(call FileDirs,$(asmout_s)) 34 | 35 | # Public headers 36 | headers_pub_dir = $(INCLUDE_BUILD_PREFIX)/$(project_id) 37 | headers_pub_export = $(call PubHeaderNames,$(headers_pub_dir),$(headers_pub)) 38 | headers_pub_export_dirs = $(call FileDirs,$(headers_pub_export)) 39 | 40 | # Sol library and program 41 | static_library = $(LIB_BUILD_PREFIX)/lib$(project_id).a 42 | main_program = $(BIN_BUILD_PREFIX)/$(project_id) 43 | 44 | # Compiler and linker flags 45 | c_flags := $(CFLAGS) -MMD 46 | cxx_flags := $(CXXFLAGS) 47 | ld_flags := $(LDFLAGS) -L"$(LIB_BUILD_PREFIX)" 48 | xxld_flags := $(XXLDFLAGS) 49 | 50 | # --- targets --------------------------------------------------------------------- 51 | 52 | clean: 53 | rm -rf $(object_dir) 54 | rm -rf $(headers_pub_export) 55 | rm -rf $(asmout_dir) 56 | rm -f $(main_program) 57 | rm -f $(static_library) 58 | rm -f "$(LIB_BUILD_PREFIX)"/libev.a 59 | @$(MAKE) -C "$(SRCROOT)"/deps/libev clean 60 | 61 | common_pre: 62 | @mkdir -p $(object_dirs) 63 | @mkdir -p $(headers_pub_export_dirs) 64 | 65 | # Create program 66 | $(project_id): common_pre lib$(project_id) libev $(main_program) 67 | $(main_program): $(main_objects) 68 | @mkdir -p $(dir $(main_program)) 69 | $(LD) $(ld_flags) -lev -l$(project_id) -o $@ $^ 70 | 71 | # External dependency: libev 72 | libev: 73 | @$(MAKE) -C $(SRCROOT)/deps/libev 74 | @mkdir -p "$(LIB_BUILD_PREFIX)" 75 | @ln -fs "$(SRCROOT)/deps/libev/.libs/libev.a" "$(LIB_BUILD_PREFIX)" 76 | 77 | # Create library archive 78 | lib$(project_id): common_pre $(headers_pub_export) $(static_library) 79 | $(static_library): $(objects) 80 | @mkdir -p $(dir $(static_library)) 81 | $(AR) -rc $@ $^ 82 | 83 | # Generate LLVM IS code (.ll files) 84 | llvm_ir_pre: common_pre 85 | @mkdir -p $(asmout_ll_dirs) 86 | llvm_ir: llvm_ir_pre $(asmout_ll) 87 | 88 | # Generate target assembly code (.s files) 89 | asm_pre: common_pre 90 | @mkdir -p $(asmout_s_dirs) 91 | asm: asm_pre $(asmout_s) 92 | 93 | # C++ source -> object 94 | $(object_dir)/%.o: %.cc 95 | $(CXXC) $(c_flags) $(cxx_flags) -c -o $@ $< 96 | 97 | # C source -> object 98 | $(object_dir)/%.o: %.c 99 | $(CC) $(c_flags) -c -o $@ $< 100 | 101 | # C source -> LLVM IR 102 | $(asmout_dir)/%.ll: %.c 103 | clang $(CFLAGS) -S -emit-llvm -DS_CODEGEN_ASM=1 -DS_CODEGEN_LLVMIR=1 -o $@ $< 104 | 105 | # C source -> target assembly 106 | $(asmout_dir)/%.s: %.c 107 | $(CC) $(CFLAGS) -S -DS_CODEGEN_ASM=1 -o $@ $< 108 | 109 | # Copy headers into $headers_pub_dir 110 | $(headers_pub_dir)/%: % 111 | @cp $^ $@ 112 | 113 | # header dependencies 114 | -include ${objects:.o=.d} 115 | -include ${main_objects:.o=.d} 116 | 117 | .PHONY: all clean common_pre $(project_id) libev lib$(project_id) \ 118 | llvm_ir_pre asm_pre 119 | -------------------------------------------------------------------------------- /sol/arec.h: -------------------------------------------------------------------------------- 1 | // An activation record (or call-stack frame) is created for each instance of a 2 | // function call. It contains the function prototype that is is executing, a PC 3 | // pointing to the current instruction in the function's code that is executing, 4 | // a reference to the parent AR (link in a linked list forming the call stack), 5 | // and finally a register that is used to store values during execution. 6 | #ifndef S_AREC_H_ 7 | #define S_AREC_H_ 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | typedef struct SARec { 14 | SFunc* func; // Function 15 | SInstr* pc; // PC 16 | struct SARec* parent; // Parent AR 17 | SValue registry[10]; // Registry 18 | } SARec; // 184 = 24+(16*10) 19 | 20 | // Create a new activation record. We inline this since it's only used in two 21 | // places: Creation of a new task and when calling a function. In the latter 22 | // case we want to avoid a function call (thus this is inlined.) 23 | inline static SARec* S_ALWAYS_INLINE 24 | SARecCreate(SFunc* func, SARec* parent) { 25 | SARec* ar = (SARec*)malloc(sizeof(SARec)); 26 | ar->func = func; 27 | 28 | // We have to set PC to instrv MINUS ONE since the VM executive sees PC as 29 | // the instruction that WAS executed and thus the first thing that happens 30 | // when a function runs is PC++, putting us at the first instruction. 31 | ar->pc = func->instructions -1; 32 | 33 | ar->parent = parent; 34 | return ar; 35 | } 36 | 37 | inline static void S_ALWAYS_INLINE 38 | SARecDestroy(SARec* ar) { 39 | free((void*)ar); 40 | } 41 | 42 | #endif // S_AREC_H_ 43 | -------------------------------------------------------------------------------- /sol/common.h: -------------------------------------------------------------------------------- 1 | #ifndef S_COMMON_H_ 2 | #define S_COMMON_H_ 3 | #define S_INTERNAL_ 4 | 5 | #define S_STR1(str) #str 6 | #define S_STR(str) S_STR1(str) 7 | 8 | #define S_VERSION_MAJOR 0 9 | #define S_VERSION_MINOR 1 10 | #define S_VERSION_BUILD 0 11 | #define S_VERSION_STRING \ 12 | S_STR(S_VERSION_MAJOR) "." S_STR(S_VERSION_MINOR) "." S_STR(S_VERSION_BUILD) 13 | 14 | #ifndef S_DEBUG 15 | #define S_DEBUG 0 16 | #endif 17 | // Disable multiprocessing? 18 | //#define S_WITHOUT_MP 0 19 | 20 | #include 21 | 22 | #ifdef __BASE_FILE__ 23 | #define S_FILENAME __BASE_FILE__ 24 | #else 25 | #define S_FILENAME ((strrchr(__FILE__, '/') ?: __FILE__ - 1) + 1) 26 | #endif 27 | 28 | #define s_countof(a) (sizeof(a)/sizeof(*(a))) 29 | 30 | // Assertion macros 31 | #if !NDEBUG 32 | #define SAssert(x) assert(x) 33 | // Important: The following macros _always_ evaluate `x`, even when compiling 34 | // w/ assertions disabled. The purpose is to allow wrapping like so: 35 | // SAssertNil(somelibcfunc(dostuff)) 36 | // 37 | #define SAssertTrue(x) SAssert((x) == true) 38 | #define SAssertFalse(x) SAssert((x) == false) 39 | #define SAssertNil(x) SAssert((x) == 0) 40 | #define SAssertNotNil(x) SAssert((x) != 0) 41 | #else 42 | #define SAssert(x) ((void)0) 43 | #define SAssertTrue(x) (x) 44 | #define SAssertFalse(x) (x) 45 | #define SAssertNil(x) (x) 46 | #define SAssertNotNil(x) (x) 47 | #endif 48 | 49 | // Terminate process with status 70 (EX_SOFTWARE), writing `fmt` with optional 50 | // arguments to stderr. 51 | #define S_FATAL(fmt, ...) \ 52 | errx(70, "FATAL: " fmt " at " __FILE__ ":" S_STR(__LINE__), ##__VA_ARGS__) 53 | 54 | // Print `fmt` with optional arguments to stderr, and abort() process causing 55 | // EXC_CRASH/SIGABRT. 56 | #define S_CRASH(fmt, ...) do { \ 57 | fprintf(stderr, "CRASH: " fmt " at " __FILE__ ":" S_STR(__LINE__) "\n", \ 58 | ##__VA_ARGS__); \ 59 | abort(); \ 60 | } while (0) 61 | 62 | // Attributes 63 | #ifndef __has_attribute 64 | #define __has_attribute(x) 0 65 | #endif 66 | #ifndef __has_builtin 67 | #define __has_builtin(x) 0 68 | #endif 69 | 70 | #if __has_attribute(always_inline) 71 | #define S_ALWAYS_INLINE __attribute__((always_inline)) 72 | #else 73 | #define S_ALWAYS_INLINE 74 | #endif 75 | #if __has_attribute(unused) 76 | // Attached to a function, means that the function is meant to be possibly 77 | // unused. The compiler will not produce a warning for this function. 78 | #define S_UNUSED __attribute__((unused)) 79 | #else 80 | #define S_UNUSED 81 | #endif 82 | #if __has_attribute(pure) 83 | #define S_PURE __attribute__((pure)) 84 | #else 85 | #define S_PURE 86 | #endif 87 | #if __has_attribute(warn_unused_result) 88 | #define S_WUNUSEDR __attribute__((warn_unused_result)) 89 | #else 90 | #define S_WUNUSEDR 91 | #endif 92 | #if __has_attribute(packed) 93 | #define S_PACKED __attribute((packed)) 94 | #else 95 | #define S_PACKED 96 | #endif 97 | #if __has_attribute(aligned) 98 | #define S_ALIGNED(bytes) __attribute__((aligned (bytes))) 99 | #else 100 | #warning "No align attribute available. Things might break" 101 | #define S_ALIGNED 102 | #endif 103 | 104 | #if __has_builtin(__builtin_unreachable) 105 | #define S_UNREACHABLE do { \ 106 | assert(!"Declared S_UNREACHABLE but was reached"); \ 107 | __builtin_unreachable(); \ 108 | } while(0) 109 | #else 110 | #define S_UNREACHABLE \ 111 | assert(!"Declared S_UNREACHABLE but was reaced") 112 | #endif 113 | 114 | #define S_NOT_IMPLEMENTED S_FATAL("NOT IMPLEMENTED in %s", __PRETTY_FUNCTION__) 115 | 116 | 117 | #define S_MAX(a,b) \ 118 | ({ __typeof__ (a) _a = (a); \ 119 | __typeof__ (b) _b = (b); \ 120 | _a > _b ? _a : _b; }) 121 | 122 | #define S_MIN(a,b) \ 123 | ({ __typeof__ (a) _a = (a); \ 124 | __typeof__ (b) _b = (b); \ 125 | _a < _b ? _a : _b; }) 126 | 127 | #include // .. include 128 | #include 129 | #include 130 | #include 131 | #include 132 | #include 133 | #include 134 | 135 | #if S_HOST_OS_POSIX 136 | #include 137 | #endif 138 | 139 | #include 140 | 141 | #undef S_INTERNAL_ 142 | #endif // S_COMMON_H_ 143 | -------------------------------------------------------------------------------- /sol/common_atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef S_COMMON_ATOMIC_H_ 2 | #define S_COMMON_ATOMIC_H_ 3 | #ifndef S_INTERNAL_ 4 | #error "This file should not be included directly" 5 | #endif 6 | 7 | // Atomically swap integers or pointers in memory. 8 | // E.g: int old_value = SAtomicSwap(&value, new_value); 9 | // T SAtomicSwap(T *ptr, T value) 10 | #if S_WITHOUT_SMP 11 | #define SAtomicSwap(ptr, value) \ 12 | ({ __typeof__ (value) oldval = *(ptr); \ 13 | *(ptr) = (value); \ 14 | oldval; }) 15 | #elif defined(__clang__) 16 | // This is more efficient than the below fallback 17 | #define SAtomicSwap __sync_swap 18 | #elif defined(__GNUC__) && (__GNUC__ >= 4) 19 | static inline void* S_UNUSED _SAtomicSwap(void* volatile* ptr, void* value) { 20 | void* oldval; 21 | do { 22 | oldval = *ptr; 23 | } while (__sync_val_compare_and_swap(ptr, oldval, value) != oldval); 24 | return oldval; 25 | } 26 | #define SAtomicSwap(ptr, value) \ 27 | _SAtomicSwap((void* volatile*)(ptr), (void*)(value)) 28 | #else 29 | #error "Unsupported compiler: Missing support for atomic operations" 30 | #endif 31 | 32 | // Atomically increment a 32-bit integer by N. There's no return value. 33 | // void SAtomicSubAndFetch(T* operand, T delta) 34 | #if S_WITHOUT_SMP 35 | #define SAtomicAdd32(operand, delta) (*(operand) += (delta)) 36 | #elif S_TARGET_ARCH_X64 || S_TARGET_ARCH_X86 37 | inline static void S_UNUSED SAtomicAdd32(int32_t* operand, int32_t delta) { 38 | // From http://www.memoryhole.net/kyle/2007/05/atomic_incrementing.html 39 | __asm__ __volatile__ ( 40 | "lock xaddl %1, %0\n" // add delta to operand 41 | : // no output 42 | : "m" (*operand), "r" (delta) 43 | ); 44 | } 45 | #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 4)) 46 | #define SAtomicAdd32 __sync_sub_and_fetch 47 | #else 48 | #error "Unsupported compiler: Missing support for atomic operations" 49 | #endif 50 | 51 | // Subtract `delta` from `operand` and return the resulting value of `operand` 52 | // T SAtomicSubAndFetch(T* operand, T delta) 53 | #if S_WITHOUT_SMP 54 | #define SAtomicSubAndFetch(operand, delta) (*(operand) -= (delta)) 55 | #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 4)) 56 | #define SAtomicSubAndFetch __sync_sub_and_fetch 57 | #else 58 | #error "Unsupported compiler: Missing support for atomic operations" 59 | #endif 60 | 61 | #endif // S_COMMON_ATOMIC_H_ 62 | -------------------------------------------------------------------------------- /sol/common_stdint.h: -------------------------------------------------------------------------------- 1 | #ifndef S_INTERNAL_ 2 | #error "This file should not be included directly" 3 | #endif 4 | 5 | #include 6 | #include // size_t, ssize_t, off_t 7 | 8 | #if defined(_WIN32) && !defined(__MINGW32__) 9 | typedef signed char int8_t; 10 | typedef unsigned char uint8_t; 11 | typedef short int16_t; // NOLINT 12 | typedef unsigned short uint16_t; // NOLINT 13 | typedef int int32_t; 14 | typedef unsigned int uint32_t; 15 | typedef __int64 int64_t; 16 | typedef unsigned __int64 uint64_t; 17 | // intptr_t and friends are defined in crtdefs.h through stdio.h. 18 | typedef unsigned char bool; 19 | #else 20 | #include 21 | #include 22 | #endif 23 | -------------------------------------------------------------------------------- /sol/common_target.h: -------------------------------------------------------------------------------- 1 | #ifndef S_COMMON_TARGET_H_ 2 | #define S_COMMON_TARGET_H_ 3 | #ifndef S_INTERNAL_ 4 | #error "This file should not be included directly" 5 | #endif 6 | 7 | //-- begin S_TARGET_ARCH_* 8 | #if defined(__i386) || defined(__i386__) || defined(_M_IX86) 9 | #define S_TARGET_ARCH_X86 1 10 | #elif defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || \ 11 | defined(_M_AMD64) 12 | #define S_TARGET_ARCH_X64 1 13 | #elif defined(__arm__) || defined(__arm) || defined(__ARM__) || defined(__ARM) 14 | #define S_TARGET_ARCH_ARM 1 15 | // #elif defined(__ppc__) || defined(__ppc) || defined(__PPC__) || \ 16 | // defined(__PPC) || defined(__powerpc__) || defined(__powerpc) || \ 17 | // defined(__POWERPC__) || defined(__POWERPC) || defined(_M_PPC) 18 | // #ifdef __NO_FPRS__ 19 | // #define S_TARGET_ARCH_PPCSPE 1 20 | // #else 21 | // #define S_TARGET_ARCH_PPC 1 22 | // #endif 23 | // #elif defined(__mips__) || defined(__mips) || defined(__MIPS__) || \ 24 | // defined(__MIPS) 25 | // #define S_TARGET_ARCH_MIPS 1 26 | #else 27 | #error "Unsupported target architecture" 28 | #endif 29 | 30 | #if S_TARGET_ARCH_X64 31 | #define S_TARGET_ARCH_NAME "x64" 32 | #define S_TARGET_ARCH_SIZE 64 33 | #define S_TARGET_LITTLE_ENDIAN 1 34 | #elif S_TARGET_ARCH_X86 35 | #define S_TARGET_ARCH_NAME "x86" 36 | #define S_TARGET_ARCH_SIZE 32 37 | #define S_TARGET_LITTLE_ENDIAN 1 38 | #elif S_TARGET_ARCH_ARM 39 | #if defined(__ARMEB__) 40 | #error "Unsupported target architecture: Big endian ARM" 41 | #endif 42 | #define S_TARGET_ARCH_NAME "arm" 43 | #define S_TARGET_ARCH_SIZE 32 44 | #define S_TARGET_LITTLE_ENDIAN 1 45 | #else 46 | #define S_TARGET_ARCH_NAME "?" 47 | #define S_TARGET_ARCH_SIZE 0 48 | #define S_TARGET_LITTLE_ENDIAN 0 49 | #endif 50 | //-- end S_TARGET_ARCH_* 51 | 52 | //-- begin S_TARGET_OS_* 53 | #if defined(_WIN32) && !defined(_XBOX_VER) 54 | #define S_TARGET_OS_WINDOWS 1 55 | #define S_TARGET_OS_NAME "win32" 56 | #elif defined(__linux__) 57 | #define S_TARGET_OS_LINUX 1 58 | #define S_TARGET_OS_POSIX 1 59 | #define S_TARGET_OS_NAME "linux" 60 | #elif defined(__MACH__) && defined(__APPLE__) 61 | #define S_TARGET_OS_DARWIN 1 62 | #define S_TARGET_OS_POSIX 1 63 | #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) 64 | #define S_TARGET_OS_OSX 1 65 | #define S_TARGET_OS_NAME "osx" 66 | #elif defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR 67 | #define S_TARGET_OS_IOS 1 68 | #define S_TARGET_OS_IOS_SIMULATOR 1 69 | #define S_TARGET_OS_NAME "ios-simulator" 70 | #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 71 | #define S_TARGET_OS_IOS 1 72 | #define S_TARGET_OS_NAME "ios" 73 | #else 74 | #define S_TARGET_OS_NAME "darwin" 75 | #endif 76 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ 77 | defined(__NetBSD__) || defined(__OpenBSD__) 78 | #define S_TARGET_OS_BSD 1 79 | #define S_TARGET_OS_POSIX 1 80 | #elif (defined(__sun__) && defined(__svr4__)) || defined(__solaris__) || \ 81 | defined(__CYGWIN__) 82 | #define S_TARGET_OS_POSIX 1 83 | #else 84 | #define S_TARGET_OS_UNKNOWN 1 85 | #endif 86 | //-- end S_TARGET_OS_* 87 | 88 | #endif // S_COMMON_TARGET_H_ 89 | -------------------------------------------------------------------------------- /sol/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef S_DEBUG_H_ 2 | #define S_DEBUG_H_ 3 | #if S_DEBUG 4 | 5 | #include 6 | 7 | // Debug: Function prototype for the DBGINSPECT instruction 8 | #if S_DEBUG 9 | typedef void (*SDebugVMCallback)(SVM*, SSched*, STask*, SInstr* arpc); 10 | #endif 11 | 12 | // Binary representation of integer `x` of length `bitcount` 13 | S_UNUSED static const char* 14 | SDFormatBin(uintmax_t x, size_t bitcount) { 15 | static char buf[9 * sizeof(uintmax_t) + 1] = {0}; // 9=8C+1SP 16 | char *s = buf + ((9 * sizeof(uintmax_t)) - bitcount - 2); 17 | size_t count = 0; 18 | *--s = 0; 19 | if (!x) { 20 | ++count; 21 | *--s = '0'; 22 | } 23 | for (; x && s > buf+1; x /= 2) { 24 | if (count++ % 8 == 0) { 25 | *--s = ' '; 26 | } 27 | *--s = '0' + x % 2; 28 | } 29 | while (s > buf+1) { 30 | if (count++ % 8 == 0) { 31 | *--s = ' '; 32 | } 33 | *--s = '0'; 34 | } 35 | return s; 36 | } 37 | 38 | #endif // S_DEBUG 39 | #endif // S_DEBUG_H_ 40 | -------------------------------------------------------------------------------- /sol/func.c: -------------------------------------------------------------------------------- 1 | #include "func.h" 2 | 3 | SFunc* SFuncCreate(SValue* constants, SInstr* instructions) { 4 | SFunc* f = (SFunc*)malloc(sizeof(SFunc)); 5 | f->constants = constants; 6 | f->instructions = instructions; 7 | return f; 8 | } 9 | 10 | void SFuncDestroy(SFunc* f) { 11 | free((void*)f); 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /sol/func.h: -------------------------------------------------------------------------------- 1 | #ifndef S_FUNC_T_ 2 | #define S_FUNC_T_ 3 | 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | SValue* constants; 9 | SInstr* instructions; 10 | } SFunc; 11 | 12 | SFunc* SFuncCreate(SValue* constants, SInstr* instructions); 13 | void SFuncDestroy(SFunc* f); 14 | 15 | #endif // S_FUNC_T_ -------------------------------------------------------------------------------- /sol/host.c: -------------------------------------------------------------------------------- 1 | #include "host.h" 2 | 3 | #if S_TARGET_OS_WINDOWS 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | #elif S_TARGET_OS_POSIX 7 | #include // sysconf 8 | #include 9 | #include 10 | #endif 11 | 12 | uint32_t SHostAvailCPUCount() { 13 | // Thanks to http://stackoverflow.com/questions/150355/programmatically- 14 | // find-the-number-of-cores-on-a-machine 15 | 16 | #if S_TARGET_OS_WINDOWS 17 | // TODO: dry code needs testing 18 | SYSTEM_INFO sysinfo; 19 | GetSystemInfo(&sysinfo); 20 | return (sysinfo.dwNumberOfProcessors < 1) ? 21 | 0 : sysinfo.dwNumberOfProcessors; 22 | 23 | #elif S_TARGET_OS_POSIX && defined(HW_NCPU) 24 | uint32_t ncpu = 0; 25 | size_t ncpusz = sizeof(ncpu); 26 | int key[4]; 27 | key[0] = CTL_HW; 28 | 29 | #ifdef HW_AVAILCPU 30 | key[1] = HW_AVAILCPU; 31 | if (sysctl(key, 2, &ncpu, &ncpusz, NULL, 0) == -1) { 32 | // Failed. Try HW_NCPU 33 | #endif 34 | key[1] = HW_NCPU; 35 | if (sysctl(key, 2, &ncpu, &ncpusz, NULL, 0 ) == -1) { 36 | return 0; 37 | } 38 | #ifdef HW_AVAILCPU 39 | } 40 | #endif 41 | return ncpu; 42 | 43 | #elif defined(_SC_NPROCESSORS_ONLN) 44 | // Possibly available on OS X, Linux, Solaris and AIX 45 | long ncpu = sysconf(_SC_NPROCESSORS_ONLN); 46 | if (ncpu < 1) { 47 | #ifdef _SC_NPROCESSORS_CONF 48 | ncpu = sysconf(_SC_NPROCESSORS_CONF); 49 | if (ncpu < 1) { 50 | ncpu = 0; 51 | } 52 | #else 53 | ncpu = 0; 54 | #endif 55 | } 56 | return (uint32_t)ncpu; 57 | 58 | #else 59 | #warning "Unsupported host" 60 | return 0; 61 | #endif 62 | } 63 | -------------------------------------------------------------------------------- /sol/host.h: -------------------------------------------------------------------------------- 1 | #ifndef S_HOST_H_ 2 | #define S_HOST_H_ 3 | #include 4 | 5 | // Number of available processing units (cores). Returns 0 on error. 6 | // This value can change at runtime (e.g. from power management settings). 7 | uint32_t SHostAvailCPUCount(); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /sol/instr.h: -------------------------------------------------------------------------------- 1 | #ifndef S_INSTR_H_ 2 | #define S_INSTR_H_ 3 | #include 4 | 5 | // We currently only support LE archs with 32-bit or larger registers 6 | #if S_TARGET_ARCH_SIZE < 32 7 | #error "Unsupported target architecture: Register size too small" 8 | #elif !S_TARGET_LITTLE_ENDIAN 9 | #error "Unsupported target architecture: Big Endian" 10 | #endif 11 | 12 | // 13 | // One instruction is 32 bits: 14 | typedef uint32_t SInstr; 15 | // 16 | // Each instruction is encoded in one of three ways: 17 | // 18 | // OP A B C 19 | // Operation OP with operands A, B and C 20 | // 21 | // | 0 5 | 6 13 | 14 22 | 23 31 | 22 | // |------------|---------------|-----------------|-----------------| 23 | // | OP | A | B | C | 24 | // |------------|---------------|-----------------------------------| 25 | // 6 8 9 9 26 | // [0..63] [0..255] [0..511] [0..511] 27 | // 28 | // OP A Bx 29 | // Operation OP with operands A and Bs|Bu 30 | // 31 | // | 0 5 | 6 13 | 14 31 | 32 | // |------------|---------------|-----------------------------------| 33 | // | OP | A | Bs/Bu | 34 | // |------------|---------------|-----------------------------------| 35 | // 6 8 18 36 | // [0..63] [0..255] Bu: [0..262143] 37 | // Bu: [-131071..131072] 38 | // 39 | // OP Bxx 40 | // Operation OP with operand Bss|Buu 41 | // 42 | // | 0 5 | 6 31 | 43 | // |------------|---------------------------------------------------| 44 | // | OP | Bss/Buu | 45 | // |------------|---------------------------------------------------| 46 | // 6 26 47 | // [0..63] Buu: [0..67108863] 48 | // Bss: [-33554431..33554432] 49 | // 50 | // There is room for 64 operations and 256 registers (OP=6 bits, A=8 bits) 51 | // 52 | #define S_INSTR_DEFINE(_) \ 53 | /* Data */ \ 54 | _(LOADK, ABu) /* R(A) = K(Bu) */\ 55 | _(MOVE, AB_) /* R(A) = R(B) */\ 56 | /* Control flow */ \ 57 | _(YIELD, ABC) /* suspend and reschedule */\ 58 | _(JUMP, Bss) /* PC += Bss */\ 59 | _(CALL, ABC) /* R(A), ... ,R(A+C-1) := R(A)(R(A+1), ... ,R(A+B)) */\ 60 | _(RETURN, AB_) /* return R(A), ... ,R(A+B-1) */\ 61 | _(SPAWN, AB_) /* R(A) = spawn(RK(B)) */\ 62 | /* Arithmetic */ \ 63 | _(ADD, ABC) /* R(A) = RK(B) + RK(C) */\ 64 | _(SUB, ABC) /* R(A) = RK(B) - RK(C) */\ 65 | _(MUL, ABC) /* R(A) = RK(B) * RK(C) */\ 66 | _(DIV, ABC) /* R(A) = RK(B) / RK(C) */\ 67 | /* Logic tests */ \ 68 | _(NOT, AB_) /* R(A) = not R(B) */\ 69 | _(EQ, ABC) /* if (A == RK(B) == RK(C)) JUMP else PC++ */\ 70 | _(LT, ABC) /* if (A == RK(B) < RK(C)) JUMP else PC++ */\ 71 | _(LE, ABC) /* if (A == RK(B) <= RK(C)) JUMP else PC++ */\ 72 | /* Debugging. TODO: Find a way to turn these off when !S_DEBUG */ \ 73 | _(DBGREG, ABC) /* Dump register values */\ 74 | _(DBGCB, ABC) /* Call C function at K(B) */\ 75 | 76 | 77 | // Macros for accessing instruction field values 78 | #define SInstrGetOP(i) ((uint8_t)((i) & S_INSTR_OP_MASK)) 79 | #define SInstrGetA(i) ((uint8_t)(((i) & S_INSTR_A_MASK) >> S_INSTR_A_OFFS)) 80 | #define SInstrGetB(i) ((uint16_t)(((i) & S_INSTR_B_MASK) >> S_INSTR_B_OFFS)) 81 | #define SInstrGetC(i) ((uint16_t)(((i) & S_INSTR_C_MASK) >> S_INSTR_C_OFFS)) 82 | #define SInstrGetBu(i) ((uint32_t)(((i) & S_INSTR_Bu_MASK) >> S_INSTR_Bu_OFFS)) 83 | #define SInstrGetBuu(i) ((uint32_t)((i) >> S_INSTR_OP_SIZE)) 84 | #define SInstrGetBs(i) ((int32_t)(SInstrGetBu(i) - (S_INSTR_Bu_MAX/2))) 85 | #define SInstrGetBss(i) ((int32_t)(SInstrGetBuu(i) - (S_INSTR_Buu_MAX/2))) 86 | 87 | // Each instruction will have a corresponding operation code identified by an 88 | // enum value "S_OP_" 89 | typedef enum { 90 | #define I_ENUM(name, args) S_OP_##name, 91 | S_INSTR_DEFINE(I_ENUM) 92 | #undef I_ENUM 93 | } S_OP_V_; 94 | 95 | // Macros to compose instructions 96 | #define S_INSTR_A__(OP, A) \ 97 | (((SInstr)(OP)) | \ 98 | (((SInstr)(A) << S_INSTR_OP_SIZE) & S_INSTR_A_MASK) \ 99 | ) 100 | 101 | #define S_INSTR_AB_(OP, A, B) \ 102 | (((SInstr)(OP)) | \ 103 | (((SInstr)(A) << S_INSTR_OP_SIZE) & S_INSTR_A_MASK) | \ 104 | (((SInstr)(B) << (S_INSTR_OP_SIZE + S_INSTR_A_SIZE)) & S_INSTR_B_MASK) \ 105 | ) 106 | 107 | #define S_INSTR_ABC(OP, A, B, C) \ 108 | (((SInstr)(OP)) | \ 109 | (((SInstr)(A) << S_INSTR_OP_SIZE) & S_INSTR_A_MASK) | \ 110 | (((SInstr)(B) << (S_INSTR_OP_SIZE + S_INSTR_A_SIZE)) & S_INSTR_B_MASK) | \ 111 | ((SInstr)(C) << (S_INSTR_OP_SIZE + S_INSTR_A_SIZE + S_INSTR_B_SIZE)) \ 112 | ) 113 | 114 | #define S_INSTR_ABu(OP, A, Bu) \ 115 | (((SInstr)(OP)) | \ 116 | (((SInstr)(A) << S_INSTR_OP_SIZE) & S_INSTR_A_MASK) | \ 117 | ((SInstr)(Bu) << (S_INSTR_OP_SIZE + S_INSTR_A_SIZE)) \ 118 | ) 119 | 120 | #define S_INSTR_ABs(OP, A, Bs) \ 121 | S_INSTR_ABu((OP), (A), ((uint32_t)(Bs) + (S_INSTR_Bu_MAX / 2)) ) 122 | 123 | #define S_INSTR_Buu(OP, Buu) \ 124 | (((SInstr)(OP)) | \ 125 | ((SInstr)(Buu) << S_INSTR_OP_SIZE) \ 126 | ) 127 | 128 | #define S_INSTR_Bss(OP, Bss) \ 129 | S_INSTR_Buu((OP), ((uint32_t)(Bss) + (S_INSTR_Buu_MAX / 2)) ) 130 | 131 | // Instruction component constants 132 | // Operation code 133 | #define S_INSTR_OP_SIZE 6 134 | #define S_INSTR_OP_MASK 0x3f // 000000000 000000000 00000000 111111 135 | #define S_INSTR_OP_MAX 0x3f 136 | // Field A 137 | #define S_INSTR_A_SIZE 8 138 | #define S_INSTR_A_OFFS 6 139 | #define S_INSTR_A_MASK 0x3fc0 // 000000000 000000000 11111111 000000 140 | #define S_INSTR_A_MAX 0xff 141 | // Field B 142 | #define S_INSTR_B_SIZE 9 143 | #define S_INSTR_B_OFFS 14 144 | #define S_INSTR_B_MASK 0x7fc000 // 000000000 111111111 00000000 000000 145 | #define S_INSTR_B_MAX 0x1ff 146 | // Field C 147 | #define S_INSTR_C_SIZE 9 148 | #define S_INSTR_C_OFFS 23 149 | #define S_INSTR_C_MASK 0xff800000 // 111111111 000000000 00000000 000000 150 | #define S_INSTR_C_MAX 0x1ff 151 | // Field Bu 152 | #define S_INSTR_Bu_SIZE 18 153 | #define S_INSTR_Bu_MASK 0xffffc000 // 111111111111111111 00000000 000000 154 | #define S_INSTR_Bu_OFFS 14 155 | #define S_INSTR_Bu_MAX 0x3ffff 156 | // Field Bs 157 | #define S_INSTR_Bs_SIZE S_INSTR_Bu_SIZE 158 | #define S_INSTR_Bs_MASK S_INSTR_Bu_MASK 159 | #define S_INSTR_Bs_OFFS S_INSTR_Bu_OFFS 160 | #define S_INSTR_Bs_MIN (-(S_INSTR_Bu_MAX/2)) 161 | #define S_INSTR_Bs_MAX ((-S_INSTR_Bs_MIN)+1) 162 | // Field Buu 163 | #define S_INSTR_Buu_SIZE 26 164 | #define S_INSTR_Buu_MASK 0xffffffc0 // 11111111111111111111111111 000000 165 | #define S_INSTR_Buu_OFFS 6 166 | #define S_INSTR_Buu_MAX 0x3ffffff 167 | // Field Bss 168 | #define S_INSTR_Bss_SIZE S_INSTR_Buu_SIZE 169 | #define S_INSTR_Bss_MASK S_INSTR_Buu_MASK 170 | #define S_INSTR_Bss_OFFS S_INSTR_Buu_OFFS 171 | #define S_INSTR_Bss_MIN (-(S_INSTR_Buu_MAX/2)) 172 | #define S_INSTR_Bss_MAX ((-S_INSTR_Bss_MIN)+1) 173 | 174 | // Number of addressable registers 175 | #define S_REG_MAX S_INSTR_A_MAX 176 | 177 | 178 | // This can be set to a lower number to allow more constants per closure, but 179 | // at the expense of more restrictive register access for instructions that read 180 | // RK(x) meaning either R(x) of x is less than S_INSTR_RK_k or K(x-S_INSTR_RK_k) 181 | #ifndef S_INSTR_RK_k 182 | // Maximum value is S_REG_MAX 183 | #define S_INSTR_RK_k S_REG_MAX 184 | #endif 185 | 186 | // Memory layout 187 | // ABC: 188 | // 189 | // 31 23 22 14 13 6 5 0 190 | // 000000000 000000000 00000000 000000 C=0, B=0, A=0, OP=0 191 | // MSB LSB 192 | // 193 | // ABu: 194 | // 195 | // 31 14 13 6 5 0 196 | // 000000000000000000 00000000 000000 Bu=0, A=0, OP=0 197 | // MSB LSB 198 | // 199 | // Buu: 200 | // 201 | // 31 6 5 0 202 | // 00000000000000000000000000 000000 Buu=0, OP=0 203 | // MSB LSB 204 | // 205 | 206 | // Define convenience functions for each instruction. These functions are 207 | // constant expressions, so they can be used as constant initializers. However, 208 | // they are not compile-time constants. 209 | #define FHEAD inline static SInstr S_UNUSED S_ALWAYS_INLINE S_PURE S_WUNUSEDR 210 | #define APPLY_ABC(name) \ 211 | FHEAD SInstr_##name(uint8_t A, uint16_t B, uint16_t C) { \ 212 | return S_INSTR_ABC(S_OP_##name, A, B, C); \ 213 | } 214 | #define APPLY_AB_(name) \ 215 | FHEAD SInstr_##name(uint8_t A, uint16_t B) { \ 216 | return S_INSTR_AB_(S_OP_##name, A, B); \ 217 | } 218 | #define APPLY_A__(name) \ 219 | FHEAD SInstr_##name(uint8_t A) { \ 220 | return S_INSTR_A__(S_OP_##name, A); \ 221 | } 222 | #define APPLY_ABu(name) \ 223 | FHEAD SInstr_##name(uint8_t A, uint32_t Bu) { \ 224 | return S_INSTR_ABu(S_OP_##name, A, Bu); \ 225 | } 226 | #define APPLY_ABs(name) \ 227 | FHEAD SInstr_##name(uint8_t A, int32_t Bs) { \ 228 | return S_INSTR_ABs(S_OP_##name, A, Bs); \ 229 | } 230 | #define APPLY_Buu(name) \ 231 | FHEAD SInstr_##name(uint32_t Buu) { \ 232 | return S_INSTR_Buu(S_OP_##name, Buu); \ 233 | } 234 | #define APPLY_Bss(name) \ 235 | FHEAD SInstr_##name(uint32_t Bss) { \ 236 | return S_INSTR_Bss(S_OP_##name, Bss); \ 237 | } 238 | #define APPLY(name, args) APPLY_##args(name) 239 | S_INSTR_DEFINE(APPLY) 240 | #undef APPLY 241 | #undef APPLY_ABC 242 | #undef APPLY_AB_ 243 | #undef APPLY_A__ 244 | #undef APPLY_ABu 245 | #undef APPLY_ABs 246 | #undef APPLY_Buu 247 | #undef APPLY_Bss 248 | #undef FHEAD 249 | 250 | 251 | #endif // S_INSTR_H_ 252 | -------------------------------------------------------------------------------- /sol/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | #include // isatty 3 | #include // va_* 4 | 5 | FILE* SLogStream = 0; 6 | 7 | void SLog__( 8 | const char *filename, 9 | int line, 10 | const char *prefix, 11 | const char *prefix_style, 12 | bool style_whole_line, 13 | const char *format, 14 | ...) { 15 | 16 | if (SLogStream == 0) { 17 | SLogStream = stderr; 18 | } 19 | 20 | static int color_output = -1; 21 | if (color_output == -1) { 22 | color_output = !!isatty(1); 23 | } 24 | 25 | flockfile(SLogStream); 26 | 27 | if (style_whole_line && color_output) { 28 | if (prefix) { 29 | fprintf(SLogStream, "\e[%sm%s ", prefix_style, prefix ? prefix : ""); 30 | } else { 31 | fprintf(SLogStream, "\e[%sm", prefix_style); 32 | } 33 | } else if (prefix) { 34 | if (color_output) { 35 | fprintf(SLogStream, "\e[%sm%s\e[0m ", prefix_style, prefix); 36 | } else { 37 | fprintf(SLogStream, "%s ", prefix); 38 | } 39 | } 40 | 41 | va_list ap; 42 | va_start(ap, format); 43 | vfprintf(SLogStream, format, ap); 44 | va_end(ap); 45 | 46 | fprintf( 47 | SLogStream, 48 | color_output ? " \e[30;1m(%s:%d)\e[0m\n" : " (%s:%d)", 49 | filename, 50 | line 51 | ); 52 | 53 | funlockfile(SLogStream); 54 | fflush(SLogStream); 55 | } -------------------------------------------------------------------------------- /sol/log.h: -------------------------------------------------------------------------------- 1 | #ifndef S_LOG_H_ 2 | #define S_LOG_H_ 3 | #include 4 | 5 | FILE* SLogStream; 6 | 7 | // 8 | // void SLog(const char* format, ...) -- log a message 9 | // void SLogT() -- log current function* 10 | // void SLogD(const char* format, ...) -- log a debugging message* 11 | // void SLogW(const char* format, ...) -- log a warning message 12 | // void SLogE(const char* format, ...) -- log an error message 13 | // 14 | // * = excluded in release builds 15 | // 16 | #if S_DEBUG 17 | #define STrace(fmt, ...) SLog_("-", "0;36", 1, "%s", __PRETTY_FUNCTION__) 18 | #define SLogD(fmt, ...) SLog_("D", "0;34", 0, fmt, ##__VA_ARGS__) 19 | #else 20 | #define STrace() ((void)0) 21 | #define SLogD(...) ((void)0) 22 | #endif 23 | #define SLogW(fmt, ...) SLog_("W", "0;33", 0, fmt, ##__VA_ARGS__) 24 | #define SLogE(fmt, ...) SLog_("E", "0;31", 0, fmt, ##__VA_ARGS__) 25 | 26 | #define SLog(fmt, ...) SLog_(0, 0, 0, fmt, ##__VA_ARGS__) 27 | 28 | // ----- 29 | 30 | #define SLog_(LN, style, fmt, ...) \ 31 | SLog__(__FILE__, __LINE__, LN, style, fmt, ##__VA_ARGS__) 32 | 33 | void SLog__( 34 | const char *filename, 35 | int line, 36 | const char *prefix, 37 | const char *prefix_style, 38 | bool style_whole_line, 39 | const char *format, 40 | ...); 41 | 42 | #endif // S_LOG_H_ 43 | -------------------------------------------------------------------------------- /sol/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | //static const SInstr load_instr1 = SInstr_LOADK(0, 1); 9 | static const SInstr load_instr = S_INSTR_ABu(S_OP_LOADK, 0, 1); 10 | 11 | int main(int argc, const char** argv) { 12 | printf("Sol " S_VERSION_STRING " " S_TARGET_ARCH_NAME "\n"); 13 | SLogD("SHostAvailCPUCount: %u", SHostAvailCPUCount()); 14 | 15 | SVM vm = SVM_INIT; 16 | 17 | // A simple program which assigns a value to a variable 'x' and decrements the 18 | // value of that variable until it is no longer larger than '0'. Each time it 19 | // decrements a value it yields for other tasks. 20 | // --------------------- 21 | // 0 x = 5 22 | // 1 while (x > 0) 23 | // 2 x = x - 1 24 | // 3 yield 25 | // 4 return 26 | // 27 | // Translates to --> 28 | // --------------------- 29 | // 0 x = 5 30 | // 1 if (x <= 0) then (goto 5) else 31 | // 2 x = x - 1 32 | // 3 yield 33 | // 4 goto 1 34 | // 5 return 35 | // SValue constants[] = { 36 | // SValueNumber(5), 37 | // SValueNumber(0), 38 | // SValueNumber(1), 39 | // }; 40 | // SInstr instructions[] = { 41 | // SInstr_LOADK(0, 0), // 0 R(0) = K(0) 42 | // SInstr_LE(0, 0, S_INSTR_RK_k+1), // 1 if (RK(0) <= RK(k+1)) else PC++ 43 | // SInstr_JUMP(3), // 2 PC += 3 to RETURN 44 | // SInstr_SUB(0, 0, S_INSTR_RK_k+2), // 3 R(0) = R(0) - RK(k+1) 45 | // SInstr_YIELD(0, 0, 0), // 4 yield 0 ; A=yield cpu 46 | // SInstr_JUMP(-5), // 5 PC -= 5 to LE 47 | // SInstr_RETURN(0, 0), // 6 return 48 | // }; 49 | // SFunc* fun1 = SFuncCreate(constants, instructions); 50 | 51 | SValue constants[] = { 52 | SValueNumber(5), 53 | SValueNumber(0), 54 | SValueNumber(1200), 55 | }; 56 | SInstr instructions[] = { 57 | SInstr_LOADK(0, 0), // R(0) = K(0) 58 | SInstr_LE(0, 0, S_INSTR_RK_k+1), // if (0 == RK(0) < RK(k+1)) else PC++ 59 | SInstr_JUMP(1), // PC += 1 to RETURN 60 | SInstr_YIELD(1, S_INSTR_RK_k+2, 0), // yield timeout (K(2) = after_ms) 61 | SInstr_RETURN(0, 0), // return 62 | }; 63 | SFunc* fun1 = SFuncCreate(constants, instructions); 64 | 65 | SValue constants2[] = { 66 | SValueNumber(5), 67 | SValueNumber(0), 68 | SValueNumber(500), 69 | SValueFunc(fun1), 70 | }; 71 | SInstr instructions2[] = { 72 | SInstr_LOADK(0, 0), // R(0) = K(0) 73 | SInstr_LE(0, 0, S_INSTR_RK_k+1), // if (0 == RK(0) < RK(k+1)) else PC++ 74 | SInstr_JUMP(2), // PC += 2 to RETURN 75 | SInstr_SPAWN(0, S_INSTR_RK_k+3), // R(0) = spawn(RK(B)) 76 | SInstr_YIELD(1, S_INSTR_RK_k+2, 0), // yield timeout (K(2) = after_ms) 77 | SInstr_RETURN(0, 0), // return 78 | }; 79 | SFunc* fun2 = SFuncCreate(constants2, instructions2); 80 | 81 | // 82 | // // Timeout timer ("sleep") test program. 83 | // // 0 delay = 1200 84 | // // 1 sleep(delay) 85 | // // 2 return 86 | // SValue constants[] = { 87 | // SValueNumber(1200), 88 | // }; 89 | // SInstr instructions[] = { 90 | // SInstr_LOADK(1, 0), // 0 R(1) = K(0) 91 | // SInstr_YIELD(1, 1, 0), // 1 yield timeout (RK(1) = after_ms) 92 | // SInstr_RETURN(0, 0), // 4 return 93 | // }; 94 | // 95 | // // Make a function out of the program 96 | // SFunc* sleepfun = SFuncCreate(constants, instructions); 97 | 98 | // // Function calling 99 | // SValue a_constants[] = { 100 | // SValueNumber(123), // return value 101 | // }; 102 | // SInstr a_instructions[] = { 103 | // // Arguments: (R(0)=sleep_ms) 104 | // SInstr_DBGREG(0, 0, 0),// debug 105 | // SInstr_YIELD(1, 0, 0), // yield timeout (RK(0) = after_ms) 106 | // SInstr_LOADK(0, 0), // R(0) = K(0) = 123 107 | // SInstr_RETURN(0, 1), // <- R(0)..R(0) = R(0) = 123 108 | // }; 109 | // SFunc* a_fun = SFuncCreate(a_constants, a_instructions); 110 | // SValue b_constants[] = { 111 | // SValueFunc(a_fun), 112 | // SValueNumber(500), // argument to a_fun 113 | // }; 114 | // SInstr b_instructions[] = { 115 | // SInstr_LOADK(0, 0), // R(0) = K(0) = a_fun 116 | // SInstr_LOADK(1, 1), // R(1) = K(1) = 500 117 | // //SInstr_DBGREG(0, 1, 1),// debug 118 | // SInstr_CALL(0, 1, 1), // R(0)..R(0) = R(0)(R(1)..R(1)) = a_fun(R(1)) 119 | // SInstr_DBGREG(0, 1, 0),// debug: So we can inspect a_fun's return value 120 | // SInstr_RETURN(0, 0), // return 121 | // }; 122 | // SFunc* b_fun = SFuncCreate(b_constants, b_instructions); 123 | 124 | // Create a scheduler 125 | SSched* sched = SSchedCreate(); 126 | 127 | // Schedule several tasks running the same program 128 | //SSchedTask(sched, STaskCreate(b_fun, 0, 0)); 129 | //SSchedTask(sched, STaskCreate(fun1, 0, 0)); 130 | SSchedTask(sched, STaskCreate(fun2, 0, 0)); 131 | 132 | SSchedRun(&vm, sched); 133 | 134 | // SFuncDestroy(b_fun); 135 | // SFuncDestroy(a_fun); 136 | SSchedDestroy(sched); 137 | printf("Scheduler runloop exited.\n"); 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /sol/msg.c: -------------------------------------------------------------------------------- 1 | #include "msg.h" 2 | 3 | // void SMsgQCreate(SMsgQ* q) { 4 | // q->head = &q->sentinel; 5 | // q->tail = &q->sentinel; 6 | // q->sentinel.next = 0; 7 | // } 8 | 9 | // Based on https://groups.google.com/d/msg/lock-free/Vd9xuHrLggE/B9-URa3B37MJ 10 | 11 | bool SMsgEnqueue(SMsgQ* q, SMsg* m) { 12 | m->next = 0; 13 | SMsg* prev = SAtomicSwap(&q->head, m); 14 | prev->next = m; 15 | return prev == &q->sentinel; 16 | } 17 | 18 | SMsg* SMsgDequeue(SMsgQ* q) { 19 | SMsg* tail = q->tail; 20 | SMsg* next = tail->next; 21 | 22 | if (tail == &q->sentinel) { 23 | if (next == 0) { 24 | return 0; 25 | } 26 | q->tail = next; 27 | tail = next; 28 | next = next->next; 29 | } 30 | 31 | if (next != 0) { 32 | q->tail = next; 33 | return tail; 34 | } 35 | 36 | SMsg* head = q->head; 37 | if (tail != head) { 38 | return 0; 39 | } 40 | 41 | SMsgEnqueue(q, &q->sentinel); 42 | next = tail->next; 43 | 44 | if (next) { 45 | q->tail = next; 46 | return tail; 47 | } 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /sol/msg.h: -------------------------------------------------------------------------------- 1 | // Multiple Producer, Single Consumer lock-free (well, one CAS per "enqueue") 2 | // message queue. Zero or more task running in any scheduler can send a message 3 | // to any other task's inbox, which is a `SMsgQ`. 4 | #ifndef S_MSG_H_ 5 | #define S_MSG_H_ 6 | #include 7 | #include 8 | 9 | struct STask; 10 | 11 | typedef struct SMsg { 12 | struct SMsg* volatile next; 13 | SValue value; 14 | struct STask* sender; 15 | } SMsg; // 32 16 | 17 | typedef struct SMsgQ { 18 | SMsg* volatile head; 19 | uint8_t _pad; // cache line hack 20 | SMsg* tail; 21 | SMsg sentinel; 22 | } SMsgQ; // 56 23 | 24 | // Constant initializer. E.g. `q = S_MSGQ_INIT(q);` 25 | #define S_MSGQ_INIT(q) (SMsgQ){&(q).sentinel, 0, &(q).sentinel, {0}} 26 | 27 | // Put message `m` at end of queue `q`. Returns true if the queue was empty. 28 | bool SMsgEnqueue(SMsgQ* q, SMsg* m); 29 | 30 | // Get the message at the beginning of the queue. Returns 0 if there are no 31 | // messages. 32 | SMsg* SMsgDequeue(SMsgQ* q); 33 | 34 | #endif // S_MSG_H_ 35 | -------------------------------------------------------------------------------- /sol/runq.h: -------------------------------------------------------------------------------- 1 | // Run queue -- maintains a queue of tasks which are usually executed by a 2 | // Scheduler in order from head to tail. 3 | #ifndef S_RUNQ_H_ 4 | #define S_RUNQ_H_ 5 | #include 6 | #include 7 | 8 | typedef struct { 9 | STask* head; // Highest priority task 10 | STask* tail; // Lowest priority task 11 | size_t count; // Number of queued tasks 12 | } SRunQ; 13 | 14 | // Constant initializer 15 | #define S_RUNQ_INIT (SRunQ){0, 0, 0} 16 | 17 | // Add to tail (end of queue) 18 | inline static void SRunQPushTail(SRunQ* q, STask* t) { 19 | // 1. [] --> [A -> x] 20 | // 2. [A -> x] --> [A -> B -> x] 21 | // ... 22 | STask* tail = q->tail; 23 | if (tail == 0) { 24 | q->head = t; 25 | } else { 26 | tail->next = t; 27 | } 28 | q->tail = t; 29 | ++q->count; 30 | } 31 | 32 | // Remove head (first in queue) 33 | inline static STask* SRunQPopHead(SRunQ* q) { 34 | // 1. [A -> x] --> [] 35 | // 2. [A -> B -> x] --> [B -> x] 36 | // ... 37 | STask* t = q->head; 38 | if (t == q->tail) { 39 | q->head = 0; 40 | q->tail = 0; 41 | q->count = 0; 42 | } else { 43 | q->head = t->next; 44 | --q->count; 45 | } 46 | return t; 47 | } 48 | 49 | // Dequeue each task 50 | #define SRunQPopEachHead(q, t) \ 51 | for (; ((t) = (q)->head) != 0; (q)->head = (t)->next) 52 | 53 | // Move head to tail 54 | inline static void SRunQPopHeadPushTail(SRunQ* q) { 55 | // 1. [A -> x] --> [A -> x] (noop) 56 | // 2. [A -> B -> x] --> [B -> A -> x] 57 | // ... 58 | STask* t = q->head; 59 | if (q->tail != t) { 60 | q->tail->next = t; 61 | q->head = t->next; 62 | t->next = 0; 63 | q->tail = t; 64 | } 65 | } 66 | 67 | #endif // S_RUNQ_H_ 68 | -------------------------------------------------------------------------------- /sol/sched.h: -------------------------------------------------------------------------------- 1 | // Scheduler -- maintains a run queue of tasks which are executed in order. 2 | // 3 | // When a task's execution is suspended with a "yield" status, the task is 4 | // placed at the end of the queue so that it can run again once other queued 5 | // tasks have had a chance to run. When a task is suspended with a "end" or 6 | // "error" status, the task is removed from the scheduler (unscheduled). 7 | // 8 | // To run a task, schedule it by calling `SSchedTask` and then enter the 9 | // scheduler's runloop by calling `SSchedRun`. `SSchedRun` will return when all 10 | // queued tasks have been unscheduled. 11 | // 12 | #ifndef S_SCHED_H_ 13 | #define S_SCHED_H_ 14 | #include 15 | #include 16 | #include 17 | 18 | // Task scheduler 19 | typedef struct { 20 | STask* rhead; // Run queue queue head 21 | STask* rtail; // Run queue queue tail 22 | STask* whead; // Waiting queue head 23 | STask* wtail; // Waiting queue tail 24 | void* events_; 25 | } SSched; 26 | 27 | // Create a new scheduler 28 | SSched* SSchedCreate(); 29 | 30 | // Destroy a scheduler. Effectively calls `STaskDestroy` on each task that is 31 | // still in the run queue. 32 | void SSchedDestroy(SSched* s); 33 | 34 | // Schedule a task `t` by adding it to the end of the run queue of scheduler 35 | // `s`. It's important not to schedule tasks that have already been scheduled. 36 | void SSchedTask(SSched* s, STask* t); 37 | 38 | // Run the scheduler. This function exits when the run queue is empty. 39 | void SSchedRun(SVM* vm, SSched* s); 40 | 41 | #if S_DEBUG 42 | // Execute the next set of instructions in task. This function returns as soon 43 | // as the task ends or yields in any way. Purpose is unit tests. 44 | STaskStatus SchedExec(SVM* vm, SSched* sched, STask *task); 45 | #endif 46 | 47 | #endif // S_SCHED_H_ 48 | -------------------------------------------------------------------------------- /sol/sched_exec_debug.h: -------------------------------------------------------------------------------- 1 | // Note: This file should only be included by vm.c 2 | 3 | // Instruction debug logging macros 4 | // 5 | // void SVMDLogI(const char* format, ...) 6 | // -> "[vm] %taskid %absicount " format ... "\n" 7 | // 8 | // 9 | #if S_VM_DEBUG_LOG 10 | #define SVMDLog(fmt, ...) SLog("[vm] %-14p %-14p " fmt, \ 11 | task, task->ar->func, ##__VA_ARGS__) 12 | 13 | S_UNUSED static const char const* _debug_op_names[] = { 14 | #define OP_TABLE(name, _) #name, 15 | S_INSTR_DEFINE(OP_TABLE) 16 | #undef OP_TABLE 17 | }; 18 | 19 | #define SVMDLogRRVal(reg, index) do { \ 20 | char __bufv[32]; \ 21 | SLogD("[vm] R(%u) = %s", (index), SValueRepr(__bufv, 32, &(reg)[(index)]));\ 22 | } while (0) 23 | 24 | #define SVMDLogRVal(r) SVMDLogRRVal(registry, r) 25 | 26 | #define SVMDLogKVal(k) do { \ 27 | char __bufv[32]; \ 28 | SLogD("[vm] K(%u) = %s", (k), SValueRepr(__bufv, 32, &constants[(k)])); \ 29 | } while (0) 30 | 31 | #define SVMDLogRKVal(rk) do { \ 32 | if (rk < 255) { \ 33 | SVMDLogRVal(rk); \ 34 | } else {\ 35 | SVMDLogKVal(rk - 255); \ 36 | } \ 37 | } while (0) 38 | 39 | #define SVMDLogInstrRVal(regname, i) do { \ 40 | uint16_t r = SInstrGet##regname((i)); \ 41 | SVMDLogRVal(r); \ 42 | } while (0) 43 | 44 | #define SVMDLogInstrKVal(regname, i) do { \ 45 | uint16_t k = SInstrGet##regname((i)); \ 46 | SVMDLogKVal(k); \ 47 | } while (0) 48 | 49 | #define SVMDLogInstrRKVal(regname, i) do { \ 50 | uint16_t rk = SInstrGet##regname((i)); \ 51 | SVMDLogRKVal(rk); \ 52 | } while (0) 53 | 54 | // SLogD("R(%u) = %s", SInstrGetA(*pc), SValueRepr(__buf_##__LINE__, 32, &K_B(*pc))); 55 | 56 | #else // S_VM_DEBUG_LOG 57 | #define SVMDLog(...) ((void)0) 58 | #define SVMDLogRRVal(reg, index) ((void)0) 59 | #define SVMDLogRVal(r) ((void)0) 60 | #define SVMDLogKVal(k) ((void)0) 61 | #define SVMDLogInstrRVal(regname, i) ((void)0) 62 | #define SVMDLogInstrKVal(regname, i) ((void)0) 63 | #define SVMDLogInstrRKVal(regname, i) ((void)0) 64 | #endif // S_VM_DEBUG_LOG 65 | 66 | #define SVMDLogI(fmt, ...) SVMDLog("%-10zu " fmt, \ 67 | ((size_t)((pc) - (ar->func->instructions))), ##__VA_ARGS__) 68 | 69 | #define SVMDLogOp(fmt, ...) \ 70 | SVMDLogI("%-6s " fmt, _debug_op_names[SInstrGetOP(*pc)], ##__VA_ARGS__) 71 | 72 | #define SVMDLogOpA SVMDLogOp( " A: %3u", (uint8_t)SInstrGetA(*pc)) 73 | #define SVMDLogOpAB() SVMDLogOp( " AB: %3u, %3u", \ 74 | (uint8_t)SInstrGetA(*pc), (uint16_t)SInstrGetB(*pc)) 75 | #define SVMDLogOpABC() SVMDLogOp(" ABC: %3u, %3u, %3u", \ 76 | (uint8_t)SInstrGetA(*pc), (uint16_t)SInstrGetB(*pc), \ 77 | (uint16_t)SInstrGetC(*pc)) 78 | #define SVMDLogOpABs() SVMDLogOp(" ABs: %3u, %6d", \ 79 | (uint8_t)SInstrGetA(*pc), SInstrGetBs(*pc)) 80 | #define SVMDLogOpABu() SVMDLogOp(" ABu: %3u, %6u", \ 81 | (uint8_t)SInstrGetA(*pc), SInstrGetBu(*pc)) 82 | #define SVMDLogOpBss() SVMDLogOp(" Bss: %8d", SInstrGetBss(*pc)) 83 | #define SVMDLogOpBuu() SVMDLogOp(" Buu: %8u", SInstrGetBuu(*pc)) 84 | -------------------------------------------------------------------------------- /sol/sol.h: -------------------------------------------------------------------------------- 1 | #ifndef S_SOL_H_ 2 | #define S_SOL_H_ 3 | 4 | #endif // S_SOL_H_ -------------------------------------------------------------------------------- /sol/task.c: -------------------------------------------------------------------------------- 1 | #include "task.h" 2 | #include "msg.h" 3 | #include "log.h" 4 | 5 | const STask STaskDead = {0}; 6 | 7 | STask* STaskCreate(SFunc* func, STask* supt, STaskFlag flags) { 8 | STask* t = (STask*)malloc(sizeof(STask)); // TODO: malloc 9 | 10 | // Scheduler doubly-linked list links 11 | t->next = 0; 12 | t->prev = 0; 13 | 14 | // Entry activation record 15 | t->ar = SARecCreate(func, 0); 16 | 17 | // Set parent task, initialize refcount and STORE flags 18 | t->supt = supt; 19 | t->refc = 1; // 1 is our "live" refcount which is decremented when we end 20 | t->flags = flags; 21 | 22 | // waiting for nothing 23 | t->wp = 0; 24 | t->wtype = 0; 25 | 26 | // Initialize inbox 27 | t->inbox = S_MSGQ_INIT(t->inbox); 28 | 29 | return t; 30 | } 31 | 32 | void STaskDestroy(STask* t) { 33 | SLogD("STaskDestroy %p", t); 34 | if (t->ar) { 35 | SARecDestroy(t->ar); 36 | } 37 | free((void*)t); // TODO 38 | } 39 | -------------------------------------------------------------------------------- /sol/task.h: -------------------------------------------------------------------------------- 1 | // Task -- represents a program that can be run. Tasks are supposed to be very 2 | // cheap so to allow creation of large amounts of tasks. A task can be seen as a 3 | // "thread" or "coroutine". A task has program code and a program counter (PC). 4 | // The PC is the "cursor" to the instruction-to-be executed next time the task 5 | // is scheduled by a scheduler and executed by the virtual machine. The virtual 6 | // machine will advance the task's PC as it executes a task's instructions. 7 | #ifndef S_TASK_H_ 8 | #define S_TASK_H_ 9 | #include 10 | #include 11 | #include 12 | 13 | // Status of a task, returned by SSchedExec after executing a task 14 | typedef enum { 15 | STaskStatusYield = 0, // The task yielded (is rescheduled) 16 | STaskStatusError, // The task ended from a fault 17 | STaskStatusEnd, // The task ended normally 18 | STaskStatusSuspend, // The task is suspended (e.g. waiting for I/O or timer) 19 | } STaskStatus; 20 | 21 | // Type of thing a task is waiting for (value of a task's `wtype` member) 22 | typedef uint8_t STaskWait; 23 | enum { 24 | STaskWaitTimer = 0, // Waiting for a timer 25 | STaskWaitMsg, // Waiting for a message to arrive to its inbox 26 | }; 27 | 28 | // Various flags set for a task 29 | typedef uint32_t STaskFlag; 30 | enum { 31 | // Normally when a subtask exits abnormally, the exit propagates to the 32 | // supertask, causing the supertask to exit with the same status. If the 33 | // STaskFlagTrapExit flag is set however, the supertask will instead receive 34 | // a message "subtask exited abnormally". 35 | STaskFlagTrapExit = 1, 36 | }; 37 | 38 | typedef struct S_PACKED STask { 39 | struct STask* volatile next; // Next task (used by scheduler queues) 40 | struct STask* prev; // Previous task (used by scheduler queues) 41 | 42 | SARec* ar; // Call stack top. Singly-linked LIFO list 43 | 44 | struct STask* supt; // Our supertask -- task that spawned us 45 | volatile uint32_t refc; // Number of live tasks that reference this task 46 | STaskFlag flags; // Flags 47 | 48 | void* wp; // Something the task is waiting for 49 | STaskWait wtype; // Type of thing the task is waiting for 50 | 51 | SMsgQ inbox; // Message inbox 52 | } STask; // 113 53 | 54 | STask* STaskCreate(SFunc* func, STask* supt, STaskFlag flags); 55 | void STaskDestroy(STask* t); 56 | 57 | // Special constant STask that gets assigned to the `next` member of tasks that 58 | // have live subtasks. The subtasks are considered "zombies" in this case. 59 | const STask STaskDead; 60 | 61 | // Increment reference count 62 | inline static void S_ALWAYS_INLINE STaskRetain(STask* t) { 63 | SAtomicAdd32((int32_t*)&t->refc, 1); 64 | } 65 | 66 | // Decrement reference count. Returns true if `t` was free'd 67 | inline static bool S_ALWAYS_INLINE STaskRelease(STask* t) { 68 | if (SAtomicSubAndFetch(&t->refc, 1) == 0) { 69 | STaskDestroy(t); 70 | return true; 71 | } else { 72 | return false; 73 | } 74 | } 75 | 76 | #endif // S_TASK_H_ 77 | -------------------------------------------------------------------------------- /sol/value.c: -------------------------------------------------------------------------------- 1 | #include "value.h" 2 | 3 | const SValue SValueNil = {{ .p = 0 }, SValueTNil}; 4 | const SValue SValueTrue = {{ .n = 1 }, SValueTTrue}; 5 | const SValue SValueFalse = {{ .n = 0 }, SValueTFalse}; 6 | 7 | char* SValueRepr(char* buf, size_t bufsize, SValue* v) { 8 | switch (v->type) { 9 | 10 | case SValueTNil: return memcpy(buf, "nil", bufsize); 11 | case SValueTTrue: return memcpy(buf, "true", bufsize); 12 | case SValueTFalse: return memcpy(buf, "false", bufsize); 13 | 14 | case SValueTNumber: { 15 | snprintf(buf, bufsize, SNumberFormat, v->value.n); 16 | return buf; 17 | } 18 | 19 | case SValueTFunc: { 20 | snprintf(buf, bufsize, "", v->value.p); 21 | return buf; 22 | } 23 | 24 | default: return memcpy(buf, "(?)", bufsize); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sol/value.h: -------------------------------------------------------------------------------- 1 | // Value -- represents a value (doh!) 2 | #ifndef S_VALUE_H_ 3 | #define S_VALUE_H_ 4 | #include 5 | 6 | typedef enum { 7 | // Atoms, value types that are their own values 8 | _SValueTAtomsBegin, 9 | SValueTNil, 10 | SValueTTrue, 11 | SValueTFalse, 12 | _SValueTAtomsEnd, 13 | 14 | // Non-atom value types 15 | SValueTNumber, 16 | SValueTFunc, 17 | SValueTOpaque, 18 | } SValueT; 19 | 20 | #define SNumberFormat "%f" 21 | typedef double SNumber; 22 | 23 | typedef struct { 24 | union { void* p; SNumber n; } value; 25 | uint8_t type; 26 | } SValue; 27 | 28 | // Atoms 29 | const SValue SValueNil; 30 | const SValue SValueTrue; 31 | const SValue SValueFalse; 32 | 33 | // SValue SValueNumber(SNumber v) 34 | #define SValueNumber(v) \ 35 | ((SValue){.type = SValueTNumber, .value = {.n = v}}) 36 | 37 | #define SValueFunc(v) \ 38 | ((SValue){.type = SValueTFunc, .value = {.p = v}}) 39 | 40 | #define SValueOpaque(v) \ 41 | ((SValue){.type = SValueTOpaque, .value = {.p = v}}) 42 | 43 | char* SValueRepr(char* buf, size_t bufsize, SValue* v); 44 | 45 | #endif // S_VALUE_H_ 46 | -------------------------------------------------------------------------------- /sol/vm.h: -------------------------------------------------------------------------------- 1 | // Virtual Machine 2 | #ifndef S_VM_H_ 3 | #define S_VM_H_ 4 | #include 5 | //#include 6 | 7 | // typedef struct { 8 | // SValue values[100]; 9 | // size_t size; 10 | // size_t count; 11 | // } SConstants; 12 | 13 | typedef struct { 14 | // TODO: own schedulers 15 | } SVM; 16 | 17 | #define SVM_INIT ((SVM){ \ 18 | /*.constants = { .values = {}, .size = 100, .count = 0 }*/ \ 19 | }) 20 | 21 | #endif // S_VM_H_ 22 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # Compiles all files matching "test_*.{c,cc}" in this directory and runs the 2 | # resulting programs. Will call make in dependencies when needed. 3 | # 4 | override DEBUG = 1 # Always build tests in debug mode 5 | include ../Make.common 6 | 7 | bin_prefix := $(TESTS_BUILD_PREFIX) 8 | object_dir = $(TESTS_BUILD_PREFIX)/.objs 9 | 10 | # Compiler and linker flags 11 | c_flags := $(CFLAGS) -I.. -DS_TEST_SUIT_RUNNING=1 12 | cxx_flags := $(CXXFLAGS) 13 | ld_flags := $(LDFLAGS) -L"$(LIB_BUILD_PREFIX)" 14 | xxld_flags := $(XXLDFLAGS) 15 | 16 | # TODO: Make it possible to disable SMP tests, so that we can run there tests on 17 | # UP systems. 18 | c_test_sources := $(wildcard test_*.c) 19 | c_test_programs = $(patsubst %.c,$(bin_prefix)/%.c-up,$(c_test_sources)) 20 | c_test_programs += $(patsubst %.c,$(bin_prefix)/%.c-smp,$(c_test_sources)) 21 | c_test_programs := $(sort $(c_test_programs)) 22 | 23 | # --- targets --- 24 | 25 | all: test 26 | 27 | test: common_pre 28 | test: $(c_test_programs) 29 | #test: 30 | # @echo c_test_sources = $(c_test_sources) 31 | # @echo c_test_objects = $(c_test_objects) 32 | # @echo c_test_programs = $(c_test_programs) 33 | 34 | -include ${c_test_sources:.c=.d} 35 | 36 | clean: 37 | rm -rf $(object_dir) 38 | rm -rf $(bin_prefix) 39 | 40 | common_pre: 41 | @$(MAKE) -C $(SRCROOT)/sol DEBUG=1 sol 42 | @mkdir -p $(bin_prefix) 43 | @mkdir -p $(object_dir) 44 | 45 | # Link and run tests (UP) 46 | $(bin_prefix)/%.c-up: $(object_dir)/%.up.o 47 | $(LD) $(ld_flags) -lev -lsol -o $@ $^ 48 | @printf "Running test: %s (UP) ... " $(patsubst %.c-up,%.c,$(@F)) 49 | @$@ >/dev/null 50 | @echo PASS 51 | @rm -f $@ $(object_dir)/%.up.o 52 | 53 | # Link and run tests (SMP) 54 | $(bin_prefix)/%.c-smp: $(object_dir)/%.smp.o 55 | $(LD) $(ld_flags) -lev -lsol -lpthread -o $@ $^ 56 | @printf "Running test: %s (SMP) ... " $(patsubst %.c-smp,%.c,$(@F)) 57 | @$@ >/dev/null 58 | @echo PASS 59 | @rm -f $@ $(object_dir)/%.smp.o 60 | 61 | # C source -> object 62 | $(object_dir)/%.up.o: %.c 63 | $(CC) $(c_flags) -MMD -DS_WITHOUT_SMP=1 -c -o $@ $< 64 | $(object_dir)/%.smp.o: %.c 65 | $(CC) $(c_flags) -MMD -c -o $@ $< 66 | 67 | .PHONY: all clean common_pre test $(bin_prefix)/%.c.up \ 68 | $(bin_prefix)/%.c.mp test_%.c 69 | -------------------------------------------------------------------------------- /test/bench.h: -------------------------------------------------------------------------------- 1 | // Benchmark helpers 2 | #ifndef S_TEST_BENCH_H_ 3 | #define S_TEST_BENCH_H_ 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // struct rusage { 10 | // struct timeval ru_utime; /* user time used */ 11 | // struct timeval ru_stime; /* system time used */ 12 | // long ru_maxrss; /* max resident set size */ 13 | // long ru_ixrss; /* integral shared text memory size */ 14 | // long ru_idrss; /* integral unshared data size */ 15 | // long ru_isrss; /* integral unshared stack size */ 16 | // long ru_minflt; /* page reclaims */ 17 | // long ru_majflt; /* page faults */ 18 | // long ru_nswap; /* swaps */ 19 | // long ru_inblock; /* block input operations */ 20 | // long ru_oublock; /* block output operations */ 21 | // long ru_msgsnd; /* messages sent */ 22 | // long ru_msgrcv; /* messages received */ 23 | // long ru_nsignals; /* signals received */ 24 | // long ru_nvcsw; /* voluntary context switches */ 25 | // long ru_nivcsw; /* involuntary context switches */ 26 | // }; 27 | 28 | typedef struct { 29 | struct rusage r; 30 | struct timeval rtime; 31 | } SResUsage; 32 | 33 | inline static bool S_UNUSED SResUsageSample(SResUsage* usage) { 34 | gettimeofday(&usage->rtime, 0); 35 | return getrusage(RUSAGE_SELF, &usage->r) == 0; 36 | } 37 | 38 | inline static uint64_t S_UNUSED STimevalUSecs(const struct timeval* tv) { 39 | return ((uint64_t)tv->tv_sec * 1000000ULL) + tv->tv_usec; 40 | } 41 | 42 | inline static void S_UNUSED 43 | STimevalDelta(const struct timeval* a, 44 | const struct timeval *b, 45 | struct timeval* d) { 46 | uint64_t d_usec = STimevalUSecs(b) - STimevalUSecs(a); 47 | d->tv_sec = d_usec / 1000000ULL; 48 | d->tv_usec = d_usec - (d->tv_sec * 1000000ULL); 49 | } 50 | 51 | inline static void S_UNUSED 52 | SResUsageDelta(const SResUsage* a, const SResUsage* b, SResUsage* d) { 53 | STimevalDelta(&a->rtime, &b->rtime, &d->rtime); 54 | STimevalDelta(&a->r.ru_utime, &b->r.ru_utime, &d->r.ru_utime); 55 | STimevalDelta(&a->r.ru_stime, &b->r.ru_stime, &d->r.ru_stime); 56 | d->r.ru_maxrss = b->r.ru_maxrss - a->r.ru_maxrss; 57 | d->r.ru_ixrss = b->r.ru_ixrss - a->r.ru_ixrss; 58 | d->r.ru_idrss = b->r.ru_idrss - a->r.ru_idrss; 59 | d->r.ru_isrss = b->r.ru_isrss - a->r.ru_isrss; 60 | d->r.ru_minflt = b->r.ru_minflt - a->r.ru_minflt; 61 | d->r.ru_majflt = b->r.ru_majflt - a->r.ru_majflt; 62 | d->r.ru_nswap = b->r.ru_nswap - a->r.ru_nswap; 63 | d->r.ru_inblock = b->r.ru_inblock - a->r.ru_inblock; 64 | d->r.ru_oublock = b->r.ru_oublock - a->r.ru_oublock; 65 | d->r.ru_msgsnd = b->r.ru_msgsnd - a->r.ru_msgsnd; 66 | d->r.ru_msgrcv = b->r.ru_msgrcv - a->r.ru_msgrcv; 67 | d->r.ru_nsignals = b->r.ru_nsignals - a->r.ru_nsignals; 68 | d->r.ru_nvcsw = b->r.ru_nvcsw - a->r.ru_nvcsw; 69 | d->r.ru_nivcsw = b->r.ru_nivcsw - a->r.ru_nivcsw; 70 | } 71 | 72 | inline static void S_UNUSED 73 | SResUsagePrint(const char *leader, const SResUsage *ru, bool is_delta) { 74 | // From http://man7.org/tlpi/code/online/dist/procres/print_rusage.c.html 75 | #if !S_TEST_SUIT_RUNNING 76 | const char *ldr; 77 | 78 | ldr = (leader == NULL) ? "" : leader; 79 | 80 | printf("%sReal time (secs): %lu.%lu\n", ldr, 81 | ((unsigned long)ru->rtime.tv_sec), 82 | ((unsigned long)ru->rtime.tv_usec / 1000UL) ); 83 | printf("%sCPU time (secs): user: %lu.%lu, system: %lu.%lu\n", ldr, 84 | ((unsigned long)ru->r.ru_utime.tv_sec), 85 | ((unsigned long)ru->r.ru_utime.tv_usec / 1000UL), 86 | ((unsigned long)ru->r.ru_stime.tv_sec), 87 | ((unsigned long)ru->r.ru_stime.tv_usec / 1000UL) ); 88 | if (is_delta) { 89 | printf("%sDelta resident set size: %ld\n", ldr, ru->r.ru_maxrss); 90 | } else { 91 | printf("%sMax resident set size: %ld\n", ldr, ru->r.ru_maxrss); 92 | } 93 | printf("%sIntegral shared memory: %ld\n", ldr, ru->r.ru_ixrss); 94 | printf("%sIntegral unshared data: %ld\n", ldr, ru->r.ru_idrss); 95 | printf("%sIntegral unshared stack: %ld\n", ldr, ru->r.ru_isrss); 96 | printf("%sPage reclaims: %ld\n", ldr, ru->r.ru_minflt); 97 | printf("%sPage faults: %ld\n", ldr, ru->r.ru_majflt); 98 | printf("%sSwaps: %ld\n", ldr, ru->r.ru_nswap); 99 | printf("%sBlock I/Os: input=%ld; output=%ld\n", 100 | ldr, ru->r.ru_inblock, ru->r.ru_oublock); 101 | printf("%sSignals received: %ld\n", ldr, ru->r.ru_nsignals); 102 | printf("%sIPC messages: sent=%ld; received=%ld\n", 103 | ldr, ru->r.ru_msgsnd, ru->r.ru_msgrcv); 104 | printf("%sContext switches: voluntary=%ld; " 105 | "involuntary=%ld\n", ldr, ru->r.ru_nvcsw, ru->r.ru_nivcsw); 106 | #endif 107 | } 108 | 109 | inline static void S_UNUSED 110 | SResUsagePrintSummary(const SResUsage *start, 111 | const SResUsage *end, 112 | const char* opname, 113 | size_t opcount, 114 | size_t thread_count) { 115 | #if !S_TEST_SUIT_RUNNING 116 | const char* _opname = (opname == 0) ? "operation" : opname; 117 | SResUsage ru_delta; 118 | SResUsageDelta(start, end, &ru_delta); 119 | SResUsagePrint("", &ru_delta, true); 120 | double u_nsperop = 121 | ((double)STimevalUSecs(&ru_delta.r.ru_utime) / opcount) * 1000.0; 122 | double r_nsperop = 123 | ((double)STimevalUSecs(&ru_delta.rtime) / opcount) * 1000.0; 124 | print("Performance: %.0f ns per %s (CPU user time)", u_nsperop, _opname); 125 | print(" %.0f ns per %s (real time)", r_nsperop, _opname); 126 | if (thread_count > 1) { 127 | print("Overhead: %.0f ns in total", 128 | (r_nsperop * thread_count) - u_nsperop ); 129 | } 130 | print("%s total: %zu", _opname, opcount); 131 | #endif 132 | } 133 | 134 | 135 | #endif // S_TEST_BENCH_H_ 136 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | // Helpers for tests 2 | #ifndef S_TEST_TEST_H_ 3 | #define S_TEST_TEST_H_ 4 | 5 | #include 6 | 7 | #if !S_DEBUG 8 | #warning "Running test in release mode (no assertions). Makes no sense." 9 | #endif 10 | 11 | #if S_TEST_SUIT_RUNNING 12 | #define print(...) ((void)0) 13 | #else 14 | #define print(fmt, ...) printf(fmt "\n", ##__VA_ARGS__) 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | static void __attribute__((constructor)) init_test() { 25 | SLogStream = stdout; 26 | } 27 | 28 | #endif // S_TEST_TEST_H_ 29 | -------------------------------------------------------------------------------- /test/test_instr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, const char** argv) { 6 | SInstr ins; 7 | 8 | // Test ABC instruction values 9 | ins = S_INSTR_ABC(S_OP_JUMP, S_INSTR_A_MAX, S_INSTR_B_MAX, S_INSTR_C_MAX); 10 | assert(SInstrGetOP(ins) == S_OP_JUMP); 11 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 12 | assert(SInstrGetB(ins) == S_INSTR_B_MAX); 13 | assert(SInstrGetC(ins) == S_INSTR_C_MAX); 14 | 15 | // Test value overflow 16 | ins = S_INSTR_ABC(S_OP_JUMP, S_INSTR_A_MAX+2, S_INSTR_B_MAX, S_INSTR_C_MAX); 17 | assert(SInstrGetOP(ins) == S_OP_JUMP); 18 | assert(SInstrGetA(ins) != S_INSTR_A_MAX); 19 | assert(SInstrGetA(ins) != S_INSTR_A_MAX+2); 20 | assert(SInstrGetB(ins) == S_INSTR_B_MAX); 21 | assert(SInstrGetC(ins) == S_INSTR_C_MAX); 22 | 23 | ins = S_INSTR_ABC(S_OP_JUMP, S_INSTR_A_MAX, S_INSTR_B_MAX+2, S_INSTR_C_MAX); 24 | assert(SInstrGetOP(ins) == S_OP_JUMP); 25 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 26 | assert(SInstrGetB(ins) != S_INSTR_B_MAX); 27 | assert(SInstrGetB(ins) != S_INSTR_B_MAX+2); 28 | assert(SInstrGetC(ins) == S_INSTR_C_MAX); 29 | 30 | ins = S_INSTR_ABC(S_OP_JUMP, S_INSTR_A_MAX, S_INSTR_B_MAX, S_INSTR_C_MAX+2); 31 | assert(SInstrGetOP(ins) == S_OP_JUMP); 32 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 33 | assert(SInstrGetB(ins) == S_INSTR_B_MAX); 34 | assert(SInstrGetC(ins) != S_INSTR_C_MAX); 35 | assert(SInstrGetC(ins) != S_INSTR_C_MAX+2); 36 | 37 | 38 | // Test AB instruction values 39 | ins = S_INSTR_AB_(S_OP_JUMP, S_INSTR_A_MAX, S_INSTR_B_MAX); 40 | assert(SInstrGetOP(ins) == S_OP_JUMP); 41 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 42 | assert(SInstrGetB(ins) == S_INSTR_B_MAX); 43 | assert(SInstrGetC(ins) == 0); 44 | 45 | ins = S_INSTR_AB_(S_OP_JUMP, S_INSTR_A_MAX+2, S_INSTR_B_MAX); 46 | assert(SInstrGetOP(ins) == S_OP_JUMP); 47 | assert(SInstrGetA(ins) != S_INSTR_A_MAX); 48 | assert(SInstrGetA(ins) != S_INSTR_A_MAX+2); 49 | assert(SInstrGetB(ins) == S_INSTR_B_MAX); 50 | assert(SInstrGetC(ins) == 0); 51 | 52 | ins = S_INSTR_AB_(S_OP_JUMP, S_INSTR_A_MAX, S_INSTR_B_MAX+2); 53 | assert(SInstrGetOP(ins) == S_OP_JUMP); 54 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 55 | assert(SInstrGetB(ins) != S_INSTR_B_MAX); 56 | assert(SInstrGetB(ins) != S_INSTR_B_MAX+2); 57 | assert(SInstrGetC(ins) == 0); 58 | 59 | 60 | // Test A instruction value 61 | ins = S_INSTR_A__(S_OP_JUMP, S_INSTR_A_MAX); 62 | assert(SInstrGetOP(ins) == S_OP_JUMP); 63 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 64 | assert(SInstrGetB(ins) == 0); 65 | assert(SInstrGetC(ins) == 0); 66 | 67 | ins = S_INSTR_A__(S_OP_JUMP, S_INSTR_A_MAX+2); 68 | assert(SInstrGetOP(ins) == S_OP_JUMP); 69 | assert(SInstrGetA(ins) != S_INSTR_A_MAX); 70 | assert(SInstrGetA(ins) != S_INSTR_A_MAX+2); 71 | assert(SInstrGetB(ins) == 0); 72 | assert(SInstrGetC(ins) == 0); 73 | 74 | 75 | // Test ABu instruction value 76 | ins = S_INSTR_ABu(S_OP_JUMP, S_INSTR_A_MAX, S_INSTR_Bu_MAX); 77 | assert(SInstrGetOP(ins) == S_OP_JUMP); 78 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 79 | assert(SInstrGetBu(ins) == S_INSTR_Bu_MAX); 80 | 81 | ins = S_INSTR_ABu(S_OP_JUMP, S_INSTR_A_MAX+2, S_INSTR_Bu_MAX); 82 | assert(SInstrGetOP(ins) == S_OP_JUMP); 83 | assert(SInstrGetA(ins) != S_INSTR_A_MAX); 84 | assert(SInstrGetA(ins) != S_INSTR_A_MAX+2); 85 | assert(SInstrGetBu(ins) == S_INSTR_Bu_MAX); 86 | 87 | ins = S_INSTR_ABu(S_OP_JUMP, S_INSTR_A_MAX, S_INSTR_Bu_MAX+2); 88 | assert(SInstrGetOP(ins) == S_OP_JUMP); 89 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 90 | assert(SInstrGetBu(ins) != S_INSTR_Bu_MAX); 91 | assert(SInstrGetBu(ins) != S_INSTR_Bu_MAX+2); 92 | 93 | 94 | // Test ABs instruction value 95 | ins = S_INSTR_ABs(S_OP_JUMP, S_INSTR_A_MAX, S_INSTR_Bs_MIN); 96 | assert(SInstrGetOP(ins) == S_OP_JUMP); 97 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 98 | assert(SInstrGetBs(ins) == S_INSTR_Bs_MIN); 99 | 100 | ins = S_INSTR_ABs(S_OP_JUMP, S_INSTR_A_MAX, S_INSTR_Bs_MAX); 101 | assert(SInstrGetOP(ins) == S_OP_JUMP); 102 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 103 | assert(SInstrGetBs(ins) == S_INSTR_Bs_MAX); 104 | 105 | ins = S_INSTR_ABs(S_OP_JUMP, S_INSTR_A_MAX+2, S_INSTR_Bs_MIN); 106 | assert(SInstrGetOP(ins) == S_OP_JUMP); 107 | assert(SInstrGetA(ins) != S_INSTR_A_MAX); 108 | assert(SInstrGetA(ins) != S_INSTR_A_MAX+2); 109 | assert(SInstrGetBs(ins) == S_INSTR_Bs_MIN); 110 | 111 | ins = S_INSTR_ABs(S_OP_JUMP, S_INSTR_A_MAX, S_INSTR_Bs_MIN-2); 112 | assert(SInstrGetOP(ins) == S_OP_JUMP); 113 | assert(SInstrGetA(ins) == S_INSTR_A_MAX); 114 | assert(SInstrGetBs(ins) != S_INSTR_Bs_MIN); 115 | assert(SInstrGetBs(ins) != S_INSTR_Bs_MIN-2); 116 | 117 | 118 | // Test Buu instruction value 119 | ins = S_INSTR_Buu(S_OP_JUMP, S_INSTR_Buu_MAX); 120 | assert(SInstrGetOP(ins) == S_OP_JUMP); 121 | assert(SInstrGetBuu(ins) == S_INSTR_Buu_MAX); 122 | 123 | ins = S_INSTR_Buu(S_OP_JUMP, S_INSTR_Buu_MAX+2); 124 | assert(SInstrGetOP(ins) == S_OP_JUMP); 125 | assert(SInstrGetBuu(ins) != S_INSTR_Buu_MAX); 126 | assert(SInstrGetBuu(ins) != S_INSTR_Buu_MAX+2); 127 | 128 | 129 | // Test Bss instruction value 130 | ins = S_INSTR_Bss(S_OP_JUMP, S_INSTR_Bss_MIN); 131 | assert(SInstrGetOP(ins) == S_OP_JUMP); 132 | assert(SInstrGetBss(ins) == S_INSTR_Bss_MIN); 133 | 134 | ins = S_INSTR_Bss(S_OP_JUMP, S_INSTR_Bss_MIN-2); 135 | assert(SInstrGetOP(ins) == S_OP_JUMP); 136 | assert(SInstrGetBss(ins) != S_INSTR_Bss_MIN); 137 | assert(SInstrGetBss(ins) != S_INSTR_Bss_MIN-2); 138 | 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /test/test_msg.c: -------------------------------------------------------------------------------- 1 | // Tests the message queue, which is a lock-free MP,SC FIFO queue. 2 | #include "test.h" 3 | #include "bench.h" 4 | #include 5 | #include // for SHostAvailCPUCount 6 | #include 7 | 8 | // TODO: Disable this test if the system does not have pthreads 9 | #include 10 | 11 | #if S_TEST_SUIT_RUNNING 12 | #define VALUE_COUNT 10000 13 | #else 14 | #define VALUE_COUNT 10000 15 | //#define VALUE_COUNT 10000000 16 | #endif 17 | 18 | size_t thread_count = 3; 19 | uint32_t total_count = 0; 20 | 21 | typedef struct Thread { 22 | uint32_t tid; 23 | pthread_t thread; 24 | SMsgQ* q; 25 | } Thread; 26 | 27 | void* thread_main(void* d) { 28 | Thread* t = (Thread*)d; 29 | bool is_consumer = (t->tid == 1); 30 | uint32_t count; 31 | 32 | if (is_consumer) { 33 | // We must receive ( * VALUE_COUNT) values. 34 | //sleep(1); 35 | print(" [consumer] starting"); 36 | uint32_t total_value_count = (thread_count-1) * VALUE_COUNT; 37 | 38 | // Eeeehhh I'm tired. This can probably be done much more elegantly 39 | SNumber sum = 0; 40 | SNumber expected_sum = 0; 41 | count = VALUE_COUNT; 42 | do { expected_sum += (SNumber)count; } while(--count); 43 | expected_sum *= (SNumber)(thread_count-1); 44 | 45 | count = total_value_count; 46 | size_t failures = 0; 47 | 48 | do { 49 | SMsg* n = SMsgDequeue(t->q); 50 | if (n) { 51 | failures = 0; 52 | --count; 53 | sum += n->value.value.n; 54 | //print(" [consumer] recv %u", n->value); 55 | free(n); 56 | } else if (++failures == 100000) { 57 | SAssert(!"Too many consecutive 'dequeue' errors"); 58 | } 59 | } while (count); 60 | print(" [consumer] received all %u values (sum: " SNumberFormat ")", 61 | total_value_count, sum); 62 | assert(sum == expected_sum); 63 | 64 | } else { 65 | // We must produce VALUE_COUNT values 66 | count = VALUE_COUNT; 67 | print("[producer %u] starting, will send %u values", t->tid, count); 68 | do { 69 | //print("[producer %u] sending %u", t->tid, count); 70 | SMsg* n = (SMsg*)malloc(sizeof(SMsg)); 71 | n->value.value.n = (SNumber)count; 72 | SMsgEnqueue(t->q, n); 73 | } while (--count); 74 | print("[producer %u] exiting", t->tid); 75 | } 76 | 77 | #if !S_WITHOUT_SMP 78 | pthread_exit(0); 79 | #endif 80 | 81 | return 0; 82 | } 83 | 84 | bool spawn_thread(Thread* t) { 85 | if (pthread_create(&t->thread, 0, &thread_main, (void*)t) != 0) { 86 | perror("pthread_create"); 87 | return false; 88 | } else { 89 | return true; 90 | } 91 | } 92 | 93 | int main() { 94 | // Use a minimum of OS threads 95 | thread_count = S_MAX(thread_count, SHostAvailCPUCount()); 96 | total_count = (thread_count-1) * VALUE_COUNT; 97 | SMsgQ q = S_MSGQ_INIT(q); 98 | SResUsage rstart; 99 | 100 | #if S_WITHOUT_SMP 101 | thread_count = 2; // or our sum and count calculations will be off 102 | total_count = VALUE_COUNT; 103 | Thread producer = {0,0,&q}; 104 | Thread consumer = {1,0,&q}; 105 | SAssertTrue(SResUsageSample(&rstart)); 106 | thread_main((void*)&producer); 107 | thread_main((void*)&consumer); 108 | 109 | #else // S_WITHOUT_SMP 110 | print("thread_count: %zu", thread_count); 111 | Thread* threads = (Thread*)malloc(sizeof(Thread) * thread_count); 112 | SAssertTrue(SResUsageSample(&rstart)); 113 | 114 | size_t i = 0; 115 | for (; i != thread_count; ++i) { 116 | threads[i].tid = (uint32_t)i; 117 | threads[i].q = &q; 118 | if (!spawn_thread(&threads[i])) { 119 | return 1; 120 | } 121 | } 122 | 123 | for (i = 0; i != thread_count; ++i) { 124 | void* exit_value = 0; 125 | if (pthread_join(threads[i].thread, &exit_value) != 0) { 126 | perror("pthread_join"); 127 | } 128 | } 129 | #endif // S_WITHOUT_SMP 130 | 131 | SAssertNil(SMsgDequeue(&q)); 132 | 133 | // Sample #2 and print stats 134 | #if !S_TEST_SUIT_RUNNING 135 | SResUsage rend; 136 | SAssertTrue(SResUsageSample(&rend)); 137 | SResUsagePrintSummary(&rstart, &rend, "send+recv", total_count, thread_count); 138 | #endif 139 | 140 | pthread_exit(NULL); 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /test/test_prog_basics.c: -------------------------------------------------------------------------------- 1 | // Tests basic programming 2 | #include "test.h" 3 | #include 4 | #include 5 | 6 | /* 7 | clang -Wall -g -std=c99 -arch x86_64 -O0 -DS_DEBUG=1 -I.. \ 8 | -DS_TEST_SUIT_RUNNING=1 -L../build/debug/lib -lev -lsol -lpthread \ 9 | -o ../build/test/test_prog_basics.c-smp test_prog_basics.c \ 10 | && lldb ../build/test/test_prog_basics.c-smp 11 | */ 12 | 13 | #define SAssertRegType(ri, expected_type) do { \ 14 | SValue* registry = task->ar->registry; \ 15 | if (registry[(ri)].type != SValueT##expected_type) { \ 16 | SLogD("R(%u) type = %d", ri, registry[(ri)].type); \ 17 | } \ 18 | assert(registry[(ri)].type == SValueT##expected_type); \ 19 | } while(0) 20 | 21 | #define SAssertRegNumVal(ri, expected_val) do { \ 22 | SValue* registry = task->ar->registry; \ 23 | SAssertRegType((ri), Number); \ 24 | if (registry[(ri)].value.n != expected_val) { \ 25 | SLogD("R(%u) = %f", ri, registry[(ri)].value.n); \ 26 | } \ 27 | assert(registry[(ri)].value.n == expected_val); \ 28 | } while(0) 29 | 30 | void test_data(SVM* vm) { 31 | // LOADK, MOVE 32 | SValue constants[] = { 33 | SValueNumber(5), 34 | SValueNumber(10), 35 | }; 36 | SInstr instructions[100] = { 37 | SInstr_LOADK(0, 1), // R(0) = K(1) = 10 38 | SInstr_LOADK(1, 0), // R(1) = K(0) = 5 39 | SInstr_YIELD(0, 0, 0), 40 | }; 41 | size_t instr_offs = 3; 42 | SInstr* start_pc = instructions + instr_offs - 1; 43 | SFunc* func = SFuncCreate(constants, instructions); 44 | SSched* sched = SSchedCreate(); 45 | STask* task = STaskCreate(func, 0, 0); 46 | 47 | // R(0) = K(1) == 10.0, R(1) = K(0) == 5.0 48 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 49 | SAssertRegNumVal(0, 10.0); 50 | SAssertRegNumVal(1, 5.0); 51 | 52 | // R(2) = R(0) == 10.0 53 | task->ar->pc = start_pc; 54 | instructions[instr_offs] = SInstr_MOVE(2, 0); 55 | instructions[instr_offs+1] = SInstr_YIELD(0, 0, 0); 56 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 57 | SAssertRegNumVal(2, 10.0); 58 | 59 | SSchedDestroy(sched); 60 | STaskRelease(task); 61 | SFuncDestroy(func); 62 | } 63 | 64 | 65 | void test_arithmetic(SVM* vm) { 66 | // ADD, SUB, MUL, DIV 67 | SValue constants[] = { 68 | SValueNumber(5), 69 | SValueNumber(10), 70 | }; 71 | SInstr instructions[100] = { 72 | SInstr_LOADK(0, 0), // R(0) = K(0) = 5 73 | SInstr_LOADK(1, 1), // R(1) = K(1) = 10 74 | SInstr_YIELD(0, 0, 0), 75 | }; 76 | size_t instr_offs = 3; 77 | SInstr* start_pc = instructions + instr_offs - 1; 78 | SFunc* func = SFuncCreate(constants, instructions); 79 | SSched* sched = SSchedCreate(); 80 | STask* task = STaskCreate(func, 0, 0); 81 | 82 | // Load Ks to Rs 83 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 84 | 85 | // R(2) = R(0) + RK(1) = 15 86 | task->ar->pc = start_pc; 87 | instructions[instr_offs] = SInstr_ADD(2, 0, 1); 88 | instructions[instr_offs+1] = SInstr_YIELD(0, 0, 0); 89 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 90 | SAssertRegNumVal(0, 5.0); 91 | SAssertRegNumVal(1, 10.0); 92 | SAssertRegNumVal(2, 15.0); 93 | 94 | // R(2) = R(0) - RK(1) = -5 95 | task->ar->pc = start_pc; 96 | instructions[instr_offs] = SInstr_SUB(2, 0, 1); 97 | instructions[instr_offs+1] = SInstr_YIELD(0, 0, 0); 98 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 99 | SAssertRegNumVal(0, 5.0); 100 | SAssertRegNumVal(1, 10.0); 101 | SAssertRegNumVal(2, -5.0); 102 | 103 | // R(2) = R(0) * RK(1) = 15 104 | task->ar->pc = start_pc; 105 | instructions[instr_offs] = SInstr_MUL(2, 0, 1); 106 | instructions[instr_offs+1] = SInstr_YIELD(0, 0, 0); 107 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 108 | SAssertRegNumVal(0, 5.0); 109 | SAssertRegNumVal(1, 10.0); 110 | SAssertRegNumVal(2, 50.0); 111 | 112 | // R(2) = R(0) / RK(1) = 15 113 | task->ar->pc = start_pc; 114 | instructions[instr_offs] = SInstr_DIV(2, 0, 1); 115 | instructions[instr_offs+1] = SInstr_YIELD(0, 0, 0); 116 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 117 | SAssertRegNumVal(0, 5.0); 118 | SAssertRegNumVal(1, 10.0); 119 | SAssertRegNumVal(2, 0.5); 120 | 121 | SSchedDestroy(sched); 122 | STaskRelease(task); 123 | SFuncDestroy(func); 124 | } 125 | 126 | 127 | void test_logic_tests(SVM* vm) { 128 | // NOT, EQ, LT, LE, JUMP 129 | // Note: This test also inherently involves some level of control flow as a 130 | // test is always followed by a JUMP. 131 | SValue constants[] = { 132 | SValueNumber(5), 133 | SValueNumber(0), 134 | }; 135 | SInstr instructions[100] = { 136 | SInstr_LOADK(0, 0), // R(0) = K(0) = 5 137 | SInstr_LOADK(1, 1), // R(1) = K(1) = 0 138 | SInstr_YIELD(0, 0, 0), 139 | }; 140 | size_t instr_offs = 3; 141 | SInstr* start_pc = instructions + instr_offs - 1; 142 | SFunc* func = SFuncCreate(constants, instructions); 143 | SSched* sched = SSchedCreate(); 144 | STask* task = STaskCreate(func, 0, 0); 145 | 146 | // Load Ks to Rs 147 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 148 | 149 | // not(5) = false 150 | task->ar->pc = start_pc; 151 | instructions[instr_offs] = SInstr_NOT(2, 0); 152 | instructions[instr_offs+1] = SInstr_YIELD(0, 0, 0); 153 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 154 | SAssertRegNumVal(0, 5.0); 155 | SAssertRegNumVal(1, 0.0); 156 | SAssertRegType(2, False); // !5 == 0 157 | 158 | // not(0) = true 159 | task->ar->pc = start_pc; 160 | instructions[instr_offs] = SInstr_NOT(2, 1); 161 | instructions[instr_offs+1] = SInstr_YIELD(0, 0, 0); 162 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 163 | SAssertRegNumVal(0, 5.0); 164 | SAssertRegNumVal(1, 0.0); 165 | SAssertRegType(2, True); // !0 == 1 166 | 167 | // 5 == 0 == false 168 | assert(task->ar->registry[1].value.n != constants[0].value.n); 169 | task->ar->pc = start_pc; 170 | instructions[instr_offs] = SInstr_EQ(0, 0, 1); // 5 == 0 171 | instructions[instr_offs+1] = SInstr_JUMP(1); 172 | instructions[instr_offs+2] = SInstr_YIELD(0, 0, 0); // if test failed 173 | instructions[instr_offs+3] = SInstr_YIELD(0, 0, 0); // if test succeeded 174 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 175 | assert(task->ar->pc == start_pc+3); // test failed 176 | 177 | // 5 == 5 == true 178 | task->ar->registry[1].value.n = constants[0].value.n; // ghetto LOADK 1,0 179 | assert(task->ar->registry[1].value.n == constants[0].value.n); 180 | task->ar->pc = start_pc; 181 | instructions[instr_offs] = SInstr_EQ(0, 0, 1); // 5 == 5 182 | instructions[instr_offs+1] = SInstr_JUMP(1); 183 | instructions[instr_offs+2] = SInstr_YIELD(0, 0, 0); // if test failed 184 | instructions[instr_offs+3] = SInstr_YIELD(0, 0, 0); // if test succeeded 185 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 186 | assert(task->ar->pc == start_pc+4); // test succeeded 187 | 188 | // 5 < 4 == false 189 | assert(task->ar->registry[0].value.n == 5.0); 190 | task->ar->registry[1].value.n = 4.0; 191 | task->ar->pc = start_pc; 192 | instructions[instr_offs] = SInstr_LT(0, 0, 1); // 5 < 4 193 | instructions[instr_offs+1] = SInstr_JUMP(1); 194 | instructions[instr_offs+2] = SInstr_YIELD(0, 0, 0); // if test failed 195 | instructions[instr_offs+3] = SInstr_YIELD(0, 0, 0); // if test succeeded 196 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 197 | assert(task->ar->pc == start_pc+3); // test failed 198 | 199 | // 5 < 5 == false 200 | assert(task->ar->registry[0].value.n == 5.0); 201 | task->ar->registry[1].value.n = 5.0; 202 | task->ar->pc = start_pc; 203 | instructions[instr_offs] = SInstr_LT(0, 0, 1); // 5 < 5 204 | instructions[instr_offs+1] = SInstr_JUMP(1); 205 | instructions[instr_offs+2] = SInstr_YIELD(0, 0, 0); // if test failed 206 | instructions[instr_offs+3] = SInstr_YIELD(0, 0, 0); // if test succeeded 207 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 208 | assert(task->ar->pc == start_pc+3); // test failed 209 | 210 | // 5 < 6 == true 211 | assert(task->ar->registry[0].value.n == 5.0); 212 | task->ar->registry[1].value.n = 6.0; 213 | task->ar->pc = start_pc; 214 | instructions[instr_offs] = SInstr_LT(0, 0, 1); // 5 < 6 215 | instructions[instr_offs+1] = SInstr_JUMP(1); 216 | instructions[instr_offs+2] = SInstr_YIELD(0, 0, 0); // if test failed 217 | instructions[instr_offs+3] = SInstr_YIELD(0, 0, 0); // if test succeeded 218 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 219 | assert(task->ar->pc == start_pc+4); // test succeeded 220 | 221 | // 5 <= 4 == false 222 | assert(task->ar->registry[0].value.n == 5.0); 223 | task->ar->registry[1].value.n = 4.0; 224 | task->ar->pc = start_pc; 225 | instructions[instr_offs] = SInstr_LE(0, 0, 1); // 5 < 4 226 | instructions[instr_offs+1] = SInstr_JUMP(1); 227 | instructions[instr_offs+2] = SInstr_YIELD(0, 0, 0); // if test failed 228 | instructions[instr_offs+3] = SInstr_YIELD(0, 0, 0); // if test succeeded 229 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 230 | assert(task->ar->pc == start_pc+3); // test failed 231 | 232 | // 5 <= 5 == true 233 | assert(task->ar->registry[0].value.n == 5.0); 234 | task->ar->registry[1].value.n = 5.0; 235 | task->ar->pc = start_pc; 236 | instructions[instr_offs] = SInstr_LE(0, 0, 1); // 5 <= 5 237 | instructions[instr_offs+1] = SInstr_JUMP(1); 238 | instructions[instr_offs+2] = SInstr_YIELD(0, 0, 0); // if test failed 239 | instructions[instr_offs+3] = SInstr_YIELD(0, 0, 0); // if test succeeded 240 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 241 | assert(task->ar->pc == start_pc+4); // test succeeded 242 | 243 | // 5 <= 6 == true 244 | assert(task->ar->registry[0].value.n == 5.0); 245 | task->ar->registry[1].value.n = 6.0; 246 | task->ar->pc = start_pc; 247 | instructions[instr_offs] = SInstr_LE(0, 0, 1); // 5 <= 6 248 | instructions[instr_offs+1] = SInstr_JUMP(1); 249 | instructions[instr_offs+2] = SInstr_YIELD(0, 0, 0); // if test failed 250 | instructions[instr_offs+3] = SInstr_YIELD(0, 0, 0); // if test succeeded 251 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 252 | assert(task->ar->pc == start_pc+4); // test succeeded 253 | 254 | SSchedDestroy(sched); 255 | STaskRelease(task); 256 | SFuncDestroy(func); 257 | } 258 | 259 | 260 | void test_control_flow(SVM* vm) { 261 | // Covered: YIELD(0), RETURN 262 | SValue constants[] = { 263 | SValueNumber(5), 264 | }; 265 | SInstr instructions[100] = { 266 | SInstr_YIELD(0, 0, 0), 267 | SInstr_RETURN(0, 0), 268 | }; 269 | 270 | SFunc* func = SFuncCreate(constants, instructions); 271 | SSched* sched = SSchedCreate(); 272 | STask* task = STaskCreate(func, 0, 0); 273 | 274 | // YIELD resources 275 | assert(SchedExec(vm, sched, task) == STaskStatusYield); 276 | 277 | // AR's PC should be 1 less than the PC when this callback function is called. 278 | assert(task->ar->pc == instructions); 279 | 280 | // AR's PC should be at the first YIELD instruction 281 | assert(SInstrGetOP(*task->ar->pc) == S_OP_YIELD); 282 | assert(SInstrGetA(*task->ar->pc) == 0); // yield resources 283 | 284 | // RETURN 285 | assert(SchedExec(vm, sched, task) == STaskStatusEnd); 286 | 287 | SSchedDestroy(sched); 288 | STaskRelease(task); 289 | SFuncDestroy(func); 290 | } 291 | 292 | int main(int argc, const char** argv) { 293 | SVM vm = SVM_INIT; 294 | 295 | test_data(&vm); 296 | test_arithmetic(&vm); 297 | test_logic_tests(&vm); 298 | test_control_flow(&vm); 299 | 300 | return 0; 301 | } 302 | -------------------------------------------------------------------------------- /test/test_prog_timer.c: -------------------------------------------------------------------------------- 1 | // Tests programming with timers. 2 | // Covers YIELD(1), DBGCB, event sequence, and run- & wait queue integrity. 3 | #include "test.h" 4 | #include 5 | #include 6 | 7 | STask* task1; // Schedules a timer, waits for it and is finally resumed 8 | STask* task2; // Scheduled after task1 to inspect the VM state 9 | int sequence = 0; // For verifying call sequence 10 | 11 | void task1_on_resumed(SVM* vm, SSched* s, STask* t, SInstr* pc) { 12 | // This is called as task1 is resumed after the timer triggered 13 | STrace(); 14 | assert(t == task1); 15 | assert(sequence++ == 2); 16 | 17 | // AR's PC should be 1 less than the PC when this callback function is called. 18 | assert(t->ar->pc+1 == pc); 19 | 20 | // AR's PC should be at the first YIELD instruction 21 | assert(SInstrGetOP(*t->ar->pc) == S_OP_YIELD); 22 | assert(SInstrGetA(*t->ar->pc) == 1); // wait for timer 23 | 24 | // Verify that task1 is the only task in the run queue (task2 has already 25 | // ended) 26 | assert(s->rhead == task1); 27 | assert(s->rtail == task1); 28 | assert(task1->next == 0); 29 | 30 | // Verify that the wait queue is empty 31 | assert(s->whead == 0); 32 | assert(s->wtail == 0); 33 | 34 | assert(sequence++ == 3); 35 | } 36 | 37 | void task2_on_task1_suspended(SVM* vm, SSched* s, STask* t, SInstr* pc) { 38 | // This is called in task2 just after task1 is suspended, waiting for a timer 39 | STrace(); 40 | assert(t == task2); 41 | assert(sequence++ == 0); 42 | 43 | // Verify that task2 is the only task in the run queue 44 | assert(s->rhead == task2); 45 | assert(s->rtail == task2); 46 | assert(task2->next == 0); 47 | 48 | // Verify that task1 is the only task in the wait queue 49 | assert(s->whead == task1); 50 | assert(s->wtail == task1); 51 | assert(task1->next == 0); 52 | 53 | // Verify that task1 is waiting for a timer 54 | assert(task1->wtype == STaskWaitTimer); 55 | assert(task1->wp != 0); 56 | 57 | assert(sequence++ == 1); 58 | } 59 | 60 | void test_timer(SVM* vm) { 61 | // Covered: YIELD(1) 62 | SValue constants1[] = { 63 | SValueNumber(5), 64 | SValueOpaque(&task1_on_resumed), 65 | }; 66 | SInstr instructions1[] = { 67 | SInstr_YIELD(1, S_INSTR_RK_k+0, 0), 68 | SInstr_DBGCB(0, 1, 0), // ccall K(B)(vm, s, t, pc) 69 | SInstr_RETURN(0, 0), 70 | }; 71 | SFunc* func1 = SFuncCreate(constants1, instructions1); 72 | task1 = STaskCreate(func1, 0, 0); 73 | 74 | // A task that is scheduled just after task1, inspecting VM state 75 | SValue constants2[] = { 76 | SValueOpaque(&task2_on_task1_suspended), 77 | }; 78 | SInstr instructions2[] = { 79 | SInstr_DBGCB(0, 0, 0), // ccall K(B)(vm, s, t, pc) 80 | SInstr_RETURN(0, 0), 81 | }; 82 | SFunc* func2 = SFuncCreate(constants2, instructions2); 83 | task2 = STaskCreate(func2, 0, 0); 84 | 85 | // Make a scheduler and add the tasks 86 | SSched* sched = SSchedCreate(); 87 | SSchedTask(sched, task1); 88 | SSchedTask(sched, task2); 89 | 90 | // Verify run queue is: -> task1 <-> task2 <- 91 | assert(sched->rhead == task1); 92 | assert(task1->next == task2); 93 | assert(task1->prev == 0); 94 | assert(sched->rtail == task2); 95 | assert(task2->prev == task1); 96 | assert(task2->next == 0); 97 | 98 | // Verify that wait queue is empty 99 | assert(sched->whead == 0); 100 | assert(sched->wtail == 0); 101 | 102 | // Run 103 | SSchedRun(vm, sched); 104 | 105 | assert(sequence == 4); 106 | 107 | SSchedDestroy(sched); 108 | STaskRelease(task1); 109 | STaskRelease(task2); 110 | SFuncDestroy(func1); 111 | SFuncDestroy(func2); 112 | } 113 | 114 | int main(int argc, const char** argv) { 115 | SVM vm = SVM_INIT; 116 | 117 | test_timer(&vm); 118 | 119 | return 0; 120 | } 121 | --------------------------------------------------------------------------------