├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── build.sh ├── ecm.yaml ├── include └── fiber │ ├── fiber.h │ └── fiber_mach.h ├── nix-build-shell.sh ├── src ├── fiber.c ├── fiber_asm.h ├── fiber_asm_aarch64_apcs.S ├── fiber_asm_amd64_sysv.S ├── fiber_asm_amd64_win64.S ├── fiber_asm_amd64_win64.asm ├── fiber_asm_arm32_eabi.S ├── fiber_asm_ppc64le_elf2.S ├── fiber_asm_riscv_elf.S ├── fiber_asm_x86_cdecl.S └── fiber_asm_x86_win32.asm ├── test ├── CMakeLists.txt ├── basic.c ├── coop.c ├── fp_stress.c ├── generators.c ├── hello.c ├── test_basic.out ├── test_coop.out ├── test_fp_stress.out ├── test_generators.out └── test_pre.h └── toolchains └── any-cross.cmake /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Mozilla 2 | AccessModifierOffset: -4 3 | IndentWidth: 4 4 | BreakConstructorInitializersBeforeComma: false 5 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 6 | IndentCaseLabels: false 7 | SpaceAfterCStyleCast: true 8 | PointerAlignment: Right 9 | AlignEscapedNewlines: Right 10 | CompactNamespaces: false 11 | FixNamespaceComments: true 12 | BreakBeforeBraces: Custom 13 | BraceWrapping: 14 | AfterClass: true 15 | AfterControlStatement: false 16 | AfterEnum: true 17 | AfterFunction: true 18 | AfterNamespace: false 19 | AfterObjCDeclaration: false 20 | AfterStruct: true 21 | AfterUnion: true 22 | AfterExternBlock: false 23 | BeforeCatch: false 24 | BeforeElse: false 25 | IndentBraces: false 26 | SplitEmptyFunction: false 27 | SplitEmptyRecord: false 28 | SplitEmptyNamespace: true 29 | IndentPPDirectives: AfterHash 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | compile_commands.json 3 | .vs 4 | CMakeSettings.json 5 | .dir-locals.el 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/cxx-header-utils"] 2 | path = deps/cxx-header-utils 3 | url = https://github.com/simonfxr/cxx-header-utils 4 | [submodule "cmake/cmake-utils"] 5 | path = cmake/cmake-utils 6 | url = https://github.com/simonfxr/cmake-utils 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(FIBER_STANDALONE_PROJECT FALSE) 4 | if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 5 | set(FIBER_STANDALONE_PROJECT TRUE) 6 | endif() 7 | 8 | if(FIBER_STANDALONE_PROJECT) 9 | project(fiber LANGUAGES C) 10 | set(CMAKE_C_STANDARD 99) 11 | set(CMAKE_C_EXTENSIONS False) 12 | 13 | # Required under windows when building a dll and running tests 14 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) 15 | endif() 16 | 17 | include("${CMAKE_CURRENT_LIST_DIR}/cmake/cmake-utils/cmake-utils.cmake") 18 | 19 | if(FIBER_STANDALONE_PROJECT) 20 | cmu_configure() 21 | endif() 22 | 23 | option(FIBER_SHARED "build shared fiber library" ${BUILD_SHARED_LIBS}) 24 | 25 | option(FIBER_ASM_CHECK_ALIGNMENT "add extra stack alignment checks to asm functions" True) 26 | 27 | option(FIBER_M32 "force 32bit compile on x86 via -m32" False) 28 | 29 | if(NOT COMMAND check_ipo_supported) 30 | include(CheckIPOSupported) 31 | endif() 32 | check_ipo_supported(RESULT FIBER_HAVE_LTO) 33 | 34 | option(FIBER_LTO "enable lto" False) 35 | 36 | if(FIBER_M32 OR CMU_BITS_32) 37 | set(FIBER_BITS_32 True) 38 | elseif(CMU_BITS_64) 39 | set(FIBER_BITS_64 True) 40 | endif() 41 | 42 | set(cflags) 43 | set(priv_cflags) 44 | set(defines) 45 | set(ldflags) 46 | set(asm_lang "ASM") 47 | 48 | if(CMU_COMP_GNUC) 49 | list(APPEND priv_cflags -Wall -Wextra) 50 | if(FIBER_STANDALONE_PROJECT) 51 | list(APPEND cflags ${CMU_FLAGS_FP_IEEE}) 52 | endif() 53 | endif() 54 | 55 | if(FIBER_ASM_CHECK_ALIGNMENT) 56 | list(APPEND defines "-DFIBER_ASM_CHECK_ALIGNMENT=1") 57 | endif() 58 | 59 | set(asm_sources False) 60 | if(CMU_OS_POSIX AND CMU_ARCH_X86 AND FIBER_BITS_64) 61 | set(asm_sources src/fiber_asm_amd64_sysv.S) 62 | elseif(CMU_OS_POSIX AND CMU_ARCH_X86 AND FIBER_BITS_32) 63 | set(asm_sources src/fiber_asm_x86_cdecl.S) 64 | elseif(CMU_OS_POSIX AND CMU_ARCH_ARM AND FIBER_BITS_64) 65 | set(asm_sources src/fiber_asm_aarch64_apcs.S) 66 | elseif(CMU_OS_POSIX AND CMU_ARCH_ARM AND FIBER_BITS_32) 67 | set(asm_sources src/fiber_asm_arm32_eabi.S) 68 | elseif(CMU_OS_WINDOWS AND CMU_ARCH_X86 AND FIBER_BITS_64) 69 | if(CMU_COMP_MSVC) 70 | set(asm_lang "ASM_MASM") 71 | set(asm_sources src/fiber_asm_amd64_win64.asm) 72 | elseif(CMU_COMP_GNUC) 73 | set(asm_sources src/fiber_asm_amd64_win64.S) 74 | endif() 75 | elseif(CMU_OS_WINDOWS AND CMU_ARCH_X86 AND FIBER_BITS_32) 76 | if(CMU_COMP_MSVC) 77 | set(asm_lang "ASM_MASM") 78 | set(asm_sources src/fiber_asm_x86_win32.asm) 79 | set_source_files_properties(${asm_sources} PROPERTIES COMPILE_OPTIONS "/safeseh") 80 | else() 81 | set(asm_sources src/fiber_asm_x86_cdecl.S) 82 | endif() 83 | elseif(CMU_OS_POSIX AND CMU_ARCH_RISCV) 84 | set(asm_sources src/fiber_asm_riscv_elf.S) 85 | elseif(CMU_OS_POSIX AND CMU_ARCH_PPC AND FIBER_BITS_64 AND CMU_LITTLE_ENDIAN) 86 | set(asm_sources src/fiber_asm_ppc64le_elf2.S) 87 | endif() 88 | 89 | if(CMU_COMP_MSVC) 90 | # make sure it gets passed to the assembler 91 | list(APPEND priv_cflags "/nologo") 92 | endif() 93 | 94 | if(NOT asm_sources) 95 | message(FATAL_ERROR "fiber: this platform is not supported ARCH=${CMU_ARCH} BITS=${CMU_BITS} OS=${CMAKE_SYSTEM_NAME}") 96 | endif() 97 | 98 | if(FIBER_M32) 99 | list(APPEND cflags "-m32") 100 | list(APPEND ldflags "-m32") 101 | endif() 102 | 103 | enable_language(${asm_lang}) 104 | if(NOT CMAKE_${asm_lang}_COMPILER_LOADED) 105 | message(FATAL_ERROR "no compatible assembler found") 106 | endif() 107 | 108 | if(NOT DEFINED FIBER_SHARED) 109 | set(FIBER_SHARED "${BUILD_SHARED_LIBS}") 110 | endif() 111 | 112 | if(NOT TARGET header-utils::header-utils) 113 | add_subdirectory(deps/cxx-header-utils) 114 | endif() 115 | 116 | set(_fiber_lib_mode) 117 | if(FIBER_OBJECT) 118 | set(_fiber_lib_mode OBJECT) 119 | elseif(FIBER_SHARED) 120 | set(_fiber_lib_mode SHARED) 121 | else() 122 | set(_fiber_lib_mode STATIC) 123 | endif() 124 | 125 | add_library(fiber ${_fiber_lib_mode} ${asm_sources} src/fiber.c) 126 | target_include_directories(fiber PUBLIC include) 127 | target_include_directories(fiber PRIVATE src) 128 | target_link_libraries(fiber PUBLIC header-utils::header-utils) 129 | target_compile_definitions(fiber PRIVATE ${defines}) 130 | 131 | if(FIBER_LTO) 132 | set_property(TARGET fiber PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) 133 | endif() 134 | 135 | target_compile_options(fiber PUBLIC ${cflags}) 136 | target_compile_options(fiber PRIVATE ${priv_cflags} ${CMU_FLAGS_W4}) 137 | 138 | if(ldflags) 139 | cmu_target_link_options(fiber PUBLIC ${ldflags}) 140 | endif() 141 | 142 | if(FIBER_SHARED) 143 | target_compile_definitions(fiber PUBLIC -DFIBER_SHARED=1) 144 | set_target_properties(fiber PROPERTIES C_VISIBILITY_PRESET hidden) 145 | endif() 146 | 147 | if(FIBER_OBJECT) 148 | set_target_properties(fiber PROPERTIES POSITION_INDEPENDENT_CODE ON) 149 | endif() 150 | 151 | if(FIBER_STANDALONE_PROJECT) 152 | enable_testing() 153 | add_subdirectory(test) 154 | endif() 155 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | Copyright (c) 2019 Simon Reiser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 21 | OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fiber: fast and lightweight cross-platform coroutines 2 | 3 | - Fast by using custom assembly per platform: switching coroutines is just a few memory/register swaps (e.g. on amd64: 9 registers) 4 | - Minimal: about 300 lines of C and under 100 lines of assembly per platform 5 | 6 | ## Usecases 7 | 8 | - replace your state machine with something more readable 9 | - replace your event loop callbacks with a more natural thread-like control flow 10 | - lazy streams and generators 11 | - agent systems (e.g. AI in games) 12 | - cooperative threading in general (see `test1.c` for an example) 13 | 14 | ## Supported platforms 15 | 16 | - ELF Targets, 32 or 64-bit, running on x86, ARM (e.g. Linux, BSD's), RISC-V 32/64 and PPC64 17 | - Windows 32 or 64-bit x86 18 | - MacOS 64bit x86 and Apple Silicon (special thanks to [@rsmmr](https://github.com/rsmmr) for Apple Silicon support!) 19 | 20 | Tested on the following machines: 21 | 22 | - ArchLinux: clang/gcc: 32 and 64-bit builds 23 | - FreeBSD 11: clang: 32 and 64-bit builds 24 | - Windows x86: MSVC: 32 and 64-bit builds 25 | - Windows x86: MinGW: 64-bit builds 26 | - RaspberryPI (armv6): gcc builds 27 | - RaspberryPI 3 (aarch64): gcc/clang builds 28 | - MacOS 10.14: clang 64-bit builds 29 | 30 | ## Caveats 31 | 32 | - Its possible to use very small stacks, but it is not recommended, since signal handlers or linkers might run code on your coroutine stack at unexpected times, better use a more conservative stack size of e.g 32kb. 33 | - Moving fibers from one OS thread to another is problematic: (caching of thread locals and platform specific use of registers) 34 | - If you use C++ exceptions: The top stack frame of each coroutine should catch and rethrow all exceptions in a toplevel fiber (this also applies to Windows SEH) 35 | - Interleaving setjmp/longjmp with fiber switching is not allowed 36 | - Debuggers are confused by the switching of stacks, in particular backtraces only show the frames of the currently active fiber. 37 | 38 | ## Building 39 | 40 | ```shell 41 | git clone https://github.com/simonfxr/fiber 42 | git submodule update --init 43 | mkdir build 44 | cd build 45 | cmake .. 46 | make 47 | ``` 48 | 49 | Or integrate it in your cmake build 50 | 51 | ```cmake 52 | add_subdirectory(path/to/fiber) 53 | target_link_libraries(your-target fiber::fiber) 54 | ``` 55 | 56 | ## Example 57 | 58 | ```c 59 | #include 60 | #include 61 | #include 62 | #include 63 | 64 | typedef struct 65 | { 66 | Fiber *caller; 67 | Fiber *self; 68 | const char *str; 69 | } Args; 70 | 71 | /* invoked if coroutine is called after it finished */ 72 | void 73 | guard(Fiber *self, void *null) 74 | { 75 | (void) self; 76 | (void) null; 77 | abort(); 78 | } 79 | 80 | /* entry point */ 81 | void 82 | coroutine(void *args0) 83 | { 84 | Args *args = (Args *) args0; 85 | printf("From coroutine: %s\n", args->str); 86 | fiber_switch(args->self, args->caller); 87 | } 88 | 89 | int 90 | main() 91 | { 92 | Fiber main_fiber; 93 | fiber_init_toplevel(&main_fiber); 94 | Fiber crt; 95 | /* allocate a stack of 16kb, additionally add an unmapped page to detect overflows */ 96 | fiber_alloc(&crt, 1024 * 16, guard, NULL, FIBER_FLAG_GUARD_LO); 97 | Args args; 98 | args.caller = &main_fiber; 99 | args.self = &crt; 100 | args.str = "Hello"; 101 | /* push a new return stack frame, defines the entry point of our coroutine */ 102 | fiber_push_return(&crt, coroutine, &args, sizeof args); 103 | /* arguments are copied into the coroutine, its safe to destroy them here */ 104 | memset(&args, 0, sizeof args); 105 | /* run our coroutine */ 106 | fiber_switch(&main_fiber, &crt); 107 | fiber_destroy(&crt); 108 | /* main_fiber does not have to be destroyed */ 109 | return 0; 110 | } 111 | ``` 112 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -uo pipefail 4 | 5 | FIBER_CROSS=$CROSS_TARGET 6 | 7 | SYS_LINUX= 8 | SYS_BSD= 9 | SYS_WINDOWS= 10 | 11 | cmake_reconfigure=0 12 | cmake_generator= 13 | cmake_configure_args=( -DCMAKE_EXPORT_COMPILE_COMMANDS=True ) 14 | 15 | BITS32= 16 | BITS64= 17 | 18 | if [[ ! $FIBER_CROSS ]]; then 19 | 20 | case "$(uname -s)" in 21 | Linux) SYS_LINUX=1 ;; 22 | *BSD) SYS_BSD=1 ;; 23 | MSYS_NT*) SYS_WINDOWS=1 ;; 24 | esac 25 | 26 | case "$(getconf LONG_BIT)" in 27 | 32) BITS32=1 ;; 28 | 64) BITS64=1 ;; 29 | esac 30 | 31 | case "$(uname -m)" in 32 | amd64|x86_64) BITS64=1 ;; 33 | aarch64) BITS64=1 ;; 34 | arm*) BITS32=1 ;; 35 | esac 36 | 37 | [[ $BITS32 || $BITS64 ]] || { 38 | echo "failed to detect 32 vs 64 bit architecture" >&2 39 | exit 1 40 | } 41 | 42 | 43 | native_bits=64 44 | [[ $BITS32 ]] && native_bits=32 45 | 46 | ccs=( ) 47 | [[ ! $SYS_WINDOWS ]] && type -P gcc &>/dev/null && ccs+=( gcc ) 48 | [[ ! $SYS_WINDOWS ]] && type -P clang &>/dev/null && ccs+=( clang ) 49 | [[ $SYS_WINDOWS ]] && type -P cl &>/dev/null && ccs+=( cl ) 50 | type -P icc &>/dev/null && ccs+=( icc ) 51 | else 52 | native_bits=_ 53 | ccs=( 'cross' ) 54 | fi 55 | 56 | build_types=( debug release ) 57 | 58 | link_modes=( static shared ) 59 | 60 | compiler_supports_m32() { 61 | [[ ! $FIBER_CROSS ]] || return 1 62 | [[ $BITS32 || $SYS_WINDOWS || "$1" = icc ]] && return 1 63 | local cc="$1" tmp_file 64 | tmp_file="$(mktemp -t test_m32_XXXXXX.c)" 65 | bin_file="$(mktemp -t test_m32_XXXXXX.bin)" 66 | echo -e '#include \nint main() { exit(0); return 0; }' > "$tmp_file" 67 | "$cc" -x c -o "$bin_file" -m32 "$tmp_file" &>/dev/null 68 | local ret=$? 69 | [[ $ret -eq 0 && -x "$bin_file" ]] || ret=1 70 | rm -f "$tmp_file" "$bin_file" &>/dev/null 71 | if [[ $ret -eq 0 ]]; then 72 | echo "Compiler $cc supports -m32" 73 | else 74 | echo "Compiler $cc does NOT support -m32" 75 | fi 76 | return $ret 77 | } 78 | 79 | build_configs=( ) 80 | 81 | add_build_config() { 82 | local bt="$1" cc="$2" bits="$3" link_mode="$4" check_align="$5" 83 | local cm_flags=( -DCMAKE_BUILD_TYPE="${bt^^}" ) 84 | 85 | if [[ $FIBER_CROSS ]]; then 86 | cm_flags+=( -DCMAKE_TOOLCHAIN_FILE="$CMAKE_TOOLCHAIN_FILE" ) 87 | else 88 | cm_flags+=( -DCMAKE_C_COMPILER="$cc" ) 89 | fi 90 | 91 | [[ $SYS_WINDOWS && $cc = $cl && $link_mode = shared ]] && return 92 | 93 | [[ $BITS64 && $bits = 32 && $cc != "cl" ]] && 94 | cm_flags+=( -DFIBER_M32=True ) 95 | 96 | if [[ $link_mode = shared ]]; then 97 | cm_flags+=( -DFIBER_SHARED=True ) 98 | else 99 | cm_flags+=( -DFIBER_SHARED=False ) 100 | fi 101 | 102 | if [[ $check_align = "checked" ]]; then 103 | cm_flags+=( -DFIBER_ASM_CHECK_ALIGNMENT=True ) 104 | else 105 | cm_flags+=( -DFIBER_ASM_CHECK_ALIGNMENT=False ) 106 | fi 107 | 108 | local conf_name 109 | if [[ $FIBER_CROSS ]]; then 110 | conf_name="$FIBER_CROSS-${bt}-${link_mode}-${check_align}" 111 | else 112 | conf_name="${bt}-${cc}${bits}-${link_mode}-${check_align}" 113 | fi 114 | eval "export cmake_flags_${conf_name//-/_}=\"${cm_flags[*]}\"" 115 | build_configs+=( "$conf_name" ) 116 | } 117 | 118 | init_configs() { 119 | for cc in "${ccs[@]}"; do 120 | bit_modes=( "$native_bits" ) 121 | compiler_supports_m32 "$cc" && bit_modes+=( 32 ) 122 | for bits in "${bit_modes[@]}"; do 123 | if [[ $cc = cl && $bits = 64 ]]; then 124 | { cl 2>&1 | grep "x64"; } || bits=32 125 | fi 126 | for bt in "${build_types[@]}"; do 127 | for link_mode in "${link_modes[@]}"; do 128 | for ca in "unchecked" "checked"; do 129 | add_build_config "$bt" "$cc" "$bits" "$link_mode" "$ca" 130 | done 131 | done 132 | done 133 | done 134 | done 135 | } 136 | 137 | do_all() { 138 | local IFS=$'\n' 139 | echo "${build_configs[*]}" | xargs -rn1 -d'\n' -P "$(nproc)" "$0" "$@" 140 | } 141 | 142 | 143 | __configure1() { 144 | local bc="$1" d 145 | d=build/$bc 146 | (( cmake_reconfigure )) || [[ ! -r $d/CMakeCache.txt ]] || return 0 147 | rm -rf "$d" 148 | mkdir "$d" || return $? 149 | cd "$d" || exit $? 150 | echo "************** $bc **************" 151 | local flagsvar=cmake_flags_${bc//-/_} 152 | local all_flags=( "${flags[@]}" ${!flagsvar} ) 153 | local gen=( ) 154 | [[ ! ${cmake_generator:-} ]] || gen+=( -G "$cmake_generator" ) 155 | cmake "${gen[@]}" "${cmake_configure_args[@]}" "${all_flags[@]}" ../.. 156 | } 157 | 158 | cmd_configure() { 159 | local flags=( "$@" ) 160 | mkdir -p build || return $? 161 | do_all __configure1 162 | } 163 | 164 | __build1() { 165 | local bc="$1" 166 | cd "build/$bc" || exit $? 167 | echo "************** $bc **************" 168 | cmake --build . "${flags[@]}" 169 | } 170 | 171 | cmd_build() { 172 | local flags=( "$@" ) 173 | mkdir -p build || return $? 174 | do_all __build1 175 | } 176 | 177 | __test1() { 178 | local bc="$1" 179 | local d="build/$bc" 180 | echo "************** $bc **************" 181 | cd "$d" || return $? 182 | ctest >/dev/null || { 183 | ret=$? 184 | echo "************** FAILED **************" 185 | return $ret 186 | } 187 | } 188 | 189 | cmd_test() { 190 | local flags=( "$@" ) 191 | mkdir -p build || return $? 192 | do_all __test1 193 | } 194 | 195 | cmd_all() { 196 | cmd_configure "$@" 197 | cmd_test 198 | } 199 | 200 | case "$1" in 201 | configure|build|test|all) 202 | init_configs 203 | "cmd_$1" "${@:2}" 204 | ;; 205 | __*) 206 | "$1" "${@:2}" ;; 207 | *) 208 | echo "invalid args: $*" >&2 209 | ;; 210 | esac 211 | -------------------------------------------------------------------------------- /ecm.yaml: -------------------------------------------------------------------------------- 1 | 2 | build-configs: 3 | - debug-clang-ninja 4 | - debug-gcc-ninja 5 | - release-gcc-ninja 6 | - release-clang-ninja 7 | -------------------------------------------------------------------------------- /include/fiber/fiber.h: -------------------------------------------------------------------------------- 1 | #ifndef FIBER_H 2 | #define FIBER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef FIBER_SHARED 14 | # ifdef fiber_EXPORTS 15 | # define FIBER_API HU_DSO_EXPORT 16 | # else 17 | # define FIBER_API HU_DSO_IMPORT 18 | # endif 19 | #else 20 | # define FIBER_API 21 | #endif 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | #include 28 | 29 | typedef uint16_t FiberState; 30 | typedef uint16_t FiberFlags; 31 | 32 | /** 33 | * A Fiber represents a couroutine which has its own call stack and a set of 34 | * preserved registers. 35 | */ 36 | typedef struct Fiber 37 | { 38 | FiberRegs regs; 39 | void *stack; 40 | void *alloc_stack; 41 | size_t stack_size; 42 | FiberState state; 43 | } Fiber; 44 | 45 | #define FIBER_STATE_CONSTANT(x) hu_static_cast(FiberState, x) 46 | #define FIBER_FLAG_CONSTANT(x) hu_static_cast(FiberFlags, x) 47 | 48 | #define FIBER_FS_EXECUTING FIBER_STATE_CONSTANT(1) 49 | #define FIBER_FS_TOPLEVEL FIBER_STATE_CONSTANT(2) 50 | #define FIBER_FS_ALIVE FIBER_STATE_CONSTANT(4) 51 | #define FIBER_FS_HAS_LO_GUARD_PAGE FIBER_STATE_CONSTANT(8) 52 | #define FIBER_FS_HAS_HI_GUARD_PAGE FIBER_STATE_CONSTANT(16) 53 | 54 | #define FIBER_FLAG_GUARD_LO FIBER_FLAG_CONSTANT(8) 55 | #define FIBER_FLAG_GUARD_HI FIBER_FLAG_CONSTANT(16) 56 | 57 | typedef void(FIBER_CCONV *FiberFunc)(void *); 58 | typedef void(FIBER_CCONV *FiberCleanupFunc)(Fiber *, void *); 59 | 60 | /** 61 | * initialize a Fiber with a preallocated stack. Stack alignment will be 62 | * correctly handled, this means in most cases the usuable stack size is 63 | * slightly less then the size of the stack buffer. The cleanup function 64 | * should never return! There is no stack frame where it could return to. In 65 | * practice this means that this function should switch to some other fiber 66 | * (usually the toplevel fiber). 67 | * @param fbr the fiber to create 68 | * @param stack preallocated stack 69 | * @param stack_size size of preallocated stack 70 | * @param cleanup the initial function on the call stack 71 | * @param arg the arg to pass to cleanup when it is invoked 72 | */ 73 | FIBER_API 74 | HU_NONNULL_PARAMS(1, 2, 4) 75 | HU_RETURNS_NONNULL 76 | Fiber * 77 | fiber_init(HU_OUT_NONNULL Fiber *fbr, 78 | HU_INOUT_NONNULL void *stack, 79 | size_t stack_size, 80 | HU_IN_NONNULL FiberCleanupFunc cleanup, 81 | void *arg); 82 | 83 | /** 84 | * initialize a toplevel Fiber, a toplevel Fiber represents an OS thread. Should 85 | * only be called once per OS Thread! 86 | * @param fbr the fiber to create 87 | */ 88 | FIBER_API 89 | HU_NONNULL_PARAMS(1) 90 | void 91 | fiber_init_toplevel(HU_OUT_NONNULL Fiber *fbr); 92 | 93 | /** 94 | * create a new Fiber by allocating a fresh stack, optionally with bottom or top 95 | * guard frames (each page usually adds an overhead of 4kb). It is recommended 96 | * to pass FIBER_FLAG_GUARD_LO, to catch stack overflows. @see fiber_init() 97 | * @param fbr the fiber to create 98 | * @param stack_size size of stack 99 | * @param cleanup the initial function on the call stack. 100 | * @param arg the arg to pass to cleanup when it is invoked 101 | */ 102 | HU_NODISCARD 103 | FIBER_API 104 | HU_NONNULL_PARAMS(1, 3) 105 | bool 106 | fiber_alloc(HU_OUT_NONNULL Fiber *fbr, 107 | size_t stack_size, 108 | HU_IN_NONNULL FiberCleanupFunc cleanup, 109 | void *arg, 110 | FiberFlags flags); 111 | 112 | /** 113 | * Deallocate the stack, does nothing if created by fiber_init(). 114 | * @param fbr the fiber to destroy 115 | */ 116 | FIBER_API 117 | HU_NONNULL_PARAMS(1) 118 | void 119 | fiber_destroy(HU_IN_NONNULL Fiber *fbr); 120 | 121 | /** 122 | * Switch from the current fiber to a different fiber by returning to the stack 123 | * frame of the new fiber. from has to be the active fiber! 124 | * @param from currently executing fiber 125 | * @param to fiber to switch to 126 | */ 127 | FIBER_API 128 | HU_NONNULL_PARAMS(1, 2) 129 | void 130 | fiber_switch(HU_INOUT_NONNULL Fiber *from, HU_INOUT_NONNULL Fiber *to); 131 | 132 | /** 133 | * Allocate a fresh stack frame at the top of a fiber with an argument buffer of 134 | * args_size. If the fiber is switched to it will execute the function. 135 | * @param fbr the fiber where the new frame will be allocated, cannot be the 136 | * currently executing fiber! 137 | * @param f the function to place in the stack frame, it will be called with a 138 | * pointer to the beginning of the argument buffer 139 | * @param args_dest the argument will receive a pointer to the buffer allocated 140 | * on the fiber stack, will be aligned to 8 bytes on all platforms. 141 | * @param args_size size of the argument buffer to allocate 142 | */ 143 | HU_NONNULL_PARAMS(1, 2, 3) 144 | FIBER_API void 145 | fiber_reserve_return(HU_INOUT_NONNULL Fiber *fbr, 146 | HU_IN_NONNULL FiberFunc f, 147 | HU_OUT_NONNULL void **args_dest, 148 | size_t args_size); 149 | /** 150 | * similar to @see fiber_reserve_return(), instead of returning the pointer to 151 | * the buffer, it will copy the contents into the stack allocated argument 152 | * buffer 153 | * @param fbr where to allocate the fresh stack frame 154 | * @param f function to place into the stack frame 155 | * @param args buffer to copy onto the stack frame 156 | * @param args_size size of argument buffer to copy 157 | */ 158 | HU_NONNULL_PARAMS(1, 2) 159 | static inline void 160 | fiber_push_return(HU_INOUT_NONNULL Fiber *fbr, 161 | HU_IN_NONNULL FiberFunc f, 162 | const void *args, 163 | size_t s) 164 | { 165 | void *args_dest; 166 | fiber_reserve_return(fbr, f, &args_dest, s); 167 | memcpy(args_dest, args, s); 168 | } 169 | 170 | /** 171 | * Temporarily switch from the active fiber to a temporary fiber and immediately 172 | * execute a function on this stack. After the call returns switch back to the 173 | * original fiber. This is useful if a call might consume a lot of stack space, 174 | * in this case temp should be a toplevel fiber. 175 | * @param active currently executing fiber 176 | * @param temp fiber where the call to f will take place 177 | * @param f function to call 178 | * @param argument to pass to f 179 | */ 180 | FIBER_API 181 | HU_NONNULL_PARAMS(1, 2, 3) 182 | void 183 | fiber_exec_on(HU_IN_NONNULL Fiber *active, 184 | HU_INOUT_NONNULL Fiber *temp, 185 | HU_IN_NONNULL FiberFunc f, 186 | void *args); 187 | 188 | /** 189 | * @return The compiled in stack alignment 190 | */ 191 | FIBER_API 192 | size_t 193 | fiber_stack_alignment(void); 194 | 195 | HU_WARN_UNUSED 196 | static inline bool 197 | fiber_is_toplevel(const Fiber *fbr) 198 | { 199 | return (fbr->state & FIBER_FS_TOPLEVEL) != 0; 200 | } 201 | 202 | HU_WARN_UNUSED 203 | HU_NONNULL_PARAMS(1) 204 | static inline bool 205 | fiber_is_executing(HU_IN_NONNULL const Fiber *fbr) 206 | { 207 | return (fbr->state & FIBER_FS_EXECUTING) != 0; 208 | } 209 | 210 | HU_WARN_UNUSED 211 | HU_NONNULL_PARAMS(1) 212 | static inline bool 213 | fiber_is_alive(HU_IN_NONNULL const Fiber *fbr) 214 | { 215 | return (fbr->state & FIBER_FS_ALIVE) != 0; 216 | } 217 | 218 | HU_NONNULL_PARAMS(1) 219 | static inline void 220 | fiber_set_alive(HU_INOUT_NONNULL Fiber *fbr, bool alive) 221 | { 222 | if (alive) 223 | fbr->state |= FIBER_FS_ALIVE; 224 | else 225 | fbr->state &= ~FIBER_FS_ALIVE; 226 | } 227 | 228 | HU_WARN_UNUSED 229 | HU_NONNULL_PARAMS(1) 230 | static inline void * 231 | fiber_stack(HU_IN_NONNULL const Fiber *fbr) 232 | { 233 | return fbr->stack; 234 | } 235 | 236 | HU_WARN_UNUSED 237 | HU_NONNULL_PARAMS(1) 238 | static inline size_t 239 | fiber_stack_size(HU_IN_NONNULL const Fiber *fbr) 240 | { 241 | return fbr->stack_size; 242 | } 243 | 244 | HU_WARN_UNUSED 245 | HU_NONNULL_PARAMS(1) 246 | static inline size_t 247 | fiber_stack_free_size(HU_IN_NONNULL const Fiber *fbr) 248 | { 249 | return hu_static_cast(char *, fbr->regs.sp) - 250 | hu_static_cast(char *, fbr->stack); 251 | } 252 | 253 | HU_WARN_UNUSED 254 | HU_NONNULL_PARAMS(1) 255 | static inline size_t 256 | fiber_stack_used_size(HU_IN_NONNULL const Fiber *fbr) 257 | { 258 | return fbr->stack_size - fiber_stack_free_size(fbr); 259 | } 260 | 261 | #ifdef __cplusplus 262 | } 263 | #endif 264 | #endif 265 | -------------------------------------------------------------------------------- /include/fiber/fiber_mach.h: -------------------------------------------------------------------------------- 1 | #ifndef FIBER_MACH_H 2 | #define FIBER_MACH_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #if HU_ARCH_X86_P && HU_BITS_32_P && (HU_OS_POSIX_P || HU_OS_WINDOWS_P) 12 | # if HU_OS_WINDOWS_P 13 | # define FIBER_TARGET_X86_WIN32 1 14 | # define FIBER_CCONV __cdecl 15 | # define FIBER_DEFAULT_STACK_ALIGNMENT 4 16 | # else 17 | # define FIBER_TARGET_X86_CDECL 1 18 | # if (HU_OS_FREEBSD_P || HU_OS_WINDOWS_P) 19 | # define FIBER_DEFAULT_STACK_ALIGNMENT 4 20 | # else 21 | # define FIBER_DEFAULT_STACK_ALIGNMENT 16 22 | # endif 23 | # endif 24 | # define FIBER_ARCH_REGS \ 25 | void *sp; \ 26 | void *lr; \ 27 | void *ebp; \ 28 | void *ebx; \ 29 | void *edi; \ 30 | void *esi 31 | 32 | #elif HU_ARCH_X86_P && HU_BITS_64_P && HU_OS_POSIX_P 33 | # define FIBER_TARGET_AMD64_SYSV 34 | # define FIBER_STACK_ALIGNMENT 16 35 | # define FIBER_ARCH_REGS \ 36 | void *sp; \ 37 | void *lr; \ 38 | void *rbp; \ 39 | void *rbx; \ 40 | void *r12; \ 41 | void *r13; \ 42 | void *r14; \ 43 | void *r15 44 | 45 | #elif HU_ARCH_X86_P && HU_BITS_64_P && HU_OS_WINDOWS_P 46 | # define FIBER_TARGET_AMD64_WIN64 1 47 | # define FIBER_DEFAULT_STACK_ALIGNMENT 16 48 | # define FIBER_ARCH_REGS \ 49 | void *sp; \ 50 | void *lr; \ 51 | void *rbx; \ 52 | void *rbp; \ 53 | void *rdi; \ 54 | void *rsi; \ 55 | void *r12; \ 56 | void *r13; \ 57 | void *r14; \ 58 | void *r15; \ 59 | /* 10 * 16 bytes, add aditional 8 bytes to make 16byte alignment \ 60 | * possible */ \ 61 | double xmm[21] 62 | 63 | #elif HU_ARCH_X86_P && HU_BITS_32_P && HU_OS_WINDOWS_P 64 | # define FIBER_TARGET_X86_WIN32 1 65 | # define FIBER_CCONV __cdecl 66 | # define FIBER_DEFAULT_STACK_ALIGNMENT 4 67 | # define FIBER_ARCH_REGS \ 68 | void *sp; \ 69 | void *lr; \ 70 | void *ebp; \ 71 | void *ebx; \ 72 | void *edi; \ 73 | void *esi 74 | 75 | #elif HU_ARCH_ARM_P && HU_BITS_32_P && HU_OS_POSIX_P 76 | # define FIBER_TARGET_ARM32_EABI 77 | # define FIBER_DEFAULT_STACK_ALIGNMENT 8 78 | # define FIBER_ARCH_REGS \ 79 | void *__pad; \ 80 | void *r[9]; /* r4 - r12 */ \ 81 | void *sp; /* r13 */ \ 82 | void *lr; /* r14 */ \ 83 | double d[8] /* d8 - d15 */ 84 | 85 | #elif HU_ARCH_ARM_P && HU_BITS_64_P && HU_OS_POSIX_P 86 | # define FIBER_TARGET_AARCH64_APCS 1 87 | # define FIBER_DEFAULT_STACK_ALIGNMENT 16 88 | # define FIBER_ARCH_REGS \ 89 | void *sp; \ 90 | void *lr; /* r30 */ \ 91 | void *fp; /* r29 */ \ 92 | void *r[10]; /* r19 - r28 */ \ 93 | double d[8]; /* d8 - d15 */ 94 | 95 | #elif HU_ARCH_RISCV_P && HU_OS_POSIX_P 96 | # define FIBER_TARGET_RISCV_ELF 1 97 | # define FIBER_DEFAULT_STACK_ALIGNMENT 16 98 | # ifndef __riscv_float_abi_double 99 | # error "this RISCV abi is not supported use -mabi=lp64d" 100 | # endif 101 | # define FIBER_ARCH_REGS \ 102 | void *sp; \ 103 | void *lr; \ 104 | void *s[12]; \ 105 | double fs[12] 106 | 107 | #elif HU_ARCH_PPC_P && HU_BITS_64_P && HU_LITTLE_ENDIAN_P && HU_OBJFMT_ELF_P 108 | # define FIBER_TARGET_PPC64LE_ELF 1 109 | # define FIBER_DEFAULT_STACK_ALIGNMENT 16 110 | # if !defined(_CALL_ELF) || _CALL_ELF != 2 111 | # error "this PowerPC ABI is not supported, use -mabi=elfv2" 112 | # endif 113 | # define FIBER_ARCH_REGS \ 114 | uint32_t cr; \ 115 | uint32_t vrsave; \ 116 | void *lr; /* r0 */ \ 117 | void *sp; /* r1 */ \ 118 | void *r[18]; /* r14 - r31 */ \ 119 | double *f[18]; /* f14 - f31 */ \ 120 | double *v[12 * 2 + 1] /* v20 - v31, adjusted to be 16 byte aligned */ 121 | 122 | #else 123 | # error "fiber: system/architecture target not supported" 124 | #endif 125 | 126 | #ifndef FIBER_CCONV 127 | # define FIBER_CCONV 128 | #endif 129 | 130 | #if HU_C_P || HU_CXX_P 131 | HU_BEGIN_EXTERN_C 132 | typedef struct FiberRegs 133 | { 134 | FIBER_ARCH_REGS; 135 | } FiberRegs; 136 | HU_END_EXTERN_C 137 | #endif 138 | 139 | #ifndef FIBER_STACK_ALIGNMENT 140 | # define FIBER_STACK_ALIGNMENT FIBER_DEFAULT_STACK_ALIGNMENT 141 | #endif 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /nix-build-shell.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | die() { echo "Error: $*" >&2; exit 1; } 5 | 6 | S=$PWD 7 | 8 | target=$(basename "$1") 9 | target=${target%%.*} 10 | shift 11 | 12 | crossSystem=$target 13 | if [[ ${1:-} != "--" ]]; then 14 | crossSystem=${1:-$target} 15 | fi 16 | 17 | (( $# == 0 )) || shift 18 | 19 | [[ ${1:-} != "--" ]] || shift 20 | 21 | B=$S/build 22 | 23 | mkdir -p "$B/nix-shells" 24 | 25 | nixfile=$B/nix-shells/${target}.nix 26 | 27 | #[[ -r $nixfile ]] || 28 | cat <"$nixfile" 29 | { pkgs ? import { } }: 30 | 31 | with pkgs.pkgsCross.${crossSystem}; mkShell {} 32 | EOF 33 | 34 | 35 | toolchain=$S/toolchains/any-cross.cmake 36 | mkdir -p "$B/toolchains" 37 | toolchain_link=$B/toolchains/${target}-cc.cmake 38 | export CMAKE_TOOLCHAIN_FILE=$toolchain_link 39 | ln -sfr "$toolchain" "$toolchain_link" 40 | 41 | (( $# > 0 )) || set -- "$S"/build-cross.sh "$toolchain_link" 42 | 43 | export CROSS_TARGET=$target 44 | exec nix-shell --run "$*" "$nixfile" 45 | -------------------------------------------------------------------------------- /src/fiber.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fiber_asm.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #if HU_OS_POSIX_P 12 | # include 13 | # include 14 | # if HU_C_11_P 15 | # define ALIGNED_ALLOC aligned_alloc 16 | # elif HU_OS_BSD_P || defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L 17 | # define USE_POSIX_MEMALIGN 1 18 | # else 19 | # include 20 | # define ALIGNED_ALLOC \ 21 | memalign /* we assume that we are able to release the memory using \ 22 | free() */ 23 | # endif 24 | # define ALIGNED_FREE free 25 | #elif HU_OS_WINDOWS_P 26 | # define WIN32_LEAN_AND_MEAN 1 27 | # define VC_EXTRALEAN 1 28 | # define NOMINMAX 1 29 | # define NOGDI 1 30 | # include 31 | # define ALIGNED_ALLOC(algn, sz) _aligned_malloc((sz), (algn)) 32 | # define ALIGNED_FREE _aligned_free 33 | #else 34 | # error "Platform not supported" 35 | #endif 36 | 37 | static const size_t STACK_ALIGNMENT = FIBER_STACK_ALIGNMENT; 38 | 39 | static const size_t ARG_ALIGNMENT = 8; 40 | static const size_t WORD_SIZE = sizeof(void *); 41 | 42 | #if HU_HAVE_NONNULL_PARAMS_P || HU_HAVE_INOUT_NONNULL_P 43 | # define NULL_CHECK(arg, msg) 44 | #else 45 | # define NULL_CHECK(arg, msg) assert(arg &&msg) 46 | #endif 47 | 48 | #define error_abort(msg) \ 49 | do { \ 50 | fprintf(stderr, "%s\n", msg); \ 51 | abort(); \ 52 | } while (0) 53 | 54 | static inline char * 55 | stack_align_n(char *sp, size_t n) 56 | { 57 | return (char *) ((uintptr_t) sp & ~(uintptr_t) (n - 1)); 58 | } 59 | 60 | HU_MAYBE_UNUSED 61 | bool 62 | is_stack_aligned(void *sp) 63 | { 64 | return ((uintptr_t) sp & (STACK_ALIGNMENT - 1)) == 0; 65 | } 66 | 67 | static inline void 68 | push(char **sp, void *val) 69 | { 70 | *sp -= WORD_SIZE; 71 | *(void **) *sp = val; 72 | } 73 | 74 | typedef struct 75 | { 76 | Fiber *fiber; 77 | FiberCleanupFunc cleanup; 78 | void *arg; 79 | } FiberGuardArgs; 80 | 81 | HU_NORETURN 82 | static void 83 | fiber_guard(void *fbr); 84 | 85 | static void 86 | fiber_init_(Fiber *fbr, FiberCleanupFunc cleanup, void *arg) 87 | { 88 | memset(&fbr->regs, 0, sizeof fbr->regs); 89 | uintptr_t sp = 90 | (uintptr_t) ((char *) fbr->stack + fbr->stack_size - WORD_SIZE); 91 | sp &= ~(STACK_ALIGNMENT - 1); 92 | fbr->regs.sp = (void *) sp; 93 | FiberGuardArgs *args; 94 | fiber_reserve_return(fbr, fiber_guard, (void **) &args, sizeof *args); 95 | args->fiber = fbr; 96 | args->cleanup = cleanup; 97 | args->arg = arg; 98 | fbr->state |= FIBER_FS_ALIVE; 99 | } 100 | 101 | Fiber * 102 | fiber_init(Fiber *fbr, 103 | void *stack, 104 | size_t stack_size, 105 | FiberCleanupFunc cleanup, 106 | void *arg) 107 | { 108 | NULL_CHECK(fbr, "Fiber cannot be NULL"); 109 | fbr->stack = stack; 110 | fbr->stack_size = stack_size; 111 | fbr->alloc_stack = NULL; 112 | fbr->state = 0; 113 | fiber_init_(fbr, cleanup, arg); 114 | return fbr; 115 | } 116 | 117 | void 118 | fiber_init_toplevel(Fiber *fbr) 119 | { 120 | NULL_CHECK(fbr, "Fiber cannot be NULL"); 121 | fbr->stack = NULL; 122 | fbr->stack_size = (size_t) -1; 123 | fbr->alloc_stack = NULL; 124 | memset(&fbr->regs, 0, sizeof fbr->regs); 125 | fbr->state = FIBER_FS_ALIVE | FIBER_FS_TOPLEVEL | FIBER_FS_EXECUTING; 126 | } 127 | 128 | static void * 129 | alloc_aligned_chunks(size_t nchunks, size_t align) 130 | { 131 | size_t sz = nchunks * align; 132 | #ifdef ALIGNED_ALLOC 133 | return ALIGNED_ALLOC(align, sz); 134 | #elif defined(USE_POSIX_MEMALIGN) 135 | void *ret; 136 | if (posix_memalign(&ret, align, sz) != 0) 137 | return NULL; 138 | return ret; 139 | #endif 140 | } 141 | 142 | static void 143 | free_pages(void *p) 144 | { 145 | ALIGNED_FREE(p); 146 | } 147 | 148 | HU_CONST_FN 149 | static size_t 150 | get_page_size() 151 | { 152 | static size_t PAGE_SIZE = 0; 153 | size_t pgsz = PAGE_SIZE; 154 | if (hu_likely(pgsz != 0)) 155 | return pgsz; 156 | 157 | #if HU_OS_POSIX_P 158 | pgsz = (size_t) sysconf(_SC_PAGESIZE); 159 | #elif HU_OS_WINDOWS_P 160 | SYSTEM_INFO sysnfo; 161 | GetSystemInfo(&sysnfo); 162 | pgsz = (size_t) sysnfo.dwPageSize; 163 | #endif 164 | PAGE_SIZE = pgsz; 165 | return pgsz; 166 | } 167 | 168 | static bool 169 | protect_page(void *p, bool rw) 170 | { 171 | #if HU_OS_POSIX_P 172 | return mprotect( 173 | p, get_page_size(), rw ? PROT_READ | PROT_WRITE : PROT_NONE) == 0; 174 | #elif HU_OS_WINDOWS_P 175 | DWORD old_protect; 176 | return VirtualProtect(p, 177 | get_page_size(), 178 | rw ? PAGE_READWRITE : PAGE_NOACCESS, 179 | &old_protect) != 0; 180 | #else 181 | # error "BUG: platform not properly handled" 182 | #endif 183 | } 184 | 185 | bool 186 | fiber_alloc(Fiber *fbr, 187 | size_t size, 188 | FiberCleanupFunc cleanup, 189 | void *arg, 190 | FiberFlags flags) 191 | { 192 | NULL_CHECK(fbr, "Fiber cannot be NULL"); 193 | flags &= FIBER_FLAG_GUARD_LO | FIBER_FLAG_GUARD_HI; 194 | fbr->stack_size = size; 195 | const size_t stack_size = size; 196 | 197 | if (!flags) { 198 | fbr->alloc_stack = fbr->stack = malloc(stack_size); 199 | if (!fbr->alloc_stack) 200 | return false; 201 | } else { 202 | size_t pgsz = get_page_size(); 203 | size_t npages = (size + pgsz - 1) / pgsz; 204 | if (flags & FIBER_FLAG_GUARD_LO) 205 | ++npages; 206 | if (flags & FIBER_FLAG_GUARD_HI) 207 | ++npages; 208 | fbr->alloc_stack = alloc_aligned_chunks(npages, pgsz); 209 | if (hu_unlikely(!fbr->alloc_stack)) 210 | return false; 211 | 212 | if (flags & FIBER_FLAG_GUARD_LO) 213 | if (hu_unlikely(!protect_page(fbr->alloc_stack, false))) 214 | goto fail; 215 | 216 | if (flags & FIBER_FLAG_GUARD_HI) 217 | if (hu_unlikely(!protect_page( 218 | (char *) fbr->alloc_stack + (npages - 1) * pgsz, false))) 219 | goto fail; 220 | if (flags & FIBER_FLAG_GUARD_LO) 221 | fbr->stack = (char *) fbr->alloc_stack + pgsz; 222 | else 223 | fbr->stack = fbr->alloc_stack; 224 | } 225 | 226 | fbr->state = flags; 227 | fiber_init_(fbr, cleanup, arg); 228 | return true; 229 | 230 | fail: 231 | free_pages(fbr->alloc_stack); 232 | return false; 233 | } 234 | 235 | void 236 | fiber_destroy(Fiber *fbr) 237 | { 238 | assert(!fiber_is_executing(fbr)); 239 | assert(!fiber_is_toplevel(fbr)); 240 | 241 | if (!fbr->alloc_stack) 242 | return; 243 | 244 | if (fbr->state & 245 | (FIBER_FS_HAS_HI_GUARD_PAGE | FIBER_FS_HAS_LO_GUARD_PAGE)) { 246 | size_t pgsz = get_page_size(); 247 | size_t npages = (fbr->stack_size + pgsz - 1) / pgsz; 248 | if (fbr->state & FIBER_FS_HAS_LO_GUARD_PAGE) { 249 | ++npages; 250 | protect_page(fbr->alloc_stack, true); 251 | } 252 | 253 | if (fbr->state & FIBER_FS_HAS_HI_GUARD_PAGE) { 254 | protect_page((char *) fbr->alloc_stack + npages * pgsz, true); 255 | } 256 | 257 | free_pages(fbr->alloc_stack); 258 | } else { 259 | free(fbr->alloc_stack); 260 | } 261 | 262 | fbr->stack = NULL; 263 | fbr->stack_size = 0; 264 | fbr->regs.sp = NULL; 265 | fbr->alloc_stack = NULL; 266 | } 267 | 268 | void 269 | fiber_switch(Fiber *from, Fiber *to) 270 | { 271 | NULL_CHECK(from, "Fiber cannot be NULL"); 272 | NULL_CHECK(to, "Fiber cannot be NULL"); 273 | 274 | if (from == to) 275 | return; 276 | 277 | assert(fiber_is_executing(from)); 278 | assert(!fiber_is_executing(to)); 279 | assert(fiber_is_alive(to)); 280 | from->state &= ~FIBER_FS_EXECUTING; 281 | to->state |= FIBER_FS_EXECUTING; 282 | fiber_asm_switch(&from->regs, &to->regs); 283 | } 284 | 285 | #if hu_has_attribute(weak) 286 | # define HAVE_probe_stack_weak_dummy 287 | __attribute__((weak)) void 288 | _probe_stack_weak_dummy(volatile char *sp, size_t sz); 289 | 290 | __attribute__((weak)) void 291 | _probe_stack_weak_dummy(volatile char *sp, size_t sz) 292 | { 293 | (void) sp; 294 | (void) sz; 295 | } 296 | #endif 297 | 298 | HU_NOINLINE 299 | static void 300 | probe_stack(volatile char *sp0, size_t sz, size_t pgsz) 301 | { 302 | volatile char *sp = sp0; 303 | #if HU_COMP_GNUC_P 304 | __asm__ __volatile__("" : : "r"(sp) : "memory"); 305 | #endif 306 | size_t i = 0; 307 | while (i < sz) { 308 | *(volatile uintptr_t *) sp |= (uintptr_t) 0; 309 | i += pgsz; 310 | sp -= pgsz; 311 | } 312 | 313 | #ifdef HAVE_probe_stack_weak_dummy 314 | _probe_stack_weak_dummy(sp0, sz); 315 | #endif 316 | } 317 | 318 | void 319 | fiber_reserve_return(Fiber *fbr, 320 | FiberFunc f, 321 | void **args_dest, 322 | size_t args_size) 323 | { 324 | NULL_CHECK(fbr, "Fiber cannot be NULL"); 325 | assert(!fiber_is_executing(fbr)); 326 | 327 | char *sp = hu_cxx_static_cast(char *, fbr->regs.sp); 328 | size_t arg_align = 329 | ARG_ALIGNMENT > STACK_ALIGNMENT ? ARG_ALIGNMENT : STACK_ALIGNMENT; 330 | sp = stack_align_n(sp - args_size, arg_align); 331 | *args_dest = sp; 332 | 333 | size_t pgsz = get_page_size(); 334 | if (hu_unlikely(args_size > pgsz - 100)) 335 | probe_stack(sp, args_size, pgsz); 336 | 337 | assert(is_stack_aligned(sp)); 338 | 339 | push(&sp, fbr->regs.lr); 340 | push(&sp, fbr->regs.sp); 341 | push(&sp, hu_cxx_reinterpret_cast(void *, f)); 342 | push(&sp, *args_dest); 343 | 344 | assert(is_stack_aligned(sp)); 345 | 346 | fbr->regs.lr = hu_cxx_reinterpret_cast(void *, fiber_asm_invoke); 347 | 348 | fbr->regs.sp = (void *) sp; 349 | } 350 | 351 | void 352 | fiber_exec_on(Fiber *active, Fiber *temp, FiberFunc f, void *args) 353 | { 354 | NULL_CHECK(active, "Fiber cannot be NULL"); 355 | NULL_CHECK(temp, "Fiber cannot be NULL"); 356 | assert(fiber_is_executing(active)); 357 | 358 | if (active == temp) { 359 | f(args); 360 | } else { 361 | assert(!fiber_is_executing(temp)); 362 | temp->state |= FIBER_FS_EXECUTING; 363 | active->state &= ~FIBER_FS_EXECUTING; 364 | fiber_asm_exec_on_stack(args, f, temp->regs.sp); 365 | active->state |= FIBER_FS_EXECUTING; 366 | temp->state &= ~FIBER_FS_EXECUTING; 367 | } 368 | } 369 | 370 | size_t 371 | fiber_stack_alignment() 372 | { 373 | return STACK_ALIGNMENT; 374 | } 375 | 376 | static void 377 | fiber_guard(void *argsp) 378 | { 379 | FiberGuardArgs *args = (FiberGuardArgs *) argsp; 380 | args->fiber->state &= ~FIBER_FS_ALIVE; 381 | args->cleanup(args->fiber, args->arg); 382 | error_abort("ERROR: fiber cleanup returned"); 383 | } 384 | 385 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 386 | HU_NORETURN 387 | HU_DSO_HIDDEN 388 | void 389 | fiber_align_check_failed(void) 390 | { 391 | error_abort("ERROR: fiber stack alignment check failed"); 392 | } 393 | #endif 394 | -------------------------------------------------------------------------------- /src/fiber_asm.h: -------------------------------------------------------------------------------- 1 | #ifndef FIBER_ASM_H 2 | #define FIBER_ASM_H 3 | 4 | extern void FIBER_CCONV 5 | fiber_asm_switch(FiberRegs *from, FiberRegs *to); 6 | 7 | /* 8 | * before this function is called, 9 | * an array containing the arguments is written onto the stack 10 | * (padded to fit stack alignment) 11 | * followed by old sp 12 | * followed by pointer to args 13 | * followed by function to call 14 | */ 15 | extern void FIBER_CCONV 16 | fiber_asm_invoke(void); 17 | 18 | extern void FIBER_CCONV 19 | fiber_asm_exec_on_stack(void *, FiberFunc, void *); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/fiber_asm_aarch64_apcs.S: -------------------------------------------------------------------------------- 1 | .altmacro 2 | .arch armv8-a 3 | .text 4 | 5 | #include 6 | 7 | #define CONC0(a, b) a ## b 8 | #define CONC(a, b) CONC0(a, b) 9 | 10 | #if HU_OBJFMT_ELF_P 11 | .macro func_entry sym 12 | .align 4 13 | .global \sym 14 | .hidden \sym 15 | .type \sym, @function 16 | .endm 17 | #define ENTRY(sym) sym 18 | #define END_FUNC(sym) .size ENTRY(sym), .-ENTRY(sym) 19 | #elif HU_OBJFMT_MACHO_P 20 | .macro func_entry sym 21 | .align 4 22 | .global \sym 23 | .endm 24 | #define ENTRY(sym) CONC(_, sym) 25 | #define END_FUNC(sym) 26 | #else 27 | #error "HU_OBJFMT_*_P neither ELF nor MACHO" 28 | #endif 29 | 30 | #define FUNC(sym) func_entry ENTRY(sym) 31 | 32 | .macro check_stack_alignment_nomove reg 33 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 34 | tst \reg, 0xF 35 | bne align_check_failed 36 | #endif 37 | .endm 38 | 39 | .macro check_stack_alignment_move reg 40 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 41 | mov \reg, sp 42 | check_stack_alignment_nomove \reg 43 | #endif 44 | .endm 45 | 46 | FUNC(fiber_asm_switch) 47 | ENTRY(fiber_asm_switch): 48 | mov x3, sp 49 | str x3, [x0], 8 50 | ldr x3, [x1], 8 51 | mov sp, x3 52 | 53 | check_stack_alignment_nomove x3 54 | 55 | stp x30, x29, [x0], 16 56 | ldp x30, x29, [x1], 16 57 | stp x19, x20, [x0], 16 58 | ldp x19, x20, [x1], 16 59 | stp x21, x22, [x0], 16 60 | ldp x21, x22, [x1], 16 61 | stp x23, x24, [x0], 16 62 | ldp x23, x24, [x1], 16 63 | stp x25, x26, [x0], 16 64 | ldp x25, x26, [x1], 16 65 | stp x27, x28, [x0], 16 66 | ldp x27, x28, [x1], 16 67 | stp d8, d9, [x0], 16 68 | ldp d8, d9, [x1], 16 69 | stp d10, d11, [x0], 16 70 | ldp d10, d11, [x1], 16 71 | stp d12, d13, [x0], 16 72 | ldp d12, d13, [x1], 16 73 | stp d14, d15, [x0] 74 | ldp d14, d15, [x1] 75 | 76 | ret 77 | END_FUNC(fiber_asm_switch) 78 | 79 | FUNC(fiber_asm_invoke) 80 | ENTRY(fiber_asm_invoke): 81 | ldp x0, x1, [sp], 16 82 | check_stack_alignment_move x3 83 | blr x1 84 | ldp x3, lr, [sp] 85 | mov sp, x3 86 | check_stack_alignment_nomove x3 87 | ret 88 | END_FUNC(fiber_asm_invoke) 89 | 90 | FUNC(fiber_asm_exec_on_stack) 91 | ENTRY(fiber_asm_exec_on_stack): 92 | mov x3, sp 93 | stp x3, lr, [x2, #-16]! 94 | mov sp, x2 95 | check_stack_alignment_nomove x2 96 | blr x1 97 | ldp x3, lr, [sp] 98 | mov sp, x3 99 | ret 100 | END_FUNC(fiber_asm_exec_on_stack) 101 | 102 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 103 | align_check_failed: 104 | b ENTRY(fiber_align_check_failed) 105 | #endif 106 | -------------------------------------------------------------------------------- /src/fiber_asm_amd64_sysv.S: -------------------------------------------------------------------------------- 1 | .altmacro 2 | .intel_syntax noprefix 3 | .text 4 | 5 | #include 6 | 7 | #define CONC0(a, b) a ## b 8 | #define CONC(a, b) CONC0(a, b) 9 | 10 | #if HU_OBJFMT_ELF_P 11 | #define FUNC(sym) \ 12 | .global sym; \ 13 | .hidden sym; \ 14 | .type sym, @function; \ 15 | sym 16 | #define END_FUNC(sym) .size sym, .-sym 17 | #define ENTRY(sym) sym 18 | #elif HU_OBJFMT_MACHO_P 19 | #define FUNC(sym) \ 20 | .global CONC(_, sym); \ 21 | CONC(_, sym) 22 | #define END_FUNC(sym) 23 | #define ENTRY(sym) CONC(_, sym) 24 | #else 25 | #error "HU_OBJFMT_*_P neither ELF nor MACHO" 26 | #endif 27 | 28 | .macro check_stack_alignment 29 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 30 | test esp, 0xF 31 | jnz .align_check_failed 32 | #endif 33 | .endm 34 | 35 | 36 | FUNC(fiber_asm_switch): 37 | pop rax 38 | .set i, 0 39 | .irp r, rsp, rax, rbp, rbx, r12, r13, r14, r15 40 | mov [rdi + 8 * i], \r 41 | mov \r, [rsi + 8 * i] 42 | .set i, i+1 43 | .endr 44 | jmp rax 45 | END_FUNC(fiber_asm_switch) 46 | 47 | 48 | FUNC(fiber_asm_invoke): 49 | pop rdi 50 | pop rsi 51 | check_stack_alignment 52 | call rsi 53 | mov rax, [rsp + 8] 54 | mov rsp, [rsp] 55 | check_stack_alignment 56 | jmp rax 57 | END_FUNC(fiber_asm_invoke) 58 | 59 | 60 | FUNC(fiber_asm_exec_on_stack): 61 | push rbp 62 | mov rbp, rsp 63 | mov rsp, rdx 64 | check_stack_alignment 65 | call rsi 66 | mov rsp, rbp 67 | pop rbp 68 | ret 69 | END_FUNC(fiber_asm_exec_on_stack) 70 | 71 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 72 | .align_check_failed: 73 | jmp ENTRY(fiber_align_check_failed) 74 | #endif 75 | -------------------------------------------------------------------------------- /src/fiber_asm_amd64_win64.S: -------------------------------------------------------------------------------- 1 | .altmacro 2 | .intel_syntax noprefix 3 | .text 4 | 5 | #define FUNC(sym) \ 6 | .global sym; \ 7 | .def sym; \ 8 | .scl 3; \ 9 | .type 32; \ 10 | .endef; \ 11 | sym 12 | 13 | .macro check_stack_alignment 14 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 15 | test esp, 0xF 16 | jnz align_check_failed 17 | #endif 18 | .endm 19 | 20 | FUNC(fiber_asm_switch): 21 | pop rax 22 | 23 | .set i, 0 24 | .irp r, rsp, rax, rbx, rbp, rdi, rsi, r12, r13, r14, r15 25 | mov [rcx + 8 * i], \r 26 | mov \r, [rdx + 8 * i] 27 | .set i, i+1 28 | .endr 29 | 30 | lea rcx, [rcx + 11 * 8] 31 | and rcx, -16 32 | lea rdx, [rdx + 11 * 8] 33 | and rdx, -16 34 | 35 | .set i, 0 36 | .irp r, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15 37 | movaps [rcx + 16 * i], \r 38 | movaps \r, [rdx + 16 * i] 39 | .set i, i+1 40 | .endr 41 | 42 | jmp rax 43 | 44 | FUNC(fiber_asm_invoke): 45 | mov rcx, [rsp] 46 | mov rdx, [rsp+8] 47 | sub rsp, 32 48 | check_stack_alignment 49 | call rdx 50 | add rsp, 48 51 | mov rax, [rsp + 8] 52 | mov rsp, [rsp] 53 | jmp rax 54 | 55 | FUNC(fiber_asm_exec_on_stack): 56 | push rbp 57 | mov rbp, rsp 58 | lea rsp, [r8 - 32] 59 | check_stack_alignment 60 | call rdx 61 | mov rsp, rbp 62 | pop rbp 63 | ret 64 | 65 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 66 | align_check_failed: 67 | jmp fiber_align_check_failed 68 | #endif 69 | -------------------------------------------------------------------------------- /src/fiber_asm_amd64_win64.asm: -------------------------------------------------------------------------------- 1 | .CODE 2 | 3 | fiber_align_check_failed PROTO C 4 | 5 | 6 | fiber_asm_switch PROC 7 | pop rax 8 | 9 | mov [rcx], rsp 10 | mov rsp, [rdx] 11 | mov [rcx+0x8], rax 12 | mov rax, [rdx+0x8] 13 | mov [rcx+0x10], rbx 14 | mov rbx, [rdx+0x10] 15 | mov [rcx+0x18], rbp 16 | mov rbp, [rdx+0x18] 17 | mov [rcx+0x20], rdi 18 | mov rdi, [rdx+0x20] 19 | mov [rcx+0x28], rsi 20 | mov rsi, [rdx+0x28] 21 | mov [rcx+0x30], r12 22 | mov r12, [rdx+0x30] 23 | mov [rcx+0x38], r13 24 | mov r13, [rdx+0x38] 25 | mov [rcx+0x40], r14 26 | mov r14, [rdx+0x40] 27 | mov [rcx+0x48], r15 28 | mov r15, [rdx+0x48] 29 | 30 | lea rcx, [rcx+0x58] 31 | and rcx, -16 32 | lea rdx, [rdx+0x58] 33 | and rdx, -16 34 | 35 | movaps [rcx], xmm6 36 | movaps xmm6, [rdx] 37 | movaps [rcx+0x10], xmm7 38 | movaps xmm7, [rdx+0x10] 39 | movaps [rcx+0x20], xmm8 40 | movaps xmm8, [rdx+0x20] 41 | movaps [rcx+0x30], xmm9 42 | movaps xmm9, [rdx+0x30] 43 | movaps [rcx+0x40], xmm10 44 | movaps xmm10, [rdx+0x40] 45 | movaps [rcx+0x50], xmm11 46 | movaps xmm11, [rdx+0x50] 47 | movaps [rcx+0x60], xmm12 48 | movaps xmm12, [rdx+0x60] 49 | movaps [rcx+0x70], xmm13 50 | movaps xmm13, [rdx+0x70] 51 | movaps [rcx+0x80], xmm14 52 | movaps xmm14, [rdx+0x80] 53 | movaps [rcx+0x90], xmm15 54 | movaps xmm15, [rdx+0x90] 55 | 56 | jmp rax 57 | fiber_asm_switch ENDP 58 | 59 | 60 | fiber_asm_invoke PROC 61 | mov rcx, [rsp] 62 | mov rdx, [rsp+8] 63 | sub rsp, 32 64 | IFDEF FIBER_ASM_CHECK_ALIGNMENT 65 | test esp, 0Fh 66 | jnz fiber_align_check_failed 67 | ENDIF 68 | call rdx 69 | add rsp, 48 70 | mov rax, [rsp+8] 71 | mov rsp, [rsp] 72 | jmp rax 73 | fiber_asm_invoke ENDP 74 | 75 | 76 | fiber_asm_exec_on_stack PROC 77 | push rbp 78 | mov rbp, rsp 79 | lea rsp, [r8 - 32] 80 | IFDEF FIBER_ASM_CHECK_ALIGNMENT 81 | test esp, 0Fh 82 | jnz fiber_align_check_failed 83 | ENDIF 84 | call rdx 85 | mov rsp, rbp 86 | pop rbp 87 | ret 88 | fiber_asm_exec_on_stack ENDP 89 | 90 | END 91 | -------------------------------------------------------------------------------- /src/fiber_asm_arm32_eabi.S: -------------------------------------------------------------------------------- 1 | .arch armv6 2 | .syntax unified 3 | .arm 4 | .fpu vfp 5 | .text 6 | 7 | #define FUNC(sym) \ 8 | .global sym; \ 9 | .hidden sym; \ 10 | .type sym, %function; \ 11 | .align 2; \ 12 | sym 13 | 14 | #define END_FUNC(sym) .size sym, .-sym 15 | 16 | .macro check_stack_alignment 17 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 18 | tst sp, #7 19 | bne align_check_failed 20 | #endif 21 | .endm 22 | 23 | FUNC(fiber_asm_switch): 24 | stm r0!, {r3-r14} 25 | ldm r1!, {r3-r14} 26 | vstm r0, {d8-d15} 27 | vldm r1, {d8-d15} 28 | check_stack_alignment 29 | bx lr 30 | END_FUNC(fiber_asm_switch) 31 | 32 | FUNC(fiber_asm_invoke): 33 | pop {r0, r1} 34 | check_stack_alignment 35 | blx r1 36 | ldm sp, {r0, lr} 37 | mov sp, r0 38 | check_stack_alignment 39 | bx lr 40 | END_FUNC(fiber_asm_invoke) 41 | 42 | FUNC(fiber_asm_exec_on_stack): 43 | stmdb r2!, {v1, lr} 44 | mov v1, sp 45 | mov sp, r2 46 | check_stack_alignment 47 | blx r1 48 | mov r0, sp 49 | mov sp, v1 50 | ldm r0, {v1, pc} 51 | END_FUNC(fiber_asm_exec_on_stack) 52 | 53 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 54 | align_check_failed: 55 | b fiber_align_check_failed 56 | #endif 57 | -------------------------------------------------------------------------------- /src/fiber_asm_ppc64le_elf2.S: -------------------------------------------------------------------------------- 1 | .altmacro 2 | .machine power8 3 | .abiversion 2 4 | .text 5 | 6 | #define CAT0(a, b) a ## b 7 | #define CAT(a, b) CAT0(a, b) 8 | 9 | #define FUNC_RAW(sym) \ 10 | .hidden sym; \ 11 | .globl sym; \ 12 | .p2align 4; \ 13 | .type sym,@function; \ 14 | sym: 15 | 16 | #define FUNC(sym) \ 17 | FUNC_RAW(sym); \ 18 | CAT(.begin_, sym): ; \ 19 | addis 2,12,.TOC.-CAT(CAT(.begin_, sym), @ha); \ 20 | addi 2,2,.TOC.-CAT(CAT(.begin_, sym), @l); \ 21 | .localentry sym,.-sym 22 | 23 | #define END_FUNC(sym) \ 24 | .long 0; \ 25 | .quad 0; \ 26 | .size sym, .-sym 27 | 28 | .macro check_stack_alignment 29 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 30 | test esp, 0xF 31 | jnz .align_check_failed 32 | #endif 33 | .endm 34 | 35 | FUNC(fiber_asm_switch) 36 | 37 | mfcr 5 38 | stw 5, 0(3) 39 | lwz 5, 0(4) 40 | mtcr 5 41 | 42 | mfvrsave 6 43 | stw 6, 4(3) 44 | lwz 6, 4(4) 45 | mtvrsave 6 46 | 47 | mflr 0 48 | std 0, 8(3) 49 | ld 0, 8(4) 50 | mtlr 0 51 | 52 | std 1, 16(3) 53 | ld 1, 16(4) 54 | 55 | .set i, 0 56 | .rep 18 57 | std 14+i, 24+8*i(3) 58 | ld 14+i, 24+8*i(4) 59 | .set i, i+1 60 | .endr 61 | 62 | .set i, 0 63 | .rep 18 64 | stfd 14+i, 168+8*i(3) 65 | lfd 14+i, 168+8*i(4) 66 | .set i, i+1 67 | .endr 68 | 69 | // 16byte align beginning of vN in r3/r4 70 | addi 3, 3, (36 + 3) * 8 + 15 71 | rldicr 3, 3, 0, 59 72 | addi 4, 4, (36 + 3) * 8 + 15 73 | rldicr 4, 4, 0, 59 74 | 75 | stvx 20, 0, 3 76 | lvx 20, 0, 4 77 | .set i, 1 78 | .rep 11 79 | li 5, i * 16 80 | stvx 20+i, 3, 5 81 | lvx 20+i, 4, 5 82 | .set i, i+1 83 | .endr 84 | 85 | blr 86 | 87 | END_FUNC(fiber_asm_switch) 88 | 89 | FUNC_RAW(fiber_asm_invoke) 90 | ld 3, 0(1) 91 | ld 12, 8(1) 92 | mtctr 12 93 | stdu 1, -48(1) 94 | std 2, 24(1) 95 | bctrl 96 | ld 2, 24(1) 97 | ld 0, 72(1) 98 | mtlr 0 99 | ld 3, 64(1) 100 | mr 1, 3 101 | blr 102 | END_FUNC(fiber_asm_invoke) 103 | 104 | FUNC(fiber_asm_exec_on_stack) 105 | mflr 0 106 | mr 6, 31 107 | mr 12, 4 108 | mtctr 4 109 | mr 31, 1 110 | std 0, 16(1) 111 | mr 1, 5 112 | std 6, -8(1) 113 | stdu 1, -48(1) 114 | std 2, 24(1) 115 | bctrl 116 | ld 2, 24(1) 117 | mr 3, 31 118 | ld 31, 40(1) 119 | mr 1, 3 120 | ld 0,16(1) 121 | mtlr 0 122 | blr 123 | END_FUNC(fiber_asm_exec_on_stack) 124 | 125 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 126 | .align_check_failed: 127 | b fiber_align_check_failed 128 | #endif 129 | -------------------------------------------------------------------------------- /src/fiber_asm_riscv_elf.S: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | .altmacro 4 | 5 | #ifdef __pic__ 6 | .option pic 7 | #else 8 | .option nopic 9 | #endif 10 | 11 | .text 12 | 13 | #if HU_BITS_32_P 14 | # define W 4 15 | # define lx lw 16 | # define sx sw 17 | #else 18 | # define W 8 19 | # define lx ld 20 | # define sx sd 21 | #endif 22 | 23 | #define FUNC(sym) \ 24 | .global sym; \ 25 | .hidden sym; \ 26 | .type sym, @function; \ 27 | .align 4; \ 28 | sym 29 | #define END_FUNC(sym) .size sym, .-sym 30 | 31 | .macro check_stack_alignment_nomove reg 32 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 33 | andi \reg, \reg, FIBER_STACK_ALIGNMENT - 1 34 | bnez \reg, align_check_failed 35 | #endif 36 | .endm 37 | 38 | .macro check_stack_alignment_move reg 39 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 40 | mv \reg, sp 41 | check_stack_alignment_nomove \reg 42 | #endif 43 | .endm 44 | 45 | FUNC(fiber_asm_switch): 46 | 47 | sx sp, 0(a0) 48 | lx sp, 0(a1) 49 | sx ra, W(a0) 50 | lx ra, W(a1) 51 | 52 | .macro restore_s n 53 | sx s\n, 2*W+W*\n(a0) 54 | lx s\n, 2*W+W*\n(a1) 55 | .endm 56 | 57 | .set i, 0 58 | .rept 12 59 | restore_s %i 60 | .set i,i+1 61 | .endr 62 | 63 | .macro restore_fs n 64 | fsd fs\n, 14*W+8*\n(a0) 65 | fld fs\n, 14*W+8*\n(a1) 66 | .endm 67 | 68 | .set i, 0 69 | .rept 12 70 | restore_fs %i 71 | .set i,i+1 72 | .endr 73 | 74 | check_stack_alignment_move t1 75 | 76 | ret 77 | END_FUNC(fiber_asm_switch) 78 | 79 | FUNC(fiber_asm_invoke): 80 | lx a0, 0(sp) 81 | lx a1, W(sp) 82 | check_stack_alignment_move t1 83 | jalr a1 84 | lx ra, 3*W(sp) 85 | lx sp, 2*W(sp) 86 | check_stack_alignment_move t1 87 | ret 88 | END_FUNC(fiber_asm_invoke) 89 | 90 | FUNC(fiber_asm_exec_on_stack): 91 | addi sp, sp, -16 92 | sx ra, 0(sp) 93 | sx s0, W(sp) 94 | mv s0, sp 95 | mv sp, a2 96 | check_stack_alignment_move t1 97 | jalr a1 98 | mv sp, s0 99 | lx ra, 0(s0) 100 | lx s0, W(s0) 101 | addi sp, sp, 16 102 | ret 103 | END_FUNC(fiber_asm_exec_on_stack) 104 | 105 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 106 | align_check_failed: 107 | j fiber_align_check_failed 108 | #endif 109 | -------------------------------------------------------------------------------- /src/fiber_asm_x86_cdecl.S: -------------------------------------------------------------------------------- 1 | .altmacro 2 | .intel_syntax noprefix 3 | .text 4 | 5 | #include 6 | 7 | #ifdef FIBER_STACK_ALIGNMENT 8 | #define STACK_ALIGNMENT FIBER_STACK_ALIGNMENT 9 | #else 10 | #define STACK_ALIGNMENT FIBER_DEFAULT_STACK_ALIGNMENT 11 | #endif 12 | 13 | #define CONC0(a, b) a ## b 14 | #define CONC(a, b) CONC0(a, b) 15 | 16 | #if HU_OS_WINDOWS_P 17 | # define ENTRY(sym) CONC(_, sym) 18 | # define FUNC(sym) \ 19 | .global ENTRY(sym); \ 20 | .def ENTRY(sym); \ 21 | .scl 2; \ 22 | .type 32; \ 23 | .endef; \ 24 | ENTRY(sym) 25 | # define END_FUNC(sym) 26 | 27 | #else 28 | # define ENTRY(sym) sym 29 | # define FUNC(sym) \ 30 | .global sym; \ 31 | .hidden sym; \ 32 | .type sym, @function; \ 33 | sym 34 | 35 | # define END_FUNC(sym) .size sym, .-sym 36 | 37 | #endif 38 | 39 | .macro check_stack_alignment 40 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 41 | test esp, STACK_ALIGNMENT - 1 42 | jnz .align_check_failed 43 | #endif 44 | .endm 45 | 46 | FUNC(fiber_asm_switch): 47 | pop eax 48 | mov edx, [esp] 49 | mov ecx, [esp+4] 50 | .set i, 0 51 | .irp r, esp, eax, ebp, ebx, edi, esi 52 | mov [edx+4*i], \r 53 | mov \r, [ecx+4*i] 54 | .set i, i+1 55 | .endr 56 | jmp eax 57 | END_FUNC(fiber_asm_switch) 58 | 59 | FUNC(fiber_asm_invoke): 60 | mov eax, [esp+4] 61 | check_stack_alignment 62 | call eax 63 | mov eax, [esp+12] 64 | mov esp, [esp+8] 65 | jmp eax 66 | END_FUNC(fiber_asm_invoke) 67 | 68 | FUNC(fiber_asm_exec_on_stack): 69 | mov eax, esp 70 | mov edx, [eax+4] 71 | mov ecx, [eax+8] 72 | mov esp, [eax+12] 73 | sub esp, 16 74 | mov [esp+4], eax 75 | mov [esp], edx 76 | check_stack_alignment 77 | call ecx 78 | mov esp, [esp+4] 79 | ret 80 | END_FUNC(fiber_asm_exec_on_stack) 81 | 82 | #ifdef FIBER_ASM_CHECK_ALIGNMENT 83 | .align_check_failed: 84 | jmp ENTRY(fiber_align_check_failed) 85 | #endif 86 | -------------------------------------------------------------------------------- /src/fiber_asm_x86_win32.asm: -------------------------------------------------------------------------------- 1 | .MODEL flat, C 2 | .CODE 3 | 4 | PUBLIC fiber_asm_switch 5 | PUBLIC fiber_asm_invoke 6 | PUBLIC fiber_asm_exec_on_stack 7 | 8 | fiber_asm_switch PROC 9 | pop eax 10 | mov edx, [esp] 11 | mov ecx, [esp+4] 12 | mov [edx], esp 13 | mov esp, [ecx] 14 | mov [edx+4], eax 15 | mov eax, [ecx+4] 16 | mov [edx+8], ebp 17 | mov ebp, [ecx+8] 18 | mov [edx+12], ebx 19 | mov ebx, [ecx+12] 20 | mov [edx+16], edi 21 | mov edi, [ecx+16] 22 | mov [edx+20], esi 23 | mov esi, [ecx+20] 24 | jmp eax 25 | fiber_asm_switch ENDP 26 | 27 | fiber_asm_invoke PROC 28 | mov eax, [esp+4] 29 | call eax 30 | mov eax, [esp+12] 31 | mov esp, [esp+8] 32 | jmp eax 33 | fiber_asm_invoke ENDP 34 | 35 | fiber_asm_exec_on_stack PROC 36 | mov eax, esp 37 | mov edx, [eax+4] 38 | mov ecx, [eax+8] 39 | mov esp, [eax+12] 40 | sub esp, 16 41 | mov [esp+4], eax 42 | mov [esp], edx 43 | call ecx 44 | mov esp, [esp+4] 45 | ret 46 | fiber_asm_exec_on_stack ENDP 47 | 48 | END 49 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | macro(add_test_exec tgt) 2 | add_executable(${tgt} ${ARGN}) 3 | target_link_libraries(${tgt} fiber) 4 | target_compile_options(${tgt} PRIVATE ${cflags}) 5 | if(CMU_COMP_MSVC) 6 | target_compile_definitions(${tgt} PRIVATE _CRT_SECURE_NO_WARNINGS=1) 7 | endif() 8 | if(FIBER_LTO) 9 | set_property(TARGET ${tgt} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) 10 | endif() 11 | endmacro() 12 | 13 | macro(add_test_run tgt) 14 | add_test_exec("${tgt}_exec" ${ARGN}) 15 | 16 | add_test( 17 | NAME "${tgt}_run" 18 | COMMAND "${tgt}_exec" "test_${tgt}.out" 19 | ) 20 | 21 | add_test( 22 | "${tgt}_cmp" 23 | ${CMAKE_COMMAND} -E compare_files "test_${tgt}.out" "${CMAKE_CURRENT_SOURCE_DIR}/test_${tgt}.out" 24 | ) 25 | 26 | add_test( 27 | "${tgt}_build" 28 | "${CMAKE_COMMAND}" 29 | --build "${CMAKE_BINARY_DIR}" 30 | --config $ 31 | --target "${tgt}_exec" 32 | ) 33 | 34 | set_tests_properties("${tgt}_build" PROPERTIES FIXTURES_SETUP "${tgt}_build_fixture") 35 | set_tests_properties("${tgt}_run" PROPERTIES FIXTURES_REQUIRED "${tgt}_build_fixture") 36 | set_tests_properties("${tgt}_run" PROPERTIES FIXTURES_SETUP "${tgt}_run_fixture") 37 | # set_tests_properties("${tgt}_run" PROPERTIES ENVIRONMENT PATH="$") 38 | set_tests_properties("${tgt}_cmp" PROPERTIES FIXTURES_REQUIRED "${tgt}_run_fixture") 39 | endmacro() 40 | 41 | add_test_exec(hello hello.c) 42 | 43 | add_test_run(basic basic.c) 44 | add_test_run(coop coop.c) 45 | add_test_run(generators generators.c) 46 | add_test_run(fp_stress fp_stress.c) 47 | -------------------------------------------------------------------------------- /test/basic.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "test_pre.h" 4 | 5 | #include 6 | #include 7 | 8 | #define STACK_SIZE ((size_t) 1024 * 16) 9 | 10 | static void 11 | fiber_cleanup(Fiber *fiber, void *args) 12 | { 13 | (void) fiber; 14 | (void) args; 15 | abort(); 16 | } 17 | 18 | typedef struct 19 | { 20 | Fiber *self; 21 | Fiber *caller; 22 | } FiberArgs; 23 | 24 | static void 25 | run_put_str(void *arg) 26 | { 27 | println((const char *) arg); 28 | } 29 | 30 | static void 31 | put_str(Fiber *active, Fiber *at, const char *arg) 32 | { 33 | fiber_exec_on(active, at, run_put_str, (void *) arg); 34 | } 35 | 36 | static void 37 | fiber_entry(void *argsp) 38 | { 39 | FiberArgs *args = (FiberArgs *) argsp; 40 | println("fiber_entry()"); 41 | fiber_switch(args->self, args->caller); 42 | 43 | put_str(args->self, args->caller, "some string"); 44 | put_str(args->self, args->caller, "some other string"); 45 | 46 | println("again fiber_entry()"); 47 | char *msg; 48 | size_t sz = 64 * 1024; 49 | fiber_reserve_return(args->caller, run_put_str, (void **) &msg, sz); 50 | memset(msg, 0, sz); 51 | strcpy(msg, "Pushed onto toplevel fiber"); 52 | fiber_switch(args->self, args->caller); 53 | } 54 | 55 | int 56 | main(int argc, char *argv[]) 57 | { 58 | test_main_begin(&argc, &argv); 59 | Fiber toplevel; 60 | fiber_init_toplevel(&toplevel); 61 | Fiber fiber; 62 | (void) fiber_alloc(&fiber, 63 | STACK_SIZE, 64 | fiber_cleanup, 65 | NULL, 66 | FIBER_FLAG_GUARD_LO | FIBER_FLAG_GUARD_HI); 67 | FiberArgs *args; 68 | fiber_reserve_return(&fiber, fiber_entry, (void **) &args, sizeof *args); 69 | args->self = &fiber; 70 | args->caller = &toplevel; 71 | fiber_switch(&toplevel, &fiber); 72 | require(fiber_stack_used_size(&fiber) < 1024); 73 | require(fiber_stack_free_size(&fiber) > STACK_SIZE - 1024); 74 | require(fiber_stack_free_size(&fiber) < STACK_SIZE); 75 | println("in main()"); 76 | fiber_switch(&toplevel, &fiber); 77 | fiber_destroy(&fiber); 78 | 79 | test_main_end(); 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /test/coop.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "test_pre.h" 4 | 5 | #include 6 | #include 7 | 8 | #define STACK_SIZE ((size_t) 16 * 1024) 9 | 10 | typedef struct Thread Thread; 11 | 12 | typedef struct Sched Sched; 13 | 14 | struct Thread 15 | { 16 | Sched *sched; 17 | Thread *prev, *next; 18 | Fiber fiber; 19 | int id; 20 | }; 21 | 22 | struct Sched 23 | { 24 | Thread main_thread; 25 | Thread *running; 26 | Thread *done; /* only next ptr used */ 27 | size_t fuel; 28 | int next_id; 29 | bool shutdown_signal; 30 | }; 31 | 32 | typedef struct 33 | { 34 | Thread *thread; 35 | void (*entry)(void); 36 | } ThreadArgs; 37 | 38 | typedef struct 39 | { 40 | Thread *thread; 41 | } CleanupArgs; 42 | 43 | static Thread *the_thread; 44 | 45 | static bool 46 | shutting_down(void) 47 | { 48 | return the_thread->sched->shutdown_signal; 49 | } 50 | 51 | static Fiber * 52 | thread_fiber(Thread *th) 53 | { 54 | return &th->fiber; 55 | } 56 | 57 | static void 58 | thread_switch(Thread *from, Thread *to) 59 | { 60 | Sched *sched = from->sched; 61 | require(sched->running == from); 62 | require(the_thread == from); 63 | 64 | if (sched->fuel > 0) 65 | --sched->fuel; 66 | 67 | sched->running = to; 68 | the_thread = to; 69 | fiber_switch(thread_fiber(from), thread_fiber(to)); 70 | } 71 | 72 | static void 73 | yield(void) 74 | { 75 | thread_switch(the_thread, the_thread->next); 76 | } 77 | 78 | static void 79 | thread_exec(void *args0) 80 | { 81 | ThreadArgs *args = (ThreadArgs *) args0; 82 | the_thread = args->thread; 83 | args->entry(); 84 | } 85 | 86 | static void 87 | thread_cleanup_cont(void *args0) 88 | { 89 | CleanupArgs *args = (CleanupArgs *) args0; 90 | fprintf(out, "[Scheduler] thread exited: %d\n", args->thread->id); 91 | fiber_destroy(&args->thread->fiber); 92 | free(args->thread); 93 | } 94 | 95 | static void 96 | thread_guard(Fiber *self, void *arg) 97 | { 98 | Thread *th = (Thread *) arg; 99 | require(the_thread); 100 | require(th == the_thread); 101 | require(self == &th->fiber); 102 | (void) self; 103 | 104 | Sched *sched = th->sched; 105 | Thread *sched_thread = &sched->main_thread; 106 | CleanupArgs *args; 107 | 108 | fiber_reserve_return( 109 | &sched_thread->fiber, thread_cleanup_cont, (void **) &args, sizeof args); 110 | args->thread = th; 111 | 112 | Thread *next = th->next; 113 | // unlink ourself 114 | th->prev->next = th->next; 115 | th->next->prev = th->prev; 116 | thread_switch(th, next); 117 | // does not return here 118 | abort(); 119 | } 120 | 121 | static void 122 | thread_start(Sched *sched, void (*func)(void)) 123 | { 124 | Thread *th = hu_cxx_static_cast(Thread *, malloc(sizeof *th)); 125 | th->sched = sched; 126 | th->id = sched->next_id++; 127 | (void) fiber_alloc( 128 | &th->fiber, STACK_SIZE, thread_guard, th, FIBER_FLAG_GUARD_LO); 129 | 130 | ThreadArgs args; 131 | args.thread = th; 132 | args.entry = func; 133 | fiber_push_return(&th->fiber, thread_exec, &args, sizeof args); 134 | 135 | th->next = sched->running->next; 136 | th->prev = sched->running; 137 | th->next->prev = th; 138 | th->prev->next = th; 139 | } 140 | 141 | static void 142 | sched_init(Sched *s) 143 | { 144 | s->main_thread.prev = &s->main_thread; 145 | s->main_thread.next = &s->main_thread; 146 | s->main_thread.sched = s; 147 | fiber_init_toplevel(&s->main_thread.fiber); 148 | 149 | s->running = &s->main_thread; 150 | s->done = 0; 151 | s->next_id = 1; 152 | s->shutdown_signal = false; 153 | } 154 | 155 | static void 156 | run_put_str(void *arg) 157 | { 158 | const char *str = *(const char **) arg; 159 | println(str); 160 | } 161 | 162 | static void 163 | put_str(const char *str) 164 | { 165 | fiber_exec_on(thread_fiber(the_thread), 166 | &the_thread->sched->main_thread.fiber, 167 | run_put_str, 168 | (void **) &str); 169 | } 170 | 171 | typedef enum Message 172 | { 173 | MsgNone, 174 | MsgPing, 175 | MsgFork, 176 | } Message; 177 | 178 | static int next_worker_id; 179 | static Message message; 180 | 181 | static void 182 | worker(void) 183 | { 184 | int my_id = next_worker_id++; 185 | int work = 13 + ((my_id * 17) % 11); 186 | unsigned h = 42; 187 | fprintf(out, "[Worker %d] started, work=%d\n", my_id, work); 188 | while (!shutting_down() && work-- > 0) { 189 | h ^= (unsigned) work; 190 | h = (h << 13) | (h >> 19); 191 | h *= 1337; 192 | yield(); 193 | } 194 | fprintf(out, "[Worker %d] exiting, result: %u\n", my_id, h); 195 | } 196 | 197 | static void 198 | thread1(void) 199 | { 200 | put_str("[Thread 1] started"); 201 | int tok = 0; 202 | while (!shutting_down()) { 203 | char str[64]; 204 | snprintf(str, sizeof str, "[Thread 1] running: %d", tok++); 205 | put_str(str); 206 | if (tok % 30 == 0) 207 | message = MsgFork; 208 | else if (tok % 6 == 0) 209 | message = MsgPing; 210 | yield(); 211 | } 212 | put_str("[Thread 1] exiting"); 213 | } 214 | 215 | static void 216 | thread2(void) 217 | { 218 | put_str("[Thread 2] started"); 219 | int tok = 0; 220 | while (!shutting_down()) { 221 | switch (message) { 222 | case MsgNone: 223 | break; 224 | case MsgPing: 225 | fprintf(out, "[Thread 2] received ping: %d\n", tok); 226 | message = MsgNone; 227 | break; 228 | case MsgFork: 229 | fprintf(out, "[Thread 2] forking worker: %d\n", tok); 230 | thread_start(the_thread->sched, worker); 231 | message = MsgNone; 232 | break; 233 | } 234 | char str[64]; 235 | snprintf(str, sizeof str, "[Thread 2] running: %d", tok++); 236 | put_str(str); 237 | yield(); 238 | } 239 | put_str("[Thread 2] exiting"); 240 | } 241 | 242 | static void 243 | thread3(void) 244 | { 245 | put_str("[Thread 3] started"); 246 | int tok = 0; 247 | int rounds = 10; 248 | while (!shutting_down() && rounds-- > 0) { 249 | char str[64]; 250 | snprintf(str, sizeof str, "[Thread 3] running: %d", tok++); 251 | put_str(str); 252 | yield(); 253 | } 254 | 255 | put_str("[Thread 3] exiting"); 256 | } 257 | 258 | static void 259 | execute(Sched *sched) 260 | { 261 | the_thread = &sched->main_thread; 262 | while (sched->running->next != &sched->main_thread) { 263 | yield(); 264 | 265 | if (sched->fuel == 0 && !sched->shutdown_signal) { 266 | sched->shutdown_signal = true; 267 | println("[Scheduler] sending shutdown signal"); 268 | } 269 | } 270 | 271 | println("[Scheduler] all threads exited"); 272 | the_thread = NULL; 273 | } 274 | 275 | int 276 | main(int argc, char *argv[]) 277 | { 278 | test_main_begin(&argc, &argv); 279 | Sched s; 280 | sched_init(&s); 281 | s.fuel = 1000; 282 | 283 | thread_start(&s, thread1); 284 | thread_start(&s, thread2); 285 | thread_start(&s, thread3); 286 | 287 | execute(&s); 288 | 289 | test_main_end(); 290 | return 0; 291 | } 292 | -------------------------------------------------------------------------------- /test/fp_stress.c: -------------------------------------------------------------------------------- 1 | #include "fiber/fiber.h" 2 | 3 | #include "test_pre.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | double A[20]; 10 | 11 | #define CAT0(a, b) a##b 12 | #define CAT(a, b) CAT0(a, b) 13 | 14 | #define TMP(a) CAT(CAT(tmp_, a), __LINE__) 15 | 16 | #define STR0(a) #a 17 | #define STR(a) STR0(a) 18 | 19 | #define DEF_A(i, j, k, l) \ 20 | double CAT(a, i) = A[i] + args->id; \ 21 | double CAT(a, j) = A[j] + 3 * args->id; \ 22 | double CAT(a, k) = A[k] - 1 * args->id; \ 23 | double CAT(a, l) = A[l] - 3 * args->id 24 | 25 | #define STORE(i, j, k, l) \ 26 | A[i] = CAT(a, i); \ 27 | A[j] = CAT(a, j); \ 28 | A[k] = CAT(a, k); \ 29 | A[l] = CAT(a, l) 30 | 31 | #define CLAMP(x, y, l) \ 32 | while (x * x + y * y > l) { \ 33 | double TMP(x) = x; \ 34 | double TMP(y) = y; \ 35 | x = (TMP(x) + TMP(y)) * (1. / 17); \ 36 | y = (TMP(x) - TMP(y)) * (1. / 18); \ 37 | } 38 | 39 | #define MIX_(a, b, c, d) \ 40 | do { \ 41 | a = b * c - 31 * d + 1; \ 42 | b = a * b - c * d * 2 + 1; \ 43 | c = d * d + a * 3 - 1; \ 44 | d = a + b - c - 11 * d - 1; \ 45 | CLAMP(a, b, 24); \ 46 | CLAMP(c, d, 27); \ 47 | CLAMP(a, d, 29); \ 48 | CLAMP(b, c, 31); \ 49 | } while (0) 50 | 51 | #define MIX(i, j, k, l) MIX_(CAT(a, i), CAT(a, j), CAT(a, k), CAT(a, l)) 52 | 53 | #define PRINT1(i) fprintf(out, "a%02d=%+.13le ", i, A[i]) 54 | 55 | #define PRINT(i, j, k, l) \ 56 | PRINT1(i); \ 57 | PRINT1(j); \ 58 | PRINT1(k); \ 59 | PRINT1(l); \ 60 | println("") 61 | 62 | #define MAP(F) \ 63 | do { \ 64 | F(0, 1, 2, 3); \ 65 | F(4, 5, 6, 7); \ 66 | F(8, 9, 10, 11); \ 67 | F(12, 13, 14, 15); \ 68 | F(16, 17, 18, 19); \ 69 | } while (0) 70 | 71 | #define STORE_ALL MAP(STORE) 72 | 73 | typedef struct 74 | { 75 | Fiber *self; 76 | Fiber *caller; 77 | int n; 78 | int id; 79 | bool done; 80 | } Args; 81 | 82 | static void 83 | print(int id) 84 | { 85 | fprintf(out, "Fiber[%d]\n", id); 86 | MAP(PRINT); 87 | println(""); 88 | } 89 | 90 | static void 91 | entry(void *args0) 92 | { 93 | Args *args = (Args *) args0; 94 | DEF_A(0, 1, 2, 3); 95 | DEF_A(4, 5, 6, 7); 96 | DEF_A(8, 9, 10, 11); 97 | DEF_A(12, 13, 14, 15); 98 | DEF_A(16, 17, 18, 19); 99 | 100 | for (int i = 0; i < args->n; ++i) { 101 | for (int j = 0; j < 16; ++j) { 102 | if (j & 1) { 103 | MIX(0, 2, 4, 6); 104 | MIX(1, 3, 5, 7); 105 | } else { 106 | MIX(8, 10, 12, 14); 107 | MIX(9, 11, 13, 15); 108 | } 109 | if (a2 > 0 || a11 < 0) { 110 | MIX(0, 16, 3, 17); 111 | MIX(12, 18, 15, 19); 112 | } 113 | } 114 | fiber_switch(args->self, args->caller); 115 | if (i % 50 == 0) { 116 | STORE_ALL; 117 | print(args->id); 118 | } 119 | } 120 | 121 | STORE_ALL; 122 | print(args->id); 123 | args->done = true; 124 | fiber_switch(args->self, args->caller); 125 | } 126 | 127 | static void 128 | guard(Fiber *fiber, void *null) 129 | { 130 | (void) fiber; 131 | (void) null; 132 | abort(); 133 | } 134 | 135 | static void 136 | setup_fiber(Fiber *caller, Fiber *fiber, Args **args, int id) 137 | { 138 | (void) fiber_alloc(fiber, 16 * 1024, guard, NULL, FIBER_FLAG_GUARD_LO); 139 | fiber_reserve_return(fiber, entry, (void **) args, sizeof *args); 140 | (*args)->self = fiber; 141 | (*args)->caller = caller; 142 | (*args)->n = 256; 143 | (*args)->id = id; 144 | (*args)->done = false; 145 | } 146 | 147 | int 148 | main(int argc, char *argv[]) 149 | { 150 | test_main_begin(&argc, &argv); 151 | #if HU_OS_WINDOWS_P && defined(_TWO_DIGIT_EXPONENT) 152 | _set_output_format(_TWO_DIGIT_EXPONENT); 153 | #endif 154 | Fiber toplevel; 155 | fiber_init_toplevel(&toplevel); 156 | Fiber fiber1; 157 | Args *args1; 158 | setup_fiber(&toplevel, &fiber1, &args1, 1); 159 | Fiber fiber2; 160 | Args *args2; 161 | setup_fiber(&toplevel, &fiber2, &args2, 2); 162 | 163 | while (!args1->done || !args2->done) { 164 | if (!args1->done) 165 | fiber_switch(&toplevel, &fiber1); 166 | if (!args2->done) 167 | fiber_switch(&toplevel, &fiber2); 168 | } 169 | fiber_destroy(&fiber1); 170 | fiber_destroy(&fiber2); 171 | test_main_end(); 172 | return 0; 173 | } 174 | -------------------------------------------------------------------------------- /test/generators.c: -------------------------------------------------------------------------------- 1 | #define __STDC_FORMAT_MACROS 1 2 | 3 | #include 4 | 5 | #include "test_pre.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define STACK_SIZE ((size_t) 16 * 1024) 14 | 15 | typedef union 16 | { 17 | uint32_t u32; 18 | uint64_t u64; 19 | int32_t i32; 20 | int64_t i64; 21 | float f32; 22 | double f64; 23 | void *ptr; 24 | } Value; 25 | 26 | typedef struct 27 | { 28 | Fiber fiber; 29 | Value result; 30 | const char *name; 31 | int id; 32 | bool done; 33 | bool should_finish; 34 | } Generator; 35 | 36 | typedef struct 37 | { 38 | void (*entry)(void *); 39 | void *args; 40 | } GeneratorArgs; 41 | 42 | typedef struct 43 | { 44 | Fiber *fiber; 45 | Fiber *caller; 46 | Generator *gen; 47 | } Context; 48 | 49 | static Context active_context; 50 | static int gen_next_id; 51 | 52 | static void 53 | zero_value(Value *v) 54 | { 55 | memset(v, 0, sizeof *v); 56 | } 57 | 58 | static void 59 | fiber_guard(Fiber *self, void *null) 60 | { 61 | (void) null; 62 | fprintf(stderr, "fiber_guard(fiber=%p) called, aborting\n", self); 63 | abort(); 64 | } 65 | 66 | static void 67 | gen_start(void *args0) 68 | { 69 | GeneratorArgs *args = (GeneratorArgs *) args0; 70 | Generator *gen = active_context.gen; 71 | require(gen); 72 | fprintf(out, "[Generator[%d] %s] STARTING\n", gen->id, gen->name); 73 | args->entry(args->args); 74 | gen->done = true; 75 | fprintf(out, "[Generator[%d] %s] FINISH\n", gen->id, gen->name); 76 | free(args->args); 77 | fiber_switch(active_context.fiber, active_context.caller); 78 | // noreturn 79 | abort(); 80 | } 81 | 82 | static Generator * 83 | gen_new(const char *name, void (*f)(void *), void *args, size_t args_size) 84 | { 85 | Generator *gen = hu_cxx_static_cast(Generator *, calloc(1, sizeof *gen)); 86 | zero_value(&gen->result); 87 | gen->name = name; 88 | gen->id = gen_next_id++; 89 | gen->done = false; 90 | gen->should_finish = false; 91 | 92 | GeneratorArgs gen_args; 93 | gen_args.entry = f; 94 | gen_args.args = calloc(1, args_size); 95 | memcpy(gen_args.args, args, args_size); 96 | 97 | (void) fiber_alloc( 98 | &gen->fiber, STACK_SIZE, fiber_guard, NULL, FIBER_FLAG_GUARD_LO); 99 | fiber_push_return(&gen->fiber, gen_start, &gen_args, sizeof gen_args); 100 | return gen; 101 | } 102 | 103 | static void 104 | gen_init_toplevel(Fiber *fiber) 105 | { 106 | require(!active_context.fiber); 107 | fiber_init_toplevel(fiber); 108 | active_context.fiber = fiber; 109 | active_context.gen = NULL; 110 | active_context.caller = NULL; 111 | } 112 | 113 | static bool 114 | gen_next(Generator *gen, Value *v) 115 | { 116 | zero_value(v); 117 | if (gen->done) 118 | return false; 119 | Context saved = active_context; 120 | active_context.fiber = &gen->fiber; 121 | active_context.gen = gen; 122 | active_context.caller = saved.fiber; 123 | fiber_switch(saved.fiber, &gen->fiber); 124 | active_context = saved; 125 | if (gen->done) 126 | return false; 127 | *v = gen->result; 128 | return true; 129 | } 130 | 131 | static void 132 | gen_close(Generator *gen) 133 | { 134 | if (!gen->done) { 135 | require(!gen->should_finish); 136 | gen->should_finish = true; 137 | Value v; 138 | gen_next(gen, &v); 139 | require(gen->done); 140 | } 141 | fiber_destroy(&gen->fiber); 142 | free(gen); 143 | } 144 | 145 | static void 146 | gen_yield(const Value *v) 147 | { 148 | Context cur = active_context; 149 | active_context.fiber = NULL; 150 | active_context.gen = NULL; 151 | active_context.caller = NULL; 152 | cur.gen->result = *v; 153 | fprintf(out, "[Generator[%d] %s] YIELD\n", cur.gen->id, cur.gen->name); 154 | fiber_switch(cur.fiber, cur.caller); 155 | } 156 | 157 | static bool 158 | gen_should_finish() 159 | { 160 | require(active_context.gen); 161 | return active_context.gen->should_finish; 162 | } 163 | 164 | typedef struct 165 | { 166 | Generator *source; 167 | int n; 168 | } TakeGenArgs; 169 | 170 | static void 171 | take_gen(void *args0) 172 | { 173 | TakeGenArgs *args = (TakeGenArgs *) args0; 174 | while (args->n-- > 0 && !gen_should_finish()) { 175 | Value v; 176 | if (!gen_next(args->source, &v)) 177 | break; 178 | gen_yield(&v); 179 | } 180 | gen_close(args->source); 181 | } 182 | 183 | static Generator * 184 | gen_take(int n, Generator *source) 185 | { 186 | TakeGenArgs args; 187 | args.source = source; 188 | args.n = n; 189 | return gen_new("gen_take", take_gen, &args, sizeof args); 190 | } 191 | 192 | static void 193 | fib_gen(void *null) 194 | { 195 | (void) null; 196 | uint64_t f0 = 0; 197 | uint64_t f1 = 1; 198 | Value v; 199 | v.u64 = f0; 200 | gen_yield(&v); 201 | while (!gen_should_finish()) { 202 | v.u64 = f1; 203 | gen_yield(&v); 204 | uint64_t tmp = f0; 205 | f0 = f1; 206 | f1 += tmp; 207 | } 208 | } 209 | 210 | static Generator * 211 | gen_fibs() 212 | { 213 | return gen_new("gen_fibs", fib_gen, NULL, 0); 214 | } 215 | 216 | typedef struct 217 | { 218 | Generator *source; 219 | bool (*pred)(const Value *); 220 | } FilterGenArgs; 221 | 222 | static void 223 | filter_gen(void *args0) 224 | { 225 | FilterGenArgs *args = (FilterGenArgs *) args0; 226 | while (!gen_should_finish()) { 227 | Value v; 228 | if (!gen_next(args->source, &v)) 229 | break; 230 | if (args->pred(&v)) 231 | gen_yield(&v); 232 | } 233 | gen_close(args->source); 234 | } 235 | 236 | static Generator * 237 | gen_filter(bool (*pred)(const Value *), Generator *source) 238 | { 239 | FilterGenArgs args; 240 | args.source = source; 241 | args.pred = pred; 242 | return gen_new("gen_filter", filter_gen, &args, sizeof args); 243 | } 244 | 245 | static bool 246 | is_odd_u64(const Value *v) 247 | { 248 | return (v->u64 & 1) != 0; 249 | } 250 | 251 | int 252 | main(int argc, char *argv[]) 253 | { 254 | test_main_begin(&argc, &argv); 255 | Fiber toplevel; 256 | gen_init_toplevel(&toplevel); 257 | Generator *gen = gen_take(20, gen_filter(is_odd_u64, gen_fibs())); 258 | Value v; 259 | while (gen_next(gen, &v)) 260 | fprintf(out, "[Main] value: %" PRIu64 "\n", v.u64); 261 | gen_close(gen); 262 | test_main_end(); 263 | return 0; 264 | } 265 | -------------------------------------------------------------------------------- /test/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef struct 7 | { 8 | Fiber *caller; 9 | Fiber *self; 10 | const char *str; 11 | } Args; 12 | 13 | /* invoked if coroutine is called after it finished */ 14 | void 15 | guard(Fiber *self, void *null) 16 | { 17 | (void) self; 18 | (void) null; 19 | abort(); 20 | } 21 | 22 | /* entry point */ 23 | void 24 | coroutine(void *args0) 25 | { 26 | Args *args = (Args *) args0; 27 | printf("From coroutine: %s\n", args->str); 28 | fiber_switch(args->self, args->caller); 29 | } 30 | 31 | int 32 | main() 33 | { 34 | Fiber main_fiber; 35 | fiber_init_toplevel(&main_fiber); 36 | Fiber crt; 37 | /* allocate a stack of 16kb, additionally add an unmapped page to detect 38 | * overflows */ 39 | (void) fiber_alloc(&crt, 1024 * 16, guard, NULL, FIBER_FLAG_GUARD_LO); 40 | Args args; 41 | args.caller = &main_fiber; 42 | args.self = &crt; 43 | args.str = "Hello"; 44 | /* push a new return stack frame, defines the entry point of our coroutine 45 | */ 46 | fiber_push_return(&crt, coroutine, &args, sizeof args); 47 | /* arguments are copied into the coroutine, its safe to destroy them here */ 48 | memset(&args, 0, sizeof args); 49 | /* run our coroutine */ 50 | fiber_switch(&main_fiber, &crt); 51 | fiber_destroy(&crt); 52 | /* main_fiber does not have to be destroyed */ 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /test/test_basic.out: -------------------------------------------------------------------------------- 1 | fiber_entry() 2 | in main() 3 | some string 4 | some other string 5 | again fiber_entry() 6 | Pushed onto toplevel fiber 7 | -------------------------------------------------------------------------------- /test/test_coop.out: -------------------------------------------------------------------------------- 1 | [Thread 3] started 2 | [Thread 3] running: 0 3 | [Thread 2] started 4 | [Thread 2] running: 0 5 | [Thread 1] started 6 | [Thread 1] running: 0 7 | [Thread 3] running: 1 8 | [Thread 2] running: 1 9 | [Thread 1] running: 1 10 | [Thread 3] running: 2 11 | [Thread 2] running: 2 12 | [Thread 1] running: 2 13 | [Thread 3] running: 3 14 | [Thread 2] running: 3 15 | [Thread 1] running: 3 16 | [Thread 3] running: 4 17 | [Thread 2] running: 4 18 | [Thread 1] running: 4 19 | [Thread 3] running: 5 20 | [Thread 2] running: 5 21 | [Thread 1] running: 5 22 | [Thread 3] running: 6 23 | [Thread 2] received ping: 6 24 | [Thread 2] running: 6 25 | [Thread 1] running: 6 26 | [Thread 3] running: 7 27 | [Thread 2] running: 7 28 | [Thread 1] running: 7 29 | [Thread 3] running: 8 30 | [Thread 2] running: 8 31 | [Thread 1] running: 8 32 | [Thread 3] running: 9 33 | [Thread 2] running: 9 34 | [Thread 1] running: 9 35 | [Thread 3] exiting 36 | [Thread 2] running: 10 37 | [Thread 1] running: 10 38 | [Scheduler] thread exited: 3 39 | [Thread 2] running: 11 40 | [Thread 1] running: 11 41 | [Thread 2] received ping: 12 42 | [Thread 2] running: 12 43 | [Thread 1] running: 12 44 | [Thread 2] running: 13 45 | [Thread 1] running: 13 46 | [Thread 2] running: 14 47 | [Thread 1] running: 14 48 | [Thread 2] running: 15 49 | [Thread 1] running: 15 50 | [Thread 2] running: 16 51 | [Thread 1] running: 16 52 | [Thread 2] running: 17 53 | [Thread 1] running: 17 54 | [Thread 2] received ping: 18 55 | [Thread 2] running: 18 56 | [Thread 1] running: 18 57 | [Thread 2] running: 19 58 | [Thread 1] running: 19 59 | [Thread 2] running: 20 60 | [Thread 1] running: 20 61 | [Thread 2] running: 21 62 | [Thread 1] running: 21 63 | [Thread 2] running: 22 64 | [Thread 1] running: 22 65 | [Thread 2] running: 23 66 | [Thread 1] running: 23 67 | [Thread 2] received ping: 24 68 | [Thread 2] running: 24 69 | [Thread 1] running: 24 70 | [Thread 2] running: 25 71 | [Thread 1] running: 25 72 | [Thread 2] running: 26 73 | [Thread 1] running: 26 74 | [Thread 2] running: 27 75 | [Thread 1] running: 27 76 | [Thread 2] running: 28 77 | [Thread 1] running: 28 78 | [Thread 2] running: 29 79 | [Thread 1] running: 29 80 | [Thread 2] forking worker: 30 81 | [Thread 2] running: 30 82 | [Worker 0] started, work=13 83 | [Thread 1] running: 30 84 | [Thread 2] running: 31 85 | [Thread 1] running: 31 86 | [Thread 2] running: 32 87 | [Thread 1] running: 32 88 | [Thread 2] running: 33 89 | [Thread 1] running: 33 90 | [Thread 2] running: 34 91 | [Thread 1] running: 34 92 | [Thread 2] running: 35 93 | [Thread 1] running: 35 94 | [Thread 2] received ping: 36 95 | [Thread 2] running: 36 96 | [Thread 1] running: 36 97 | [Thread 2] running: 37 98 | [Thread 1] running: 37 99 | [Thread 2] running: 38 100 | [Thread 1] running: 38 101 | [Thread 2] running: 39 102 | [Thread 1] running: 39 103 | [Thread 2] running: 40 104 | [Thread 1] running: 40 105 | [Thread 2] running: 41 106 | [Thread 1] running: 41 107 | [Thread 2] received ping: 42 108 | [Thread 2] running: 42 109 | [Thread 1] running: 42 110 | [Thread 2] running: 43 111 | [Worker 0] exiting, result: 1247736706 112 | [Thread 1] running: 43 113 | [Scheduler] thread exited: 4 114 | [Thread 2] running: 44 115 | [Thread 1] running: 44 116 | [Thread 2] running: 45 117 | [Thread 1] running: 45 118 | [Thread 2] running: 46 119 | [Thread 1] running: 46 120 | [Thread 2] running: 47 121 | [Thread 1] running: 47 122 | [Thread 2] received ping: 48 123 | [Thread 2] running: 48 124 | [Thread 1] running: 48 125 | [Thread 2] running: 49 126 | [Thread 1] running: 49 127 | [Thread 2] running: 50 128 | [Thread 1] running: 50 129 | [Thread 2] running: 51 130 | [Thread 1] running: 51 131 | [Thread 2] running: 52 132 | [Thread 1] running: 52 133 | [Thread 2] running: 53 134 | [Thread 1] running: 53 135 | [Thread 2] received ping: 54 136 | [Thread 2] running: 54 137 | [Thread 1] running: 54 138 | [Thread 2] running: 55 139 | [Thread 1] running: 55 140 | [Thread 2] running: 56 141 | [Thread 1] running: 56 142 | [Thread 2] running: 57 143 | [Thread 1] running: 57 144 | [Thread 2] running: 58 145 | [Thread 1] running: 58 146 | [Thread 2] running: 59 147 | [Thread 1] running: 59 148 | [Thread 2] forking worker: 60 149 | [Thread 2] running: 60 150 | [Worker 1] started, work=19 151 | [Thread 1] running: 60 152 | [Thread 2] running: 61 153 | [Thread 1] running: 61 154 | [Thread 2] running: 62 155 | [Thread 1] running: 62 156 | [Thread 2] running: 63 157 | [Thread 1] running: 63 158 | [Thread 2] running: 64 159 | [Thread 1] running: 64 160 | [Thread 2] running: 65 161 | [Thread 1] running: 65 162 | [Thread 2] received ping: 66 163 | [Thread 2] running: 66 164 | [Thread 1] running: 66 165 | [Thread 2] running: 67 166 | [Thread 1] running: 67 167 | [Thread 2] running: 68 168 | [Thread 1] running: 68 169 | [Thread 2] running: 69 170 | [Thread 1] running: 69 171 | [Thread 2] running: 70 172 | [Thread 1] running: 70 173 | [Thread 2] running: 71 174 | [Thread 1] running: 71 175 | [Thread 2] received ping: 72 176 | [Thread 2] running: 72 177 | [Thread 1] running: 72 178 | [Thread 2] running: 73 179 | [Thread 1] running: 73 180 | [Thread 2] running: 74 181 | [Thread 1] running: 74 182 | [Thread 2] running: 75 183 | [Thread 1] running: 75 184 | [Thread 2] running: 76 185 | [Thread 1] running: 76 186 | [Thread 2] running: 77 187 | [Thread 1] running: 77 188 | [Thread 2] received ping: 78 189 | [Thread 2] running: 78 190 | [Thread 1] running: 78 191 | [Thread 2] running: 79 192 | [Worker 1] exiting, result: 2814585027 193 | [Thread 1] running: 79 194 | [Scheduler] thread exited: 5 195 | [Thread 2] running: 80 196 | [Thread 1] running: 80 197 | [Thread 2] running: 81 198 | [Thread 1] running: 81 199 | [Thread 2] running: 82 200 | [Thread 1] running: 82 201 | [Thread 2] running: 83 202 | [Thread 1] running: 83 203 | [Thread 2] received ping: 84 204 | [Thread 2] running: 84 205 | [Thread 1] running: 84 206 | [Thread 2] running: 85 207 | [Thread 1] running: 85 208 | [Thread 2] running: 86 209 | [Thread 1] running: 86 210 | [Thread 2] running: 87 211 | [Thread 1] running: 87 212 | [Thread 2] running: 88 213 | [Thread 1] running: 88 214 | [Thread 2] running: 89 215 | [Thread 1] running: 89 216 | [Thread 2] forking worker: 90 217 | [Thread 2] running: 90 218 | [Worker 2] started, work=14 219 | [Thread 1] running: 90 220 | [Thread 2] running: 91 221 | [Thread 1] running: 91 222 | [Thread 2] running: 92 223 | [Thread 1] running: 92 224 | [Thread 2] running: 93 225 | [Thread 1] running: 93 226 | [Thread 2] running: 94 227 | [Thread 1] running: 94 228 | [Thread 2] running: 95 229 | [Thread 1] running: 95 230 | [Thread 2] received ping: 96 231 | [Thread 2] running: 96 232 | [Thread 1] running: 96 233 | [Thread 2] running: 97 234 | [Thread 1] running: 97 235 | [Thread 2] running: 98 236 | [Thread 1] running: 98 237 | [Thread 2] running: 99 238 | [Thread 1] running: 99 239 | [Thread 2] running: 100 240 | [Thread 1] running: 100 241 | [Thread 2] running: 101 242 | [Thread 1] running: 101 243 | [Thread 2] received ping: 102 244 | [Thread 2] running: 102 245 | [Thread 1] running: 102 246 | [Thread 2] running: 103 247 | [Thread 1] running: 103 248 | [Thread 2] running: 104 249 | [Worker 2] exiting, result: 918515341 250 | [Thread 1] running: 104 251 | [Scheduler] thread exited: 6 252 | [Thread 2] running: 105 253 | [Thread 1] running: 105 254 | [Thread 2] running: 106 255 | [Thread 1] running: 106 256 | [Thread 2] running: 107 257 | [Thread 1] running: 107 258 | [Thread 2] received ping: 108 259 | [Thread 2] running: 108 260 | [Thread 1] running: 108 261 | [Thread 2] running: 109 262 | [Thread 1] running: 109 263 | [Thread 2] running: 110 264 | [Thread 1] running: 110 265 | [Thread 2] running: 111 266 | [Thread 1] running: 111 267 | [Thread 2] running: 112 268 | [Thread 1] running: 112 269 | [Thread 2] running: 113 270 | [Thread 1] running: 113 271 | [Thread 2] received ping: 114 272 | [Thread 2] running: 114 273 | [Thread 1] running: 114 274 | [Thread 2] running: 115 275 | [Thread 1] running: 115 276 | [Thread 2] running: 116 277 | [Thread 1] running: 116 278 | [Thread 2] running: 117 279 | [Thread 1] running: 117 280 | [Thread 2] running: 118 281 | [Thread 1] running: 118 282 | [Thread 2] running: 119 283 | [Thread 1] running: 119 284 | [Thread 2] forking worker: 120 285 | [Thread 2] running: 120 286 | [Worker 3] started, work=20 287 | [Thread 1] running: 120 288 | [Thread 2] running: 121 289 | [Thread 1] running: 121 290 | [Thread 2] running: 122 291 | [Thread 1] running: 122 292 | [Thread 2] running: 123 293 | [Thread 1] running: 123 294 | [Thread 2] running: 124 295 | [Thread 1] running: 124 296 | [Thread 2] running: 125 297 | [Thread 1] running: 125 298 | [Thread 2] received ping: 126 299 | [Thread 2] running: 126 300 | [Thread 1] running: 126 301 | [Thread 2] running: 127 302 | [Thread 1] running: 127 303 | [Thread 2] running: 128 304 | [Thread 1] running: 128 305 | [Thread 2] running: 129 306 | [Thread 1] running: 129 307 | [Thread 2] running: 130 308 | [Thread 1] running: 130 309 | [Thread 2] running: 131 310 | [Thread 1] running: 131 311 | [Thread 2] received ping: 132 312 | [Thread 2] running: 132 313 | [Thread 1] running: 132 314 | [Thread 2] running: 133 315 | [Thread 1] running: 133 316 | [Thread 2] running: 134 317 | [Thread 1] running: 134 318 | [Thread 2] running: 135 319 | [Thread 1] running: 135 320 | [Thread 2] running: 136 321 | [Thread 1] running: 136 322 | [Thread 2] running: 137 323 | [Thread 1] running: 137 324 | [Thread 2] received ping: 138 325 | [Thread 2] running: 138 326 | [Thread 1] running: 138 327 | [Thread 2] running: 139 328 | [Thread 1] running: 139 329 | [Thread 2] running: 140 330 | [Worker 3] exiting, result: 3554181924 331 | [Thread 1] running: 140 332 | [Scheduler] thread exited: 7 333 | [Thread 2] running: 141 334 | [Thread 1] running: 141 335 | [Thread 2] running: 142 336 | [Thread 1] running: 142 337 | [Thread 2] running: 143 338 | [Thread 1] running: 143 339 | [Thread 2] received ping: 144 340 | [Thread 2] running: 144 341 | [Thread 1] running: 144 342 | [Thread 2] running: 145 343 | [Thread 1] running: 145 344 | [Thread 2] running: 146 345 | [Thread 1] running: 146 346 | [Thread 2] running: 147 347 | [Thread 1] running: 147 348 | [Thread 2] running: 148 349 | [Thread 1] running: 148 350 | [Thread 2] running: 149 351 | [Thread 1] running: 149 352 | [Thread 2] forking worker: 150 353 | [Thread 2] running: 150 354 | [Worker 4] started, work=15 355 | [Thread 1] running: 150 356 | [Thread 2] running: 151 357 | [Thread 1] running: 151 358 | [Thread 2] running: 152 359 | [Thread 1] running: 152 360 | [Thread 2] running: 153 361 | [Thread 1] running: 153 362 | [Thread 2] running: 154 363 | [Thread 1] running: 154 364 | [Thread 2] running: 155 365 | [Thread 1] running: 155 366 | [Thread 2] received ping: 156 367 | [Thread 2] running: 156 368 | [Thread 1] running: 156 369 | [Thread 2] running: 157 370 | [Thread 1] running: 157 371 | [Thread 2] running: 158 372 | [Thread 1] running: 158 373 | [Thread 2] running: 159 374 | [Thread 1] running: 159 375 | [Thread 2] running: 160 376 | [Thread 1] running: 160 377 | [Thread 2] running: 161 378 | [Thread 1] running: 161 379 | [Thread 2] received ping: 162 380 | [Thread 2] running: 162 381 | [Thread 1] running: 162 382 | [Thread 2] running: 163 383 | [Thread 1] running: 163 384 | [Thread 2] running: 164 385 | [Thread 1] running: 164 386 | [Thread 2] running: 165 387 | [Worker 4] exiting, result: 1383045683 388 | [Thread 1] running: 165 389 | [Scheduler] thread exited: 8 390 | [Thread 2] running: 166 391 | [Thread 1] running: 166 392 | [Thread 2] running: 167 393 | [Thread 1] running: 167 394 | [Thread 2] received ping: 168 395 | [Thread 2] running: 168 396 | [Thread 1] running: 168 397 | [Thread 2] running: 169 398 | [Thread 1] running: 169 399 | [Thread 2] running: 170 400 | [Thread 1] running: 170 401 | [Thread 2] running: 171 402 | [Thread 1] running: 171 403 | [Thread 2] running: 172 404 | [Thread 1] running: 172 405 | [Thread 2] running: 173 406 | [Thread 1] running: 173 407 | [Thread 2] received ping: 174 408 | [Thread 2] running: 174 409 | [Thread 1] running: 174 410 | [Thread 2] running: 175 411 | [Thread 1] running: 175 412 | [Thread 2] running: 176 413 | [Thread 1] running: 176 414 | [Thread 2] running: 177 415 | [Thread 1] running: 177 416 | [Thread 2] running: 178 417 | [Thread 1] running: 178 418 | [Thread 2] running: 179 419 | [Thread 1] running: 179 420 | [Thread 2] forking worker: 180 421 | [Thread 2] running: 180 422 | [Worker 5] started, work=21 423 | [Thread 1] running: 180 424 | [Thread 2] running: 181 425 | [Thread 1] running: 181 426 | [Thread 2] running: 182 427 | [Thread 1] running: 182 428 | [Thread 2] running: 183 429 | [Thread 1] running: 183 430 | [Thread 2] running: 184 431 | [Thread 1] running: 184 432 | [Thread 2] running: 185 433 | [Thread 1] running: 185 434 | [Thread 2] received ping: 186 435 | [Thread 2] running: 186 436 | [Thread 1] running: 186 437 | [Thread 2] running: 187 438 | [Thread 1] running: 187 439 | [Thread 2] running: 188 440 | [Thread 1] running: 188 441 | [Thread 2] running: 189 442 | [Thread 1] running: 189 443 | [Thread 2] running: 190 444 | [Thread 1] running: 190 445 | [Thread 2] running: 191 446 | [Thread 1] running: 191 447 | [Thread 2] received ping: 192 448 | [Thread 2] running: 192 449 | [Thread 1] running: 192 450 | [Thread 2] running: 193 451 | [Thread 1] running: 193 452 | [Thread 2] running: 194 453 | [Thread 1] running: 194 454 | [Thread 2] running: 195 455 | [Thread 1] running: 195 456 | [Thread 2] running: 196 457 | [Thread 1] running: 196 458 | [Thread 2] running: 197 459 | [Thread 1] running: 197 460 | [Thread 2] received ping: 198 461 | [Thread 2] running: 198 462 | [Thread 1] running: 198 463 | [Thread 2] running: 199 464 | [Thread 1] running: 199 465 | [Thread 2] running: 200 466 | [Thread 1] running: 200 467 | [Thread 2] running: 201 468 | [Worker 5] exiting, result: 238580104 469 | [Thread 1] running: 201 470 | [Scheduler] thread exited: 9 471 | [Thread 2] running: 202 472 | [Thread 1] running: 202 473 | [Thread 2] running: 203 474 | [Thread 1] running: 203 475 | [Thread 2] received ping: 204 476 | [Thread 2] running: 204 477 | [Thread 1] running: 204 478 | [Thread 2] running: 205 479 | [Thread 1] running: 205 480 | [Thread 2] running: 206 481 | [Thread 1] running: 206 482 | [Thread 2] running: 207 483 | [Thread 1] running: 207 484 | [Thread 2] running: 208 485 | [Thread 1] running: 208 486 | [Thread 2] running: 209 487 | [Thread 1] running: 209 488 | [Thread 2] forking worker: 210 489 | [Thread 2] running: 210 490 | [Worker 6] started, work=16 491 | [Thread 1] running: 210 492 | [Thread 2] running: 211 493 | [Thread 1] running: 211 494 | [Thread 2] running: 212 495 | [Thread 1] running: 212 496 | [Thread 2] running: 213 497 | [Thread 1] running: 213 498 | [Thread 2] running: 214 499 | [Thread 1] running: 214 500 | [Thread 2] running: 215 501 | [Thread 1] running: 215 502 | [Thread 2] received ping: 216 503 | [Thread 2] running: 216 504 | [Thread 1] running: 216 505 | [Thread 2] running: 217 506 | [Thread 1] running: 217 507 | [Thread 2] running: 218 508 | [Thread 1] running: 218 509 | [Thread 2] running: 219 510 | [Thread 1] running: 219 511 | [Thread 2] running: 220 512 | [Thread 1] running: 220 513 | [Thread 2] running: 221 514 | [Thread 1] running: 221 515 | [Thread 2] received ping: 222 516 | [Thread 2] running: 222 517 | [Thread 1] running: 222 518 | [Thread 2] running: 223 519 | [Thread 1] running: 223 520 | [Thread 2] running: 224 521 | [Thread 1] running: 224 522 | [Thread 2] running: 225 523 | [Thread 1] running: 225 524 | [Thread 2] running: 226 525 | [Worker 6] exiting, result: 942786308 526 | [Thread 1] running: 226 527 | [Scheduler] thread exited: 10 528 | [Thread 2] running: 227 529 | [Thread 1] running: 227 530 | [Thread 2] received ping: 228 531 | [Thread 2] running: 228 532 | [Thread 1] running: 228 533 | [Thread 2] running: 229 534 | [Thread 1] running: 229 535 | [Thread 2] running: 230 536 | [Thread 1] running: 230 537 | [Thread 2] running: 231 538 | [Thread 1] running: 231 539 | [Thread 2] running: 232 540 | [Thread 1] running: 232 541 | [Thread 2] running: 233 542 | [Thread 1] running: 233 543 | [Thread 2] received ping: 234 544 | [Thread 2] running: 234 545 | [Thread 1] running: 234 546 | [Thread 2] running: 235 547 | [Thread 1] running: 235 548 | [Thread 2] running: 236 549 | [Thread 1] running: 236 550 | [Thread 2] running: 237 551 | [Thread 1] running: 237 552 | [Thread 2] running: 238 553 | [Thread 1] running: 238 554 | [Thread 2] running: 239 555 | [Thread 1] running: 239 556 | [Thread 2] forking worker: 240 557 | [Thread 2] running: 240 558 | [Worker 7] started, work=22 559 | [Thread 1] running: 240 560 | [Thread 2] running: 241 561 | [Thread 1] running: 241 562 | [Thread 2] running: 242 563 | [Thread 1] running: 242 564 | [Thread 2] running: 243 565 | [Thread 1] running: 243 566 | [Thread 2] running: 244 567 | [Thread 1] running: 244 568 | [Thread 2] running: 245 569 | [Thread 1] running: 245 570 | [Thread 2] received ping: 246 571 | [Thread 2] running: 246 572 | [Thread 1] running: 246 573 | [Thread 2] running: 247 574 | [Thread 1] running: 247 575 | [Thread 2] running: 248 576 | [Thread 1] running: 248 577 | [Thread 2] running: 249 578 | [Thread 1] running: 249 579 | [Thread 2] running: 250 580 | [Thread 1] running: 250 581 | [Thread 2] running: 251 582 | [Thread 1] running: 251 583 | [Thread 2] received ping: 252 584 | [Thread 2] running: 252 585 | [Thread 1] running: 252 586 | [Thread 2] running: 253 587 | [Thread 1] running: 253 588 | [Thread 2] running: 254 589 | [Thread 1] running: 254 590 | [Thread 2] running: 255 591 | [Thread 1] running: 255 592 | [Thread 2] running: 256 593 | [Thread 1] running: 256 594 | [Thread 2] running: 257 595 | [Thread 1] running: 257 596 | [Thread 2] received ping: 258 597 | [Thread 2] running: 258 598 | [Thread 1] running: 258 599 | [Thread 2] running: 259 600 | [Thread 1] running: 259 601 | [Thread 2] running: 260 602 | [Thread 1] running: 260 603 | [Thread 2] running: 261 604 | [Thread 1] running: 261 605 | [Thread 2] running: 262 606 | [Worker 7] exiting, result: 602671278 607 | [Thread 1] running: 262 608 | [Scheduler] thread exited: 11 609 | [Thread 2] running: 263 610 | [Thread 1] running: 263 611 | [Thread 2] received ping: 264 612 | [Thread 2] running: 264 613 | [Thread 1] running: 264 614 | [Thread 2] running: 265 615 | [Thread 1] running: 265 616 | [Thread 2] running: 266 617 | [Thread 1] running: 266 618 | [Thread 2] running: 267 619 | [Thread 1] running: 267 620 | [Thread 2] running: 268 621 | [Thread 1] running: 268 622 | [Thread 2] running: 269 623 | [Thread 1] running: 269 624 | [Thread 2] forking worker: 270 625 | [Thread 2] running: 270 626 | [Worker 8] started, work=17 627 | [Thread 1] running: 270 628 | [Thread 2] running: 271 629 | [Thread 1] running: 271 630 | [Thread 2] running: 272 631 | [Thread 1] running: 272 632 | [Thread 2] running: 273 633 | [Thread 1] running: 273 634 | [Thread 2] running: 274 635 | [Thread 1] running: 274 636 | [Thread 2] running: 275 637 | [Thread 1] running: 275 638 | [Thread 2] received ping: 276 639 | [Thread 2] running: 276 640 | [Thread 1] running: 276 641 | [Thread 2] running: 277 642 | [Thread 1] running: 277 643 | [Scheduler] sending shutdown signal 644 | [Thread 2] exiting 645 | [Worker 8] exiting, result: 2510412744 646 | [Thread 1] exiting 647 | [Scheduler] thread exited: 1 648 | [Scheduler] thread exited: 12 649 | [Scheduler] thread exited: 2 650 | [Scheduler] all threads exited 651 | -------------------------------------------------------------------------------- /test/test_fp_stress.out: -------------------------------------------------------------------------------- 1 | Fiber[1] 2 | a00=-1.4183337503078e+00 a01=+3.3012991003809e-01 a02=+3.8863200488179e+00 a03=-1.8566306507936e+00 3 | a04=+1.0993470248247e+00 a05=+3.9242934222167e-01 a06=-6.6082008625271e-01 a07=+1.8969074274507e+00 4 | a08=+6.3507206082653e-01 a09=+6.8360930822675e-01 a10=-7.9725987974116e-01 a11=-1.1586429655269e+00 5 | a12=+2.9660546332121e+00 a13=+2.1564586171471e+00 a14=-1.8529410049181e+00 a15=+7.7492660211691e-01 6 | a16=-5.4554831010582e-01 a17=-3.9746689439630e+00 a18=+1.5046307710106e+00 a19=-3.2198289750003e-01 7 | 8 | Fiber[2] 9 | a00=+1.6431791828042e-01 a01=+3.0226679086707e+00 a02=-1.2595868613307e-01 a03=+4.4745289534712e-01 10 | a04=+1.4685009012437e+00 a05=+4.7305480371018e-01 a06=-8.9564034082860e-01 a07=-6.3913661615753e-02 11 | a08=-1.7278731210418e+00 a09=-7.3506262532255e-01 a10=-2.2112742252276e+00 a11=-1.9657754800493e+00 12 | a12=+7.9894019451881e-01 a13=-4.7618067226914e-01 a14=+3.6564651554849e-01 a15=+4.8120879417644e-01 13 | a16=+5.8376703613797e-01 a17=+3.6023247338933e-01 a18=-1.8025371652555e+00 a19=-3.6917443287100e+00 14 | 15 | Fiber[1] 16 | a00=+5.1516686950262e-01 a01=-3.9003241121611e+00 a02=+1.5212456003685e+00 a03=+1.5950666826292e+00 17 | a04=+5.2309429867134e-01 a05=-4.8071933087461e-01 a06=-2.7425322564638e-01 a07=-9.5396319001649e-01 18 | a08=+2.0376939152589e-01 a09=+1.9584852992634e-01 a10=-8.1174543592319e-01 a11=+6.7747919031481e-01 19 | a12=+8.3717757405739e-01 a13=+5.3442216804073e-01 a14=-1.9584116072482e+00 a15=+2.6018145414595e+00 20 | a16=-6.7124222559116e-01 a17=-1.5194100268513e+00 a18=-4.6224444315013e-01 a19=-1.8434762427418e+00 21 | 22 | Fiber[2] 23 | a00=-1.2174570670512e+00 a01=-1.9766362284717e+00 a02=-3.3652532949209e+00 a03=-1.5582775158419e+00 24 | a04=-2.4350001062664e-01 a05=-4.8335575945146e-01 a06=+4.2312031495057e-01 a07=+1.7395925199281e-01 25 | a08=-2.5113198196973e-01 a09=-4.1211015413661e-01 a10=+5.0089860306528e-01 a11=-1.4244463287610e+00 26 | a12=+7.7437287038884e-01 a13=-8.2603736816863e-01 a14=+8.8868874923182e-01 a15=+2.3975930949172e+00 27 | a16=-3.0433553987224e-01 a17=-2.9807691737668e+00 a18=-4.4005739882394e-01 a19=-1.7121491382903e+00 28 | 29 | Fiber[1] 30 | a00=+1.1568268637311e+00 a01=+4.0982844100475e+00 a02=-4.6588410084974e-01 a03=+8.3377044923364e-01 31 | a04=+2.3242600493840e+00 a05=+5.2599166025901e-01 a06=-1.6989256499242e+00 a07=+5.3153577194205e-03 32 | a08=+8.7377596536880e-03 a09=-1.8494232790216e+00 a10=-7.0114644995318e-01 a11=-1.1238612556936e+00 33 | a12=-4.3617347797324e-01 a13=-5.1624243722745e-01 a14=-2.0246871664402e+00 a15=-6.4023204250674e-01 34 | a16=+3.5700131320328e+00 a17=-5.7154741037274e-01 a18=-2.3199326631503e-01 a19=-1.4326544282471e+00 35 | 36 | Fiber[2] 37 | a00=-1.7100463706555e+00 a01=-3.6171688677133e-01 a02=+4.6095456347347e-01 a03=-8.9924708577559e-01 38 | a04=-3.0045765452867e-01 a05=-1.0242948754582e+00 a06=+6.0230625363589e-01 a07=-3.5300931040990e-02 39 | a08=-4.7874952197391e-01 a09=-5.2105026329238e-01 a10=+7.1346861129060e-01 a11=-1.5210641820805e-01 40 | a12=-1.2146724629747e+00 a13=-1.4958335096245e+00 a14=+1.4501301924707e+00 a15=-1.6249974607298e+00 41 | a16=-3.4778619045776e+00 a17=+5.4374640605965e-01 a18=-6.2613992293244e-01 a19=-3.8635943303935e+00 42 | 43 | Fiber[1] 44 | a00=+1.9036384707807e+00 a01=-9.8045069477337e-01 a02=-1.1867151885657e-01 a03=+2.0596290305410e+00 45 | a04=+1.3914952765779e+00 a05=-1.3017742774688e+00 a06=-8.4755623625370e-01 a07=-2.8290592804317e+00 46 | a08=-1.7517398798217e+00 a09=+3.7468776534059e-01 a10=-9.4445498694288e-01 a11=-5.2325721616015e-01 47 | a12=+1.6930592036662e+00 a13=+1.1523514178327e+00 a14=+1.8654298125063e-01 a15=+1.8349150538075e+00 48 | a16=-6.3183900360129e-01 a17=+1.4540550512094e+00 a18=-2.9776637682595e-01 a19=+2.0703029904931e+00 49 | 50 | Fiber[2] 51 | a00=+2.5688688897435e+00 a01=-1.2032680123060e+00 a02=-9.2369495925888e-01 a03=-6.3301981662607e-01 52 | a04=+2.7439613108326e+00 a05=-1.6076649849242e+00 a06=+1.8192676192765e+00 a07=-3.8693875900363e+00 53 | a08=+5.2120933858942e-01 a09=+2.4730946005258e+00 a10=-4.8870008516678e-01 a11=+1.2889653521659e+00 54 | a12=-1.0521105033944e-01 a13=+6.5013956418271e-01 a14=-1.3375194305513e+00 a15=-3.4306944217184e-01 55 | a16=+1.7961620104541e+00 a17=-2.8440350482019e-01 a18=+4.7415081919775e-01 a19=+6.4608224876092e-01 56 | 57 | Fiber[1] 58 | a00=+8.4777040096849e-01 a01=+2.7732324880744e+00 a02=+2.0603818401352e+00 a03=+1.0072923875993e+00 59 | a04=+6.1341192432690e-01 a05=+2.9956416762563e+00 a06=-3.5318403153133e-01 a07=+2.6353590507790e+00 60 | a08=-3.9047809574499e-01 a09=+2.4651423374061e-01 a10=+4.5543698438618e-01 a11=-4.4953604191502e-01 61 | a12=+1.6671844916708e+00 a13=+7.5720016676078e-01 a14=+1.0326876611070e+00 a15=+5.6034752910917e-01 62 | a16=+4.8180961257815e+00 a17=-7.4993291563296e-01 a18=+1.5683000896730e+00 a19=-2.8680956857234e-01 63 | 64 | Fiber[2] 65 | a00=+1.6685391416837e+00 a01=-3.4073500854890e-01 a02=+1.6166510954748e+00 a03=+6.1222076745178e-01 66 | a04=+5.1218919922076e-01 a05=-9.6623722612115e-01 a06=-2.8304030446707e-01 a07=-4.9693027913416e-02 67 | a08=+1.7344935290216e+00 a09=-7.7526420733033e-01 a10=+2.3326597678762e+00 a11=+4.5026854069397e+00 68 | a12=-9.6475582778821e-02 a13=+6.6548067187716e-01 a14=-4.0432746210331e-01 a15=-3.1520451149881e-01 69 | a16=+1.8548019940905e+00 a17=-3.3016099988786e-01 a18=+4.3799997045815e-01 a19=+5.9166710282564e-01 70 | 71 | Fiber[1] 72 | a00=+2.0038760438639e+00 a01=+5.0132968634486e-01 a02=-3.2604298328705e+00 a03=-7.4996701024439e-01 73 | a04=-2.4116182119913e-01 a05=+1.5537753415390e+00 a06=+4.1084458042706e-01 a07=-1.5833155893043e+00 74 | a08=+3.8090651216452e-01 a09=-8.8354590057734e-03 a10=-1.4599578333222e-01 a11=+1.2264479919837e+00 75 | a12=+1.1487967099091e+00 a13=+1.2488592231136e-01 a14=-7.4724331440979e-01 a15=+3.4350898657268e+00 76 | a16=+1.8237592794072e-01 a17=+8.4626397583974e-01 a18=+4.5355132834414e-01 a19=+5.9392685025999e-01 77 | 78 | Fiber[2] 79 | a00=+1.3820364450915e+00 a01=+3.0567657688208e-01 a02=+7.1940914126849e-01 a03=+6.3420586729605e-01 80 | a04=+5.5072664376774e-01 a05=+8.8278860561486e-01 a06=+4.3298434169557e-01 a07=-5.0359072076603e-02 81 | a08=+1.6295970159980e-01 a09=-4.9188379696205e-01 a10=+5.1855557439047e-01 a11=+1.3113379072001e-01 82 | a12=+2.0936649751810e-01 a13=-1.4295748963622e+00 a14=+2.8660214920000e-01 a15=+5.6891199882858e-01 83 | a16=+2.2516880412554e+00 a17=-3.7971790826633e-01 a18=+7.7504754914825e-01 a19=+4.7977853285001e-01 84 | 85 | Fiber[1] 86 | a00=+1.1811388868793e-01 a01=-1.1478447497424e+00 a02=-6.6487345252679e-01 a03=-1.7148639090996e+00 87 | a04=-7.6872239470223e-02 a05=-5.0159374882243e-01 a06=-1.6935536587331e+00 a07=+2.7941911828606e-01 88 | a08=-3.5448324722224e+00 a09=+2.5307292472043e+00 a10=-4.5834682171117e-01 a11=+1.3386745025637e+00 89 | a12=-6.8407371799215e-01 a13=+6.6924033980842e-01 a14=+1.6457657685919e-01 a15=-2.8079952793491e-01 90 | a16=-3.1722867062453e-01 a17=+1.6588126475746e+00 a18=+2.2290655922133e-01 a19=+7.5447835495350e-01 91 | 92 | Fiber[2] 93 | a00=+1.8244779857568e-01 a01=-1.1685866925504e-01 a02=-5.0465266572140e-01 a03=-4.0470994267335e-02 94 | a04=+1.9724795633467e+00 a05=-3.4364314562506e-01 a06=+1.7337202122150e+00 a07=-2.2495760225913e+00 95 | a08=+1.3576247188233e+00 a09=+1.8431747878815e-01 a10=+3.5073480201482e+00 a11=-1.6000821682906e+00 96 | a12=+5.7032015305944e-01 a13=-1.7410428926925e-01 a14=-5.6808637807046e-01 a15=+1.7428847265848e+00 97 | a16=-8.3226957849942e-01 a17=-2.0456158273779e+00 a18=-2.7074778241060e-01 a19=-1.1885720768385e+00 98 | 99 | -------------------------------------------------------------------------------- /test/test_generators.out: -------------------------------------------------------------------------------- 1 | [Generator[2] gen_take] STARTING 2 | [Generator[1] gen_filter] STARTING 3 | [Generator[0] gen_fibs] STARTING 4 | [Generator[0] gen_fibs] YIELD 5 | [Generator[0] gen_fibs] YIELD 6 | [Generator[1] gen_filter] YIELD 7 | [Generator[2] gen_take] YIELD 8 | [Main] value: 1 9 | [Generator[0] gen_fibs] YIELD 10 | [Generator[1] gen_filter] YIELD 11 | [Generator[2] gen_take] YIELD 12 | [Main] value: 1 13 | [Generator[0] gen_fibs] YIELD 14 | [Generator[0] gen_fibs] YIELD 15 | [Generator[1] gen_filter] YIELD 16 | [Generator[2] gen_take] YIELD 17 | [Main] value: 3 18 | [Generator[0] gen_fibs] YIELD 19 | [Generator[1] gen_filter] YIELD 20 | [Generator[2] gen_take] YIELD 21 | [Main] value: 5 22 | [Generator[0] gen_fibs] YIELD 23 | [Generator[0] gen_fibs] YIELD 24 | [Generator[1] gen_filter] YIELD 25 | [Generator[2] gen_take] YIELD 26 | [Main] value: 13 27 | [Generator[0] gen_fibs] YIELD 28 | [Generator[1] gen_filter] YIELD 29 | [Generator[2] gen_take] YIELD 30 | [Main] value: 21 31 | [Generator[0] gen_fibs] YIELD 32 | [Generator[0] gen_fibs] YIELD 33 | [Generator[1] gen_filter] YIELD 34 | [Generator[2] gen_take] YIELD 35 | [Main] value: 55 36 | [Generator[0] gen_fibs] YIELD 37 | [Generator[1] gen_filter] YIELD 38 | [Generator[2] gen_take] YIELD 39 | [Main] value: 89 40 | [Generator[0] gen_fibs] YIELD 41 | [Generator[0] gen_fibs] YIELD 42 | [Generator[1] gen_filter] YIELD 43 | [Generator[2] gen_take] YIELD 44 | [Main] value: 233 45 | [Generator[0] gen_fibs] YIELD 46 | [Generator[1] gen_filter] YIELD 47 | [Generator[2] gen_take] YIELD 48 | [Main] value: 377 49 | [Generator[0] gen_fibs] YIELD 50 | [Generator[0] gen_fibs] YIELD 51 | [Generator[1] gen_filter] YIELD 52 | [Generator[2] gen_take] YIELD 53 | [Main] value: 987 54 | [Generator[0] gen_fibs] YIELD 55 | [Generator[1] gen_filter] YIELD 56 | [Generator[2] gen_take] YIELD 57 | [Main] value: 1597 58 | [Generator[0] gen_fibs] YIELD 59 | [Generator[0] gen_fibs] YIELD 60 | [Generator[1] gen_filter] YIELD 61 | [Generator[2] gen_take] YIELD 62 | [Main] value: 4181 63 | [Generator[0] gen_fibs] YIELD 64 | [Generator[1] gen_filter] YIELD 65 | [Generator[2] gen_take] YIELD 66 | [Main] value: 6765 67 | [Generator[0] gen_fibs] YIELD 68 | [Generator[0] gen_fibs] YIELD 69 | [Generator[1] gen_filter] YIELD 70 | [Generator[2] gen_take] YIELD 71 | [Main] value: 17711 72 | [Generator[0] gen_fibs] YIELD 73 | [Generator[1] gen_filter] YIELD 74 | [Generator[2] gen_take] YIELD 75 | [Main] value: 28657 76 | [Generator[0] gen_fibs] YIELD 77 | [Generator[0] gen_fibs] YIELD 78 | [Generator[1] gen_filter] YIELD 79 | [Generator[2] gen_take] YIELD 80 | [Main] value: 75025 81 | [Generator[0] gen_fibs] YIELD 82 | [Generator[1] gen_filter] YIELD 83 | [Generator[2] gen_take] YIELD 84 | [Main] value: 121393 85 | [Generator[0] gen_fibs] YIELD 86 | [Generator[0] gen_fibs] YIELD 87 | [Generator[1] gen_filter] YIELD 88 | [Generator[2] gen_take] YIELD 89 | [Main] value: 317811 90 | [Generator[0] gen_fibs] YIELD 91 | [Generator[1] gen_filter] YIELD 92 | [Generator[2] gen_take] YIELD 93 | [Main] value: 514229 94 | [Generator[0] gen_fibs] FINISH 95 | [Generator[1] gen_filter] FINISH 96 | [Generator[2] gen_take] FINISH 97 | -------------------------------------------------------------------------------- /test/test_pre.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_PRE_H 2 | #define TEST_PRE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static FILE *out; 9 | 10 | #define require(x) \ 11 | if (!(x)) { \ 12 | fprintf(stderr, "requirement not met: %s\n", HU_TOSTR(x)); \ 13 | abort(); \ 14 | } 15 | 16 | static void 17 | println(const char *s) 18 | { 19 | fprintf(out, "%s\n", s); 20 | } 21 | 22 | static void 23 | test_main_begin(int *argc, char ***argv) 24 | { 25 | require(*argc == 1 || *argc == 2); 26 | if (*argc == 1) { 27 | out = stdout; 28 | return; 29 | } 30 | out = fopen((*argv)[1], "wb"); 31 | require(out != NULL); 32 | } 33 | 34 | static void 35 | test_main_end() 36 | { 37 | if (out != stdout) { 38 | fclose(out); 39 | } 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /toolchains/any-cross.cmake: -------------------------------------------------------------------------------- 1 | get_filename_component(target ${CMAKE_CURRENT_LIST_FILE} NAME) 2 | 3 | string(REPLACE ".cmake" "" target "${target}") 4 | string(REGEX REPLACE "^.*-([^-]*)\$" "\\1" cc "${target}") 5 | string(REGEX REPLACE "-[^-]*\$" "" target "${target}") 6 | string(REGEX REPLACE "-.*" "" CMAKE_SYSTEM_PROCESSOR "${target}") 7 | 8 | if(target MATCHES linux) 9 | set(CMAKE_SYSTEM_NAME Linux) 10 | elseif(target MATCHES "mingw|win") 11 | set(CMAKE_SYSTEM_NAME Windows) 12 | else() 13 | message(FATAL_ERROR "Failed to detect CMAKE_SYSTEM_NAME from target ${target}") 14 | return() 15 | endif() 16 | set(CMAKE_CROSSCOMPILING TRUE) 17 | 18 | if(cc STREQUAL "gcc") 19 | set(cxx "g++") 20 | elseif(cc STREQUAL "cc") 21 | set(cxx "c++") 22 | elseif(cc STREQUAL "icc") 23 | set(cxx "icpc") 24 | else() 25 | set(cxx "${cc}++") 26 | endif() 27 | 28 | set(cross ${target}-) 29 | 30 | set(CMAKE_AR ${cross}ar) 31 | #set(CMAKE_ASM_COMPILER ${cross}as) 32 | set(CMAKE_C_COMPILER ${cross}${cc}) 33 | set(CMAKE_CXX_COMPILER ${cross}${cxx}) 34 | set(CMAKE_LINKER ${cross}ld) 35 | set(CMAKE_OBJCOPY ${cross}objcopy CACHE INTERNAL "") 36 | set(CMAKE_RANLIB ${cross}ranlib CACHE INTERNAL "") 37 | set(CMAKE_SIZE ${cross}size CACHE INTERNAL "") 38 | set(CMAKE_STRIP ${cross}strip CACHE INTERNAL "") 39 | set(CMAKE_GCOV ${cross}gcov CACHE INTERNAL "") 40 | 41 | 42 | # Where to look for the target environment. (More paths can be added here) 43 | set(CMAKE_FIND_ROOT_PATH /usr/${target}) 44 | set(CMAKE_INCLUDE_PATH /usr/include/${target}) 45 | set(CMAKE_LIBRARY_PATH /usr/lib/${target}) 46 | set(CMAKE_PROGRAM_PATH /usr/${target}/bin) 47 | 48 | # Adjust the default behavior of the FIND_XXX() commands: 49 | # search programs in the host environment only. 50 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 51 | 52 | # Search headers and libraries in the target environment only. 53 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 54 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 55 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 56 | 57 | unset(cross) 58 | unset(cxx) 59 | unset(cc) 60 | unset(target) 61 | --------------------------------------------------------------------------------