├── debian ├── compat ├── source │ └── format ├── libevfibers0.dirs ├── libevfibers0.install ├── libevfibers-dev.dirs ├── libevfibers-dev.install ├── rules ├── control ├── copyright └── changelog ├── vimrc ├── .clang_complete ├── .gitignore ├── .checkpatch.conf ├── examples ├── sample_http_server │ ├── Makefile │ ├── LICENSE.http-parser │ ├── sample_http_server.c │ └── http_parser.h └── useless_time_server.c ├── async.bench.results ├── cmake ├── FindLibEv.cmake ├── FindLibVrb.cmake └── FindLibEio.cmake ├── libevfibers.pc.in ├── ci ├── install_deps.sh └── run_tests.sh ├── test_coverage.sh ├── test ├── io.h ├── eio.h ├── key.h ├── init.h ├── cond.h ├── mutex.h ├── popen3.h ├── buffer.h ├── logger.h ├── reclaim.h ├── async-wait.h ├── CMakeLists.txt ├── init.c ├── main.c ├── logger.c ├── key.c ├── async-wait.c ├── popen3.c ├── buffer.c ├── reclaim.c ├── mutex.c ├── cond.c ├── eio.c └── io.c ├── .travis.yml ├── include ├── evfibers │ ├── config.h.in │ └── eio.h └── evfibers_private │ ├── trace.h │ └── fiber.h ├── coro ├── LICENSE ├── coro.h └── coro.c ├── src └── trace.c ├── bench ├── buffer.c └── condvar.c ├── doxygen └── header.html ├── README.md ├── CMakeLists.txt └── LICENSE /debian/compat: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /vimrc: -------------------------------------------------------------------------------- 1 | set makeprg=make\ -C\ build 2 | -------------------------------------------------------------------------------- /debian/libevfibers0.dirs: -------------------------------------------------------------------------------- 1 | usr/lib 2 | usr/bin 3 | -------------------------------------------------------------------------------- /debian/libevfibers0.install: -------------------------------------------------------------------------------- 1 | usr/lib/lib*.so.* 2 | -------------------------------------------------------------------------------- /debian/libevfibers-dev.dirs: -------------------------------------------------------------------------------- 1 | usr/lib 2 | usr/include 3 | -------------------------------------------------------------------------------- /.clang_complete: -------------------------------------------------------------------------------- 1 | -I./include -I./build/libevfibers/include -I./coro 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # CMake build diry 2 | build 3 | 4 | # Vim swap files 5 | .*.swp 6 | -------------------------------------------------------------------------------- /debian/libevfibers-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/* 2 | usr/lib/lib*.a 3 | usr/lib/lib*.so 4 | -------------------------------------------------------------------------------- /.checkpatch.conf: -------------------------------------------------------------------------------- 1 | --no-tree 2 | --show-types 3 | --ignore SWITCH_CASE_INDENT_LEVEL,SPLIT_STRING,LONG_LINE,PREFER_PRINTF 4 | --emacs 5 | --file 6 | -------------------------------------------------------------------------------- /examples/sample_http_server/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -O2 http_parser.c sample_http_server.c ../../build/libevfibers.a -o sample_http_server -lev -leio 3 | 4 | clean: 5 | rm -f sample_http_server 6 | 7 | run: 8 | ./sample_http_server 9 | -------------------------------------------------------------------------------- /async.bench.results: -------------------------------------------------------------------------------- 1 | do_job 45.433348 open_trunc: 0.247575 small_msg: 17.278001, big_msg: 27.776948, close: 0.128902 2 | do_job_eio 57.250496 open_trunc: 0.184991 small_msg: 28.061991, big_msg: 28.921616, close: 0.080152 3 | do_job_sync 45.454047 open_trunc: 0.052595 small_msg: 17.364090, big_msg: 28.017982, close: 0.017014 4 | 5 | do_job_stat 44.955093 6 | do_job_stat_eio 27.519920 7 | do_job_stat_sync 0.467883 8 | -------------------------------------------------------------------------------- /cmake/FindLibEv.cmake: -------------------------------------------------------------------------------- 1 | find_path(LIBEV_INCLUDE_DIR ev.h 2 | HINTS $ENV{LIBEV_DIR} 3 | PATH_SUFFIXES include 4 | PATHS /usr/local /usr 5 | ) 6 | find_library(LIBEV_LIBRARY 7 | NAMES ev 8 | HINTS $ENV{LIBEV_DIR} 9 | PATH_SUFFIXES lib 10 | PATHS /usr/local /usr 11 | ) 12 | include(FindPackageHandleStandardArgs) 13 | find_package_handle_standard_args(LibEv DEFAULT_MSG LIBEV_LIBRARY LIBEV_INCLUDE_DIR) 14 | mark_as_advanced(LIBEV_INCLUDE_DIR LIBEV_LIBRARY) 15 | -------------------------------------------------------------------------------- /cmake/FindLibVrb.cmake: -------------------------------------------------------------------------------- 1 | find_path(LIBVRB_INCLUDE_DIR vrb.h 2 | HINTS $ENV{LIBVRB_DIR} 3 | PATH_SUFFIXES include 4 | PATHS /usr/local /usr 5 | ) 6 | find_library(LIBVRB_LIBRARY 7 | NAMES vrb 8 | HINTS $ENV{LIBVRB_DIR} 9 | PATH_SUFFIXES lib 10 | PATHS /usr/local /usr 11 | ) 12 | include(FindPackageHandleStandardArgs) 13 | find_package_handle_standard_args(LibVrb DEFAULT_MSG LIBVRB_LIBRARY LIBVRB_INCLUDE_DIR) 14 | mark_as_advanced(LIBVRB_INCLUDE_DIR LIBVRB_LIBRARY) 15 | -------------------------------------------------------------------------------- /libevfibers.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix}/bin 3 | libdir=${prefix}/lib 4 | includedir=${prefix}/include 5 | 6 | Name: libevfibers 7 | Description: Small C fiber library that uses libev based event loop and libcoro based coroutine context switching 8 | Version: @VERSION_STRING@ 9 | URL: https://github.com/Lupus/libevfibers 10 | Requires: 11 | Libs: -L${libdir} -levfibers 12 | Libs.private: -lev @CMAKE_THREAD_LIBS_INIT@ 13 | Cflags: -I${includedir} 14 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | #export DH_VERBOSE=1 11 | 12 | %: 13 | dh $@ 14 | -------------------------------------------------------------------------------- /ci/install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | os=$(uname -s) 4 | err= 5 | if [[ "$os" == "Linux" ]] ; then 6 | sudo apt-get update -qq 7 | sudo apt-get install -y cmake libev-dev check cvs libtool autoconf 8 | err=$? 9 | elif [[ "$os" == "Darwin" ]] ; then 10 | brew install cmake libev check cvs libtool autoconf 11 | # Ugly, but brew return 1 if package already installed 12 | #err=$? 13 | err=0 14 | else 15 | echo "Unrecognized OS: $os" >&2 16 | err=1 17 | fi 18 | 19 | exit $err 20 | -------------------------------------------------------------------------------- /ci/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ -d build ] ; then 6 | rm -rf build 7 | fi 8 | mkdir build 9 | 10 | pushd build 11 | 12 | echo cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE $EIO .. 13 | echo 14 | cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE $EIO .. 15 | echo 16 | 17 | make 18 | 19 | echo "======================================" 20 | echo " Running unit tests" 21 | echo "======================================" 22 | echo 23 | 24 | export CK_TIMEOUT_MULTIPLIER=25 25 | ./test/evfibers_test 26 | 27 | popd 28 | 29 | echo "Test run has finished successfully" 30 | -------------------------------------------------------------------------------- /cmake/FindLibEio.cmake: -------------------------------------------------------------------------------- 1 | find_path(LIBEIO_INCLUDE_DIR eio.h 2 | HINTS $ENV{LIBEIO_DIR} 3 | PATH_SUFFIXES include 4 | PATHS /usr/local /usr 5 | ) 6 | find_library(LIBEIO_LIBRARY 7 | NAMES ev-eio eio 8 | HINTS $ENV{LIBEIO_DIR} 9 | PATH_SUFFIXES lib 10 | PATHS /usr/local /usr 11 | ) 12 | check_library_exists(${LIBEIO_LIBRARY} eio_custom "" EIO_CUSTOM_IS_PRESENT) 13 | if (NOT EIO_CUSTOM_IS_PRESENT) 14 | message(FATAL_ERROR "symbol eio_custom is not found in ${LIBEIO_LIBRARY}") 15 | endif() 16 | include(FindPackageHandleStandardArgs) 17 | find_package_handle_standard_args(LibEio DEFAULT_MSG LIBEIO_LIBRARY LIBEIO_INCLUDE_DIR) 18 | mark_as_advanced(LIBEIO_INCLUDE_DIR LIBEIO_LIBRARY) 19 | -------------------------------------------------------------------------------- /test_coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | function remove_extra { 6 | lcov -q --remove $1 "coro/*" -o $1 7 | lcov -q --remove $1 "bench/*" -o $1 8 | lcov -q --remove $1 "build/CMakeFiles/*" -o $1 9 | lcov -q --remove $1 "/usr/include/*" -o $1 10 | } 11 | 12 | ./build.sh +eio cov 13 | 14 | pushd build 15 | lcov -c -i -d . -o evfibers_base.info 16 | remove_extra evfibers_base.info 17 | make test 18 | lcov -c -d . -o evfibers_test.info 19 | remove_extra evfibers_test.info 20 | lcov -a evfibers_base.info -a evfibers_test.info -o evfibers_total.info 21 | mkdir html 22 | genhtml -o html -b evfibers_base.info -t "libevfibers" --highlight evfibers_total.info 23 | popd 24 | xdg-open file://$(pwd)/build/html/index.html 25 | -------------------------------------------------------------------------------- /test/io.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _IO_H_ 20 | #define _IO_H_ 21 | 22 | TCase * io_tcase(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/eio.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _EIO_H_ 20 | #define _EIO_H_ 21 | 22 | TCase * eio_tcase(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/key.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _KEY_H_ 20 | #define _KEY_H_ 21 | 22 | TCase * key_tcase(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/init.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _INIT_H_ 20 | #define _INIT_H_ 21 | 22 | TCase * init_tcase(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/cond.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _COND_H_ 20 | #define _COND_H_ 21 | 22 | TCase * cond_tcase(void); 23 | 24 | #endif 25 | 26 | -------------------------------------------------------------------------------- /test/mutex.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _MUTEX_H_ 20 | #define _MUTEX_H_ 21 | 22 | TCase * mutex_tcase(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/popen3.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _POPEN3_H_ 20 | #define _POPEN3_H_ 21 | 22 | TCase *popen3_tcase(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/buffer.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _BUFFER_H_ 20 | #define _BUFFER_H_ 21 | 22 | TCase * buffer_tcase(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/logger.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _LOGGER_H_ 20 | #define _LOGGER_H_ 21 | 22 | TCase * logger_tcase(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/reclaim.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _RECLAIM_H_ 20 | #define _RECLAIM_H_ 21 | 22 | TCase * reclaim_tcase(void); 23 | 24 | #endif 25 | 26 | -------------------------------------------------------------------------------- /test/async-wait.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _ASYNC_WAIT_H_ 20 | #define _ASYNC_WAIT_H_ 21 | 22 | TCase * async_wait_tcase(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Threads REQUIRED) 2 | 3 | aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} TEST_SOURCES) 4 | set (FBR_TEST_LIBS evfibers check m) 5 | if (APPLE) 6 | # don't want to test mknod - requires super-user privileges 7 | add_definitions(-DFBR_TEST_NO_MKNOD) 8 | else(APPLE) 9 | # OSX doesn't use librt, while linux boxes do 10 | list(APPEND FBR_TEST_LIBS rt) 11 | # Dependency of libcheck on Deabin/Ubuntu 12 | list(APPEND FBR_TEST_LIBS subunit) 13 | endif(APPLE) 14 | 15 | add_executable(evfibers_test ${TEST_SOURCES}) 16 | if(THREADS_HAVE_PTHREAD_ARG) 17 | target_compile_options(evfibers_test PUBLIC "-pthread") 18 | endif() 19 | if(CMAKE_THREAD_LIBS_INIT) 20 | list(APPEND FBR_TEST_LIBS ${CMAKE_THREAD_LIBS_INIT}) 21 | endif() 22 | 23 | target_link_libraries(evfibers_test ${FBR_TEST_LIBS}) 24 | enable_testing() 25 | add_test(evfibers_test ${CMAKE_CURRENT_BINARY_DIR}/evfibers_test) 26 | add_custom_target(tests COMMAND ${CMAKE_CTEST_COMMAND} 27 | DEPENDS evfibers_test) 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # MacOS: disable test with '-DWANT_EIO=TRUE -DWANT_EMBEDDED_EIO=TRUE' - embedded libeio not found. 2 | 3 | install: "./ci/install_deps.sh" 4 | language: c 5 | script: "./ci/run_tests.sh" 6 | 7 | # gcc is symlinked to clang on OSX - could install with homebrew and fiddle 8 | # with paths to make sure the one we want is being used, but save that for 9 | # another day if interest arises 10 | 11 | matrix: 12 | include: 13 | - os: linux 14 | compiler: gcc 15 | env: BUILD_TYPE=RelWithDebInfo EIO="-DWANT_EIO=TRUE -DWANT_EMBEDDED_EIO=TRUE" 16 | - os: linux 17 | compiler: clang 18 | env: BUILD_TYPE=RelWithDebInfo EIO="-DWANT_EIO=TRUE -DWANT_EMBEDDED_EIO=TRUE" 19 | - os: linux 20 | compiler: gcc 21 | env: BUILD_TYPE=RelWithDebInfo EIO="-DWANT_EIO=FALSE" 22 | - os: linux 23 | compiler: clang 24 | env: BUILD_TYPE=RelWithDebInfo EIO="-DWANT_EIO=FALSE" 25 | - os: osx 26 | compiler: clang 27 | env: BUILD_TYPE=RelWithDebInfo EIO="-DWANT_EIO=FALSE" 28 | -------------------------------------------------------------------------------- /include/evfibers/config.h.in: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _FBR_CONFIG_H_ 20 | #define _FBR_CONFIG_H_ 21 | 22 | #cmakedefine HAVE_VALGRIND_H 23 | #cmakedefine FBR_EIO_ENABLED 24 | #cmakedefine FBR_USE_EMBEDDED_EIO 25 | #cmakedefine FBR_MAP_ANON_FLAG @FBR_MAP_ANON_FLAG@ 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/evfibers_private/trace.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _FBR_TRACE_PRIVATE_H_ 20 | #define _FBR_TRACE_PRIVATE_H_ 21 | 22 | #include 23 | 24 | #define TRACE_SIZE 16 25 | 26 | struct trace_info { 27 | void *array[TRACE_SIZE]; 28 | size_t size; 29 | }; 30 | 31 | void fill_trace_info(FBR_P_ struct trace_info *info); 32 | void print_trace_info(FBR_P_ struct trace_info *info, fbr_logutil_func_t log); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: libevfibers 2 | Priority: extra 3 | Maintainer: Konstantin Olkhovskiy 4 | Build-Depends: debhelper (>= 8.0.0), cmake, libev-dev, valgrind, uthash-dev, 5 | check, libeio-dev 6 | Standards-Version: 3.9.2 7 | Section: libs 8 | Homepage: http://code.google.com/p/libevfibers 9 | Vcs-Browser: http://code.google.com/p/libevfibers/source/browse 10 | 11 | Package: libevfibers-dev 12 | Section: libdevel 13 | Architecture: any 14 | Depends: libevfibers0 (= ${binary:Version}), libev-dev, libeio-dev 15 | Description: Small C fibers library --- development files 16 | Small c fiber library that uses libev based event loop and libcoro based 17 | coroutine context switching. as libcoro alone is barely enough to do something 18 | useful, this project aims at building a complete fiber api around it while 19 | leveraging libev's high performance and flexibility. 20 | 21 | Package: libevfibers0 22 | Section: libs 23 | Architecture: any 24 | Depends: ${shlibs:Depends}, ${misc:Depends} 25 | Description: Small C fibers library --- runtime files 26 | Small c fiber library that uses libev based event loop and libcoro based 27 | coroutine context switching. As libcoro alone is barely enough to do something 28 | useful, this project aims at building a complete fiber api around it while 29 | leveraging libev's high performance and flexibility. 30 | -------------------------------------------------------------------------------- /examples/sample_http_server/LICENSE.http-parser: -------------------------------------------------------------------------------- 1 | http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright 2 | Igor Sysoev. 3 | 4 | Additional changes are licensed under the same terms as NGINX and 5 | copyright Joyent, Inc. and other Node contributors. All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to 9 | deal in the Software without restriction, including without limitation the 10 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | sell copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /coro/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2000-2009 Marc Alexander Lehmann 2 | 3 | Redistribution and use in source and binary forms, with or without modifica- 4 | tion, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 14 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 15 | CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 16 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 17 | CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 18 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 19 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 21 | ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 22 | OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | Alternatively, the following files carry an additional notice that 25 | explicitly allows relicensing under the GPLv2: coro.c, coro.h. 26 | 27 | -------------------------------------------------------------------------------- /src/trace.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | void fill_trace_info(FBR_P_ struct trace_info *info) 26 | { 27 | if (0 == fctx->__p->backtraces_enabled) 28 | return; 29 | info->size = backtrace(info->array, TRACE_SIZE); 30 | } 31 | 32 | void print_trace_info(FBR_P_ struct trace_info *info, fbr_logutil_func_t log) 33 | { 34 | size_t i; 35 | char **strings; 36 | if (0 == fctx->__p->backtraces_enabled) { 37 | (*log)(FBR_A_ "(No backtrace since they are disabled)"); 38 | return; 39 | } 40 | strings = backtrace_symbols(info->array, info->size); 41 | for (i = 0; i < info->size; i++) 42 | (*log)(FBR_A_ "%s", strings[i]); 43 | free(strings); 44 | } 45 | -------------------------------------------------------------------------------- /test/init.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "init.h" 24 | 25 | START_TEST(test_init) 26 | { 27 | struct fbr_context context; 28 | context.__p = NULL; 29 | fbr_init(&context, EV_DEFAULT); 30 | fail_if(NULL == context.__p, NULL); 31 | fbr_destroy(&context); 32 | } 33 | END_TEST 34 | 35 | START_TEST(test_init_evloop) 36 | { 37 | struct fbr_context context; 38 | fbr_init(&context, EV_DEFAULT); 39 | ev_run(EV_DEFAULT, 0); 40 | //Should return if we do not set up any unnecessary watchers 41 | fbr_destroy(&context); 42 | } 43 | END_TEST 44 | 45 | TCase * init_tcase(void) 46 | { 47 | TCase *tc_init = tcase_create ("Init"); 48 | tcase_add_test(tc_init, test_init); 49 | tcase_add_test(tc_init, test_init_evloop); 50 | return tc_init; 51 | } 52 | -------------------------------------------------------------------------------- /bench/buffer.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | int main() 29 | { 30 | int retval; 31 | struct fbr_context context; 32 | fbr_init(&context, EV_DEFAULT); 33 | const size_t count = 10000; 34 | const size_t repeats = 100; 35 | struct fbr_buffer *buffers; 36 | size_t i, j; 37 | 38 | signal(SIGPIPE, SIG_IGN); 39 | buffers = calloc(count, sizeof(struct fbr_buffer)); 40 | 41 | for (j = 0; j < repeats; j++) { 42 | printf("Repeat #%zd...", j); 43 | for (i = 0; i < count; i++) { 44 | retval = fbr_buffer_init(&context, buffers + i, 0); 45 | if (retval) { 46 | fprintf(stderr, "error at count = %zd\n", i); 47 | fprintf(stderr, "fbr_buffer_init: %s\n", 48 | fbr_strerror(&context, 49 | context.f_errno)); 50 | fprintf(stderr, "errno: %s\n", strerror(errno)); 51 | exit(-1); 52 | } 53 | } 54 | 55 | for (i = 0; i < count; i++) { 56 | fbr_buffer_destroy(&context, buffers + i); 57 | } 58 | printf(" Done!\n"); 59 | } 60 | 61 | fbr_destroy(&context); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://dep.debian.net/deps/dep5 2 | Upstream-Name: libevfibers 3 | Source: http://code.google.com/p/libevfibers 4 | 5 | Files: * 6 | Copyright: 2012 Konstantin Olkhovskiy 7 | License: LGPL-3.0+ 8 | 9 | Files: debian/* 10 | Copyright: 2012 Konstantin Olkhovskiy 11 | License: GPL-3.0+ 12 | 13 | License: LGPL-3.0+ 14 | This program is free software: you can redistribute it and/or modify 15 | it under the terms of the GNU Lesser General Public License as published by 16 | the Free Software Foundation, either version 3 of the License, or 17 | (at your option) any later version. 18 | . 19 | This package is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | GNU Lesser General Public License for more details. 23 | . 24 | You should have received a copy of the GNU Lesser General Public License 25 | along with this program. If not, see . 26 | . 27 | On Debian systems, the complete text of the GNU Lesser General 28 | Public License version 3 can be found in "/usr/share/common-licenses/LGPL-3". 29 | 30 | License: GPL-3.0+ 31 | This program is free software: you can redistribute it and/or modify 32 | it under the terms of the GNU General Public License as published by 33 | the Free Software Foundation, either version 3 of the License, or 34 | (at your option) any later version. 35 | . 36 | This package is distributed in the hope that it will be useful, 37 | but WITHOUT ANY WARRANTY; without even the implied warranty of 38 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 39 | GNU General Public License for more details. 40 | . 41 | You should have received a copy of the GNU General Public License 42 | along with this program. If not, see . 43 | . 44 | On Debian systems, the complete text of the GNU General 45 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". 46 | -------------------------------------------------------------------------------- /examples/useless_time_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static void conn_handler(struct fbr_context *fctx, void *_arg) 11 | { 12 | int fd = *(int *)_arg; 13 | time_t now; 14 | char *ct; 15 | 16 | now = time(NULL); 17 | ct = ctime(&now); 18 | fbr_write(fctx, fd, ct, strlen(ct)); 19 | close(fd); 20 | } 21 | 22 | static void ticker(struct fbr_context *fctx, void *_arg) 23 | { 24 | time_t now; 25 | char *ct; 26 | 27 | for (;;) { 28 | fbr_sleep(fctx, 5); 29 | now = time(NULL); 30 | ct = ctime(&now); 31 | ct[strlen(ct)-1] = '\0'; 32 | printf("ticker: time: %s\n", ct); 33 | } 34 | } 35 | 36 | static void acceptor(struct fbr_context *fctx, void *_arg) 37 | { 38 | int fd = *(int *)_arg; 39 | int client_fd; 40 | socklen_t peer_len; 41 | struct sockaddr_in peer_addr; 42 | fbr_id_t id; 43 | peer_len = sizeof(peer_addr); 44 | for (;;) { 45 | client_fd = fbr_accept(fctx, fd, (struct sockaddr *)&peer_addr, 46 | &peer_len); 47 | if (client_fd < 0) 48 | err(EXIT_FAILURE, "accept failed"); 49 | id = fbr_create(fctx, "handler", conn_handler, &client_fd, 0); 50 | if (fbr_id_isnull(id)) 51 | errx(EXIT_FAILURE, "unable to create a fiber"); 52 | fbr_transfer(fctx, id); 53 | 54 | } 55 | } 56 | 57 | int main(int argc, char *argv[]) 58 | { 59 | struct sockaddr_in sar; 60 | struct protoent *pe; 61 | int sa, sw; 62 | int port; 63 | struct fbr_context fbr; 64 | fbr_id_t ticker_id; 65 | fbr_id_t acceptor_id; 66 | 67 | fbr_init(&fbr, EV_DEFAULT); 68 | signal(SIGPIPE, SIG_IGN); 69 | 70 | ticker_id = fbr_create(&fbr, "ticker", ticker, NULL, 0); 71 | if (fbr_id_isnull(ticker_id)) 72 | errx(EXIT_FAILURE, "unable to create a fiber"); 73 | fbr_transfer(&fbr, ticker_id); 74 | 75 | pe = getprotobyname("tcp"); 76 | sa = socket(AF_INET, SOCK_STREAM, pe->p_proto); 77 | sar.sin_family = AF_INET; 78 | sar.sin_addr.s_addr = INADDR_ANY; 79 | sar.sin_port = htons(12345); 80 | bind(sa, (struct sockaddr *)&sar, sizeof(struct sockaddr_in)); 81 | listen(sa, 10); 82 | 83 | acceptor_id = fbr_create(&fbr, "acceptor", acceptor, &sa, 0); 84 | if (fbr_id_isnull(acceptor_id)) 85 | errx(EXIT_FAILURE, "unable to create a fiber"); 86 | fbr_transfer(&fbr, acceptor_id); 87 | 88 | ev_run(EV_DEFAULT, 0); 89 | } 90 | -------------------------------------------------------------------------------- /doxygen/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $projectname: $title 7 | $title 8 | 9 | 10 | 11 | $treeview 12 | $search 13 | $mathjax 14 | 15 | 28 | 29 | 30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
42 |
$projectname 43 |  $projectnumber 44 |
45 |
$projectbrief
46 |
51 |
$projectbrief
52 |
$searchbox
63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /test/main.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include "init.h" 25 | #include "mutex.h" 26 | #include "cond.h" 27 | #include "reclaim.h" 28 | #include "io.h" 29 | #include "logger.h" 30 | #include "buffer.h" 31 | #include "key.h" 32 | #include "eio.h" 33 | #include "async-wait.h" 34 | #include "popen3.h" 35 | 36 | Suite *evfibers_suite(void) 37 | { 38 | Suite *s; 39 | TCase *tc_init, *tc_mutex, *tc_cond, *tc_reclaim, *tc_io, *tc_logger, 40 | *tc_buffer, *tc_key, *tc_eio, *tc_async_wait, *tc_popen3; 41 | 42 | s = suite_create ("evfibers"); 43 | tc_init = init_tcase(); 44 | tc_mutex = mutex_tcase(); 45 | tc_cond = cond_tcase(); 46 | tc_reclaim = reclaim_tcase(); 47 | tc_io = io_tcase(); 48 | tc_logger = logger_tcase(); 49 | tc_buffer = buffer_tcase(); 50 | tc_key = key_tcase(); 51 | tc_eio = eio_tcase(); 52 | tc_async_wait = async_wait_tcase(); 53 | tc_popen3 = popen3_tcase(); 54 | suite_add_tcase(s, tc_init); 55 | suite_add_tcase(s, tc_mutex); 56 | suite_add_tcase(s, tc_cond); 57 | suite_add_tcase(s, tc_reclaim); 58 | suite_add_tcase(s, tc_io); 59 | suite_add_tcase(s, tc_logger); 60 | suite_add_tcase(s, tc_buffer); 61 | suite_add_tcase(s, tc_key); 62 | suite_add_tcase(s, tc_eio); 63 | suite_add_tcase(s, tc_async_wait); 64 | suite_add_tcase(s, tc_popen3); 65 | 66 | return s; 67 | } 68 | 69 | int main(void) 70 | { 71 | int number_failed; 72 | Suite *s; 73 | SRunner *sr; 74 | 75 | srand(42); 76 | 77 | /* pbuilder workaround: it does not mount tmpfs to /dev/shm */ 78 | setenv("FBR_BUFFER_FILE_PATTERN", "/tmp/pbuilder_fbr_buffer.XXXXXX", 0); 79 | 80 | s = evfibers_suite(); 81 | sr = srunner_create(s); 82 | srunner_run_all(sr, CK_NORMAL); 83 | number_failed = srunner_ntests_failed(sr); 84 | srunner_free(sr); 85 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 86 | } 87 | -------------------------------------------------------------------------------- /test/logger.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "logger.h" 25 | 26 | static void test_fiber(FBR_P_ _unused_ void *_arg) 27 | { 28 | const char fname[] = "logget_test_fiber"; 29 | fctx->logger->level = FBR_LOG_DEBUG; 30 | 31 | fprintf(stderr, "\n==== BEGIN LOGGER TEST ====\n\n"); 32 | 33 | fbr_set_name(FBR_A_ fbr_self(FBR_A), fname); 34 | 35 | fbr_dump_stack(FBR_A_ fbr_log_d); 36 | fbr_dump_stack(FBR_A_ fbr_log_i); 37 | fbr_dump_stack(FBR_A_ fbr_log_n); 38 | fbr_dump_stack(FBR_A_ fbr_log_w); 39 | fbr_dump_stack(FBR_A_ fbr_log_e); 40 | 41 | fbr_log_d(FBR_A_ "%s", fbr_strerror(FBR_A_ FBR_SUCCESS)); 42 | fbr_log_d(FBR_A_ "%s", fbr_strerror(FBR_A_ FBR_EINVAL)); 43 | fbr_log_d(FBR_A_ "%s", fbr_strerror(FBR_A_ FBR_ENOFIBER)); 44 | fbr_log_d(FBR_A_ "%s", fbr_strerror(FBR_A_ FBR_ESYSTEM)); 45 | fbr_log_d(FBR_A_ "%s", fbr_strerror(FBR_A_ FBR_EBUFFERMMAP)); 46 | fbr_log_d(FBR_A_ "%s", fbr_strerror(FBR_A_ FBR_ENOKEY)); 47 | fbr_log_d(FBR_A_ "%s", fbr_strerror(FBR_A_ FBR_EPROTOBUF)); 48 | fbr_log_d(FBR_A_ "%s", fbr_strerror(FBR_A_ FBR_EBUFFERNOSPACE)); 49 | fbr_log_d(FBR_A_ "%s", fbr_strerror(FBR_A_ -1)); 50 | 51 | fail_unless(!strcmp(fbr_get_name(FBR_A_ fbr_self(FBR_A)), fname)); 52 | 53 | printf("\n==== END LOGGER TEST ====\n\n"); 54 | } 55 | 56 | START_TEST(test_logger) 57 | { 58 | struct fbr_context context; 59 | fbr_id_t test = FBR_ID_NULL; 60 | int retval; 61 | 62 | fbr_init(&context, EV_DEFAULT); 63 | 64 | fbr_enable_backtraces(&context, 0); 65 | fbr_enable_backtraces(&context, 1); 66 | 67 | test = fbr_create(&context, "test", test_fiber, NULL, 0); 68 | fail_if(fbr_id_isnull(test), NULL); 69 | 70 | retval = fbr_transfer(&context, test); 71 | fail_unless(0 == retval, NULL); 72 | 73 | fbr_destroy(&context); 74 | } 75 | END_TEST 76 | 77 | TCase * logger_tcase(void) 78 | { 79 | TCase *tc_logger = tcase_create ("Logger"); 80 | tcase_add_test(tc_logger, test_logger); 81 | return tc_logger; 82 | } 83 | -------------------------------------------------------------------------------- /test/key.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "key.h" 30 | 31 | int x; 32 | int y; 33 | 34 | START_TEST(test_key) 35 | { 36 | struct fbr_context context; 37 | fbr_id_t fiber; 38 | int retval; 39 | fbr_key_t key; 40 | fbr_key_t key2; 41 | void *val; 42 | 43 | fbr_init(&context, EV_DEFAULT); 44 | 45 | fiber = fbr_create(&context, "key_fiber", NULL, NULL, 0); 46 | fail_if(fbr_id_isnull(fiber), NULL); 47 | 48 | retval = fbr_key_create(&context, &key); 49 | fail_unless(0 == retval); 50 | retval = fbr_key_create(&context, &key2); 51 | fail_unless(0 == retval); 52 | 53 | retval = fbr_key_set(&context, fiber, key, &x); 54 | fail_unless(0 == retval); 55 | retval = fbr_key_set(&context, fiber, key2, &y); 56 | fail_unless(0 == retval); 57 | val = fbr_key_get(&context, fiber, key); 58 | fail_unless(val == &x); 59 | val = fbr_key_get(&context, fiber, key2); 60 | fail_unless(val == &y); 61 | retval = fbr_key_delete(&context, key); 62 | fail_unless(0 == retval); 63 | retval = fbr_key_delete(&context, key2); 64 | fail_unless(0 == retval); 65 | 66 | retval = fbr_key_set(&context, fiber, key, &x); 67 | fail_unless(-1 == retval); 68 | val = fbr_key_get(&context, fiber, key); 69 | fail_unless(NULL == val); 70 | retval = fbr_key_delete(&context, key); 71 | fail_unless(-1 == retval); 72 | 73 | retval = fbr_key_set(&context, fiber, key2, &y); 74 | fail_unless(-1 == retval); 75 | val = fbr_key_get(&context, fiber, key2); 76 | fail_unless(NULL == val); 77 | retval = fbr_key_delete(&context, key2); 78 | fail_unless(-1 == retval); 79 | 80 | fbr_destroy(&context); 81 | 82 | } 83 | END_TEST 84 | 85 | START_TEST(test_multiple_keys) 86 | { 87 | struct fbr_context context; 88 | int retval; 89 | fbr_key_t key; 90 | unsigned int i; 91 | 92 | fbr_init(&context, EV_DEFAULT); 93 | 94 | for (i = 0; i < 64; i++) { 95 | retval = fbr_key_create(&context, &key); 96 | fail_unless(0 == retval); 97 | fail_unless(key == i); 98 | } 99 | 100 | fbr_destroy(&context); 101 | 102 | } 103 | END_TEST 104 | 105 | TCase * key_tcase(void) 106 | { 107 | TCase *tc_key = tcase_create ("Key"); 108 | tcase_add_test(tc_key, test_key); 109 | tcase_add_test(tc_key, test_multiple_keys); 110 | return tc_key; 111 | } 112 | -------------------------------------------------------------------------------- /test/async-wait.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "async-wait.h" 24 | 25 | struct fiber_arg { 26 | ev_async *async; 27 | fbr_id_t *fibers; 28 | }; 29 | 30 | static int async_recvd = 0; 31 | 32 | static void async_sender(FBR_P_ void *_arg) 33 | { 34 | struct fiber_arg *arg = _arg; 35 | ev_async *async = arg->async; 36 | 37 | /* wait briefly to make sure other fiber blocks */ 38 | fbr_sleep(FBR_A_ 1); 39 | 40 | /* send async event in libev to wake up other fiber */ 41 | ev_async_send(fctx->__p->loop, async); 42 | return; 43 | } 44 | 45 | static void async_waiter(FBR_P_ void *_arg) 46 | { 47 | struct fiber_arg *arg = _arg; 48 | ev_async *async = arg->async; 49 | 50 | fbr_async_wait(FBR_A_ async); 51 | 52 | async_recvd = 1; 53 | 54 | return; 55 | } 56 | 57 | static void async_handler_cb(EV_P_ ev_async *w, int revents) 58 | { 59 | /* hush warnings */ 60 | (void)w; 61 | (void)revents; 62 | 63 | async_recvd = 1; 64 | /* This is kind of a hack for testing purposes; make sure we break out of 65 | * the underlying event loop 66 | */ 67 | ev_break(EV_A_ EVBREAK_ONE); 68 | 69 | return; 70 | } 71 | 72 | START_TEST(test_async_wait) 73 | { 74 | struct fbr_context context; 75 | fbr_id_t fibers[2] = { 76 | FBR_ID_NULL, 77 | FBR_ID_NULL, 78 | }; 79 | ev_async async; 80 | int retval; 81 | struct fiber_arg arg; 82 | 83 | fbr_init(&context, EV_DEFAULT); 84 | 85 | ev_async_init(&async, async_handler_cb); 86 | ev_async_start(EV_DEFAULT, &async); 87 | 88 | arg.fibers = fibers; 89 | arg.async = &async; 90 | 91 | fibers[0] = fbr_create(&context, "async_sender", async_sender, &arg, 0); 92 | fail_if(fbr_id_isnull(fibers[0]), NULL); 93 | fibers[1] = fbr_create(&context, "async_waiter", async_waiter, &arg, 0); 94 | fail_if(fbr_id_isnull(fibers[1]), NULL); 95 | 96 | retval = fbr_transfer(&context, fibers[1]); 97 | fail_unless(0 == retval, NULL); 98 | /* it should not have recieved async event yet; the next fiber will 99 | * do that 100 | */ 101 | fail_unless(0 == async_recvd); 102 | 103 | retval = fbr_transfer(&context, fibers[0]); 104 | fail_unless(0 == retval, NULL); 105 | 106 | /* Run the event loop */ 107 | ev_run(EV_DEFAULT, 0); 108 | 109 | fail_unless(1 == async_recvd); 110 | 111 | fbr_destroy(&context); 112 | } 113 | END_TEST 114 | 115 | TCase * async_wait_tcase(void) 116 | { 117 | TCase *tc_async_wait = tcase_create ("Async_wait"); 118 | tcase_add_test(tc_async_wait, test_async_wait); 119 | return tc_async_wait; 120 | } 121 | 122 | -------------------------------------------------------------------------------- /examples/sample_http_server/sample_http_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "http_parser.h" 11 | 12 | struct parser_arg { 13 | struct fbr_context *fctx; 14 | int fd; 15 | int finish; 16 | }; 17 | 18 | static char response[] = 19 | "HTTP/1.1 200 OK\n" 20 | "Content-Type: text/plain\n" 21 | "\n" 22 | "Hello World"; 23 | 24 | int on_headers_complete(http_parser* parser) 25 | { 26 | struct parser_arg *arg = parser->data; 27 | ssize_t retval; 28 | size_t size = strlen(response); 29 | retval = fbr_write_all(arg->fctx, arg->fd, response, size); 30 | if (retval != size) 31 | err(EXIT_FAILURE, "fbr_write_all"); 32 | arg->finish = 1; 33 | return 0; 34 | } 35 | 36 | static void conn_handler(struct fbr_context *fctx, void *_arg) 37 | { 38 | int fd = *(int *)_arg; 39 | time_t now; 40 | char *ct; 41 | http_parser_settings settings; 42 | http_parser parser; 43 | char buf[BUFSIZ]; 44 | size_t nparsed; 45 | ssize_t retval; 46 | struct parser_arg parg; 47 | 48 | memset(&settings, 0x00, sizeof(settings)); 49 | settings.on_headers_complete = on_headers_complete; 50 | http_parser_init(&parser, HTTP_REQUEST); 51 | parg.fctx = fctx; 52 | parg.fd = fd; 53 | parg.finish = 0; 54 | parser.data = &parg; 55 | 56 | for (;;) { 57 | retval = fbr_read(fctx, fd, buf, sizeof(buf)); 58 | if (0 > retval) 59 | err(EXIT_FAILURE, "fbr_read"); 60 | if (0 == retval) 61 | break; 62 | 63 | nparsed = http_parser_execute(&parser, &settings, buf, retval); 64 | 65 | if (nparsed != retval) 66 | errx(EXIT_FAILURE, "error parsing HTTP"); 67 | 68 | if (parg.finish) 69 | break; 70 | } 71 | 72 | shutdown(fd, SHUT_RDWR); 73 | close(fd); 74 | } 75 | 76 | static void acceptor(struct fbr_context *fctx, void *_arg) 77 | { 78 | int fd = *(int *)_arg; 79 | int client_fd; 80 | socklen_t peer_len; 81 | struct sockaddr_in peer_addr; 82 | fbr_id_t id; 83 | peer_len = sizeof(peer_addr); 84 | for (;;) { 85 | client_fd = fbr_accept(fctx, fd, (struct sockaddr *)&peer_addr, 86 | &peer_len); 87 | if (client_fd < 0) 88 | err(EXIT_FAILURE, "accept failed"); 89 | id = fbr_create(fctx, "handler", conn_handler, &client_fd, 0); 90 | if (fbr_id_isnull(id)) 91 | errx(EXIT_FAILURE, "unable to create a fiber"); 92 | fbr_transfer(fctx, id); 93 | 94 | } 95 | } 96 | 97 | int main(int argc, char *argv[]) 98 | { 99 | struct sockaddr_in sar; 100 | struct protoent *pe; 101 | int sa, sw; 102 | int port; 103 | struct fbr_context fbr; 104 | int retval; 105 | int yes = 1; 106 | fbr_id_t ticker_id; 107 | fbr_id_t acceptor_id; 108 | 109 | fbr_init(&fbr, EV_DEFAULT); 110 | signal(SIGPIPE, SIG_IGN); 111 | 112 | pe = getprotobyname("tcp"); 113 | sa = socket(AF_INET, SOCK_STREAM, pe->p_proto); 114 | if (0 > sa) 115 | err(EXIT_FAILURE, "socket"); 116 | retval = setsockopt(sa, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); 117 | if (retval) 118 | err(EXIT_FAILURE, "setsockopt"); 119 | sar.sin_family = AF_INET; 120 | sar.sin_addr.s_addr = INADDR_ANY; 121 | sar.sin_port = htons(12345); 122 | retval = bind(sa, (struct sockaddr *)&sar, sizeof(struct sockaddr_in)); 123 | if (retval) 124 | err(EXIT_FAILURE, "bind"); 125 | retval = listen(sa, 64); 126 | if (retval) 127 | err(EXIT_FAILURE, "listen"); 128 | 129 | acceptor_id = fbr_create(&fbr, "acceptor", acceptor, &sa, 0); 130 | if (fbr_id_isnull(acceptor_id)) 131 | errx(EXIT_FAILURE, "unable to create a fiber"); 132 | fbr_transfer(&fbr, acceptor_id); 133 | 134 | ev_run(EV_DEFAULT, 0); 135 | } 136 | -------------------------------------------------------------------------------- /bench/condvar.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | struct fiber_arg { 29 | struct fbr_mutex mutex1; 30 | struct fbr_cond_var cond1; 31 | int cond1_set; 32 | struct fbr_mutex mutex2; 33 | struct fbr_cond_var cond2; 34 | int cond2_set; 35 | size_t count; 36 | }; 37 | 38 | static void cond_fiber1(FBR_P_ void *_arg) 39 | { 40 | struct fiber_arg *arg = _arg; 41 | for (;;) { 42 | arg->cond1_set = 1; 43 | fbr_cond_signal(FBR_A_ &arg->cond1); 44 | while (0 == arg->cond2_set) { 45 | fbr_mutex_lock(FBR_A_ &arg->mutex2); 46 | fbr_cond_wait(FBR_A_ &arg->cond2, &arg->mutex2); 47 | fbr_mutex_unlock(FBR_A_ &arg->mutex2); 48 | } 49 | arg->cond2_set = 0; 50 | arg->count++; 51 | } 52 | } 53 | 54 | static void cond_fiber2(FBR_P_ void *_arg) 55 | { 56 | struct fiber_arg *arg = _arg; 57 | for (;;) { 58 | arg->cond2_set = 1; 59 | fbr_cond_signal(FBR_A_ &arg->cond2); 60 | while (0 == arg->cond1_set) { 61 | fbr_mutex_lock(FBR_A_ &arg->mutex1); 62 | fbr_cond_wait(FBR_A_ &arg->cond1, &arg->mutex1); 63 | fbr_mutex_unlock(FBR_A_ &arg->mutex1); 64 | } 65 | arg->cond1_set = 0; 66 | arg->count++; 67 | } 68 | } 69 | 70 | static void stats_fiber(FBR_P_ void *_arg) 71 | { 72 | struct fiber_arg *arg = _arg; 73 | size_t last; 74 | size_t diff; 75 | int count = 0; 76 | int max_samples = 100; 77 | for (;;) { 78 | last = arg->count; 79 | fbr_sleep(FBR_A_ 1.0); 80 | diff = arg->count - last; 81 | printf("%zd\n", diff); 82 | if (count++ > max_samples) { 83 | ev_break(fctx->__p->loop, EVBREAK_ALL); 84 | } 85 | } 86 | } 87 | 88 | int main() 89 | { 90 | struct fbr_context context; 91 | fbr_id_t fiber1, fiber2, fiber_stats; 92 | int retval; 93 | (void)retval; 94 | struct fiber_arg arg = { 95 | .count = 0 96 | }; 97 | 98 | fbr_init(&context, EV_DEFAULT); 99 | 100 | fbr_mutex_init(&context, &arg.mutex1); 101 | fbr_mutex_init(&context, &arg.mutex2); 102 | 103 | fbr_cond_init(&context, &arg.cond1); 104 | fbr_cond_init(&context, &arg.cond2); 105 | 106 | fiber1 = fbr_create(&context, "fiber1", cond_fiber1, &arg, 0); 107 | assert(!fbr_id_isnull(fiber1)); 108 | retval = fbr_transfer(&context, fiber1); 109 | assert(0 == retval); 110 | 111 | fiber2 = fbr_create(&context, "fiber2", cond_fiber2, &arg, 0); 112 | assert(!fbr_id_isnull(fiber2)); 113 | retval = fbr_transfer(&context, fiber2); 114 | assert(0 == retval); 115 | 116 | fiber_stats = fbr_create(&context, "fiber_stats", stats_fiber, &arg, 0); 117 | assert(!fbr_id_isnull(fiber_stats)); 118 | retval = fbr_transfer(&context, fiber_stats); 119 | assert(0 == retval); 120 | 121 | ev_run(EV_DEFAULT, 0); 122 | 123 | fbr_cond_destroy(&context, &arg.cond1); 124 | fbr_cond_destroy(&context, &arg.cond2); 125 | fbr_mutex_destroy(&context, &arg.mutex1); 126 | fbr_mutex_destroy(&context, &arg.mutex2); 127 | fbr_destroy(&context); 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | libevfibers (0.4.1) unstable; urgency=low 2 | 3 | * Fixed building with embedded libeio 4 | 5 | -- Konstantin Olkhovskiy Thu, 13 Mar 2014 19:22:02 +0400 6 | 7 | libevfibers (0.4.0) unstable; urgency=low 8 | 9 | * Added no-reclaim mode for a fiber 10 | * Got rid of 128 bit integers in favour of packed struct 11 | * Added fbr_ev_wait with timeout (fbr_ev_wait_to) 12 | * Assert for a yield in the root fiber 13 | * Added fbr_connect function and test case for it (thanks, Mikhail) 14 | * Added fbr_buffer_reset method 15 | * Implemented libeio support (configurable at build time) 16 | * Debian package includes libeio support by default 17 | * In-place download&build support for libeio (if it's missing on the system) 18 | * Replaced libvrb with in-house implementation 19 | * Fixed unit tests run inside of pbuilder (/dev/shm workaround) 20 | * Support for cmake-level embedding into other cmake projects 21 | * Default build type set to RelWithDebInfo 22 | * Support for building with lto support 23 | * Added fbr_system, fbr_popenN and fbr_waitpid calls 24 | * Added fbr_send and fbr_recv wrappers 25 | * Marked fiber memory pool APIs as deprecated 26 | * Dropped usage of fiber memory pools in tests 27 | * Valgrind is now an optional dependency 28 | * Added travis-ci support 29 | * Added missing build script (used in test coverage script) 30 | * Changing license to Apache License, Version 2.0 31 | * Improved fbr_ev_wait_one performance 32 | 33 | -- Konstantin Olkhovskiy Thu, 13 Mar 2014 17:19:22 +0400 34 | 35 | libevfibers (0.3.0) unstable; urgency=low 36 | 37 | * Fiber names are now gettable/settable 38 | * Also a name is now an array, embedded into a fiber structure to simplify 39 | memory management. 40 | * Support for fiber-local storage 41 | * fbr_reclaim fixes. Now when a fiber is reclaimed, it's filtered out from 42 | the stack to make sure that fbr_yield will not ield to a reclaimed fiber. 43 | Also fbr_reclaim of self will now never return. 44 | * Switched fbr_need_log and fbr_set_log_level to static inline 45 | * Now passing real fiber stack size to coro_create 46 | * Added ``active'' flag to destructor. To support this I've also added static 47 | and dynamic initializers for a destructor structure. 48 | * Moved fbr_mutex, fbr_cond_var, fbr_buffer into public API to allow stack 49 | allocation of these structures which should result in some speedup. 50 | * Protected libev watchers with desctructors. Now when doing fbr_read a 51 | destructor is set before fbr_ev_wait that will stop the watcher in case 52 | fiber is reclaimed while we were waiting. 53 | * ev_now() timestamp in default logger 54 | * Fixed fbr_ev_wait_one behaviour to match documentation 55 | * Added missing call of destructors upon fiber reclaim 56 | * Added stack-allocatable destructor API 57 | * More rethinking and reworking of the event facility (see 701e067) 58 | 59 | -- Konstantin Olkhovskiy Sun, 12 May 2013 12:57:10 +0400 60 | 61 | libevfibers (0.2.0) unstable; urgency=low 62 | 63 | * Dropped ``call'' and ``multicall'' interfaces alltogether 64 | * Added fbr_ev_wait and corresponding machinery 65 | * Added transactional ring buffer 66 | * Implemented error subsystem with f_errno 67 | * Fixed ABA problem with reclaimed fibers 68 | * Added logging system 69 | * Added conditional variables 70 | * Minor utility additions to API 71 | * Unit tests 72 | 73 | -- Konstantin Olkhovskiy Sun, 09 Dec 2012 21:00:33 +0400 74 | 75 | libevfibers (0.1.2) unstable; urgency=low 76 | 77 | * Fixed mutexes 78 | * Some unit tests 79 | 80 | -- Konstantin Olkhovskiy Sat, 13 Oct 2012 23:12:01 +0400 81 | 82 | libevfibers (0.1.1-1) unstable; urgency=low 83 | 84 | * Backtrace capturing is now optional. 85 | 86 | -- Konstantin Olkhovskiy Sat, 22 Sep 2012 18:30:40 +0400 87 | 88 | libevfibers (0.1.0-1) unstable; urgency=low 89 | 90 | * Initial Release. 91 | 92 | -- Konstantin Olkhovskiy Thu, 20 Sep 2012 21:26:32 +0400 93 | -------------------------------------------------------------------------------- /test/popen3.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | static void assert_pipe_content(int fd, const char *content) 32 | { 33 | ssize_t retval; 34 | const size_t len = strlen(content); 35 | char *buf; 36 | buf = alloca(len + 1); 37 | retval = read(fd, buf, len); 38 | fail_if(retval < 0); 39 | fail_unless((size_t)retval == len); 40 | retval = read(fd, buf, len); 41 | fail_unless(retval == 0); 42 | fail_if(memcmp(content, buf, len)); 43 | } 44 | 45 | static void assert_pipe_empty(int fd) 46 | { 47 | ssize_t retval; 48 | char buf[16]; 49 | retval = read(fd, buf, sizeof(buf)); 50 | fail_unless(retval == 0); 51 | } 52 | 53 | static void popen_fiber(FBR_P_ void *_arg) 54 | { 55 | pid_t pid; 56 | int retval; 57 | int stdin_w, stdout_r, stderr_r; 58 | //char buf[512]; 59 | (void)_arg; 60 | const char *echo_test_str = "this is mirror test message"; 61 | 62 | /* Simple stdout test */ 63 | pid = fbr_popen3( 64 | FBR_A_ "/bin/bash", 65 | (char *[]){ 66 | "/bin/bash", "-c", "echo 'simple stdout test'", NULL, 67 | }, 68 | (char *[]){NULL}, "/", &stdin_w, &stdout_r, &stderr_r); 69 | fail_unless(pid > 0); 70 | retval = fbr_waitpid(FBR_A_ pid); 71 | fail_unless(retval == 0); 72 | assert_pipe_content(stdout_r, "simple stdout test\n"); 73 | assert_pipe_empty(stderr_r); 74 | fail_if(close(stdin_w) != 0); 75 | fail_if(close(stdout_r) != 0); 76 | fail_if(close(stderr_r) != 0); 77 | 78 | /* Same as above, but no input and error requested */ 79 | pid = fbr_popen3( 80 | FBR_A_ "/bin/bash", 81 | (char *[]){ 82 | "/bin/bash", "-c", "echo 'simple stdout test2'", NULL, 83 | }, 84 | (char *[]){NULL}, "/", NULL, &stdout_r, NULL); 85 | fail_unless(pid > 0); 86 | retval = fbr_waitpid(FBR_A_ pid); 87 | fail_unless(retval == 0); 88 | assert_pipe_content(stdout_r, "simple stdout test2\n"); 89 | fail_if(close(stdout_r) != 0); 90 | 91 | /* Just run something */ 92 | pid = fbr_popen3( 93 | FBR_A_ "/bin/ls", 94 | (char *[]){ 95 | "/bin/ls", NULL, 96 | }, 97 | (char *[]){NULL}, NULL, NULL, NULL, NULL); 98 | fail_unless(pid > 0); 99 | retval = fbr_waitpid(FBR_A_ pid); 100 | fail_unless(retval == 0); 101 | 102 | /* Simple stderr test */ 103 | pid = fbr_popen3(FBR_A_ "/bin/bash", 104 | (char *[]){ 105 | "/bin/bash", "-c", 106 | "echo 'simple stderr test' >&2", NULL, 107 | }, 108 | (char *[]){NULL}, "/", &stdin_w, &stdout_r, &stderr_r); 109 | fail_unless(pid > 0); 110 | retval = fbr_waitpid(FBR_A_ pid); 111 | fail_unless(retval == 0); 112 | assert_pipe_empty(stdout_r); 113 | assert_pipe_content(stderr_r, "simple stderr test\n"); 114 | fail_if(close(stdin_w) != 0); 115 | fail_if(close(stdout_r) != 0); 116 | fail_if(close(stderr_r) != 0); 117 | 118 | /* Echo application test */ 119 | pid = fbr_popen3(FBR_A_ "/bin/cat", 120 | (char *[]){ 121 | "/bin/cat", NULL, 122 | }, 123 | (char *[]){NULL}, "/", &stdin_w, &stdout_r, &stderr_r); 124 | fail_unless(pid > 0); 125 | retval = write(stdin_w, echo_test_str, strlen(echo_test_str)); 126 | fail_unless(retval == (int)strlen(echo_test_str)); 127 | fail_if(close(stdin_w) != 0); 128 | 129 | retval = fbr_waitpid(FBR_A_ pid); 130 | fail_unless(retval == 0); 131 | assert_pipe_empty(stderr_r); 132 | assert_pipe_content(stdout_r, echo_test_str); 133 | fail_if(close(stdout_r) != 0); 134 | fail_if(close(stderr_r) != 0); 135 | } 136 | 137 | 138 | START_TEST(test_popen3) 139 | { 140 | int retval; 141 | fbr_id_t fiber = FBR_ID_NULL; 142 | struct fbr_context context; 143 | fbr_init(&context, EV_DEFAULT); 144 | signal(SIGPIPE, SIG_IGN); 145 | 146 | fiber = fbr_create(&context, "popen_fiber", popen_fiber, NULL, 0); 147 | fail_if(fbr_id_isnull(fiber)); 148 | retval = fbr_transfer(&context, fiber); 149 | fail_unless(0 == retval, NULL); 150 | 151 | ev_run(EV_DEFAULT, 0); 152 | fbr_destroy(&context); 153 | } 154 | END_TEST 155 | 156 | TCase *popen3_tcase(void) 157 | { 158 | TCase *tc_popen3 = tcase_create("popen3"); 159 | tcase_add_test(tc_popen3, test_popen3); 160 | return tc_popen3; 161 | } 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | About the project 2 | ================= 3 | 4 | libevfibers is a small C fiber library that uses libev based event loop and libcoro based coroutine context switching. As libcoro alone is barely enough to do something useful, this project aims at building a complete fiber API around it while leveraging libev's high performance and flexibility. 5 | 6 | You may ask why yet another fiber library, there are GNU Pth, State threads, etc. When I was looking at their API, I found it being too restrictive: you cannot use other event loop. For GNU Pth it's solely select based implementation, as for state threads --- they provide several implementations including poll, epoll, select though event loop is hidden underneath the public API and is not usable directly. I found another approach more sensible, namely: just put fiber layer on top of well-known and robust event loop implementation. 7 | 8 | So what's so cool about fibers? Fibers are user-space threads. User-space means that context switching from one fiber to an other fiber takes no effort from the kernel. There are different ways to achieve this, but it's not relevant here since libcoro already does all the dirty job. At top level you have a set of functions that execute on private stacks that do not intersect. Whenever such function is going to do some blocking operation, i.e. socket read, it calls fiber library wrapper, that asks event loop to transfer execution to this function whenever some data arrives, then it yields execution to some other fiber. From the function's point of view it runs in exclusive mode and blocks on all operations, but really other such functions execute while this one is waiting. Typically most of them are waiting for something and event loop dispatches the events. 9 | 10 | This approach helps a lot. Imagine that you have some function that requires 3 events. In classic asynchronous model you will have to arrange your function in 3 callbacks and register them in the event loop. On the other hand having one function waiting for 3 events in ``blocking'' fashion one after one is both more readable and maintainable. 11 | 12 | Then why use event loop when you have fancy callback-less fiber wrappers? Sometimes you just need a function that will set a flag in some object when a timer times out. Creating a fiber solely for this simple task is a bit awkward. 13 | 14 | libevfibers allows you to use fiber style wrappers for blocking operations as well as fall back to usual event loop style programming when you need it. 15 | 16 | Recent changes have brough you (yet unreleased) libeio wrapper support, which enables you to wrap all blocking POSIX API, such as file I/O, into non-blocking fiber calls. Additionally you can wrap any 3rd-party library (such as libcurl for example) with eio custom request and integrate the library into you fiber-enabled application. 17 | 18 | More information can be found on the [web site](http://lupus.github.io/libevfibers). 19 | 20 | Downloading and building 21 | ======================== 22 | 23 | There are tagged releases, but generally master should be stable, so I will provide download and build instructions for building master branch. 24 | 25 | There are some dependencies, which you need to get installed. For debian based derivatives you can use the following command: 26 | 27 | $ sudo apt-get install cmake libev-dev check 28 | 29 | It is unlikely that you have libeio installed, since it's not currently officially packaged. To aid you in building libeio-enabled version, there is a fetch&build support, which requires the following additonal packages: 30 | 31 | $ sudo apt-get install cvs libtool autoconf 32 | 33 | So now, after all the dependencies installed, you need to clone the repository: 34 | 35 | $ git clone git@github.com:Lupus/libevfibers.git 36 | 37 | The library comes with handy build.sh script, which handles cmake invocation and appropriate flags. So in case you have libeio installed on your system, the following should do the work: 38 | 39 | $ ./build.sh 40 | 41 | In case you dont, use the following command instead: 42 | 43 | $ ./build.sh +eioe 44 | 45 | This will checkout libeio from CVS and build it in-place. 46 | 47 | Who uses this project 48 | ===================== 49 | 50 | Although libevfibers is a relatively young project, it is currently used in production at NVIDIA, inside of its internal server software products. 51 | 52 | If you are using libevfibers in your environment, I would be glad to hear from you (see "Questions and feedback below")! 53 | 54 | Running unit tests 55 | ================== 56 | 57 | libevfibers come with extensive test suite. In order to run it, you need to do the following, given that you have built the library using the instructions above: 58 | 59 | $ ./build/test/evfibers_test 60 | 61 | Recently I have integrated Travis for unit testing of each commit. 62 | 63 | Here is the current status for master branch: [![Build Status](https://travis-ci.org/Lupus/libevfibers.png?branch=master)](https://travis-ci.org/Lupus/libevfibers) 64 | 65 | Documentation 66 | ============= 67 | 68 | The main header file has all public symbols documented with Doxygen. 69 | A snapshot of documentation is eventually generated for master branch and can be viewed [online](http://olkhovskiy.me/libevfibers/docs/current/index.html). 70 | 71 | A simple example can be found [in the source tree](https://github.com/Lupus/libevfibers/blob/master/examples/useless_time_server.c). 72 | 73 | Questions and feedback 74 | ====================== 75 | 76 | If you have any questions or suggestions --- feel free to use [project mailing list](http://www.freelists.org/list/libevfibers). 77 | 78 | If you happen to encounter a bug, please create a GitHub issue. 79 | 80 | Thanks for your interest in libevfibers! 81 | -------------------------------------------------------------------------------- /test/buffer.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "buffer.h" 29 | 30 | struct fiber_arg { 31 | struct fbr_buffer buffer; 32 | size_t write_size; 33 | size_t count; 34 | uint64_t magic; 35 | }; 36 | 37 | static void buffer_reader_fiber(FBR_P_ void *_arg) 38 | { 39 | struct fiber_arg *arg = _arg; 40 | struct fbr_buffer *buffer = &arg->buffer; 41 | size_t i; 42 | uint64_t *ptr; 43 | 44 | for (i = 0; i < 2 * arg->count; i++) { 45 | ptr = fbr_buffer_read_address(FBR_A_ buffer, arg->write_size); 46 | fail_if(NULL == ptr); 47 | fail_unless(*ptr == arg->magic); 48 | fbr_buffer_read_advance(FBR_A_ buffer); 49 | } 50 | 51 | } 52 | 53 | static void buffer_writer_fiber(FBR_P_ void *_arg) 54 | { 55 | struct fiber_arg *arg = _arg; 56 | struct fbr_buffer *buffer = &arg->buffer; 57 | size_t i; 58 | uint64_t *ptr; 59 | 60 | for (i = 0; i < arg->count; i++) { 61 | ptr = fbr_buffer_alloc_prepare(FBR_A_ buffer, arg->write_size); 62 | *ptr = arg->magic; 63 | fbr_buffer_alloc_commit(FBR_A_ buffer); 64 | } 65 | } 66 | 67 | START_TEST(test_buffer) 68 | { 69 | struct fbr_context context; 70 | fbr_id_t reader = FBR_ID_NULL, 71 | writer1 = FBR_ID_NULL, 72 | writer2 = FBR_ID_NULL; 73 | int retval; 74 | struct fiber_arg arg; 75 | 76 | fbr_init(&context, EV_DEFAULT); 77 | 78 | retval = fbr_buffer_init(&context, &arg.buffer, 0); 79 | fail_unless(0 == retval); 80 | arg.write_size = fbr_buffer_free_bytes(&context, &arg.buffer) / 3; 81 | arg.count = 1e3; 82 | arg.magic = 0xdeadbeef; 83 | 84 | reader = fbr_create(&context, "reader_buffer", buffer_reader_fiber, &arg, 0); 85 | fail_if(fbr_id_isnull(reader), NULL); 86 | writer1 = fbr_create(&context, "writer_buffer_1", buffer_writer_fiber, &arg, 0); 87 | fail_if(fbr_id_isnull(writer1), NULL); 88 | writer2 = fbr_create(&context, "writer_buffer_2", buffer_writer_fiber, &arg, 0); 89 | fail_if(fbr_id_isnull(writer2), NULL); 90 | 91 | 92 | retval = fbr_transfer(&context, reader); 93 | fail_unless(0 == retval, NULL); 94 | retval = fbr_transfer(&context, writer1); 95 | fail_unless(0 == retval, NULL); 96 | retval = fbr_transfer(&context, writer2); 97 | fail_unless(0 == retval, NULL); 98 | 99 | 100 | ev_run(EV_DEFAULT, 0); 101 | 102 | fail_unless(fbr_is_reclaimed(&context, writer1)); 103 | fail_unless(fbr_is_reclaimed(&context, writer2)); 104 | fail_unless(fbr_is_reclaimed(&context, reader)); 105 | 106 | fbr_buffer_destroy(&context, &arg.buffer); 107 | fbr_destroy(&context); 108 | } 109 | END_TEST 110 | 111 | static void buffer_basic_fiber(FBR_P_ _unused_ void *_arg) 112 | { 113 | struct fbr_buffer buffer; 114 | const uint64_t count = 1e3; 115 | uint64_t i; 116 | uint64_t *ptr; 117 | int retval; 118 | 119 | retval = fbr_buffer_init(FBR_A_ &buffer, count * sizeof(uint64_t)); 120 | fail_unless(0 == retval); 121 | 122 | ptr = fbr_buffer_alloc_prepare(FBR_A_ &buffer, 10 * sizeof(uint64_t)); 123 | fail_if(NULL == ptr); 124 | fbr_buffer_alloc_abort(FBR_A_ &buffer); 125 | 126 | for (i = 0; i < count; i++) { 127 | ptr = fbr_buffer_alloc_prepare(FBR_A_ &buffer, sizeof(uint64_t)); 128 | *ptr = i; 129 | fbr_buffer_alloc_commit(FBR_A_ &buffer); 130 | } 131 | 132 | retval = fbr_buffer_resize(FBR_A_ &buffer, 0); 133 | fail_unless(0 == retval); 134 | retval = fbr_buffer_resize(FBR_A_ &buffer, count * sizeof(uint64_t)); 135 | fail_unless(0 == retval); 136 | retval = fbr_buffer_resize(FBR_A_ &buffer, count * sizeof(uint64_t) * 2); 137 | fail_unless(0 == retval); 138 | 139 | ptr = fbr_buffer_read_address(FBR_A_ &buffer, sizeof(uint64_t)); 140 | fail_if(NULL == ptr); 141 | fbr_buffer_read_discard(FBR_A_ &buffer); 142 | 143 | for (i = 0; i < count; i++) { 144 | ptr = fbr_buffer_read_address(FBR_A_ &buffer, sizeof(uint64_t)); 145 | fail_if(NULL == ptr); 146 | fail_unless(*ptr == i); 147 | fbr_buffer_read_advance(FBR_A_ &buffer); 148 | } 149 | 150 | fbr_buffer_destroy(FBR_A_ &buffer); 151 | } 152 | 153 | START_TEST(test_buffer_basic) 154 | { 155 | struct fbr_context context; 156 | fbr_id_t fiber; 157 | int retval; 158 | 159 | fbr_init(&context, EV_DEFAULT); 160 | 161 | fiber = fbr_create(&context, "basic_buffer", buffer_basic_fiber, NULL, 0); 162 | fail_if(fbr_id_isnull(fiber), NULL); 163 | 164 | retval = fbr_transfer(&context, fiber); 165 | fail_unless(0 == retval, NULL); 166 | 167 | ev_run(EV_DEFAULT, 0); 168 | 169 | fail_unless(fbr_is_reclaimed(&context, fiber)); 170 | 171 | fbr_destroy(&context); 172 | 173 | } 174 | END_TEST 175 | 176 | 177 | TCase * buffer_tcase(void) 178 | { 179 | TCase *tc_buffer = tcase_create ("Buffer"); 180 | tcase_add_test(tc_buffer, test_buffer_basic); 181 | tcase_add_test(tc_buffer, test_buffer); 182 | return tc_buffer; 183 | } 184 | -------------------------------------------------------------------------------- /include/evfibers/eio.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _FBR_EIO_H_ 20 | #define _FBR_EIO_H_ 21 | /** 22 | * @file evfibers/eio.h 23 | * This file contains API for libeio fiber wrappers. 24 | * 25 | * Wrapper functions are not documented as they clone the libeio prototypes and 26 | * their documenting would result in useless copy'n'paste here. libeio 27 | * documentation can be used as a reference on this functions. The only 28 | * difference is that first argument in the wrappers is always fiber context, 29 | * and eio_cb and data pointer are passed internally, and so are not present in 30 | * the prototypes. 31 | */ 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | #include 38 | #ifndef FBR_EIO_ENABLED 39 | # error "This build of libevfibers lacks support for libeio" 40 | #endif 41 | #include 42 | #include 43 | #include 44 | #include 45 | #ifdef FBR_USE_EMBEDDED_EIO 46 | #include 47 | #else 48 | #include 49 | #endif 50 | #include 51 | 52 | /** 53 | * eio custom callback function type. 54 | */ 55 | typedef eio_ssize_t (*fbr_eio_custom_func_t)(void *data); 56 | 57 | /** 58 | * eio event. 59 | * 60 | * This event struct can represent an eio event. 61 | * @see fbr_ev_upcast 62 | * @see fbr_ev_wait 63 | */ 64 | struct fbr_ev_eio { 65 | eio_req *req; /*!< the libeio request itself */ 66 | fbr_eio_custom_func_t custom_func; 67 | void *custom_arg; 68 | struct fbr_ev_base ev_base; 69 | }; 70 | 71 | /** 72 | * Initializer for eio event. 73 | * 74 | * This functions properly initializes fbr_ev_eio struct. You should not do 75 | * it manually. 76 | * @see fbr_ev_eio 77 | * @see fbr_ev_wait 78 | */ 79 | void fbr_ev_eio_init(FBR_P_ struct fbr_ev_eio *ev, eio_req *req); 80 | 81 | /** 82 | * Initialization routine for libeio fiber wrapper. 83 | * 84 | * This functions initializes libeio and sets up the necessary glue code to 85 | * interact with libev (and in turn libevfibers). 86 | * 87 | * Must be called only once, uses EV_DEFAULT event loop internally, but any 88 | * fiber scheduler can interact with libeio independently. 89 | * @see fbr_ev_eio 90 | * @see fbr_ev_wait 91 | */ 92 | void fbr_eio_init(); 93 | 94 | int fbr_eio_open(FBR_P_ const char *path, int flags, mode_t mode, int pri); 95 | int fbr_eio_truncate(FBR_P_ const char *path, off_t offset, int pri); 96 | int fbr_eio_chown(FBR_P_ const char *path, uid_t uid, gid_t gid, int pri); 97 | int fbr_eio_chmod(FBR_P_ const char *path, mode_t mode, int pri); 98 | int fbr_eio_mkdir(FBR_P_ const char *path, mode_t mode, int pri); 99 | int fbr_eio_rmdir(FBR_P_ const char *path, int pri); 100 | int fbr_eio_unlink(FBR_P_ const char *path, int pri); 101 | int fbr_eio_utime(FBR_P_ const char *path, eio_tstamp atime, eio_tstamp mtime, 102 | int pri); 103 | int fbr_eio_mknod(FBR_P_ const char *path, mode_t mode, dev_t dev, int pri); 104 | int fbr_eio_link(FBR_P_ const char *path, const char *new_path, int pri); 105 | int fbr_eio_symlink(FBR_P_ const char *path, const char *new_path, int pri); 106 | int fbr_eio_rename(FBR_P_ const char *path, const char *new_path, int pri); 107 | int fbr_eio_mlock(FBR_P_ void *addr, size_t length, int pri); 108 | int fbr_eio_close(FBR_P_ int fd, int pri); 109 | int fbr_eio_sync(FBR_P_ int pri); 110 | int fbr_eio_fsync(FBR_P_ int fd, int pri); 111 | int fbr_eio_fdatasync(FBR_P_ int fd, int pri); 112 | int fbr_eio_futime(FBR_P_ int fd, eio_tstamp atime, eio_tstamp mtime, int pri); 113 | int fbr_eio_ftruncate(FBR_P_ int fd, off_t offset, int pri); 114 | int fbr_eio_fchmod(FBR_P_ int fd, mode_t mode, int pri); 115 | int fbr_eio_fchown(FBR_P_ int fd, uid_t uid, gid_t gid, int pri); 116 | int fbr_eio_dup2(FBR_P_ int fd, int fd2, int pri); 117 | ssize_t fbr_eio_seek(FBR_P_ int fd, off_t offset, int whence, int pri); 118 | ssize_t fbr_eio_read(FBR_P_ int fd, void *buf, size_t length, off_t offset, 119 | int pri); 120 | ssize_t fbr_eio_write(FBR_P_ int fd, void *buf, size_t length, off_t offset, 121 | int pri); 122 | int fbr_eio_mlockall(FBR_P_ int flags, int pri); 123 | int fbr_eio_msync(FBR_P_ void *addr, size_t length, int flags, int pri); 124 | int fbr_eio_readlink(FBR_P_ const char *path, char *buf, size_t size, int pri); 125 | int fbr_eio_realpath(FBR_P_ const char *path, char *buf, size_t size, int pri); 126 | int fbr_eio_stat(FBR_P_ const char *path, EIO_STRUCT_STAT *statdata, int pri); 127 | int fbr_eio_lstat(FBR_P_ const char *path, EIO_STRUCT_STAT *statdata, int pri); 128 | int fbr_eio_fstat(FBR_P_ int fd, EIO_STRUCT_STAT *statdata, int pri); 129 | int fbr_eio_statvfs(FBR_P_ const char *path, EIO_STRUCT_STATVFS *statdata, 130 | int pri); 131 | int fbr_eio_fstatvfs(FBR_P_ int fd, EIO_STRUCT_STATVFS *statdata, int pri); 132 | int fbr_eio_readahead(FBR_P_ int fd, off_t offset, size_t length, int pri); 133 | int fbr_eio_sendfile(FBR_P_ int out_fd, int in_fd, off_t in_offset, 134 | size_t length, int pri); 135 | int fbr_eio_readahead(FBR_P_ int fd, off_t offset, size_t length, int pri); 136 | int fbr_eio_syncfs(FBR_P_ int fd, int pri); 137 | int fbr_eio_sync_file_range(FBR_P_ int fd, off_t offset, size_t nbytes, 138 | unsigned int flags, int pri); 139 | int fbr_eio_fallocate(FBR_P_ int fd, int mode, off_t offset, off_t len, 140 | int pri); 141 | eio_ssize_t fbr_eio_custom(FBR_P_ fbr_eio_custom_func_t func, void *data, 142 | int pri); 143 | 144 | #ifdef __cplusplus 145 | } 146 | #endif 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /test/reclaim.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "reclaim.h" 24 | 25 | static fbr_id_t new_parent; 26 | static fbr_id_t test_fiber; 27 | 28 | static void reclaim_fiber1(_unused_ FBR_P_ _unused_ void *_arg) 29 | { 30 | fail_unless(fbr_id_isnull(fbr_parent(FBR_A))); 31 | return; 32 | } 33 | 34 | static void reclaim_fiber2(_unused_ FBR_P_ _unused_ void *_arg) 35 | { 36 | fail_unless(fbr_id_eq(fbr_self(FBR_A), test_fiber)); 37 | fbr_disown(FBR_A_ new_parent); 38 | fail_unless(fbr_id_eq(new_parent, fbr_parent(FBR_A))); 39 | for (;;) 40 | fbr_yield(FBR_A); 41 | } 42 | 43 | static void reclaim_fiber3(_unused_ FBR_P_ _unused_ void *_arg) 44 | { 45 | test_fiber = fbr_create(FBR_A_ "test_fiber", reclaim_fiber2, NULL, 0); 46 | fail_if(fbr_id_isnull(test_fiber)); 47 | fbr_yield(FBR_A); 48 | return; 49 | } 50 | 51 | 52 | START_TEST(test_disown) 53 | { 54 | struct fbr_context context; 55 | fbr_id_t fiber = FBR_ID_NULL; 56 | int retval; 57 | 58 | fbr_init(&context, EV_DEFAULT); 59 | 60 | fiber = fbr_create(&context, "reclaim_fiber", reclaim_fiber3, NULL, 0); 61 | fail_if(fbr_id_isnull(fiber), NULL); 62 | 63 | new_parent = fbr_create(&context, "new_fiber", reclaim_fiber1, NULL, 0); 64 | fail_if(fbr_id_isnull(new_parent), NULL); 65 | 66 | retval = fbr_transfer(&context, fiber); 67 | fail_unless(0 == retval, NULL); 68 | 69 | fail_if(fbr_id_isnull(test_fiber), NULL); 70 | fail_unless(0 == fbr_is_reclaimed(&context, test_fiber), NULL); 71 | 72 | retval = fbr_transfer(&context, test_fiber); 73 | fail_unless(0 == retval, NULL); 74 | 75 | retval = fbr_reclaim(&context, fiber); 76 | fail_unless(0 == retval, NULL); 77 | fail_unless(fbr_is_reclaimed(&context, fiber), NULL); 78 | 79 | retval = fbr_transfer(&context, test_fiber); 80 | fail_unless(0 == retval, NULL); 81 | 82 | retval = fbr_reclaim(&context, new_parent); 83 | fail_unless(0 == retval, NULL); 84 | fail_unless(fbr_is_reclaimed(&context, new_parent), NULL); 85 | fail_unless(fbr_is_reclaimed(&context, test_fiber), NULL); 86 | 87 | fbr_destroy(&context); 88 | } 89 | END_TEST 90 | 91 | START_TEST(test_reclaim) 92 | { 93 | struct fbr_context context; 94 | fbr_id_t fiber = FBR_ID_NULL; 95 | fbr_id_t new_fiber = FBR_ID_NULL; 96 | int retval; 97 | 98 | fbr_init(&context, EV_DEFAULT); 99 | fiber = fbr_create(&context, "reclaim_fiber", reclaim_fiber1, NULL, 0); 100 | fail_if(fbr_id_isnull(fiber), NULL); 101 | 102 | retval = fbr_transfer(&context, fiber); 103 | fail_unless(0 == retval, NULL); 104 | 105 | new_fiber = fbr_create(&context, "reclaim_fiber2", reclaim_fiber1, NULL, 0); 106 | fail_if(fbr_id_isnull(new_fiber), NULL); 107 | /* should be same pointer */ 108 | fail_unless(fiber.p == new_fiber.p); 109 | /* old id should be invalid any more to avoid ABA problem */ 110 | retval = fbr_transfer(&context, fiber); 111 | fail_unless(-1 == retval, NULL); 112 | fail_unless(FBR_ENOFIBER == context.f_errno, NULL); 113 | 114 | /* new fiber should be called without problems */ 115 | retval = fbr_transfer(&context, new_fiber); 116 | fail_unless(0 == retval, NULL); 117 | /* but consequent call should fail as fiber is reclaimed */ 118 | retval = fbr_transfer(&context, new_fiber); 119 | fail_unless(-1 == retval, NULL); 120 | fail_unless(FBR_ENOFIBER == context.f_errno, NULL); 121 | 122 | fbr_destroy(&context); 123 | } 124 | END_TEST 125 | 126 | static void no_reclaim_fiber2(FBR_P_ _unused_ void *_arg) 127 | { 128 | fbr_set_noreclaim(FBR_A_ fbr_self(FBR_A)); 129 | fbr_sleep(FBR_A_ 1.5); 130 | fbr_set_reclaim(FBR_A_ fbr_self(FBR_A)); 131 | } 132 | 133 | static void no_reclaim_fiber1(FBR_P_ _unused_ void *_arg) 134 | { 135 | fbr_id_t fiber = FBR_ID_NULL; 136 | int retval; 137 | ev_tstamp ts1, ts2; 138 | 139 | fbr_sleep(FBR_A_ 0.1); 140 | 141 | fiber = fbr_create(FBR_A_ "no_reclaim_fiber2", no_reclaim_fiber2, 142 | NULL, 0); 143 | fail_if(fbr_id_isnull(fiber)); 144 | 145 | retval = fbr_transfer(FBR_A_ fiber); 146 | fail_unless(0 == retval); 147 | 148 | ts1 = ev_now(EV_DEFAULT); 149 | retval = fbr_reclaim(FBR_A_ fiber); 150 | fail_unless(0 == retval); 151 | ts2 = ev_now(EV_DEFAULT); 152 | fbr_log_e(FBR_A_ "ts2 - ts1 = %f", ts2 - ts1); 153 | fail_unless(ts2 - ts1 > 1.5); 154 | } 155 | 156 | START_TEST(test_no_reclaim) 157 | { 158 | struct fbr_context context; 159 | fbr_id_t fiber = FBR_ID_NULL; 160 | int retval; 161 | 162 | fbr_init(&context, EV_DEFAULT); 163 | fiber = fbr_create(&context, "no_reclaim_fiber", no_reclaim_fiber1, 164 | NULL, 0); 165 | fail_if(fbr_id_isnull(fiber)); 166 | 167 | retval = fbr_transfer(&context, fiber); 168 | fail_unless(0 == retval, NULL); 169 | 170 | ev_run(EV_DEFAULT, 0); 171 | 172 | fbr_destroy(&context); 173 | } 174 | END_TEST 175 | 176 | START_TEST(test_user_data) 177 | { 178 | struct fbr_context context; 179 | fbr_id_t fiber = FBR_ID_NULL; 180 | int retval; 181 | void *ptr = (void *)0xdeadbeaf; 182 | 183 | fbr_init(&context, EV_DEFAULT); 184 | 185 | fiber = fbr_create(&context, "null_fiber", NULL, NULL, 0); 186 | fail_if(fbr_id_isnull(fiber)); 187 | 188 | retval = fbr_set_user_data(&context, fiber, ptr); 189 | fail_unless(0 == retval); 190 | 191 | fail_if(ptr != fbr_get_user_data(&context, fiber)); 192 | 193 | fbr_destroy(&context); 194 | } 195 | END_TEST 196 | 197 | 198 | TCase * reclaim_tcase(void) 199 | { 200 | TCase *tc_reclaim = tcase_create ("Reclaim"); 201 | tcase_add_test(tc_reclaim, test_reclaim); 202 | tcase_add_test(tc_reclaim, test_no_reclaim); 203 | tcase_add_test(tc_reclaim, test_disown); 204 | tcase_add_test(tc_reclaim, test_user_data); 205 | return tc_reclaim; 206 | } 207 | -------------------------------------------------------------------------------- /include/evfibers_private/fiber.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #ifndef _FBR_FIBER_PRIVATE_H_ 20 | #define _FBR_FIBER_PRIVATE_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define max(a,b) ({ \ 31 | const typeof(a) __tmp_a = (a); \ 32 | const typeof(b) __tmp_b = (b); \ 33 | __tmp_a > __tmp_b ? __tmp_a : __tmp_b; \ 34 | }) 35 | 36 | #define min(a,b) ({ \ 37 | const typeof(a) __tmp_a = (a); \ 38 | const typeof(b) __tmp_b = (b); \ 39 | __tmp_a < __tmp_b ? __tmp_a : __tmp_b; \ 40 | }) 41 | 42 | #define _unused_ __attribute__((unused)) 43 | 44 | #ifndef LIST_FOREACH_SAFE 45 | #define LIST_FOREACH_SAFE(var, head, field, next_var) \ 46 | for ((var) = ((head)->lh_first); \ 47 | (var) && ((next_var) = ((var)->field.le_next), 1); \ 48 | (var) = (next_var)) 49 | 50 | #endif 51 | 52 | #ifndef TAILQ_FOREACH_SAFE 53 | #define TAILQ_FOREACH_SAFE(var, head, field, next_var) \ 54 | for ((var) = ((head)->tqh_first); \ 55 | (var) ? ({ (next_var) = ((var)->field.tqe_next); 1; }) \ 56 | : 0; \ 57 | (var) = (next_var)) 58 | #endif 59 | 60 | 61 | #define ENSURE_ROOT_FIBER do { \ 62 | assert(fctx->__p->sp->fiber == &fctx->__p->root); \ 63 | } while (0) 64 | 65 | #define CURRENT_FIBER (fctx->__p->sp->fiber) 66 | #define CURRENT_FIBER_ID (fbr_id_pack(CURRENT_FIBER)) 67 | #define CALLED_BY_ROOT ((fctx->__p->sp - 1)->fiber == &fctx->__p->root) 68 | 69 | #define unpack_transfer_errno(value, ptr, id) \ 70 | do { \ 71 | if (-1 == fbr_id_unpack(fctx, ptr, id)) \ 72 | return (value); \ 73 | } while (0) 74 | 75 | #define return_success(value) \ 76 | do { \ 77 | fctx->f_errno = FBR_SUCCESS; \ 78 | return (value); \ 79 | } while (0) 80 | 81 | #define return_error(value, code) \ 82 | do { \ 83 | fctx->f_errno = (code); \ 84 | return (value); \ 85 | } while (0) 86 | 87 | 88 | #ifndef LIST_FOREACH_SAFE 89 | #define LIST_FOREACH_SAFE(var, head, field, next_var) \ 90 | for ((var) = ((head)->lh_first); \ 91 | (var) && ((next_var) = ((var)->field.le_next), 1); \ 92 | (var) = (next_var)) 93 | 94 | #endif 95 | 96 | #ifndef TAILQ_FOREACH_SAFE 97 | #define TAILQ_FOREACH_SAFE(var, head, field, next_var) \ 98 | for ((var) = ((head)->tqh_first); \ 99 | (var) ? ({ (next_var) = ((var)->field.tqe_next); 1; }) \ 100 | : 0; \ 101 | (var) = (next_var)) 102 | #endif 103 | 104 | 105 | #define ENSURE_ROOT_FIBER do { \ 106 | assert(fctx->__p->sp->fiber == &fctx->__p->root); \ 107 | } while (0) 108 | 109 | #define CURRENT_FIBER (fctx->__p->sp->fiber) 110 | #define CURRENT_FIBER_ID (fbr_id_pack(CURRENT_FIBER)) 111 | #define CALLED_BY_ROOT ((fctx->__p->sp - 1)->fiber == &fctx->__p->root) 112 | 113 | #define unpack_transfer_errno(value, ptr, id) \ 114 | do { \ 115 | if (-1 == fbr_id_unpack(fctx, ptr, id)) \ 116 | return (value); \ 117 | } while (0) 118 | 119 | #define return_success(value) \ 120 | do { \ 121 | fctx->f_errno = FBR_SUCCESS; \ 122 | return (value); \ 123 | } while (0) 124 | 125 | #define return_error(value, code) \ 126 | do { \ 127 | fctx->f_errno = (code); \ 128 | return (value); \ 129 | } while (0) 130 | 131 | 132 | struct mem_pool { 133 | void *ptr; 134 | fbr_alloc_destructor_func_t destructor; 135 | void *destructor_context; 136 | LIST_ENTRY(mem_pool) entries; 137 | }; 138 | 139 | LIST_HEAD(mem_pool_list, mem_pool); 140 | 141 | TAILQ_HEAD(fiber_destructor_tailq, fbr_destructor); 142 | LIST_HEAD(fiber_list, fbr_fiber); 143 | 144 | struct fbr_fiber { 145 | uint64_t id; 146 | char name[FBR_MAX_FIBER_NAME]; 147 | fbr_fiber_func_t func; 148 | void *func_arg; 149 | coro_context ctx; 150 | char *stack; 151 | size_t stack_size; 152 | struct { 153 | struct fbr_ev_base **waiting; 154 | int arrived; 155 | } ev; 156 | struct trace_info reclaim_tinfo; 157 | struct fiber_list children; 158 | struct fbr_fiber *parent; 159 | struct mem_pool_list pool; 160 | struct { 161 | LIST_ENTRY(fbr_fiber) reclaimed; 162 | LIST_ENTRY(fbr_fiber) children; 163 | } entries; 164 | struct fiber_destructor_tailq destructors; 165 | void *user_data; 166 | void *key_data[FBR_MAX_KEY]; 167 | int no_reclaim; 168 | int want_reclaim; 169 | struct fbr_cond_var reclaim_cond; 170 | }; 171 | 172 | TAILQ_HEAD(mutex_tailq, fbr_mutex); 173 | 174 | struct fbr_stack_item { 175 | struct fbr_fiber *fiber; 176 | struct trace_info tinfo; 177 | }; 178 | 179 | struct fbr_context_private { 180 | struct fbr_stack_item stack[FBR_CALL_STACK_SIZE]; 181 | struct fbr_stack_item *sp; 182 | struct fbr_fiber root; 183 | struct fiber_list reclaimed; 184 | struct ev_async pending_async; 185 | struct fbr_id_tailq pending_fibers; 186 | int backtraces_enabled; 187 | uint64_t last_id; 188 | uint64_t key_free_mask; 189 | const char *buffer_file_pattern; 190 | 191 | struct ev_loop *loop; 192 | }; 193 | 194 | struct fbr_mq { 195 | struct fbr_context *fctx; 196 | void **rb; 197 | unsigned head; 198 | unsigned tail; 199 | unsigned max; 200 | int flags; 201 | struct fbr_cond_var bytes_available_cond; 202 | struct fbr_cond_var bytes_freed_cond; 203 | }; 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /test/mutex.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "mutex.h" 24 | 25 | struct fiber_arg { 26 | struct fbr_mutex *mutex; 27 | int *flag_ptr; 28 | fbr_id_t *fibers; 29 | int count; 30 | }; 31 | 32 | static void mutex_fiber1(FBR_P_ void *_arg) 33 | { 34 | struct fiber_arg *arg = _arg; 35 | struct fbr_mutex *mutex = arg->mutex; 36 | fail_unless(fbr_mutex_trylock(FBR_A_ mutex), NULL); 37 | fbr_yield(FBR_A); 38 | fbr_mutex_unlock(FBR_A_ mutex); 39 | fbr_yield(FBR_A); 40 | } 41 | 42 | static void mutex_fiber2(FBR_P_ void *_arg) 43 | { 44 | struct fiber_arg *arg = _arg; 45 | struct fbr_mutex *mutex = arg->mutex; 46 | fail_if(fbr_mutex_trylock(FBR_A_ mutex), NULL); 47 | fbr_yield(FBR_A); 48 | } 49 | 50 | static void mutex_fiber3(FBR_P_ void *_arg) 51 | { 52 | struct fiber_arg *arg = _arg; 53 | struct fbr_mutex *mutex = arg->mutex; 54 | int *flag_ptr = arg->flag_ptr; 55 | fbr_mutex_lock(FBR_A_ mutex); 56 | *flag_ptr = 1; 57 | fbr_yield(FBR_A); 58 | } 59 | 60 | static void mutex_fiber4(FBR_P_ void *_arg) 61 | { 62 | struct fiber_arg *arg = _arg; 63 | struct fbr_mutex *mutex = arg->mutex; 64 | fbr_mutex_lock(FBR_A_ mutex); 65 | fail("Should never get here"); 66 | } 67 | 68 | START_TEST(test_mutex) 69 | { 70 | struct fbr_context context; 71 | fbr_id_t fibers[5] = { 72 | FBR_ID_NULL, 73 | FBR_ID_NULL, 74 | FBR_ID_NULL, 75 | FBR_ID_NULL, 76 | FBR_ID_NULL, 77 | }; 78 | struct fbr_mutex mutex; 79 | int flag = 0; 80 | int retval; 81 | struct fiber_arg arg = { 82 | .flag_ptr = &flag 83 | }; 84 | 85 | fbr_init(&context, EV_DEFAULT); 86 | 87 | fbr_mutex_init(&context, &mutex); 88 | arg.mutex = &mutex; 89 | 90 | fibers[0] = fbr_create(&context, "mutex1", mutex_fiber1, &arg, 0); 91 | fail_if(fbr_id_isnull(fibers[0]), NULL); 92 | fibers[1] = fbr_create(&context, "mutex2", mutex_fiber2, &arg, 0); 93 | fail_if(fbr_id_isnull(fibers[1]), NULL); 94 | fibers[2] = fbr_create(&context, "mutex3", mutex_fiber3, &arg, 0); 95 | fail_if(fbr_id_isnull(fibers[2]), NULL); 96 | fibers[3] = fbr_create(&context, "mutex4", mutex_fiber4, &arg, 0); 97 | fail_if(fbr_id_isnull(fibers[3]), NULL); 98 | 99 | /* ``mutex1'' fiber will aquire the mutex and yield */ 100 | retval = fbr_transfer(&context, fibers[0]); 101 | fail_unless(0 == retval, NULL); 102 | /* so we make sure that it holds the mutex */ 103 | fail_unless(fbr_id_eq(mutex.locked_by, fibers[0]), NULL); 104 | 105 | /* ``mutex2'' fiber tries to lock and yields */ 106 | retval = fbr_transfer(&context, fibers[1]); 107 | fail_unless(0 == retval, NULL); 108 | /* so we make sure that ``mutex1'' still holds the mutex */ 109 | fail_unless(fbr_id_eq(mutex.locked_by, fibers[0]), NULL); 110 | 111 | /* ``mutex3'' fiber blocks on mutex lock and yields */ 112 | retval = fbr_transfer(&context, fibers[2]); 113 | fail_unless(0 == retval, NULL); 114 | /* we still expect ``mutex1'' to hold the mutex */ 115 | fail_unless(fbr_id_eq(mutex.locked_by, fibers[0]), NULL); 116 | 117 | /* ``mutex4'' fiber blocks on mutex lock as well */ 118 | retval = fbr_transfer(&context, fibers[3]); 119 | fail_unless(0 == retval, NULL); 120 | /* ``mutex'' shoud still hold the mutex */ 121 | fail_unless(fbr_id_eq(mutex.locked_by, fibers[0]), NULL); 122 | 123 | /* ``mutex1'' releases the mutex */ 124 | retval = fbr_transfer(&context, fibers[0]); 125 | fail_unless(0 == retval, NULL); 126 | /* now mutex should be acquired by the next fiber in the queue: 127 | * ``mutex3'' 128 | */ 129 | fail_unless(fbr_id_eq(mutex.locked_by, fibers[2]), NULL); 130 | 131 | /* Run the event loop once */ 132 | ev_run(EV_DEFAULT, EVRUN_ONCE); 133 | 134 | /* ensure that it's still locked by ``mutex3'' */ 135 | fail_unless(fbr_id_eq(mutex.locked_by, fibers[2]), NULL); 136 | fail_if(0 == flag, NULL); 137 | 138 | /* Run event loot to make sure async watcher stops itself */ 139 | ev_run(EV_DEFAULT, 0); 140 | 141 | fbr_mutex_destroy(&context, &mutex); 142 | fbr_destroy(&context); 143 | } 144 | END_TEST 145 | 146 | static void mutex_fiber5(FBR_P_ void *_arg) 147 | { 148 | struct fiber_arg *arg = _arg; 149 | struct fbr_mutex *mutex = arg->mutex; 150 | int *flag_ptr = arg->flag_ptr; 151 | int i, old = -1; 152 | const int repeat = 10; 153 | const ev_tstamp sleep_interval = 0.01; 154 | fbr_mutex_lock(FBR_A_ mutex); 155 | for(i = 0; i < 2 * repeat; i++) { 156 | if(old >= 0) 157 | fail_unless(*flag_ptr == old, NULL); 158 | if(i < repeat) 159 | *flag_ptr += 1; 160 | else 161 | *flag_ptr -= 1; 162 | old = *flag_ptr; 163 | fbr_sleep(FBR_A_ sleep_interval); 164 | } 165 | fbr_mutex_unlock(FBR_A_ mutex); 166 | } 167 | 168 | static void mutex_fiber6(FBR_P_ void *_arg) 169 | { 170 | struct fiber_arg *arg = _arg; 171 | fbr_id_t *fibers = arg->fibers; 172 | int count = arg->count; 173 | int i; 174 | const ev_tstamp sleep_interval = 0.01; 175 | int retval; 176 | for(;;) { 177 | for(i = 0; i < count; i++) { 178 | if(fbr_is_reclaimed(FBR_A_ fibers[i])) 179 | goto finish; 180 | else { 181 | retval = fbr_transfer(FBR_A_ fibers[i]); 182 | fail_unless(0 == retval, NULL); 183 | } 184 | } 185 | fbr_sleep(FBR_A_ sleep_interval); 186 | } 187 | finish: 188 | return; 189 | } 190 | 191 | START_TEST(test_mutex_evloop) 192 | { 193 | #define fiber_count 10 194 | int i; 195 | struct fbr_context context; 196 | fbr_id_t fibers[fiber_count]; 197 | fbr_id_t extra = FBR_ID_NULL; 198 | struct fbr_mutex mutex; 199 | int flag = 0; 200 | int retval; 201 | struct fiber_arg arg = { 202 | .flag_ptr = &flag, 203 | .fibers = fibers, 204 | .count = fiber_count 205 | }; 206 | 207 | for (i = 0; i < fiber_count; i++) 208 | fibers[i] = FBR_ID_NULL; 209 | 210 | fbr_mutex_init(&context, &mutex); 211 | arg.mutex = &mutex; 212 | 213 | fbr_init(&context, EV_DEFAULT); 214 | for(i = 0; i < fiber_count; i++) { 215 | fibers[i] = fbr_create(&context, "fiber_i", mutex_fiber5, &arg, 0); 216 | fail_if(fbr_id_isnull(fibers[i]), NULL); 217 | retval = fbr_transfer(&context, fibers[i]); 218 | fail_unless(0 == retval, NULL); 219 | } 220 | 221 | extra = fbr_create(&context, "fiber_extra", mutex_fiber6, &arg, 0); 222 | fail_if(fbr_id_isnull(extra), NULL); 223 | retval = fbr_transfer(&context, extra); 224 | fail_unless(0 == retval, NULL); 225 | 226 | ev_run(EV_DEFAULT, 0); 227 | 228 | fbr_mutex_destroy(&context, &mutex); 229 | fbr_destroy(&context); 230 | #undef fiber_count 231 | } 232 | END_TEST 233 | 234 | TCase * mutex_tcase(void) 235 | { 236 | TCase *tc_mutex = tcase_create ("Mutex"); 237 | tcase_add_test(tc_mutex, test_mutex); 238 | tcase_add_test(tc_mutex, test_mutex_evloop); 239 | return tc_mutex; 240 | } 241 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.7) 2 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) 3 | # We are building as stand-alone project 4 | project(libevfibers C) 5 | else() 6 | # We are building as part of other project 7 | set(EVFIBERS_EMBED TRUE) 8 | endif() 9 | 10 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 11 | set(CMAKE_EXPORT_COMPILE_COMMANDS 1) 12 | 13 | if(NOT CMAKE_BUILD_TYPE) 14 | message(STATUS "No build type selected, defaulting to RelWithDebInfo") 15 | set(CMAKE_BUILD_TYPE "RelWithDebInfo") 16 | endif(NOT CMAKE_BUILD_TYPE) 17 | 18 | set(VERSION_MAJOR 0) 19 | set(VERSION_MINOR 4) 20 | set(VERSION_PATCH 1) 21 | set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") 22 | 23 | include(CheckIncludeFiles) 24 | include(CheckCCompilerFlag) 25 | 26 | get_property(LIB64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS) 27 | 28 | if ("${LIB64}" STREQUAL "TRUE") 29 | set(LIBSUFFIX 64) 30 | else("${LIB64}" STREQUAL "TRUE") 31 | set(LIBSUFFIX "") 32 | endif("${LIB64}" STREQUAL "TRUE") 33 | 34 | set(INSTALL_LIB_DIR lib${LIBSUFFIX} CACHE PATH "Installation directory for libraries") 35 | mark_as_advanced(INSTALL_LIB_DIR) 36 | 37 | if(NOT DEFINED WANT_EIO) 38 | message(STATUS "WANT_EIO flag not specified, defaulting to TRUE") 39 | set(WANT_EIO TRUE) 40 | endif(NOT DEFINED WANT_EIO) 41 | 42 | aux_source_directory("${CMAKE_CURRENT_SOURCE_DIR}/src" EVFIBERS_SOURCES) 43 | aux_source_directory("${CMAKE_CURRENT_SOURCE_DIR}/coro" CORO_SOURCES) 44 | 45 | # OSX-related checks - linking, using ucontext, etc. is slightly different than 46 | # on linux 47 | if(APPLE) 48 | # def needed to get ucontext support 49 | set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_XOPEN_SOURCE") 50 | add_definitions(-D_XOPEN_SOURCE) 51 | # ucontext is labeled deprecated, so make sure -Werror doesn't kill the 52 | # compile 53 | set(CMAKE_C_FLAGS "-Wno-error=deprecated-declarations ${CMAKE_C_FLAGS}") 54 | # OSX uses MAP_ANON for mmap while linux uses MAP_ANONYMOUS 55 | set(FBR_MAP_ANON_FLAG MAP_ANON) 56 | set(EIO_LD_WHOLE_ARCHIVE -Wl,-all_load) 57 | set(EIO_LD_NOWHOLE_ARCHIVE ) 58 | # use of swapcontext hits false positives on OSX when using either 59 | # address-sanitizer or stack protector (stack protector is on by default in 60 | # clang). Disable them. 61 | if(WANT_ASAN) 62 | message(WARNING "Address sanitizer causes false positive errors for makecontext/swapcontext on OSX, disabling...") 63 | set(WANT_ASAN FALSE) 64 | endif(WANT_ASAN) 65 | CHECK_C_COMPILER_FLAG("-fno-stack-protector" FBR_HAS_NO_STACK_PROTECT) 66 | if(FBR_HAS_NO_STACK_PROTECT) 67 | set(CMAKE_C_FLAGS "-fno-stack-protector ${CMAKE_C_FLAGS}") 68 | endif(FBR_HAS_NO_STACK_PROTECT) 69 | else(APPLE) 70 | set(FBR_MAP_ANON_FLAG MAP_ANONYMOUS) 71 | set(EIO_LD_WHOLE_ARCHIVE -Wl,-whole-archive) 72 | set(EIO_LD_NOWHOLE_ARCHIVE -Wl,-no-whole-archive) 73 | endif(APPLE) 74 | 75 | # need to provide this define on the command line instead of config.h so 76 | # we don't have to modify the embedded libcoro. 77 | check_include_files(ucontext.h HAVE_UCONTEXT_H) 78 | if(HAVE_UCONTEXT_H) 79 | add_definitions(-DHAVE_UCONTEXT_H) 80 | endif(HAVE_UCONTEXT_H) 81 | 82 | find_package(LibEv REQUIRED) 83 | if(WANT_EIO) 84 | find_package(Threads REQUIRED) 85 | if(WANT_EMBEDDED_EIO) 86 | include(ExternalProject) 87 | ExternalProject_Add( 88 | libeio 89 | PREFIX "${CMAKE_CURRENT_BINARY_DIR}/eio" 90 | CVS_REPOSITORY :pserver:anonymous@cvs.schmorp.de/schmorpforge 91 | CVS_MODULE libeio 92 | CVS_TAG -r rel-4_32 93 | UPDATE_COMMAND "" 94 | CONFIGURE_COMMAND CFLAGS=-fpic ./configure --enable-static --disable-shared --prefix=${CMAKE_CURRENT_BINARY_DIR}/eio 95 | BUILD_IN_SOURCE 1 96 | ) 97 | ExternalProject_Add_Step(libeio autogen 98 | COMMAND "./autogen.sh" 99 | DEPENDEES update 100 | DEPENDERS configure 101 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/eio/src/libeio" 102 | ) 103 | ExternalProject_Add_Step(libeio autoconf 104 | COMMAND "autoconf" 105 | DEPENDEES autogen 106 | DEPENDERS configure 107 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/eio/src/libeio" 108 | ) 109 | ExternalProject_Add_Step(libeio mvembedheader 110 | COMMAND mv ${CMAKE_CURRENT_BINARY_DIR}/eio/include/eio.h ${CMAKE_CURRENT_BINARY_DIR}/include/evfibers/libeio_embedded.h 111 | DEPENDEES install 112 | ) 113 | set(LIBEIO_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/eio/include") 114 | set(LIBEIO_LIBRARY 115 | "-L${CMAKE_CURRENT_BINARY_DIR}/eio/lib" 116 | "${EIO_LD_WHOLE_ARCHIVE}" "-leio" "${EIO_LD_NOWHOLE_ARCHIVE}" 117 | "${CMAKE_THREAD_LIBS_INIT}") 118 | set(LIBEIO_FOUND TRUE) 119 | set(FBR_USE_EMBEDDED_EIO TRUE) 120 | else(WANT_EMBEDDED_EIO) 121 | find_package(LibEio) 122 | endif(WANT_EMBEDDED_EIO) 123 | endif(WANT_EIO) 124 | 125 | if(WANT_ASAN) 126 | # Force ucontext.h as libcoro backend so as to better interact with ASan 127 | add_definitions(-DCORO_UCONTEXT) 128 | set(ASAN_FLAGS "-fsanitize=address") 129 | endif(WANT_ASAN) 130 | 131 | if(WANT_VALGRIND) 132 | check_include_files(valgrind/valgrind.h HAVE_VALGRIND_H) 133 | if (NOT HAVE_VALGRIND_H) 134 | message(FATAL_ERROR "cannot find valgrind.h") 135 | endif(NOT HAVE_VALGRIND_H) 136 | endif(WANT_VALGRIND) 137 | 138 | include_directories( 139 | "${CMAKE_CURRENT_SOURCE_DIR}/include" 140 | "${CMAKE_CURRENT_BINARY_DIR}/include" 141 | "${CMAKE_CURRENT_SOURCE_DIR}/coro" 142 | ${LIBEV_INCLUDE_DIR} 143 | ${LIBEIO_INCLUDE_DIR} 144 | ) 145 | 146 | if(WANT_LTO) 147 | set(LTO_FLAGS "-flto") 148 | endif(WANT_LTO) 149 | set(CMAKE_C_FLAGS "-W -Wall -Werror -fno-strict-aliasing ${LTO_FLAGS} ${ASAN_FLAGS} ${CMAKE_C_FLAGS}") 150 | set(SOURCES ${EVFIBERS_SOURCES} ${CORO_SOURCES}) 151 | 152 | add_library(evfibers SHARED ${SOURCES}) 153 | set_target_properties(evfibers 154 | PROPERTIES 155 | SOVERSION ${VERSION_MAJOR} 156 | VERSION ${VERSION_STRING} 157 | ) 158 | target_link_libraries(evfibers 159 | ${LIBEV_LIBRARY} 160 | ${LIBEIO_LIBRARY} 161 | ${CMAKE_THREAD_LIBS_INIT}) 162 | if(WANT_EIO AND WANT_EMBEDDED_EIO) 163 | add_dependencies(evfibers libeio) 164 | endif(WANT_EIO AND WANT_EMBEDDED_EIO) 165 | 166 | add_library(evfibers_static STATIC ${SOURCES}) 167 | set_target_properties(evfibers_static PROPERTIES 168 | OUTPUT_NAME evfibers 169 | COMPILE_FLAGS "-fPIC") 170 | 171 | if(EVFIBERS_EMBED) 172 | set(EVFIBERS_EMBED_LIBS evfibers_static 173 | ${LIBEV_LIBRARY} 174 | ${LIBEIO_LIBRARY} 175 | ${CMAKE_THREAD_LIBS_INIT} 176 | PARENT_SCOPE) 177 | endif(EVFIBERS_EMBED) 178 | 179 | add_executable(fiber_bench_buffer "${CMAKE_CURRENT_SOURCE_DIR}/bench/buffer.c") 180 | target_link_libraries(fiber_bench_buffer evfibers ${CMAKE_THREAD_LIBS_INIT}) 181 | add_executable(fiber_bench_condvar "${CMAKE_CURRENT_SOURCE_DIR}/bench/condvar.c") 182 | target_link_libraries(fiber_bench_condvar evfibers ${CMAKE_THREAD_LIBS_INIT}) 183 | 184 | # Variables for config.h 185 | if(WANT_EIO AND THREADS_FOUND AND LIBEIO_FOUND) 186 | set(FBR_EIO_ENABLED TRUE) 187 | message(STATUS "libeio support has been ENABLED") 188 | else(WANT_EIO AND THREADS_FOUND AND LIBEIO_FOUND) 189 | message(STATUS "libeio support has been DISABLED") 190 | endif(WANT_EIO AND THREADS_FOUND AND LIBEIO_FOUND) 191 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/include/evfibers/config.h.in" 192 | "${CMAKE_CURRENT_BINARY_DIR}/include/evfibers/config.h") 193 | 194 | # provide pkg-config file 195 | configure_file( 196 | ${CMAKE_CURRENT_SOURCE_DIR}/libevfibers.pc.in 197 | ${CMAKE_CURRENT_BINARY_DIR}/libevfibers.pc @ONLY 198 | ) 199 | install( 200 | FILES 201 | ${CMAKE_CURRENT_BINARY_DIR}/libevfibers.pc 202 | DESTINATION 203 | "${INSTALL_LIB_DIR}/pkgconfig" 204 | ) 205 | 206 | subdirs(test) 207 | 208 | if(NOT EVFIBERS_EMBED) 209 | install(TARGETS evfibers 210 | RUNTIME DESTINATION bin 211 | LIBRARY DESTINATION ${INSTALL_LIB_DIR} 212 | ARCHIVE DESTINATION ${INSTALL_LIB_DIR} 213 | ) 214 | install(TARGETS evfibers_static 215 | RUNTIME DESTINATION bin 216 | LIBRARY DESTINATION ${INSTALL_LIB_DIR} 217 | ARCHIVE DESTINATION ${INSTALL_LIB_DIR} 218 | ) 219 | file(GLOB DEV_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/evfibers/*.h") 220 | file(GLOB DEV_HEADERS2 "${CMAKE_CURRENT_BINARY_DIR}/include/evfibers/*.h") 221 | install(FILES ${DEV_HEADERS} ${DEV_HEADERS2} 222 | DESTINATION "include/evfibers") 223 | if(WANT_EMBEDDED_EIO) 224 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/evfibers/libeio_embedded.h 225 | DESTINATION "include/evfibers") 226 | endif(WANT_EMBEDDED_EIO) 227 | endif(NOT EVFIBERS_EMBED) 228 | -------------------------------------------------------------------------------- /test/cond.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "cond.h" 24 | 25 | struct fiber_arg { 26 | struct fbr_mutex *mutex; 27 | struct fbr_cond_var *cond; 28 | int *flag_ptr; 29 | }; 30 | 31 | static void cond_fiber1(FBR_P_ void *_arg) 32 | { 33 | struct fiber_arg *arg = _arg; 34 | struct fbr_mutex *mutex = arg->mutex; 35 | struct fbr_cond_var *cond = arg->cond; 36 | int *flag_ptr = arg->flag_ptr; 37 | fbr_mutex_lock(FBR_A_ mutex); 38 | fbr_cond_wait(FBR_A_ cond, mutex); 39 | *flag_ptr += 1; 40 | fbr_mutex_unlock(FBR_A_ mutex); 41 | } 42 | 43 | START_TEST(test_cond_broadcast) 44 | { 45 | struct fbr_context context; 46 | fbr_id_t fiber = FBR_ID_NULL; 47 | struct fbr_mutex mutex; 48 | struct fbr_cond_var cond; 49 | int flag = 0; 50 | int i; 51 | const int num_fibers = 100; 52 | int retval; 53 | struct fiber_arg arg = { 54 | .flag_ptr = &flag 55 | }; 56 | 57 | fbr_init(&context, EV_DEFAULT); 58 | 59 | fbr_mutex_init(&context, &mutex); 60 | arg.mutex = &mutex; 61 | 62 | fbr_cond_init(&context, &cond); 63 | arg.cond = &cond; 64 | 65 | for(i = 0; i < num_fibers; i++) { 66 | fiber = fbr_create(&context, "cond_i", cond_fiber1, &arg, 0); 67 | fail_if(fbr_id_isnull(fiber)); 68 | retval = fbr_transfer(&context, fiber); 69 | fail_unless(0 == retval, NULL); 70 | } 71 | 72 | fail_unless(flag == 0, NULL); 73 | 74 | fbr_cond_broadcast(&context, &cond); 75 | fbr_cond_broadcast(&context, &cond); 76 | 77 | ev_run(EV_DEFAULT, 0); 78 | 79 | fail_unless(flag == num_fibers, NULL); 80 | 81 | fbr_cond_destroy(&context, &cond); 82 | fbr_mutex_destroy(&context, &mutex); 83 | fbr_destroy(&context); 84 | } 85 | END_TEST 86 | 87 | START_TEST(test_cond_signal) 88 | { 89 | struct fbr_context context; 90 | fbr_id_t fiber = FBR_ID_NULL; 91 | struct fbr_mutex mutex; 92 | struct fbr_cond_var cond; 93 | int flag = 0; 94 | int i; 95 | const int num_fibers = 100; 96 | int retval; 97 | struct fiber_arg arg = { 98 | .flag_ptr = &flag 99 | }; 100 | 101 | fbr_init(&context, EV_DEFAULT); 102 | 103 | fbr_mutex_init(&context, &mutex); 104 | arg.mutex = &mutex; 105 | 106 | fbr_cond_init(&context, &cond); 107 | arg.cond = &cond; 108 | 109 | for(i = 0; i < num_fibers; i++) { 110 | fiber = fbr_create(&context, "cond_i", cond_fiber1, &arg, 0); 111 | fail_if(fbr_id_isnull(fiber)); 112 | retval = fbr_transfer(&context, fiber); 113 | fail_unless(0 == retval, NULL); 114 | } 115 | 116 | fail_unless(flag == 0, NULL); 117 | 118 | for(i = 0; i <= num_fibers; i++) 119 | fbr_cond_signal(&context, &cond); 120 | 121 | ev_run(EV_DEFAULT, 0); 122 | 123 | fail_unless(flag == num_fibers, NULL); 124 | 125 | fbr_cond_destroy(&context, &cond); 126 | fbr_mutex_destroy(&context, &mutex); 127 | fbr_destroy(&context); 128 | } 129 | END_TEST 130 | 131 | START_TEST(test_cond_bad_mutex) 132 | { 133 | struct fbr_context context; 134 | struct fbr_mutex mutex; 135 | struct fbr_cond_var cond; 136 | int retval; 137 | 138 | fbr_init(&context, EV_DEFAULT); 139 | fbr_mutex_init(&context, &mutex); 140 | fbr_cond_init(&context, &cond); 141 | 142 | retval = fbr_cond_wait(&context, &cond, &mutex); 143 | fail_unless(retval == -1., NULL); 144 | fail_unless(context.f_errno == FBR_EINVAL); 145 | 146 | fbr_cond_destroy(&context, &cond); 147 | fbr_mutex_destroy(&context, &mutex); 148 | fbr_destroy(&context); 149 | } 150 | END_TEST 151 | 152 | struct fiber_arg2 { 153 | struct fbr_cond_var cond1; 154 | struct fbr_mutex mutex1; 155 | struct fbr_cond_var cond2; 156 | struct fbr_mutex mutex2; 157 | }; 158 | 159 | static void cond_fiber_waiter(FBR_P_ void *_arg) 160 | { 161 | struct fiber_arg2 *arg = _arg; 162 | struct fbr_ev_cond_var ev_c1; 163 | struct fbr_ev_cond_var ev_c2; 164 | struct fbr_ev_base *fb_events[3]; 165 | int n_events; 166 | 167 | fbr_ev_cond_var_init(FBR_A_ &ev_c1, &arg->cond1, &arg->mutex1); 168 | fbr_ev_cond_var_init(FBR_A_ &ev_c2, &arg->cond2, &arg->mutex2); 169 | 170 | fb_events[0] = &ev_c1.ev_base; 171 | fb_events[1] = &ev_c2.ev_base; 172 | fb_events[2] = NULL; 173 | 174 | fbr_mutex_lock(FBR_A_ &arg->mutex1); 175 | fbr_mutex_lock(FBR_A_ &arg->mutex2); 176 | 177 | n_events = fbr_ev_wait_to(FBR_A_ fb_events, 5.0); 178 | fail_unless(n_events > 0); 179 | fail_unless(ev_c1.ev_base.arrived); 180 | fail_unless(ev_c2.ev_base.arrived); 181 | 182 | fbr_mutex_unlock(FBR_A_ &arg->mutex1); 183 | fbr_mutex_unlock(FBR_A_ &arg->mutex2); 184 | } 185 | 186 | static void cond_fiber_signaller(FBR_P_ void *_arg) 187 | { 188 | struct fiber_arg2 *arg = _arg; 189 | 190 | fbr_sleep(FBR_A_ 0.3); 191 | fbr_cond_signal(FBR_A_ &arg->cond1); 192 | fbr_cond_signal(FBR_A_ &arg->cond2); 193 | } 194 | 195 | START_TEST(test_two_conds) 196 | { 197 | struct fbr_context context; 198 | fbr_id_t fiber_waiter = FBR_ID_NULL, fiber_signaller = FBR_ID_NULL; 199 | int retval; 200 | struct fiber_arg2 arg; 201 | 202 | fbr_init(&context, EV_DEFAULT); 203 | fbr_cond_init(&context, &arg.cond1); 204 | fbr_mutex_init(&context, &arg.mutex1); 205 | fbr_cond_init(&context, &arg.cond2); 206 | fbr_mutex_init(&context, &arg.mutex2); 207 | 208 | fiber_waiter = fbr_create(&context, "cond_waiter", cond_fiber_waiter, &arg, 0); 209 | fail_if(fbr_id_isnull(fiber_waiter)); 210 | retval = fbr_transfer(&context, fiber_waiter); 211 | fail_unless(0 == retval, NULL); 212 | 213 | fiber_signaller = fbr_create(&context, "cond_signaller", cond_fiber_signaller, &arg, 0); 214 | fail_if(fbr_id_isnull(fiber_signaller)); 215 | retval = fbr_transfer(&context, fiber_signaller); 216 | fail_unless(0 == retval, NULL); 217 | 218 | ev_run(EV_DEFAULT, 0); 219 | 220 | fbr_cond_destroy(&context, &arg.cond1); 221 | fbr_mutex_destroy(&context, &arg.mutex1); 222 | fbr_cond_destroy(&context, &arg.cond2); 223 | fbr_mutex_destroy(&context, &arg.mutex2); 224 | fbr_destroy(&context); 225 | } 226 | END_TEST 227 | 228 | struct fiber_arg3 { 229 | struct fbr_cond_var cond; 230 | struct fbr_mutex mutex; 231 | fbr_id_t *fibers; 232 | int fiber_num; 233 | }; 234 | 235 | static void cond_premature_waiter(FBR_P_ void *_arg) 236 | { 237 | struct fiber_arg3 *arg = _arg; 238 | struct fbr_ev_cond_var ev_cond; 239 | int retval; 240 | 241 | fbr_ev_cond_var_init(FBR_A_ &ev_cond, &arg->cond, &arg->mutex); 242 | 243 | fbr_mutex_lock(FBR_A_ &arg->mutex); 244 | 245 | retval = fbr_ev_wait_one(FBR_A_ &ev_cond.ev_base); 246 | fail_unless(0 == retval); 247 | 248 | fbr_mutex_unlock(FBR_A_ &arg->mutex); 249 | } 250 | 251 | static void cond_premature_reaper(FBR_P_ void *_arg) 252 | { 253 | struct fiber_arg3 *arg = _arg; 254 | int i; 255 | 256 | for(i = 0; i < arg->fiber_num / 4; i++) 257 | fbr_cond_signal(FBR_A_ &arg->cond); 258 | 259 | for(i = arg->fiber_num / 4; i < arg->fiber_num; i++) 260 | fbr_reclaim(FBR_A_ arg->fibers[i]); 261 | 262 | for(i = 0; i < arg->fiber_num; i++) 263 | fbr_cond_signal(FBR_A_ &arg->cond); 264 | } 265 | 266 | START_TEST(test_premature_cond) 267 | { 268 | struct fbr_context context; 269 | int retval; 270 | struct fiber_arg3 arg; 271 | const int num_fibers = 100; 272 | fbr_id_t fibers[num_fibers + 1]; 273 | fbr_id_t fiber; 274 | int i; 275 | 276 | fbr_init(&context, EV_DEFAULT); 277 | 278 | fbr_mutex_init(&context, &arg.mutex); 279 | fbr_cond_init(&context, &arg.cond); 280 | 281 | for(i = 0; i < num_fibers; i++) { 282 | fiber = fbr_create(&context, "cond_premature_i", 283 | cond_premature_waiter, &arg, 0); 284 | fail_if(fbr_id_isnull(fiber)); 285 | retval = fbr_transfer(&context, fiber); 286 | fail_unless(0 == retval, NULL); 287 | fibers[i] = fiber; 288 | } 289 | 290 | arg.fibers = fibers; 291 | arg.fiber_num = num_fibers; 292 | 293 | fiber = fbr_create(&context, "cond_premature_reaper", 294 | cond_premature_reaper, &arg, 0); 295 | fail_if(fbr_id_isnull(fiber)); 296 | retval = fbr_transfer(&context, fiber); 297 | fail_unless(0 == retval, NULL); 298 | fibers[i] = fiber; 299 | 300 | ev_run(EV_DEFAULT, 0); 301 | 302 | fbr_cond_destroy(&context, &arg.cond); 303 | fbr_mutex_destroy(&context, &arg.mutex); 304 | fbr_destroy(&context); 305 | } 306 | END_TEST 307 | 308 | TCase * cond_tcase(void) 309 | { 310 | TCase *tc_cond = tcase_create ("Cond"); 311 | tcase_add_test(tc_cond, test_cond_broadcast); 312 | tcase_add_test(tc_cond, test_cond_signal); 313 | tcase_add_test(tc_cond, test_cond_bad_mutex); 314 | tcase_add_test(tc_cond, test_two_conds); 315 | tcase_add_test(tc_cond, test_premature_cond); 316 | return tc_cond; 317 | } 318 | 319 | 320 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /test/eio.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #ifdef FBR_EIO_ENABLED 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | static char small_msg[] = "Small test line\n\n"; 34 | static char big_msg[] = 35 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mi ante, elementum" 36 | "ac pharetra id, porttitor eu leo. Phasellus quam tortor, cursus quis accumsan" 37 | "eget, molestie ac leo. Donec a velit elit. Ut placerat leo arcu. Donec" 38 | "consectetur convallis metus, ac varius ante elementum et. Vestibulum ligula" 39 | "ligula, molestie in tincidunt ac, vulputate nec elit. Ut lacus felis, sagittis" 40 | "ut porta eu, fermentum non nibh. Curabitur lacinia, eros eget cursus" 41 | "vestibulum, dolor eros pretium felis, ac faucibus arcu purus ultricies magna." 42 | "Etiam eleifend diam et mauris vehicula euismod. Suspendisse at lorem tellus." 43 | "Nam in enim dui. Donec nec dui ac erat luctus gravida in venenatis nisl.\n" 44 | 45 | "Vivamus quis turpis feugiat odio convallis interdum in quis magna. Sed nec" 46 | "ipsum ligula. Etiam sit amet mi justo. Quisque at ante nibh. Class aptent" 47 | "taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos." 48 | "Aenean hendrerit porta enim quis pretium. Sed rhoncus bibendum orci, non" 49 | "fermentum tellus dignissim vitae. Aenean fringilla tristique libero non" 50 | "fringilla. Suspendisse congue fringilla ullamcorper.\n" 51 | 52 | "Suspendisse potenti. Donec feugiat congue elit eu mollis. Maecenas tempus, mi" 53 | "sed aliquet scelerisque, magna mi pharetra orci, ut scelerisque lacus elit a" 54 | "nunc. Suspendisse odio lorem, commodo sed consectetur sed, congue in urna." 55 | "Nullam nec libero id est pulvinar convallis. Donec id eros et risus mattis" 56 | "tempor. Aenean sollicitudin blandit ligula et vulputate. Pellentesque" 57 | "consectetur pulvinar augue non laoreet. Aliquam erat volutpat. Etiam eget justo" 58 | "ipsum. Nulla ultrices odio quis ipsum tristique ornare. Praesent rhoncus porta" 59 | "lectus, vel ultrices augue porttitor sed.\n" 60 | 61 | "Etiam ac purus nisl. Donec viverra vestibulum lacus non sagittis. Vivamus ac" 62 | "ornare mi. Quisque ac arcu lacus, id hendrerit erat. In vel augue tellus." 63 | "Pellentesque mattis augue sed odio tristique sollicitudin. Sed quis elementum" 64 | "quam. Proin placerat vestibulum nulla sit amet hendrerit.\n" 65 | 66 | "Phasellus gravida ante et purus hendrerit rutrum fringilla nulla scelerisque." 67 | "Aenean ut quam sed nisl commodo egestas sed eget ipsum. Curabitur condimentum" 68 | "sollicitudin nulla id scelerisque. Donec aliquet nibh et neque rutrum posuere." 69 | "Vestibulum in mauris urna, quis vehicula purus. Proin vulputate tortor non" 70 | "ligula consequat dapibus. Quisque varius blandit risus sed imperdiet. Nam nec" 71 | "orci ut ipsum blandit blandit vel a diam. Quisque eget sapien eros, at faucibus" 72 | "velit. Vestibulum dapibus tempus fringilla. Vestibulum ante ipsum primis in" 73 | "faucibus orci luctus et ultrices posuere cubilia Curae; Aenean congue dapibus" 74 | "imperdiet. Cras fringilla dui quis elit pulvinar eleifend a sed nisl. Aenean" 75 | "vel neque magna.\n" 76 | 77 | "Integer ultrices lorem sed velit bibendum aliquet. In congue vestibulum" 78 | "malesuada. Curabitur molestie venenatis felis sit amet ultrices. Phasellus" 79 | "nulla dui, volutpat et mollis vel, placerat sed nulla. Donec vel nulla eu nisl" 80 | "hendrerit mollis sed ut lacus. Nulla nisi arcu, pellentesque a ornare non," 81 | "tristique at libero. Ut elementum risus et ipsum varius sit amet lobortis dui" 82 | "cursus. Proin consequat pellentesque lorem vel rhoncus. Curabitur nec bibendum" 83 | "nulla. Aliquam id justo nulla. Aenean eget urna non elit tincidunt posuere." 84 | "Nunc eget mauris id ante hendrerit convallis.\n" 85 | 86 | "Sed adipiscing nisl sit amet urna tincidunt laoreet. Nullam rhoncus nulla" 87 | "velit. Fusce porta turpis ornare mi accumsan viverra. Vestibulum sed ipsum" 88 | "eros. Duis tincidunt iaculis erat sed suscipit. Mauris id laoreet lorem. Nullam" 89 | "molestie massa eu tellus dapibus pretium. Morbi eu odio arcu. Etiam posuere" 90 | "elit nec nunc ultrices rutrum id vel neque. Phasellus iaculis turpis nulla, ut" 91 | "hendrerit eros. Vestibulum justo turpis, consequat non facilisis non, iaculis" 92 | "id urna.\n" 93 | 94 | "Maecenas ac nibh libero. Nam lectus velit, fermentum eget blandit in, molestie" 95 | "et nunc. Nam mollis adipiscing dictum. Vestibulum congue mollis odio sed" 96 | "dapibus. Aenean et dictum felis. Donec convallis orci sed lectus rutrum aliquet" 97 | "tempus vitae nunc. Quisque pellentesque leo vel turpis vulputate sodales." 98 | "Vestibulum eu erat neque. Sed aliquet, eros vel turpis duis.\n"; 99 | 100 | static void io_fiber(FBR_P_ _unused_ void *_arg) 101 | { 102 | ssize_t retval; 103 | int fd; 104 | void *buf; 105 | off_t offt; 106 | char path_buf[PATH_MAX + 1]; 107 | char path_buf2[PATH_MAX + 1]; 108 | EIO_STRUCT_STAT statdata; 109 | EIO_STRUCT_STATVFS statvdata; 110 | 111 | memset(&statvdata, 0x00, sizeof(statvdata)); 112 | retval = fbr_eio_statvfs(FBR_A_ "/", &statvdata, 0); 113 | assert(0 == retval); 114 | 115 | fd = fbr_eio_open(FBR_A_ "./async.test", O_RDWR | O_CREAT | O_TRUNC, 116 | 0644, 0); 117 | fail_unless(0 <= fd); 118 | 119 | retval = fbr_eio_ftruncate(FBR_A_ fd, 0, 0); 120 | fail_unless(0 == retval); 121 | 122 | retval = fbr_eio_syncfs(FBR_A_ fd, 0); 123 | fail_unless(0 == retval || ENOSYS == errno); 124 | 125 | retval = fbr_eio_fallocate(FBR_A_ fd, EIO_FALLOC_FL_KEEP_SIZE, 0, 126 | sizeof(big_msg), 0); 127 | fail_unless(0 == retval || ENOSYS == errno || EOPNOTSUPP == errno); 128 | 129 | memset(&statdata, 0x00, sizeof(statdata)); 130 | retval = fbr_eio_fstat(FBR_A_ fd, &statdata, 0); 131 | assert(0 == retval); 132 | 133 | memset(&statvdata, 0x00, sizeof(statvdata)); 134 | retval = fbr_eio_fstatvfs(FBR_A_ fd, &statvdata, 0); 135 | assert(0 == retval); 136 | 137 | retval = fbr_eio_futime(FBR_A_ fd, ev_now(fctx->__p->loop), 138 | ev_now(fctx->__p->loop), 0); 139 | fail_unless(0 == retval); 140 | 141 | retval = fbr_eio_fchmod(FBR_A_ fd, 0644, 0); 142 | fail_unless(0 == retval); 143 | 144 | retval = fbr_eio_fchown(FBR_A_ fd, getuid(), getgid(), 0); 145 | fail_unless(0 == retval); 146 | 147 | /* Small message test */ 148 | retval = fbr_eio_write(FBR_A_ fd, small_msg, sizeof(small_msg), -1, 0); 149 | fail_unless(0 < retval); 150 | 151 | retval = fbr_eio_fsync(FBR_A_ fd, 0); 152 | fail_unless(0 == retval); 153 | 154 | retval = fbr_eio_sync_file_range(FBR_A_ fd, 0, sizeof(small_msg), 155 | EIO_SYNC_FILE_RANGE_WRITE, 0); 156 | fail_unless(0 == retval); 157 | 158 | retval = fbr_eio_seek(FBR_A_ fd, 0, EIO_SEEK_SET, 0); 159 | fail_unless(0 == retval); 160 | 161 | buf = malloc(sizeof(small_msg)); 162 | fail_unless(NULL != buf); 163 | 164 | retval = fbr_eio_readahead(FBR_A_ fd, 0, sizeof(small_msg), 0); 165 | fail_unless(NULL != buf); 166 | 167 | retval = fbr_eio_read(FBR_A_ fd, buf, sizeof(small_msg), -1, 0); 168 | fail_unless(0 <= retval); 169 | fail_unless(!memcmp(small_msg, buf, sizeof(small_msg))); 170 | 171 | free(buf); 172 | 173 | /* Big message test */ 174 | retval = fbr_eio_seek(FBR_A_ fd, 0, EIO_SEEK_CUR, 0); 175 | fail_unless(0 < retval); 176 | 177 | offt = retval; 178 | 179 | retval = fbr_eio_write(FBR_A_ fd, big_msg, sizeof(big_msg), -1, 0); 180 | fail_unless(0 < retval); 181 | 182 | retval = fbr_eio_fdatasync(FBR_A_ fd, 0); 183 | fail_unless(0 == retval); 184 | 185 | retval = fbr_eio_seek(FBR_A_ fd, offt, EIO_SEEK_SET, 0); 186 | fail_unless(0 < retval); 187 | 188 | buf = malloc(sizeof(big_msg)); 189 | fail_unless(NULL != buf); 190 | 191 | retval = fbr_eio_read(FBR_A_ fd, buf, sizeof(big_msg), -1, 0); 192 | fail_unless(0 < retval); 193 | fail_unless(!memcmp(big_msg, buf, sizeof(big_msg))); 194 | 195 | free(buf); 196 | 197 | retval = fbr_eio_sync(FBR_A_ 0); 198 | fail_unless(0 == retval); 199 | 200 | retval = fbr_eio_close(FBR_A_ fd, 0); 201 | fail_unless(0 == retval); 202 | 203 | 204 | retval = fbr_eio_chown(FBR_A_ "./async.test", getuid(), getgid(), 0); 205 | assert(0 == retval); 206 | 207 | retval = fbr_eio_chmod(FBR_A_ "./async.test", 0644, 0); 208 | assert(0 == retval); 209 | 210 | retval = fbr_eio_utime(FBR_A_ "./async.test", 211 | ev_now(fctx->__p->loop), 212 | ev_now(fctx->__p->loop), 0); 213 | assert(0 == retval); 214 | 215 | memset(&statdata, 0x00, sizeof(statdata)); 216 | retval = fbr_eio_stat(FBR_A_ "./async.test", &statdata, 0); 217 | assert(0 == retval); 218 | 219 | memset(&statdata, 0x00, sizeof(statdata)); 220 | retval = fbr_eio_lstat(FBR_A_ "./async.test", &statdata, 0); 221 | assert(0 == retval); 222 | 223 | retval = fbr_eio_truncate(FBR_A_ "./async.test", 0, 0); 224 | assert(0 == retval); 225 | 226 | retval = fbr_eio_unlink(FBR_A_ "./async.test", 0); 227 | assert(0 == retval); 228 | 229 | retval = fbr_eio_mkdir(FBR_A_ "./async.test.dir", 0755, 0); 230 | assert(0 == retval); 231 | 232 | retval = fbr_eio_rmdir(FBR_A_ "./async.test.dir", 0); 233 | assert(0 == retval); 234 | 235 | #ifdef FBR_TEST_NO_MKNOD 236 | fd = fbr_eio_open(FBR_A_ "./async.node", O_RDWR | O_CREAT | O_TRUNC, 237 | 0644, 0); 238 | fail_unless(0 <= fd); 239 | retval = fbr_eio_close(FBR_A_ fd, 0); 240 | fail_unless(0 == retval); 241 | #else 242 | retval = fbr_eio_mknod(FBR_A_ "./async.node", 0644, S_IFREG, 0); 243 | assert(0 == retval); 244 | #endif 245 | 246 | retval = fbr_eio_link(FBR_A_ "./async.node", 247 | "./async.node.link", 0); 248 | assert(0 == retval); 249 | 250 | retval = fbr_eio_symlink(FBR_A_ "./async.node", 251 | "./async.node.symlink", 0); 252 | assert(0 == retval); 253 | 254 | memset(path_buf, 0x00, sizeof(path_buf)); 255 | retval = fbr_eio_readlink(FBR_A_ "./async.node.symlink", path_buf, 256 | sizeof(path_buf) - 1, 0); 257 | fail_unless(retval > 0); 258 | fail_unless(!strcmp(path_buf, "./async.node")); 259 | 260 | fail_unless(NULL != getcwd(path_buf2, sizeof(path_buf2))); 261 | strcat(path_buf2, "/async.node"); 262 | memset(path_buf, 0x00, sizeof(path_buf)); 263 | retval = fbr_eio_realpath(FBR_A_ "./././async.node", 264 | path_buf, sizeof(path_buf) - 1, 0); 265 | fail_unless(retval > 0); 266 | fail_unless(!strcmp(path_buf2, path_buf)); 267 | 268 | retval = fbr_eio_rename(FBR_A_ "./async.node.link", 269 | "./async.node.symlink", 0); 270 | assert(0 == retval); 271 | 272 | retval = fbr_eio_unlink(FBR_A_ "./async.node.symlink", 0); 273 | assert(0 == retval); 274 | 275 | retval = fbr_eio_unlink(FBR_A_ "./async.node", 0); 276 | assert(0 == retval); 277 | } 278 | 279 | START_TEST(test_eio) 280 | { 281 | int retval; 282 | fbr_id_t fiber = FBR_ID_NULL; 283 | struct fbr_context context; 284 | fbr_init(&context, EV_DEFAULT); 285 | fbr_eio_init(); 286 | signal(SIGPIPE, SIG_IGN); 287 | 288 | fiber = fbr_create(&context, "io_fiber", io_fiber, NULL, 0); 289 | fail_if(fbr_id_isnull(fiber)); 290 | retval = fbr_transfer(&context, fiber); 291 | fail_unless(0 == retval, NULL); 292 | 293 | ev_run(EV_DEFAULT, 0); 294 | fbr_destroy(&context); 295 | } 296 | END_TEST 297 | 298 | TCase * eio_tcase(void) 299 | { 300 | TCase *tc_eio = tcase_create("EIO"); 301 | tcase_add_test(tc_eio, test_eio); 302 | return tc_eio; 303 | } 304 | 305 | #else 306 | 307 | TCase * eio_tcase(void) 308 | { 309 | TCase *tc_eio = tcase_create("EIO_DISABLED"); 310 | return tc_eio; 311 | } 312 | 313 | #endif 314 | -------------------------------------------------------------------------------- /examples/sample_http_server/http_parser.h: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | #ifndef http_parser_h 22 | #define http_parser_h 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | /* Also update SONAME in the Makefile whenever you change these. */ 28 | #define HTTP_PARSER_VERSION_MAJOR 2 29 | #define HTTP_PARSER_VERSION_MINOR 3 30 | #define HTTP_PARSER_VERSION_PATCH 0 31 | 32 | #include 33 | #if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) 34 | #include 35 | #include 36 | typedef __int8 int8_t; 37 | typedef unsigned __int8 uint8_t; 38 | typedef __int16 int16_t; 39 | typedef unsigned __int16 uint16_t; 40 | typedef __int32 int32_t; 41 | typedef unsigned __int32 uint32_t; 42 | typedef __int64 int64_t; 43 | typedef unsigned __int64 uint64_t; 44 | #else 45 | #include 46 | #endif 47 | 48 | /* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run 49 | * faster 50 | */ 51 | #ifndef HTTP_PARSER_STRICT 52 | # define HTTP_PARSER_STRICT 1 53 | #endif 54 | 55 | /* Maximium header size allowed. If the macro is not defined 56 | * before including this header then the default is used. To 57 | * change the maximum header size, define the macro in the build 58 | * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove 59 | * the effective limit on the size of the header, define the macro 60 | * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) 61 | */ 62 | #ifndef HTTP_MAX_HEADER_SIZE 63 | # define HTTP_MAX_HEADER_SIZE (80*1024) 64 | #endif 65 | 66 | typedef struct http_parser http_parser; 67 | typedef struct http_parser_settings http_parser_settings; 68 | 69 | 70 | /* Callbacks should return non-zero to indicate an error. The parser will 71 | * then halt execution. 72 | * 73 | * The one exception is on_headers_complete. In a HTTP_RESPONSE parser 74 | * returning '1' from on_headers_complete will tell the parser that it 75 | * should not expect a body. This is used when receiving a response to a 76 | * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: 77 | * chunked' headers that indicate the presence of a body. 78 | * 79 | * http_data_cb does not return data chunks. It will be call arbitrarally 80 | * many times for each string. E.G. you might get 10 callbacks for "on_url" 81 | * each providing just a few characters more data. 82 | */ 83 | typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); 84 | typedef int (*http_cb) (http_parser*); 85 | 86 | 87 | /* Request Methods */ 88 | #define HTTP_METHOD_MAP(XX) \ 89 | XX(0, DELETE, DELETE) \ 90 | XX(1, GET, GET) \ 91 | XX(2, HEAD, HEAD) \ 92 | XX(3, POST, POST) \ 93 | XX(4, PUT, PUT) \ 94 | /* pathological */ \ 95 | XX(5, CONNECT, CONNECT) \ 96 | XX(6, OPTIONS, OPTIONS) \ 97 | XX(7, TRACE, TRACE) \ 98 | /* webdav */ \ 99 | XX(8, COPY, COPY) \ 100 | XX(9, LOCK, LOCK) \ 101 | XX(10, MKCOL, MKCOL) \ 102 | XX(11, MOVE, MOVE) \ 103 | XX(12, PROPFIND, PROPFIND) \ 104 | XX(13, PROPPATCH, PROPPATCH) \ 105 | XX(14, SEARCH, SEARCH) \ 106 | XX(15, UNLOCK, UNLOCK) \ 107 | /* subversion */ \ 108 | XX(16, REPORT, REPORT) \ 109 | XX(17, MKACTIVITY, MKACTIVITY) \ 110 | XX(18, CHECKOUT, CHECKOUT) \ 111 | XX(19, MERGE, MERGE) \ 112 | /* upnp */ \ 113 | XX(20, MSEARCH, M-SEARCH) \ 114 | XX(21, NOTIFY, NOTIFY) \ 115 | XX(22, SUBSCRIBE, SUBSCRIBE) \ 116 | XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ 117 | /* RFC-5789 */ \ 118 | XX(24, PATCH, PATCH) \ 119 | XX(25, PURGE, PURGE) \ 120 | 121 | enum http_method 122 | { 123 | #define XX(num, name, string) HTTP_##name = num, 124 | HTTP_METHOD_MAP(XX) 125 | #undef XX 126 | }; 127 | 128 | 129 | enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; 130 | 131 | 132 | /* Flag values for http_parser.flags field */ 133 | enum flags 134 | { F_CHUNKED = 1 << 0 135 | , F_CONNECTION_KEEP_ALIVE = 1 << 1 136 | , F_CONNECTION_CLOSE = 1 << 2 137 | , F_TRAILING = 1 << 3 138 | , F_UPGRADE = 1 << 4 139 | , F_SKIPBODY = 1 << 5 140 | }; 141 | 142 | 143 | /* Map for errno-related constants 144 | * 145 | * The provided argument should be a macro that takes 2 arguments. 146 | */ 147 | #define HTTP_ERRNO_MAP(XX) \ 148 | /* No error */ \ 149 | XX(OK, "success") \ 150 | \ 151 | /* Callback-related errors */ \ 152 | XX(CB_message_begin, "the on_message_begin callback failed") \ 153 | XX(CB_url, "the on_url callback failed") \ 154 | XX(CB_header_field, "the on_header_field callback failed") \ 155 | XX(CB_header_value, "the on_header_value callback failed") \ 156 | XX(CB_headers_complete, "the on_headers_complete callback failed") \ 157 | XX(CB_body, "the on_body callback failed") \ 158 | XX(CB_message_complete, "the on_message_complete callback failed") \ 159 | XX(CB_status, "the on_status callback failed") \ 160 | \ 161 | /* Parsing-related errors */ \ 162 | XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ 163 | XX(HEADER_OVERFLOW, \ 164 | "too many header bytes seen; overflow detected") \ 165 | XX(CLOSED_CONNECTION, \ 166 | "data received after completed connection: close message") \ 167 | XX(INVALID_VERSION, "invalid HTTP version") \ 168 | XX(INVALID_STATUS, "invalid HTTP status code") \ 169 | XX(INVALID_METHOD, "invalid HTTP method") \ 170 | XX(INVALID_URL, "invalid URL") \ 171 | XX(INVALID_HOST, "invalid host") \ 172 | XX(INVALID_PORT, "invalid port") \ 173 | XX(INVALID_PATH, "invalid path") \ 174 | XX(INVALID_QUERY_STRING, "invalid query string") \ 175 | XX(INVALID_FRAGMENT, "invalid fragment") \ 176 | XX(LF_EXPECTED, "LF character expected") \ 177 | XX(INVALID_HEADER_TOKEN, "invalid character in header") \ 178 | XX(INVALID_CONTENT_LENGTH, \ 179 | "invalid character in content-length header") \ 180 | XX(INVALID_CHUNK_SIZE, \ 181 | "invalid character in chunk size header") \ 182 | XX(INVALID_CONSTANT, "invalid constant string") \ 183 | XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ 184 | XX(STRICT, "strict mode assertion failed") \ 185 | XX(PAUSED, "parser is paused") \ 186 | XX(UNKNOWN, "an unknown error occurred") 187 | 188 | 189 | /* Define HPE_* values for each errno value above */ 190 | #define HTTP_ERRNO_GEN(n, s) HPE_##n, 191 | enum http_errno { 192 | HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) 193 | }; 194 | #undef HTTP_ERRNO_GEN 195 | 196 | 197 | /* Get an http_errno value from an http_parser */ 198 | #define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) 199 | 200 | 201 | struct http_parser { 202 | /** PRIVATE **/ 203 | unsigned int type : 2; /* enum http_parser_type */ 204 | unsigned int flags : 6; /* F_* values from 'flags' enum; semi-public */ 205 | unsigned int state : 8; /* enum state from http_parser.c */ 206 | unsigned int header_state : 8; /* enum header_state from http_parser.c */ 207 | unsigned int index : 8; /* index into current matcher */ 208 | 209 | uint32_t nread; /* # bytes read in various scenarios */ 210 | uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ 211 | 212 | /** READ-ONLY **/ 213 | unsigned short http_major; 214 | unsigned short http_minor; 215 | unsigned int status_code : 16; /* responses only */ 216 | unsigned int method : 8; /* requests only */ 217 | unsigned int http_errno : 7; 218 | 219 | /* 1 = Upgrade header was present and the parser has exited because of that. 220 | * 0 = No upgrade header present. 221 | * Should be checked when http_parser_execute() returns in addition to 222 | * error checking. 223 | */ 224 | unsigned int upgrade : 1; 225 | 226 | /** PUBLIC **/ 227 | void *data; /* A pointer to get hook to the "connection" or "socket" object */ 228 | }; 229 | 230 | 231 | struct http_parser_settings { 232 | http_cb on_message_begin; 233 | http_data_cb on_url; 234 | http_data_cb on_status; 235 | http_data_cb on_header_field; 236 | http_data_cb on_header_value; 237 | http_cb on_headers_complete; 238 | http_data_cb on_body; 239 | http_cb on_message_complete; 240 | }; 241 | 242 | 243 | enum http_parser_url_fields 244 | { UF_SCHEMA = 0 245 | , UF_HOST = 1 246 | , UF_PORT = 2 247 | , UF_PATH = 3 248 | , UF_QUERY = 4 249 | , UF_FRAGMENT = 5 250 | , UF_USERINFO = 6 251 | , UF_MAX = 7 252 | }; 253 | 254 | 255 | /* Result structure for http_parser_parse_url(). 256 | * 257 | * Callers should index into field_data[] with UF_* values iff field_set 258 | * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and 259 | * because we probably have padding left over), we convert any port to 260 | * a uint16_t. 261 | */ 262 | struct http_parser_url { 263 | uint16_t field_set; /* Bitmask of (1 << UF_*) values */ 264 | uint16_t port; /* Converted UF_PORT string */ 265 | 266 | struct { 267 | uint16_t off; /* Offset into buffer in which field starts */ 268 | uint16_t len; /* Length of run in buffer */ 269 | } field_data[UF_MAX]; 270 | }; 271 | 272 | 273 | /* Returns the library version. Bits 16-23 contain the major version number, 274 | * bits 8-15 the minor version number and bits 0-7 the patch level. 275 | * Usage example: 276 | * 277 | * unsigned long version = http_parser_version(); 278 | * unsigned major = (version >> 16) & 255; 279 | * unsigned minor = (version >> 8) & 255; 280 | * unsigned patch = version & 255; 281 | * printf("http_parser v%u.%u.%u\n", major, minor, version); 282 | */ 283 | unsigned long http_parser_version(void); 284 | 285 | void http_parser_init(http_parser *parser, enum http_parser_type type); 286 | 287 | 288 | size_t http_parser_execute(http_parser *parser, 289 | const http_parser_settings *settings, 290 | const char *data, 291 | size_t len); 292 | 293 | 294 | /* If http_should_keep_alive() in the on_headers_complete or 295 | * on_message_complete callback returns 0, then this should be 296 | * the last message on the connection. 297 | * If you are the server, respond with the "Connection: close" header. 298 | * If you are the client, close the connection. 299 | */ 300 | int http_should_keep_alive(const http_parser *parser); 301 | 302 | /* Returns a string version of the HTTP method. */ 303 | const char *http_method_str(enum http_method m); 304 | 305 | /* Return a string name of the given error */ 306 | const char *http_errno_name(enum http_errno err); 307 | 308 | /* Return a string description of the given error */ 309 | const char *http_errno_description(enum http_errno err); 310 | 311 | /* Parse a URL; return nonzero on failure */ 312 | int http_parser_parse_url(const char *buf, size_t buflen, 313 | int is_connect, 314 | struct http_parser_url *u); 315 | 316 | /* Pause or un-pause the parser; a nonzero value pauses */ 317 | void http_parser_pause(http_parser *parser, int paused); 318 | 319 | /* Checks if this is the final chunk of the body. */ 320 | int http_body_is_final(const http_parser *parser); 321 | 322 | #ifdef __cplusplus 323 | } 324 | #endif 325 | #endif 326 | -------------------------------------------------------------------------------- /test/io.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | 3 | Copyright 2013 Konstantin Olkhovskiy 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | ********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "io.h" 29 | 30 | static void reader_fiber(FBR_P_ void *_arg) 31 | { 32 | int fd = *(int *)_arg; 33 | const int buf_size = 10; 34 | char buf[buf_size]; 35 | size_t retval; 36 | size_t count = 0; 37 | for (;;) { 38 | retval = fbr_read(FBR_A_ fd, buf, buf_size); 39 | if (0 == retval) { 40 | fail_unless(1000 == count); 41 | return; 42 | } 43 | fail_unless(retval > 0, NULL); 44 | count += retval; 45 | } 46 | } 47 | 48 | static void writer_fiber(FBR_P_ void *_arg) 49 | { 50 | int fd = *(int *)_arg; 51 | int i; 52 | int retval; 53 | const int buf_size = 100; 54 | char buf[buf_size]; 55 | memset(buf, 0x00, buf_size); 56 | for(i = 0; i < 10; i++) { 57 | retval = fbr_write(FBR_A_ fd, buf, buf_size); 58 | fail_if(retval != buf_size); 59 | } 60 | close(fd); 61 | } 62 | 63 | 64 | START_TEST(test_read_write) 65 | { 66 | struct fbr_context context; 67 | fbr_id_t reader = FBR_ID_NULL, writer = FBR_ID_NULL; 68 | int fds[2]; 69 | int retval; 70 | 71 | retval = pipe(fds); 72 | fail_unless(0 == retval); 73 | retval = fbr_fd_nonblock(&context, fds[0]); 74 | fail_unless(0 == retval); 75 | retval = fbr_fd_nonblock(&context, fds[1]); 76 | fail_unless(0 == retval); 77 | 78 | fbr_init(&context, EV_DEFAULT); 79 | 80 | reader = fbr_create(&context, "reader", reader_fiber, fds + 0, 0); 81 | fail_if(fbr_id_isnull(reader), NULL); 82 | writer = fbr_create(&context, "writer", writer_fiber, fds + 1, 0); 83 | fail_if(fbr_id_isnull(reader), NULL); 84 | 85 | retval = fbr_transfer(&context, reader); 86 | fail_unless(0 == retval, NULL); 87 | retval = fbr_transfer(&context, writer); 88 | fail_unless(0 == retval, NULL); 89 | 90 | ev_run(EV_DEFAULT, 0); 91 | 92 | fail_unless(fbr_is_reclaimed(&context, reader)); 93 | fail_unless(fbr_is_reclaimed(&context, writer)); 94 | 95 | fbr_destroy(&context); 96 | } 97 | END_TEST 98 | 99 | START_TEST(test_read_write_premature) 100 | { 101 | struct fbr_context context; 102 | fbr_id_t reader = FBR_ID_NULL; 103 | int fds[2]; 104 | int retval; 105 | 106 | retval = pipe(fds); 107 | fail_unless(0 == retval); 108 | retval = fbr_fd_nonblock(&context, fds[0]); 109 | fail_unless(0 == retval); 110 | retval = fbr_fd_nonblock(&context, fds[1]); 111 | fail_unless(0 == retval); 112 | 113 | fbr_init(&context, EV_DEFAULT); 114 | 115 | reader = fbr_create(&context, "reader", reader_fiber, fds + 0, 0); 116 | fail_if(fbr_id_isnull(reader), NULL); 117 | 118 | retval = fbr_transfer(&context, reader); 119 | fail_unless(0 == retval, NULL); 120 | 121 | close(fds[1]); 122 | 123 | retval = fbr_reclaim(&context, reader); 124 | fail_unless(0 == retval, NULL); 125 | 126 | ev_run(EV_DEFAULT, 0); 127 | 128 | fail_unless(fbr_is_reclaimed(&context, reader)); 129 | 130 | fbr_destroy(&context); 131 | } 132 | END_TEST 133 | 134 | #define buf_size (1 * 1024 * 1024) 135 | static void all_reader_fiber(FBR_P_ void *_arg) 136 | { 137 | int fd = *(int *)_arg; 138 | char *buf = malloc(buf_size); 139 | size_t retval; 140 | retval = fbr_read_all(FBR_A_ fd, buf, buf_size); 141 | fail_unless(buf_size == retval, NULL); 142 | free(buf); 143 | } 144 | 145 | static void all_writer_fiber(FBR_P_ void *_arg) 146 | { 147 | int fd = *(int *)_arg; 148 | size_t retval; 149 | char *buf = calloc(buf_size, 1); 150 | retval = fbr_write_all(FBR_A_ fd, buf, buf_size); 151 | fail_if(retval != buf_size, NULL); 152 | close(fd); 153 | free(buf); 154 | } 155 | #undef buf_size 156 | 157 | 158 | START_TEST(test_read_write_all) 159 | { 160 | struct fbr_context context; 161 | fbr_id_t reader = FBR_ID_NULL, writer = FBR_ID_NULL; 162 | int fds[2]; 163 | int retval; 164 | 165 | retval = pipe(fds); 166 | fail_unless(0 == retval); 167 | retval = fbr_fd_nonblock(&context, fds[0]); 168 | fail_unless(0 == retval); 169 | retval = fbr_fd_nonblock(&context, fds[1]); 170 | fail_unless(0 == retval); 171 | 172 | fbr_init(&context, EV_DEFAULT); 173 | 174 | reader = fbr_create(&context, "reader_all", all_reader_fiber, fds + 0, 175 | 0); 176 | fail_if(fbr_id_isnull(reader), NULL); 177 | writer = fbr_create(&context, "writer_all", all_writer_fiber, fds + 1, 178 | 0); 179 | fail_if(fbr_id_isnull(reader), NULL); 180 | 181 | retval = fbr_transfer(&context, reader); 182 | fail_unless(0 == retval, NULL); 183 | retval = fbr_transfer(&context, writer); 184 | fail_unless(0 == retval, NULL); 185 | 186 | ev_run(EV_DEFAULT, 0); 187 | 188 | fail_unless(fbr_is_reclaimed(&context, reader)); 189 | fail_unless(fbr_is_reclaimed(&context, writer)); 190 | 191 | fbr_destroy(&context); 192 | } 193 | END_TEST 194 | 195 | static void line_reader_fiber(FBR_P_ void *_arg) 196 | { 197 | int fd = *(int *)_arg; 198 | const int buf_size = 34; 199 | char buf[buf_size]; 200 | char *expected; 201 | size_t retval; 202 | 203 | expected = "Lorem ipsum dolor sit amet,\n"; 204 | retval = fbr_readline(FBR_A_ fd, buf, buf_size); 205 | fail_unless(retval > 0); 206 | fail_unless(0 == strcmp(expected, buf), "``%s'' != ``%s''", expected, buf); 207 | 208 | expected = "consectetur adipiscing elit.\n"; 209 | retval = fbr_readline(FBR_A_ fd, buf, buf_size); 210 | fail_unless(retval > 0); 211 | fail_unless(0 == strcmp(expected, buf), "``%s'' != ``%s''", expected, buf); 212 | 213 | expected = "Phasellus pharetra turpis eros,\n"; 214 | retval = fbr_readline(FBR_A_ fd, buf, buf_size); 215 | fail_unless(retval > 0); 216 | fail_unless(0 == strcmp(expected, buf), "``%s'' != ``%s''", expected, buf); 217 | 218 | expected = "eu blandit nulla.\n"; 219 | retval = fbr_readline(FBR_A_ fd, buf, buf_size); 220 | fail_unless(retval > 0); 221 | fail_unless(0 == strcmp(expected, buf), "``%s'' != ``%s''", expected, buf); 222 | 223 | expected = "Cras placerat egestas tortor,\n"; 224 | retval = fbr_readline(FBR_A_ fd, buf, buf_size); 225 | fail_unless(retval > 0); 226 | fail_unless(0 == strcmp(expected, buf), "``%s'' != ``%s''", expected, buf); 227 | 228 | /* buffer is shorter than the whole line */ 229 | expected = "vel ullamcorper turpis commodo vi"; 230 | retval = fbr_readline(FBR_A_ fd, buf, buf_size); 231 | fail_unless(retval > 0); 232 | fail_unless(0 == strcmp(expected, buf), "``%s'' != ``%s''", expected, buf); 233 | 234 | expected = "In."; 235 | retval = fbr_readline(FBR_A_ fd, buf, buf_size); 236 | fail_unless(retval > 0); 237 | fail_unless(0 == strcmp(expected, buf), "``%s'' != ``%s''", expected, buf); 238 | } 239 | 240 | static void line_writer_fiber(FBR_P_ _unused_ void *_arg) 241 | { 242 | int fd = *(int *)_arg; 243 | size_t retval; 244 | char *buf = "Lorem ipsum dolor sit amet,\n" 245 | "consectetur adipiscing elit.\n" 246 | "Phasellus pharetra turpis eros,\n" 247 | "eu blandit nulla.\n" 248 | "Cras placerat egestas tortor,\n" 249 | "vel ullamcorper turpis commodo vitae.\n" 250 | "In."; 251 | retval = fbr_write_all(FBR_A_ fd, buf, strlen(buf)); 252 | fail_unless(retval > 0, NULL); 253 | close(fd); 254 | } 255 | 256 | START_TEST(test_read_line) 257 | { 258 | struct fbr_context context; 259 | fbr_id_t reader = FBR_ID_NULL, writer = FBR_ID_NULL; 260 | int fds[2]; 261 | int retval; 262 | 263 | retval = pipe(fds); 264 | fail_unless(0 == retval); 265 | retval = fbr_fd_nonblock(&context, fds[0]); 266 | fail_unless(0 == retval); 267 | retval = fbr_fd_nonblock(&context, fds[1]); 268 | fail_unless(0 == retval); 269 | 270 | fbr_init(&context, EV_DEFAULT); 271 | 272 | reader = fbr_create(&context, "reader_line", line_reader_fiber, fds + 0, 0); 273 | fail_if(fbr_id_isnull(reader), NULL); 274 | writer = fbr_create(&context, "writer_line", line_writer_fiber, fds + 1, 0); 275 | fail_if(fbr_id_isnull(reader), NULL); 276 | 277 | retval = fbr_transfer(&context, reader); 278 | fail_unless(0 == retval, NULL); 279 | retval = fbr_transfer(&context, writer); 280 | fail_unless(0 == retval, NULL); 281 | 282 | ev_run(EV_DEFAULT, 0); 283 | 284 | fail_unless(fbr_is_reclaimed(&context, reader)); 285 | fail_unless(fbr_is_reclaimed(&context, writer)); 286 | 287 | fbr_destroy(&context); 288 | } 289 | END_TEST 290 | 291 | #define buf_size 1200 292 | #define ADDRESS "127.0.0.1" 293 | #define PORT 12345 294 | #define count 10 295 | static void udp_reader_fiber(FBR_P_ _unused_ void *_arg) 296 | { 297 | int fd; 298 | int i; 299 | char *buf = malloc(buf_size); 300 | size_t retval; 301 | struct sockaddr_in addr; 302 | socklen_t addrlen; 303 | 304 | memset(&addr, 0x00, sizeof(addr)); 305 | addr.sin_family = AF_INET; 306 | retval = inet_aton(ADDRESS, &addr.sin_addr); 307 | fail_if(0 == retval); 308 | addr.sin_port = PORT; 309 | 310 | fd = socket(AF_INET, SOCK_DGRAM, 0); 311 | fail_if(fd < 0); 312 | 313 | retval = fbr_fd_nonblock(FBR_A_ fd); 314 | fail_unless(0 == retval); 315 | 316 | retval = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int)); 317 | fail_unless(0 == retval); 318 | 319 | retval = bind(fd, (struct sockaddr *) &addr, sizeof(addr)); 320 | fail_unless(0 == retval); 321 | 322 | for (i = 0; i < count; i++) { 323 | retval = fbr_recvfrom(FBR_A_ fd, buf, buf_size, 0, 324 | (struct sockaddr *)&addr, &addrlen); 325 | fail_unless(retval == buf_size); 326 | } 327 | free(buf); 328 | } 329 | 330 | static void udp_writer_fiber(FBR_P_ _unused_ void *_arg) 331 | { 332 | int fd; 333 | int i; 334 | char *buf = calloc(buf_size, 1); 335 | size_t retval; 336 | struct sockaddr_in addr; 337 | socklen_t addrlen = sizeof(addr); 338 | 339 | addr.sin_family = AF_INET; 340 | retval = inet_aton(ADDRESS, &addr.sin_addr); 341 | fail_if(0 == retval); 342 | addr.sin_port = PORT; 343 | 344 | fd = socket(AF_INET, SOCK_DGRAM, 0); 345 | fail_if(fd < 0); 346 | 347 | retval = fbr_fd_nonblock(FBR_A_ fd); 348 | fail_unless(0 == retval); 349 | 350 | for (i = 0; i < count; i++) { 351 | retval = fbr_sendto(FBR_A_ fd, buf, buf_size, 0, 352 | (struct sockaddr *)&addr, addrlen); 353 | fail_unless(retval == buf_size); 354 | } 355 | close(fd); 356 | free(buf); 357 | } 358 | 359 | static void tcp_reader_fiber(FBR_P_ _unused_ void *_arg) 360 | { 361 | int fd, client_fd; 362 | char *buf = malloc(buf_size); 363 | size_t retval; 364 | struct sockaddr_in addr, peer_addr; 365 | socklen_t addrlen; 366 | 367 | memset(&addr, 0x00, sizeof(addr)); 368 | addr.sin_family = AF_INET; 369 | retval = inet_aton(ADDRESS, &addr.sin_addr); 370 | fail_if(0 == retval); 371 | addr.sin_port = PORT; 372 | 373 | fd = socket(AF_INET, SOCK_STREAM, 0); 374 | fail_if(fd < 0); 375 | 376 | retval = fbr_fd_nonblock(FBR_A_ fd); 377 | fail_unless(0 == retval); 378 | 379 | retval = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int)); 380 | fail_unless(0 == retval); 381 | 382 | retval = bind(fd, (struct sockaddr *) &addr, sizeof(addr)); 383 | fail_unless(0 == retval); 384 | 385 | retval = listen(fd, 10); 386 | fail_unless(0 == retval); 387 | 388 | addrlen = sizeof(peer_addr); 389 | client_fd = fbr_accept(FBR_A_ fd, (struct sockaddr *) &peer_addr, &addrlen); 390 | fail_if(0 > client_fd); 391 | 392 | retval = fbr_fd_nonblock(FBR_A_ client_fd); 393 | fail_unless(0 == retval); 394 | 395 | retval = fbr_read_all(FBR_A_ client_fd, buf, buf_size); 396 | fail_unless(retval == buf_size); 397 | 398 | close(client_fd); 399 | close(fd); 400 | free(buf); 401 | } 402 | 403 | static void tcp_writer_fiber(FBR_P_ _unused_ void *_arg) 404 | { 405 | int fd; 406 | char *buf = calloc(buf_size, 1); 407 | size_t retval; 408 | struct sockaddr_in addr; 409 | socklen_t addrlen = sizeof(addr); 410 | 411 | addr.sin_family = AF_INET; 412 | retval = inet_aton(ADDRESS, &addr.sin_addr); 413 | fail_if(0 == retval); 414 | addr.sin_port = PORT; 415 | 416 | fd = socket(AF_INET, SOCK_STREAM, 0); 417 | fail_if(fd < 0); 418 | 419 | retval = fbr_fd_nonblock(FBR_A_ fd); 420 | fail_unless(0 == retval); 421 | 422 | retval = fbr_connect(FBR_A_ fd, (struct sockaddr *) &addr, addrlen); 423 | fail_unless(0 == retval); 424 | 425 | retval = fbr_write_all(FBR_A_ fd, buf, buf_size); 426 | fail_unless(retval == buf_size); 427 | 428 | close(fd); 429 | free(buf); 430 | } 431 | #undef PORT 432 | #undef ADDRESS 433 | #undef buf_size 434 | #undef count 435 | 436 | 437 | START_TEST(test_udp) 438 | { 439 | struct fbr_context context; 440 | fbr_id_t reader = FBR_ID_NULL, writer = FBR_ID_NULL; 441 | int retval; 442 | 443 | fbr_init(&context, EV_DEFAULT); 444 | 445 | reader = fbr_create(&context, "reader_udp", udp_reader_fiber, NULL, 0); 446 | fail_if(fbr_id_isnull(reader), NULL); 447 | writer = fbr_create(&context, "writer_udp", udp_writer_fiber, NULL, 0); 448 | fail_if(fbr_id_isnull(reader), NULL); 449 | 450 | retval = fbr_transfer(&context, reader); 451 | fail_unless(0 == retval, NULL); 452 | retval = fbr_transfer(&context, writer); 453 | fail_unless(0 == retval, NULL); 454 | 455 | ev_run(EV_DEFAULT, 0); 456 | 457 | fail_unless(fbr_is_reclaimed(&context, reader)); 458 | fail_unless(fbr_is_reclaimed(&context, writer)); 459 | 460 | fbr_destroy(&context); 461 | } 462 | END_TEST 463 | 464 | START_TEST(test_tcp) 465 | { 466 | struct fbr_context context; 467 | fbr_id_t reader = FBR_ID_NULL, writer = FBR_ID_NULL; 468 | int retval; 469 | 470 | fbr_init(&context, EV_DEFAULT); 471 | 472 | reader = fbr_create(&context, "reader_tcp", tcp_reader_fiber, NULL, 0); 473 | fail_if(fbr_id_isnull(reader), NULL); 474 | writer = fbr_create(&context, "writer_tcp", tcp_writer_fiber, NULL, 0); 475 | fail_if(fbr_id_isnull(reader), NULL); 476 | 477 | retval = fbr_transfer(&context, reader); 478 | fail_unless(0 == retval, NULL); 479 | retval = fbr_transfer(&context, writer); 480 | fail_unless(0 == retval, NULL); 481 | 482 | ev_run(EV_DEFAULT, 0); 483 | 484 | fail_unless(fbr_is_reclaimed(&context, reader)); 485 | fail_unless(fbr_is_reclaimed(&context, writer)); 486 | 487 | fbr_destroy(&context); 488 | } 489 | END_TEST 490 | 491 | 492 | TCase * io_tcase(void) 493 | { 494 | TCase *tc_io = tcase_create ("IO"); 495 | tcase_add_test(tc_io, test_read_write); 496 | tcase_add_test(tc_io, test_read_write_all); 497 | tcase_add_test(tc_io, test_read_line); 498 | tcase_add_test(tc_io, test_udp); 499 | tcase_add_test(tc_io, test_tcp); 500 | tcase_add_test(tc_io, test_read_write_premature); 501 | return tc_io; 502 | } 503 | -------------------------------------------------------------------------------- /coro/coro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2012 Marc Alexander Lehmann 3 | * 4 | * Redistribution and use in source and binary forms, with or without modifica- 5 | * tion, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 16 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 18 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 22 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * Alternatively, the contents of this file may be used under the terms of 26 | * the GNU General Public License ("GPL") version 2 or any later version, 27 | * in which case the provisions of the GPL are applicable instead of 28 | * the above. If you wish to allow the use of your version of this file 29 | * only under the terms of the GPL and not to allow others to use your 30 | * version of this file under the BSD license, indicate your decision 31 | * by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL. If you do not delete the 33 | * provisions above, a recipient may use your version of this file under 34 | * either the BSD or the GPL. 35 | * 36 | * This library is modelled strictly after Ralf S. Engelschalls article at 37 | * http://www.gnu.org/software/pth/rse-pmt.ps. So most of the credit must 38 | * go to Ralf S. Engelschall . 39 | * 40 | * This coroutine library is very much stripped down. You should either 41 | * build your own process abstraction using it or - better - just use GNU 42 | * Portable Threads, http://www.gnu.org/software/pth/. 43 | * 44 | */ 45 | 46 | /* 47 | * 2006-10-26 Include stddef.h on OS X to work around one of its bugs. 48 | * Reported by Michael_G_Schwern. 49 | * 2006-11-26 Use _setjmp instead of setjmp on GNU/Linux. 50 | * 2007-04-27 Set unwind frame info if gcc 3+ and ELF is detected. 51 | * Use _setjmp instead of setjmp on _XOPEN_SOURCE >= 600. 52 | * 2007-05-02 Add assembly versions for x86 and amd64 (to avoid reliance 53 | * on SIGUSR2 and sigaltstack in Crossfire). 54 | * 2008-01-21 Disable CFI usage on anything but GNU/Linux. 55 | * 2008-03-02 Switched to 2-clause BSD license with GPL exception. 56 | * 2008-04-04 New (but highly unrecommended) pthreads backend. 57 | * 2008-04-24 Reinstate CORO_LOSER (had wrong stack adjustments). 58 | * 2008-10-30 Support assembly method on x86 with and without frame pointer. 59 | * 2008-11-03 Use a global asm statement for CORO_ASM, idea by pippijn. 60 | * 2008-11-05 Hopefully fix misaligned stacks with CORO_ASM/SETJMP. 61 | * 2008-11-07 rbp wasn't saved in CORO_ASM on x86_64. 62 | * introduce coro_destroy, which is a nop except for pthreads. 63 | * speed up CORO_PTHREAD. Do no longer leak threads either. 64 | * coro_create now allows one to create source coro_contexts. 65 | * do not rely on makecontext passing a void * correctly. 66 | * try harder to get _setjmp/_longjmp. 67 | * major code cleanup/restructuring. 68 | * 2008-11-10 the .cfi hacks are no longer needed. 69 | * 2008-11-16 work around a freebsd pthread bug. 70 | * 2008-11-19 define coro_*jmp symbols for easier porting. 71 | * 2009-06-23 tentative win32-backend support for mingw32 (Yasuhiro Matsumoto). 72 | * 2010-12-03 tentative support for uclibc (which lacks all sorts of things). 73 | * 2011-05-30 set initial callee-saved-registers to zero with CORO_ASM. 74 | * use .cfi_undefined rip on linux-amd64 for better backtraces. 75 | * 2011-06-08 maybe properly implement weird windows amd64 calling conventions. 76 | * 2011-07-03 rely on __GCC_HAVE_DWARF2_CFI_ASM for cfi detection. 77 | * 2011-08-08 cygwin trashes stacks, use pthreads with double stack on cygwin. 78 | * 2012-12-04 reduce misprediction penalty for x86/amd64 assembly switcher. 79 | * 2012-12-05 experimental fiber backend (allocates stack twice). 80 | * 2012-12-07 API version 3 - add coro_stack_alloc/coro_stack_free. 81 | * 2012-12-21 valgrind stack registering was broken. 82 | */ 83 | 84 | #ifndef CORO_H 85 | #define CORO_H 86 | 87 | #if __cplusplus 88 | extern "C" { 89 | #endif 90 | 91 | /* 92 | * This library consists of only three files 93 | * coro.h, coro.c and LICENSE (and optionally README) 94 | * 95 | * It implements what is known as coroutines, in a hopefully 96 | * portable way. 97 | * 98 | * All compiletime symbols must be defined both when including coro.h 99 | * (using libcoro) as well as when compiling coro.c (the implementation). 100 | * 101 | * You can manually specify which flavour you want. If you don't define 102 | * any of these, libcoro tries to choose a safe and fast default: 103 | * 104 | * -DCORO_UCONTEXT 105 | * 106 | * This flavour uses SUSv2's get/set/swap/makecontext functions that 107 | * unfortunately only some unices support, and is quite slow. 108 | * 109 | * -DCORO_SJLJ 110 | * 111 | * This flavour uses SUSv2's setjmp/longjmp and sigaltstack functions to 112 | * do it's job. Coroutine creation is much slower than UCONTEXT, but 113 | * context switching is a bit cheaper. It should work on almost all unices. 114 | * 115 | * -DCORO_LINUX 116 | * 117 | * CORO_SJLJ variant. 118 | * Old GNU/Linux systems (<= glibc-2.1) only work with this implementation 119 | * (it is very fast and therefore recommended over other methods, but 120 | * doesn't work with anything newer). 121 | * 122 | * -DCORO_LOSER 123 | * 124 | * CORO_SJLJ variant. 125 | * Microsoft's highly proprietary platform doesn't support sigaltstack, and 126 | * this selects a suitable workaround for this platform. It might not work 127 | * with your compiler though - it has only been tested with MSVC 6. 128 | * 129 | * -DCORO_FIBER 130 | * 131 | * Slower, but probably more portable variant for the Microsoft operating 132 | * system, using fibers. Ignores the passed stack and allocates it internally. 133 | * Also, due to bugs in cygwin, this does not work with cygwin. 134 | * 135 | * -DCORO_IRIX 136 | * 137 | * CORO_SJLJ variant. 138 | * For SGI's version of Microsoft's NT ;) 139 | * 140 | * -DCORO_ASM 141 | * 142 | * Hand coded assembly, known to work only on a few architectures/ABI: 143 | * GCC + x86/IA32 and amd64/x86_64 + GNU/Linux and a few BSDs. Fastest choice, 144 | * if it works. 145 | * 146 | * -DCORO_PTHREAD 147 | * 148 | * Use the pthread API. You have to provide and -lpthread. 149 | * This is likely the slowest backend, and it also does not support fork(), 150 | * so avoid it at all costs. 151 | * 152 | * If you define neither of these symbols, coro.h will try to autodetect 153 | * the best/safest model. To help with the autodetection, you should check 154 | * (e.g. using autoconf) and define the following symbols: HAVE_UCONTEXT_H 155 | * / HAVE_SETJMP_H / HAVE_SIGALTSTACK. 156 | */ 157 | 158 | /* 159 | * Changes when the API changes incompatibly. 160 | * This is ONLY the API version - there is no ABI compatibility between releases. 161 | * 162 | * Changes in API version 2: 163 | * replaced bogus -DCORO_LOOSE with grammatically more correct -DCORO_LOSER 164 | * Changes in API version 3: 165 | * introduced stack management (CORO_STACKALLOC) 166 | */ 167 | #define CORO_VERSION 3 168 | 169 | #include 170 | 171 | /* 172 | * This is the type for the initialization function of a new coroutine. 173 | */ 174 | typedef void (*coro_func)(void *); 175 | 176 | /* 177 | * A coroutine state is saved in the following structure. Treat it as an 178 | * opaque type. errno and sigmask might be saved, but don't rely on it, 179 | * implement your own switching primitive if you need that. 180 | */ 181 | typedef struct coro_context coro_context; 182 | 183 | /* 184 | * This function creates a new coroutine. Apart from a pointer to an 185 | * uninitialised coro_context, it expects a pointer to the entry function 186 | * and the single pointer value that is given to it as argument. 187 | * 188 | * Allocating/deallocating the stack is your own responsibility. 189 | * 190 | * As a special case, if coro, arg, sptr and ssze are all zero, 191 | * then an "empty" coro_context will be created that is suitable 192 | * as an initial source for coro_transfer. 193 | * 194 | * This function is not reentrant, but putting a mutex around it 195 | * will work. 196 | */ 197 | void coro_create (coro_context *ctx, /* an uninitialised coro_context */ 198 | coro_func coro, /* the coroutine code to be executed */ 199 | void *arg, /* a single pointer passed to the coro */ 200 | void *sptr, /* start of stack area */ 201 | size_t ssze); /* size of stack area in bytes */ 202 | 203 | /* 204 | * The following prototype defines the coroutine switching function. It is 205 | * sometimes implemented as a macro, so watch out. 206 | * 207 | * This function is thread-safe and reentrant. 208 | */ 209 | #if 0 210 | void coro_transfer (coro_context *prev, coro_context *next); 211 | #endif 212 | 213 | /* 214 | * The following prototype defines the coroutine destroy function. It 215 | * is sometimes implemented as a macro, so watch out. It also serves no 216 | * purpose unless you want to use the CORO_PTHREAD backend, where it is 217 | * used to clean up the thread. You are responsible for freeing the stack 218 | * and the context itself. 219 | * 220 | * This function is thread-safe and reentrant. 221 | */ 222 | #if 0 223 | void coro_destroy (coro_context *ctx); 224 | #endif 225 | 226 | /*****************************************************************************/ 227 | /* optional stack management */ 228 | /*****************************************************************************/ 229 | /* 230 | * You can disable all of the stack management functions by 231 | * defining CORO_STACKALLOC to 0. Otherwise, they are enabled by default. 232 | * 233 | * If stack management is enabled, you can influence the implementation via these 234 | * symbols: 235 | * 236 | * -DCORO_USE_VALGRIND 237 | * 238 | * If defined, then libcoro will include valgrind/valgrind.h and register 239 | * and unregister stacks with valgrind. 240 | * 241 | * -DCORO_GUARDPAGES=n 242 | * 243 | * libcoro will try to use the specified number of guard pages to protect against 244 | * stack overflow. If n is 0, then the feature will be disabled. If it isn't 245 | * defined, then libcoro will choose a suitable default. If guardpages are not 246 | * supported on the platform, then the feature will be silently disabled. 247 | */ 248 | #ifndef CORO_STACKALLOC 249 | # define CORO_STACKALLOC 1 250 | #endif 251 | 252 | #if CORO_STACKALLOC 253 | 254 | /* 255 | * The only allowed operations on these struct members is to read the 256 | * "sptr" and "ssze" members to pass it to coro_create, to read the "sptr" 257 | * member to see if it is false, in which case the stack isn't allocated, 258 | * and to set the "sptr" member to 0, to indicate to coro_stack_free to 259 | * not actually do anything. 260 | */ 261 | 262 | struct coro_stack 263 | { 264 | void *sptr; 265 | size_t ssze; 266 | #if CORO_USE_VALGRIND 267 | int valgrind_id; 268 | #endif 269 | }; 270 | 271 | /* 272 | * Try to allocate a stack of at least the given size and return true if 273 | * successful, or false otherwise. 274 | * 275 | * The size is *NOT* specified in bytes, but in units of sizeof (void *), 276 | * i.e. the stack is typically 4(8) times larger on 32 bit(64 bit) platforms 277 | * then the size passed in. 278 | * 279 | * If size is 0, then a "suitable" stack size is chosen (usually 1-2MB). 280 | */ 281 | int coro_stack_alloc (struct coro_stack *stack, unsigned int size); 282 | 283 | /* 284 | * Free the stack allocated by coro_stack_alloc again. It is safe to 285 | * call this function on the coro_stack structure even if coro_stack_alloc 286 | * failed. 287 | */ 288 | void coro_stack_free (struct coro_stack *stack); 289 | 290 | #endif 291 | 292 | /* 293 | * That was it. No other user-serviceable parts below here. 294 | */ 295 | 296 | /*****************************************************************************/ 297 | 298 | #if !defined CORO_LOSER && !defined CORO_UCONTEXT \ 299 | && !defined CORO_SJLJ && !defined CORO_LINUX \ 300 | && !defined CORO_IRIX && !defined CORO_ASM \ 301 | && !defined CORO_PTHREAD && !defined CORO_FIBER 302 | # if defined WINDOWS && (defined __i386 || __x86_64 || defined _M_IX86 || defined _M_AMD64) 303 | # define CORO_ASM 1 304 | # elif defined WINDOWS || defined _WIN32 305 | # define CORO_LOSER 1 /* you don't win with windoze */ 306 | # elif __linux && (__i386 || (__x86_64 && !__ILP32)) 307 | # define CORO_ASM 1 308 | # elif defined HAVE_UCONTEXT_H 309 | # define CORO_UCONTEXT 1 310 | # elif defined HAVE_SETJMP_H && defined HAVE_SIGALTSTACK 311 | # define CORO_SJLJ 1 312 | # else 313 | error unknown or unsupported architecture 314 | # endif 315 | #endif 316 | 317 | /*****************************************************************************/ 318 | 319 | #if CORO_UCONTEXT 320 | 321 | # include 322 | 323 | struct coro_context 324 | { 325 | ucontext_t uc; 326 | }; 327 | 328 | # define coro_transfer(p,n) swapcontext (&((p)->uc), &((n)->uc)) 329 | # define coro_destroy(ctx) (void *)(ctx) 330 | 331 | #elif CORO_SJLJ || CORO_LOSER || CORO_LINUX || CORO_IRIX 332 | 333 | # if defined(CORO_LINUX) && !defined(_GNU_SOURCE) 334 | # define _GNU_SOURCE /* for glibc */ 335 | # endif 336 | 337 | # if !CORO_LOSER 338 | # include 339 | # endif 340 | 341 | /* solaris is hopelessly borked, it expands _XOPEN_UNIX to nothing */ 342 | # if __sun 343 | # undef _XOPEN_UNIX 344 | # define _XOPEN_UNIX 1 345 | # endif 346 | 347 | # include 348 | 349 | # if _XOPEN_UNIX > 0 || defined (_setjmp) 350 | # define coro_jmp_buf jmp_buf 351 | # define coro_setjmp(env) _setjmp (env) 352 | # define coro_longjmp(env) _longjmp ((env), 1) 353 | # elif CORO_LOSER 354 | # define coro_jmp_buf jmp_buf 355 | # define coro_setjmp(env) setjmp (env) 356 | # define coro_longjmp(env) longjmp ((env), 1) 357 | # else 358 | # define coro_jmp_buf sigjmp_buf 359 | # define coro_setjmp(env) sigsetjmp (env, 0) 360 | # define coro_longjmp(env) siglongjmp ((env), 1) 361 | # endif 362 | 363 | struct coro_context 364 | { 365 | coro_jmp_buf env; 366 | }; 367 | 368 | # define coro_transfer(p,n) do { if (!coro_setjmp ((p)->env)) coro_longjmp ((n)->env); } while (0) 369 | # define coro_destroy(ctx) (void *)(ctx) 370 | 371 | #elif CORO_ASM 372 | 373 | struct coro_context 374 | { 375 | void **sp; /* must be at offset 0 */ 376 | }; 377 | 378 | void __attribute__ ((__noinline__, __regparm__(2))) 379 | coro_transfer (coro_context *prev, coro_context *next); 380 | 381 | # define coro_destroy(ctx) (void *)(ctx) 382 | 383 | #elif CORO_PTHREAD 384 | 385 | # include 386 | 387 | extern pthread_mutex_t coro_mutex; 388 | 389 | struct coro_context 390 | { 391 | pthread_cond_t cv; 392 | pthread_t id; 393 | }; 394 | 395 | void coro_transfer (coro_context *prev, coro_context *next); 396 | void coro_destroy (coro_context *ctx); 397 | 398 | #elif CORO_FIBER 399 | 400 | struct coro_context 401 | { 402 | void *fiber; 403 | /* only used for initialisation */ 404 | coro_func coro; 405 | void *arg; 406 | }; 407 | 408 | void coro_transfer (coro_context *prev, coro_context *next); 409 | void coro_destroy (coro_context *ctx); 410 | 411 | #endif 412 | 413 | #if __cplusplus 414 | } 415 | #endif 416 | 417 | #endif 418 | 419 | -------------------------------------------------------------------------------- /coro/coro.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2011 Marc Alexander Lehmann 3 | * 4 | * Redistribution and use in source and binary forms, with or without modifica- 5 | * tion, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 16 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 18 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 22 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * Alternatively, the contents of this file may be used under the terms of 26 | * the GNU General Public License ("GPL") version 2 or any later version, 27 | * in which case the provisions of the GPL are applicable instead of 28 | * the above. If you wish to allow the use of your version of this file 29 | * only under the terms of the GPL and not to allow others to use your 30 | * version of this file under the BSD license, indicate your decision 31 | * by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL. If you do not delete the 33 | * provisions above, a recipient may use your version of this file under 34 | * either the BSD or the GPL. 35 | * 36 | * This library is modelled strictly after Ralf S. Engelschalls article at 37 | * http://www.gnu.org/software/pth/rse-pmt.ps. So most of the credit must 38 | * go to Ralf S. Engelschall . 39 | */ 40 | 41 | #include "coro.h" 42 | 43 | #include 44 | #include 45 | 46 | /*****************************************************************************/ 47 | /* ucontext/setjmp/asm backends */ 48 | /*****************************************************************************/ 49 | #if CORO_UCONTEXT || CORO_SJLJ || CORO_LOSER || CORO_LINUX || CORO_IRIX || CORO_ASM 50 | 51 | # if CORO_UCONTEXT 52 | # include 53 | # endif 54 | 55 | # if !defined(STACK_ADJUST_PTR) 56 | # if __sgi 57 | /* IRIX is decidedly NON-unix */ 58 | # define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss) - 8) 59 | # define STACK_ADJUST_SIZE(sp,ss) ((ss) - 8) 60 | # elif (__i386__ && CORO_LINUX) || (_M_IX86 && CORO_LOSER) 61 | # define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss)) 62 | # define STACK_ADJUST_SIZE(sp,ss) (ss) 63 | # elif (__amd64__ && CORO_LINUX) || ((_M_AMD64 || _M_IA64) && CORO_LOSER) 64 | # define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss) - 8) 65 | # define STACK_ADJUST_SIZE(sp,ss) (ss) 66 | # else 67 | # define STACK_ADJUST_PTR(sp,ss) (sp) 68 | # define STACK_ADJUST_SIZE(sp,ss) (ss) 69 | # endif 70 | # endif 71 | 72 | # include 73 | 74 | # if CORO_SJLJ 75 | # include 76 | # include 77 | # include 78 | # endif 79 | 80 | static coro_func coro_init_func; 81 | static void *coro_init_arg; 82 | static coro_context *new_coro, *create_coro; 83 | 84 | static void 85 | coro_init (void) 86 | { 87 | volatile coro_func func = coro_init_func; 88 | volatile void *arg = coro_init_arg; 89 | 90 | coro_transfer (new_coro, create_coro); 91 | 92 | #if __GCC_HAVE_DWARF2_CFI_ASM && __amd64 93 | asm (".cfi_undefined rip"); 94 | #endif 95 | 96 | func ((void *)arg); 97 | 98 | /* the new coro returned. bad. just abort() for now */ 99 | abort (); 100 | } 101 | 102 | # if CORO_SJLJ 103 | 104 | static volatile int trampoline_done; 105 | 106 | /* trampoline signal handler */ 107 | static void 108 | trampoline (int sig) 109 | { 110 | if (coro_setjmp (new_coro->env)) 111 | coro_init (); /* start it */ 112 | else 113 | trampoline_done = 1; 114 | } 115 | 116 | # endif 117 | 118 | # if CORO_ASM 119 | 120 | #if _WIN32 || __CYGWIN__ 121 | #define CORO_WIN_TIB 1 122 | #endif 123 | 124 | asm ( 125 | "\t.text\n" 126 | #if _WIN32 || __CYGWIN__ 127 | "\t.globl _coro_transfer\n" 128 | "_coro_transfer:\n" 129 | #else 130 | "\t.globl coro_transfer\n" 131 | "coro_transfer:\n" 132 | #endif 133 | /* windows, of course, gives a shit on the amd64 ABI and uses different registers */ 134 | /* http://blogs.msdn.com/freik/archive/2005/03/17/398200.aspx */ 135 | #if __amd64 136 | 137 | #if _WIN32 || __CYGWIN__ 138 | #define NUM_SAVED 29 139 | "\tsubq $168, %rsp\t" /* one dummy qword to improve alignment */ 140 | "\tmovaps %xmm6, (%rsp)\n" 141 | "\tmovaps %xmm7, 16(%rsp)\n" 142 | "\tmovaps %xmm8, 32(%rsp)\n" 143 | "\tmovaps %xmm9, 48(%rsp)\n" 144 | "\tmovaps %xmm10, 64(%rsp)\n" 145 | "\tmovaps %xmm11, 80(%rsp)\n" 146 | "\tmovaps %xmm12, 96(%rsp)\n" 147 | "\tmovaps %xmm13, 112(%rsp)\n" 148 | "\tmovaps %xmm14, 128(%rsp)\n" 149 | "\tmovaps %xmm15, 144(%rsp)\n" 150 | "\tpushq %rsi\n" 151 | "\tpushq %rdi\n" 152 | "\tpushq %rbp\n" 153 | "\tpushq %rbx\n" 154 | "\tpushq %r12\n" 155 | "\tpushq %r13\n" 156 | "\tpushq %r14\n" 157 | "\tpushq %r15\n" 158 | #if CORO_WIN_TIB 159 | "\tpushq %fs:0x0\n" 160 | "\tpushq %fs:0x8\n" 161 | "\tpushq %fs:0xc\n" 162 | #endif 163 | "\tmovq %rsp, (%rcx)\n" 164 | "\tmovq (%rdx), %rsp\n" 165 | #if CORO_WIN_TIB 166 | "\tpopq %fs:0xc\n" 167 | "\tpopq %fs:0x8\n" 168 | "\tpopq %fs:0x0\n" 169 | #endif 170 | "\tpopq %r15\n" 171 | "\tpopq %r14\n" 172 | "\tpopq %r13\n" 173 | "\tpopq %r12\n" 174 | "\tpopq %rbx\n" 175 | "\tpopq %rbp\n" 176 | "\tpopq %rdi\n" 177 | "\tpopq %rsi\n" 178 | "\tmovaps (%rsp), %xmm6\n" 179 | "\tmovaps 16(%rsp), %xmm7\n" 180 | "\tmovaps 32(%rsp), %xmm8\n" 181 | "\tmovaps 48(%rsp), %xmm9\n" 182 | "\tmovaps 64(%rsp), %xmm10\n" 183 | "\tmovaps 80(%rsp), %xmm11\n" 184 | "\tmovaps 96(%rsp), %xmm12\n" 185 | "\tmovaps 112(%rsp), %xmm13\n" 186 | "\tmovaps 128(%rsp), %xmm14\n" 187 | "\tmovaps 144(%rsp), %xmm15\n" 188 | "\taddq $168, %rsp\n" 189 | #else 190 | #define NUM_SAVED 6 191 | "\tpushq %rbp\n" 192 | "\tpushq %rbx\n" 193 | "\tpushq %r12\n" 194 | "\tpushq %r13\n" 195 | "\tpushq %r14\n" 196 | "\tpushq %r15\n" 197 | "\tmovq %rsp, (%rdi)\n" 198 | "\tmovq (%rsi), %rsp\n" 199 | "\tpopq %r15\n" 200 | "\tpopq %r14\n" 201 | "\tpopq %r13\n" 202 | "\tpopq %r12\n" 203 | "\tpopq %rbx\n" 204 | "\tpopq %rbp\n" 205 | #endif 206 | "\tpopq %rcx\n" 207 | "\tjmpq *%rcx\n" 208 | 209 | #elif __i386 210 | 211 | #define NUM_SAVED 4 212 | "\tpushl %ebp\n" 213 | "\tpushl %ebx\n" 214 | "\tpushl %esi\n" 215 | "\tpushl %edi\n" 216 | #if CORO_WIN_TIB 217 | #undef NUM_SAVED 218 | #define NUM_SAVED 7 219 | "\tpushl %fs:0\n" 220 | "\tpushl %fs:4\n" 221 | "\tpushl %fs:8\n" 222 | #endif 223 | "\tmovl %esp, (%eax)\n" 224 | "\tmovl (%edx), %esp\n" 225 | #if CORO_WIN_TIB 226 | "\tpopl %fs:8\n" 227 | "\tpopl %fs:4\n" 228 | "\tpopl %fs:0\n" 229 | #endif 230 | "\tpopl %edi\n" 231 | "\tpopl %esi\n" 232 | "\tpopl %ebx\n" 233 | "\tpopl %ebp\n" 234 | "\tpopl %ecx\n" 235 | "\tjmpl *%ecx\n" 236 | 237 | #else 238 | #error unsupported architecture 239 | #endif 240 | ); 241 | 242 | # endif 243 | 244 | void 245 | coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, size_t ssize) 246 | { 247 | coro_context nctx; 248 | # if CORO_SJLJ 249 | stack_t ostk, nstk; 250 | struct sigaction osa, nsa; 251 | sigset_t nsig, osig; 252 | # endif 253 | 254 | if (!coro) 255 | return; 256 | 257 | coro_init_func = coro; 258 | coro_init_arg = arg; 259 | 260 | new_coro = ctx; 261 | create_coro = &nctx; 262 | 263 | # if CORO_SJLJ 264 | /* we use SIGUSR2. first block it, then fiddle with it. */ 265 | 266 | sigemptyset (&nsig); 267 | sigaddset (&nsig, SIGUSR2); 268 | sigprocmask (SIG_BLOCK, &nsig, &osig); 269 | 270 | nsa.sa_handler = trampoline; 271 | sigemptyset (&nsa.sa_mask); 272 | nsa.sa_flags = SA_ONSTACK; 273 | 274 | if (sigaction (SIGUSR2, &nsa, &osa)) 275 | { 276 | perror ("sigaction"); 277 | abort (); 278 | } 279 | 280 | /* set the new stack */ 281 | nstk.ss_sp = STACK_ADJUST_PTR (sptr, ssize); /* yes, some platforms (IRIX) get this wrong. */ 282 | nstk.ss_size = STACK_ADJUST_SIZE (sptr, ssize); 283 | nstk.ss_flags = 0; 284 | 285 | if (sigaltstack (&nstk, &ostk) < 0) 286 | { 287 | perror ("sigaltstack"); 288 | abort (); 289 | } 290 | 291 | trampoline_done = 0; 292 | kill (getpid (), SIGUSR2); 293 | sigfillset (&nsig); sigdelset (&nsig, SIGUSR2); 294 | 295 | while (!trampoline_done) 296 | sigsuspend (&nsig); 297 | 298 | sigaltstack (0, &nstk); 299 | nstk.ss_flags = SS_DISABLE; 300 | if (sigaltstack (&nstk, 0) < 0) 301 | perror ("sigaltstack"); 302 | 303 | sigaltstack (0, &nstk); 304 | if (~nstk.ss_flags & SS_DISABLE) 305 | abort (); 306 | 307 | if (~ostk.ss_flags & SS_DISABLE) 308 | sigaltstack (&ostk, 0); 309 | 310 | sigaction (SIGUSR2, &osa, 0); 311 | sigprocmask (SIG_SETMASK, &osig, 0); 312 | 313 | # elif CORO_LOSER 314 | 315 | coro_setjmp (ctx->env); 316 | #if __CYGWIN__ && __i386 317 | ctx->env[8] = (long) coro_init; 318 | ctx->env[7] = (long) ((char *)sptr + ssize) - sizeof (long); 319 | #elif __CYGWIN__ && __x86_64 320 | ctx->env[7] = (long) coro_init; 321 | ctx->env[6] = (long) ((char *)sptr + ssize) - sizeof (long); 322 | #elif defined __MINGW32__ 323 | ctx->env[5] = (long) coro_init; 324 | ctx->env[4] = (long) ((char *)sptr + ssize) - sizeof (long); 325 | #elif defined _M_IX86 326 | ((_JUMP_BUFFER *)&ctx->env)->Eip = (long) coro_init; 327 | ((_JUMP_BUFFER *)&ctx->env)->Esp = (long) STACK_ADJUST_PTR (sptr, ssize) - sizeof (long); 328 | #elif defined _M_AMD64 329 | ((_JUMP_BUFFER *)&ctx->env)->Rip = (__int64) coro_init; 330 | ((_JUMP_BUFFER *)&ctx->env)->Rsp = (__int64) STACK_ADJUST_PTR (sptr, ssize) - sizeof (__int64); 331 | #elif defined _M_IA64 332 | ((_JUMP_BUFFER *)&ctx->env)->StIIP = (__int64) coro_init; 333 | ((_JUMP_BUFFER *)&ctx->env)->IntSp = (__int64) STACK_ADJUST_PTR (sptr, ssize) - sizeof (__int64); 334 | #else 335 | #error "microsoft libc or architecture not supported" 336 | #endif 337 | 338 | # elif CORO_LINUX 339 | 340 | coro_setjmp (ctx->env); 341 | #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined (JB_PC) && defined (JB_SP) 342 | ctx->env[0].__jmpbuf[JB_PC] = (long) coro_init; 343 | ctx->env[0].__jmpbuf[JB_SP] = (long) STACK_ADJUST_PTR (sptr, ssize) - sizeof (long); 344 | #elif __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined (__mc68000__) 345 | ctx->env[0].__jmpbuf[0].__aregs[0] = (long int)coro_init; 346 | ctx->env[0].__jmpbuf[0].__sp = (int *) ((char *)sptr + ssize) - sizeof (long); 347 | #elif defined (__GNU_LIBRARY__) && defined (__i386__) 348 | ctx->env[0].__jmpbuf[0].__pc = (char *) coro_init; 349 | ctx->env[0].__jmpbuf[0].__sp = (void *) ((char *)sptr + ssize) - sizeof (long); 350 | #elif defined (__GNU_LIBRARY__) && defined (__amd64__) 351 | ctx->env[0].__jmpbuf[JB_PC] = (long) coro_init; 352 | ctx->env[0].__jmpbuf[0].__sp = (void *) ((char *)sptr + ssize) - sizeof (long); 353 | #else 354 | #error "linux libc or architecture not supported" 355 | #endif 356 | 357 | # elif CORO_IRIX 358 | 359 | coro_setjmp (ctx->env, 0); 360 | ctx->env[JB_PC] = (__uint64_t)coro_init; 361 | ctx->env[JB_SP] = (__uint64_t)STACK_ADJUST_PTR (sptr, ssize) - sizeof (long); 362 | 363 | # elif CORO_ASM 364 | 365 | ctx->sp = (void **)(ssize + (char *)sptr); 366 | *--ctx->sp = (void *)abort; /* needed for alignment only */ 367 | *--ctx->sp = (void *)coro_init; 368 | 369 | #if CORO_WIN_TIB 370 | *--ctx->sp = 0; /* ExceptionList */ 371 | *--ctx->sp = (char *)sptr + ssize; /* StackBase */ 372 | *--ctx->sp = sptr; /* StackLimit */ 373 | #endif 374 | 375 | ctx->sp -= NUM_SAVED; 376 | memset (ctx->sp, 0, sizeof (*ctx->sp) * NUM_SAVED); 377 | 378 | # elif CORO_UCONTEXT 379 | 380 | getcontext (&(ctx->uc)); 381 | 382 | ctx->uc.uc_link = 0; 383 | ctx->uc.uc_stack.ss_sp = sptr; 384 | ctx->uc.uc_stack.ss_size = (size_t)ssize; 385 | ctx->uc.uc_stack.ss_flags = 0; 386 | 387 | makecontext (&(ctx->uc), (void (*)())coro_init, 0); 388 | 389 | # endif 390 | 391 | coro_transfer (create_coro, new_coro); 392 | } 393 | 394 | /*****************************************************************************/ 395 | /* pthread backend */ 396 | /*****************************************************************************/ 397 | #elif CORO_PTHREAD 398 | 399 | /* this mutex will be locked by the running coroutine */ 400 | pthread_mutex_t coro_mutex = PTHREAD_MUTEX_INITIALIZER; 401 | 402 | struct coro_init_args 403 | { 404 | coro_func func; 405 | void *arg; 406 | coro_context *self, *main; 407 | }; 408 | 409 | static pthread_t null_tid; 410 | 411 | /* I'd so love to cast pthread_mutex_unlock to void (*)(void *)... */ 412 | static void 413 | mutex_unlock_wrapper (void *arg) 414 | { 415 | pthread_mutex_unlock ((pthread_mutex_t *)arg); 416 | } 417 | 418 | static void * 419 | coro_init (void *args_) 420 | { 421 | struct coro_init_args *args = (struct coro_init_args *)args_; 422 | coro_func func = args->func; 423 | void *arg = args->arg; 424 | 425 | pthread_mutex_lock (&coro_mutex); 426 | 427 | /* we try to be good citizens and use deferred cancellation and cleanup handlers */ 428 | pthread_cleanup_push (mutex_unlock_wrapper, &coro_mutex); 429 | coro_transfer (args->self, args->main); 430 | func (arg); 431 | pthread_cleanup_pop (1); 432 | 433 | return 0; 434 | } 435 | 436 | void 437 | coro_transfer (coro_context *prev, coro_context *next) 438 | { 439 | pthread_cond_signal (&next->cv); 440 | pthread_cond_wait (&prev->cv, &coro_mutex); 441 | #if __FreeBSD__ /* freebsd is of course broken and needs manual testcancel calls... yay... */ 442 | pthread_testcancel (); 443 | #endif 444 | } 445 | 446 | void 447 | coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, size_t ssize) 448 | { 449 | static coro_context nctx; 450 | static int once; 451 | 452 | if (!once) 453 | { 454 | once = 1; 455 | 456 | pthread_mutex_lock (&coro_mutex); 457 | pthread_cond_init (&nctx.cv, 0); 458 | null_tid = pthread_self (); 459 | } 460 | 461 | pthread_cond_init (&ctx->cv, 0); 462 | 463 | if (coro) 464 | { 465 | pthread_attr_t attr; 466 | struct coro_init_args args; 467 | 468 | args.func = coro; 469 | args.arg = arg; 470 | args.self = ctx; 471 | args.main = &nctx; 472 | 473 | pthread_attr_init (&attr); 474 | #if __UCLIBC__ 475 | /* exists, but is borked */ 476 | /*pthread_attr_setstacksize (&attr, (size_t)ssize);*/ 477 | #elif __CYGWIN__ 478 | /* POSIX, not here */ 479 | pthread_attr_setstacksize (&attr, (size_t)ssize); 480 | #else 481 | pthread_attr_setstack (&attr, sptr, (size_t)ssize); 482 | #endif 483 | pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS); 484 | pthread_create (&ctx->id, &attr, coro_init, &args); 485 | 486 | coro_transfer (args.main, args.self); 487 | } 488 | else 489 | ctx->id = null_tid; 490 | } 491 | 492 | void 493 | coro_destroy (coro_context *ctx) 494 | { 495 | if (!pthread_equal (ctx->id, null_tid)) 496 | { 497 | pthread_cancel (ctx->id); 498 | pthread_mutex_unlock (&coro_mutex); 499 | pthread_join (ctx->id, 0); 500 | pthread_mutex_lock (&coro_mutex); 501 | } 502 | 503 | pthread_cond_destroy (&ctx->cv); 504 | } 505 | 506 | /*****************************************************************************/ 507 | /* fiber backend */ 508 | /*****************************************************************************/ 509 | #elif CORO_FIBER 510 | 511 | #define WIN32_LEAN_AND_MEAN 512 | #if _WIN32_WINNT < 0x0400 513 | #undef _WIN32_WINNT 514 | #define _WIN32_WINNT 0x0400 515 | #endif 516 | #include 517 | 518 | VOID CALLBACK 519 | coro_init (PVOID arg) 520 | { 521 | coro_context *ctx = (coro_context *)arg; 522 | 523 | ctx->coro (ctx->arg); 524 | } 525 | 526 | void 527 | coro_transfer (coro_context *prev, coro_context *next) 528 | { 529 | if (!prev->fiber) 530 | { 531 | prev->fiber = GetCurrentFiber (); 532 | 533 | if (prev->fiber == 0 || prev->fiber == (void *)0x1e00) 534 | prev->fiber = ConvertThreadToFiber (0); 535 | } 536 | 537 | SwitchToFiber (next->fiber); 538 | } 539 | 540 | void 541 | coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, size_t ssize) 542 | { 543 | ctx->fiber = 0; 544 | ctx->coro = coro; 545 | ctx->arg = arg; 546 | 547 | if (!coro) 548 | return; 549 | 550 | ctx->fiber = CreateFiber (ssize, coro_init, ctx); 551 | } 552 | 553 | void 554 | coro_destroy (coro_context *ctx) 555 | { 556 | DeleteFiber (ctx->fiber); 557 | } 558 | 559 | #else 560 | #error unsupported backend 561 | #endif 562 | 563 | /*****************************************************************************/ 564 | /* stack management */ 565 | /*****************************************************************************/ 566 | #if CORO_STACKALLOC 567 | 568 | #include 569 | 570 | #ifndef _WIN32 571 | # include 572 | #endif 573 | 574 | #if CORO_USE_VALGRIND 575 | # include 576 | #endif 577 | 578 | #if _POSIX_MAPPED_FILES 579 | # include 580 | # define CORO_MMAP 1 581 | # ifndef MAP_ANONYMOUS 582 | # ifdef MAP_ANON 583 | # define MAP_ANONYMOUS MAP_ANON 584 | # else 585 | # undef CORO_MMAP 586 | # endif 587 | # endif 588 | # include 589 | #else 590 | # undef CORO_MMAP 591 | #endif 592 | 593 | #if _POSIX_MEMORY_PROTECTION 594 | # ifndef CORO_GUARDPAGES 595 | # define CORO_GUARDPAGES 4 596 | # endif 597 | #else 598 | # undef CORO_GUARDPAGES 599 | #endif 600 | 601 | #if !CORO_MMAP 602 | # undef CORO_GUARDPAGES 603 | #endif 604 | 605 | #if !__i386 && !__x86_64 && !__powerpc && !__m68k && !__alpha && !__mips && !__sparc64 606 | # undef CORO_GUARDPAGES 607 | #endif 608 | 609 | #ifndef CORO_GUARDPAGES 610 | # define CORO_GUARDPAGES 0 611 | #endif 612 | 613 | #if !PAGESIZE 614 | #if !CORO_MMAP 615 | #define PAGESIZE 4096 616 | #else 617 | static size_t 618 | coro_pagesize (void) 619 | { 620 | static size_t pagesize; 621 | 622 | if (!pagesize) 623 | pagesize = sysconf (_SC_PAGESIZE); 624 | 625 | return pagesize; 626 | } 627 | 628 | #define PAGESIZE coro_pagesize () 629 | #endif 630 | #endif 631 | 632 | int 633 | coro_stack_alloc (struct coro_stack *stack, unsigned int size) 634 | { 635 | if (!size) 636 | size = 256 * 1024; 637 | 638 | stack->sptr = 0; 639 | stack->ssze = ((size_t)size * sizeof (void *) + PAGESIZE - 1) / PAGESIZE * PAGESIZE; 640 | 641 | #if CORO_FIBER 642 | 643 | stack->sptr = (void *)stack; 644 | return 1; 645 | 646 | #else 647 | 648 | size_t ssze = stack->ssze + CORO_GUARDPAGES * PAGESIZE; 649 | void *base; 650 | 651 | #if CORO_MMAP 652 | /* mmap supposedly does allocate-on-write for us */ 653 | base = mmap (0, ssze, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 654 | 655 | if (base == (void *)-1) 656 | { 657 | /* some systems don't let us have executable heap */ 658 | /* we assume they won't need executable stack in that case */ 659 | base = mmap (0, ssze, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 660 | 661 | if (base == (void *)-1) 662 | return 0; 663 | } 664 | 665 | #if CORO_GUARDPAGES 666 | mprotect (base, CORO_GUARDPAGES * PAGESIZE, PROT_NONE); 667 | #endif 668 | 669 | base = (void*)((char *)base + CORO_GUARDPAGES * PAGESIZE); 670 | #else 671 | base = malloc (ssze); 672 | if (!base) 673 | return 0; 674 | #endif 675 | 676 | #if CORO_USE_VALGRIND 677 | stack->valgrind_id = VALGRIND_STACK_REGISTER ((char *)base, ((char *)base) + ssze - CORO_GUARDPAGES * PAGESIZE); 678 | #endif 679 | 680 | stack->sptr = base; 681 | return 1; 682 | 683 | #endif 684 | } 685 | 686 | void 687 | coro_stack_free (struct coro_stack *stack) 688 | { 689 | #if CORO_FIBER 690 | /* nop */ 691 | #else 692 | #if CORO_USE_VALGRIND 693 | VALGRIND_STACK_DEREGISTER (stack->valgrind_id); 694 | #endif 695 | 696 | #if CORO_MMAP 697 | if (stack->sptr) 698 | munmap ((void*)((char *)stack->sptr - CORO_GUARDPAGES * PAGESIZE), 699 | stack->ssze + CORO_GUARDPAGES * PAGESIZE); 700 | #else 701 | free (stack->sptr); 702 | #endif 703 | #endif 704 | } 705 | 706 | #endif 707 | 708 | --------------------------------------------------------------------------------