├── .clang-format ├── .github └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── doc ├── musl │ └── COPYRIGHT └── poll.txt ├── epoll-shim-interpose.pc.cmakein ├── epoll-shim.pc.cmakein ├── external ├── CMakeLists.txt ├── microatf │ ├── .clang-format │ ├── CMakeLists.txt │ ├── cmake │ │ ├── ATFRunTest.cmake │ │ ├── ATFTest.cmake │ │ ├── ATFTestAddTests.cmake │ │ └── Copyright.txt │ ├── src │ │ ├── CMakeLists.txt │ │ ├── atf-c.c │ │ ├── atf-c.h │ │ └── translate-signal.c │ └── test │ │ ├── CMakeLists.txt │ │ ├── atf-test.c │ │ └── atf-test2.c ├── queue-macros │ ├── CMakeLists.txt │ └── include │ │ └── sys │ │ └── queue.h └── tree-macros │ ├── CMakeLists.txt │ └── include │ └── sys │ └── tree.h ├── include ├── epoll-shim │ └── detail │ │ ├── common.h │ │ ├── poll.h │ │ ├── read.h │ │ └── write.h └── sys │ ├── epoll.h │ ├── eventfd.h │ ├── signalfd.h │ └── timerfd.h ├── src ├── CMakeLists.txt ├── compat_itimerspec.c ├── compat_itimerspec.h ├── compat_kqueue1.c ├── compat_kqueue1.h ├── compat_pipe2.c ├── compat_pipe2.h ├── compat_ppoll.c ├── compat_ppoll.h ├── compat_sem.c ├── compat_sem.h ├── compat_sigops.c ├── compat_sigops.h ├── compat_socket.c ├── compat_socket.h ├── compat_socketpair.c ├── compat_socketpair.h ├── epoll.c ├── epoll_shim_ctx.c ├── epoll_shim_ctx.h ├── epoll_shim_interpose.c ├── epollfd_ctx.c ├── epollfd_ctx.h ├── errno_return.h ├── eventfd.c ├── eventfd_ctx.c ├── eventfd_ctx.h ├── kqueue_event.c ├── kqueue_event.h ├── pollable_desc.h ├── rwlock.c ├── rwlock.h ├── signalfd.c ├── signalfd_ctx.c ├── signalfd_ctx.h ├── timerfd.c ├── timerfd_ctx.c ├── timerfd_ctx.h ├── timespec_util.c ├── timespec_util.h ├── wrap.c └── wrap.h └── test ├── CMakeLists.txt ├── atf-c-leakcheck.h ├── atf-test.c ├── epoll-include-c89-test.c ├── epoll-include-test.c ├── epoll-test.c ├── eventfd-ctx-test.c ├── fcntl-warning.c ├── malloc-fail-test.c ├── perf-many-fds.c ├── pipe-test.c ├── real_close.c ├── rwlock-test.c ├── signalfd-test.c ├── socketpair-test.c ├── timerfd-mock-test.c ├── timerfd-root-test.c ├── timerfd-test.c ├── tst-epoll.c └── tst-timerfd.c /.clang-format: -------------------------------------------------------------------------------- 1 | # $FreeBSD$ 2 | # Basic .clang-format 3 | --- 4 | BasedOnStyle: WebKit 5 | AlignAfterOpenBracket: DontAlign 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: false 10 | AlignTrailingComments: true 11 | AllowAllArgumentsOnNextLine: false 12 | AllowAllParametersOfDeclarationOnNextLine: false 13 | AllowShortBlocksOnASingleLine: Never 14 | AllowShortCaseLabelsOnASingleLine: false 15 | AllowShortFunctionsOnASingleLine: InlineOnly 16 | AllowShortIfStatementsOnASingleLine: Never 17 | AllowShortLoopsOnASingleLine: false 18 | AlwaysBreakAfterReturnType: TopLevelDefinitions 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: MultiLine 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BreakBeforeBinaryOperators: None 24 | BreakBeforeBraces: WebKit 25 | BreakBeforeTernaryOperators: false 26 | # TODO: BreakStringLiterals can cause very strange formatting so turn it off? 27 | BreakStringLiterals: false 28 | # Prefer: 29 | # some_var = function(arg1, 30 | # arg2) 31 | # over: 32 | # some_var = 33 | # function(arg1, arg2) 34 | PenaltyBreakAssignment: 100 35 | # Prefer: 36 | # some_long_function(arg1, arg2 37 | # arg3) 38 | # over: 39 | # some_long_function( 40 | # arg1, arg2, arg3) 41 | PenaltyBreakBeforeFirstCallParameter: 100 42 | CompactNamespaces: true 43 | DerivePointerAlignment: false 44 | DisableFormat: false 45 | ForEachMacros: 46 | - ARB_ARRFOREACH 47 | - ARB_ARRFOREACH_REVWCOND 48 | - ARB_ARRFOREACH_REVERSE 49 | - ARB_FOREACH 50 | - ARB_FOREACH_FROM 51 | - ARB_FOREACH_SAFE 52 | - ARB_FOREACH_REVERSE 53 | - ARB_FOREACH_REVERSE_FROM 54 | - ARB_FOREACH_REVERSE_SAFE 55 | - BIT_FOREACH_ISCLR 56 | - BIT_FOREACH_ISSET 57 | - CPU_FOREACH 58 | - CPU_FOREACH_ISCLR 59 | - CPU_FOREACH_ISSET 60 | - FOREACH_THREAD_IN_PROC 61 | - FOREACH_PROC_IN_SYSTEM 62 | - FOREACH_PRISON_CHILD 63 | - FOREACH_PRISON_DESCENDANT 64 | - FOREACH_PRISON_DESCENDANT_LOCKED 65 | - FOREACH_PRISON_DESCENDANT_LOCKED_LEVEL 66 | - MNT_VNODE_FOREACH_ALL 67 | - MNT_VNODE_FOREACH_ACTIVE 68 | - RB_FOREACH 69 | - RB_FOREACH_FROM 70 | - RB_FOREACH_SAFE 71 | - RB_FOREACH_REVERSE 72 | - RB_FOREACH_REVERSE_FROM 73 | - RB_FOREACH_REVERSE_SAFE 74 | - SLIST_FOREACH 75 | - SLIST_FOREACH_FROM 76 | - SLIST_FOREACH_FROM_SAFE 77 | - SLIST_FOREACH_SAFE 78 | - SLIST_FOREACH_PREVPTR 79 | - SPLAY_FOREACH 80 | - LIST_FOREACH 81 | - LIST_FOREACH_FROM 82 | - LIST_FOREACH_FROM_SAFE 83 | - LIST_FOREACH_SAFE 84 | - STAILQ_FOREACH 85 | - STAILQ_FOREACH_FROM 86 | - STAILQ_FOREACH_FROM_SAFE 87 | - STAILQ_FOREACH_SAFE 88 | - TAILQ_FOREACH 89 | - TAILQ_FOREACH_FROM 90 | - TAILQ_FOREACH_FROM_SAFE 91 | - TAILQ_FOREACH_REVERSE 92 | - TAILQ_FOREACH_REVERSE_FROM 93 | - TAILQ_FOREACH_REVERSE_FROM_SAFE 94 | - TAILQ_FOREACH_REVERSE_SAFE 95 | - TAILQ_FOREACH_SAFE 96 | - VM_MAP_ENTRY_FOREACH 97 | - VM_PAGE_DUMP_FOREACH 98 | IndentCaseLabels: false 99 | IndentPPDirectives: None 100 | Language: Cpp 101 | NamespaceIndentation: None 102 | PointerAlignment: Right 103 | ContinuationIndentWidth: 4 104 | IndentWidth: 8 105 | TabWidth: 8 106 | ColumnLimit: 80 107 | # QualifierAlignment: Right 108 | UseTab: Always 109 | SpaceAfterCStyleCast: false 110 | # LLVM's header include ordering style is almost the exact opposite of ours. 111 | # Unfortunately, they have hard-coded their preferences into clang-format. 112 | # Clobbering this regular expression to avoid matching prevents non-system 113 | # headers from being forcibly moved to the top of the include list. 114 | # http://llvm.org/docs/CodingStandards.html#include-style 115 | IncludeIsMainRegex: 'BLAH_DONT_MATCH_ANYTHING' 116 | SortIncludes: true 117 | KeepEmptyLinesAtTheStartOfBlocks: true 118 | TypenameMacros: 119 | - ARB_ELMTYPE 120 | - ARB_HEAD 121 | - ARB8_HEAD 122 | - ARB16_HEAD 123 | - ARB32_HEAD 124 | - ARB_ENTRY 125 | - ARB8_ENTRY 126 | - ARB16_ENTRY 127 | - ARB32_ENTRY 128 | - LIST_CLASS_ENTRY 129 | - LIST_CLASS_HEAD 130 | - LIST_ENTRY 131 | - LIST_HEAD 132 | - QUEUE_TYPEOF 133 | - RB_ENTRY 134 | - RB_HEAD 135 | - SLIST_CLASS_HEAD 136 | - SLIST_CLASS_ENTRY 137 | - SLIST_HEAD 138 | - SLIST_ENTRY 139 | - SMR_POINTER 140 | - SPLAY_ENTRY 141 | - SPLAY_HEAD 142 | - STAILQ_CLASS_ENTRY 143 | - STAILQ_CLASS_HEAD 144 | - STAILQ_ENTRY 145 | - STAILQ_HEAD 146 | - TAILQ_CLASS_ENTRY 147 | - TAILQ_CLASS_HEAD 148 | - TAILQ_ENTRY 149 | - TAILQ_HEAD 150 | MaxEmptyLinesToKeep: 2 151 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push] 4 | 5 | env: 6 | BUILD_TYPE: RelWithDebInfo 7 | CFLAGS: -DALLOW_TIMER_SLACK 8 | 9 | jobs: 10 | test: 11 | strategy: 12 | matrix: 13 | os: [ubuntu-20.04, macos-13] 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Create Build Environment 18 | run: cmake -E make_directory ${{github.workspace}}/build 19 | - name: Configure CMake 20 | shell: bash 21 | working-directory: ${{github.workspace}}/build 22 | run: | 23 | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 24 | sed -e 's/-DNDEBUG//g' < CMakeCache.txt > CMakeCache.txt.new 25 | mv -- CMakeCache.txt.new CMakeCache.txt 26 | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 27 | - name: Build 28 | working-directory: ${{github.workspace}}/build 29 | shell: bash 30 | run: cmake --build . --config $BUILD_TYPE 31 | - name: Test 32 | working-directory: ${{github.workspace}}/build 33 | shell: bash 34 | run: ctest -C $BUILD_TYPE --output-on-failure 35 | 36 | testfreebsd: 37 | strategy: 38 | matrix: 39 | release: [13.3] 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v2 43 | - name: Test in FreeBSD 44 | id: test 45 | uses: vmactions/freebsd-vm@v1 46 | with: 47 | release: ${{ matrix.release }} 48 | envs: 'BUILD_TYPE' 49 | usesh: true 50 | prepare: pkg install -y cmake 51 | run: | 52 | mkdir build || exit 1 53 | cd build || exit 1 54 | cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE || exit 1 55 | sed -e 's/-DNDEBUG//g' < CMakeCache.txt > CMakeCache.txt.new || exit 1 56 | mv -- CMakeCache.txt.new CMakeCache.txt || exit 1 57 | cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE || exit 1 58 | cmake --build . --config $BUILD_TYPE || exit 1 59 | ctest -C $BUILD_TYPE --output-on-failure || exit 1 60 | 61 | testopenbsd: 62 | runs-on: ubuntu-latest 63 | steps: 64 | - uses: actions/checkout@v2 65 | - name: Test in OpenBSD 66 | id: test 67 | uses: vmactions/openbsd-vm@v1 68 | with: 69 | release: 7.5 70 | envs: 'BUILD_TYPE' 71 | usesh: true 72 | prepare: pkg_add cmake 73 | run: | 74 | mkdir build || exit 1 75 | cd build || exit 1 76 | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE || exit 1 77 | sed -e 's/-DNDEBUG//g' < CMakeCache.txt > CMakeCache.txt.new || exit 1 78 | mv -- CMakeCache.txt.new CMakeCache.txt || exit 1 79 | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE || exit 1 80 | cmake --build . --config $BUILD_TYPE || exit 1 81 | ctest -C $BUILD_TYPE --output-on-failure || exit 1 82 | 83 | testnetbsd: 84 | runs-on: ubuntu-latest 85 | steps: 86 | - uses: actions/checkout@v2 87 | - name: Test in NetBSD 88 | id: test 89 | uses: vmactions/netbsd-vm@v1 90 | with: 91 | release: 9.4 92 | envs: 'BUILD_TYPE' 93 | usesh: true 94 | prepare: /usr/sbin/pkg_add cmake 95 | run: | 96 | mkdir build || exit 1 97 | cd build || exit 1 98 | cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE || exit 1 99 | sed -e 's/-DNDEBUG//g' < CMakeCache.txt > CMakeCache.txt.new || exit 1 100 | mv -- CMakeCache.txt.new CMakeCache.txt || exit 1 101 | cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE || exit 1 102 | cmake --build . --config $BUILD_TYPE || exit 1 103 | ctest -C $BUILD_TYPE --output-on-failure || exit 1 104 | 105 | testdragonflybsd: 106 | runs-on: ubuntu-latest 107 | steps: 108 | - uses: actions/checkout@v2 109 | - name: Test in DragonFly BSD 110 | id: test 111 | uses: vmactions/dragonflybsd-vm@v1 112 | with: 113 | release: 6.4.0 114 | envs: 'BUILD_TYPE' 115 | usesh: true 116 | prepare: pkg install -y cmake 117 | run: | 118 | mkdir build || exit 1 119 | cd build || exit 1 120 | cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE || exit 1 121 | sed -e 's/-DNDEBUG//g' < CMakeCache.txt > CMakeCache.txt.new || exit 1 122 | mv -- CMakeCache.txt.new CMakeCache.txt || exit 1 123 | cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE || exit 1 124 | cmake --build . --config $BUILD_TYPE || exit 1 125 | ctest -C $BUILD_TYPE --output-on-failure || exit 1 126 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | .vscode 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(epoll-shim LANGUAGES C) 3 | 4 | option(BUILD_SHARED_LIBS "build libepoll-shim as shared lib" ON) 5 | option(ENABLE_COMPILER_WARNINGS "enable compiler warnings" OFF) 6 | 7 | if(ENABLE_COMPILER_WARNINGS) 8 | add_compile_options( 9 | "-Wall" 10 | "-Wextra" 11 | "-Wconversion" 12 | "-Wsign-conversion" 13 | "-Wmissing-prototypes" 14 | "-Werror=implicit-function-declaration" 15 | "-Werror=return-type" 16 | "-Werror=incompatible-pointer-types") 17 | endif() 18 | 19 | include(CTest) 20 | 21 | set(CMAKE_C_STANDARD 11) 22 | set(CMAKE_C_EXTENSIONS ON) 23 | 24 | add_subdirectory(external) 25 | 26 | include(CheckTypeSize) 27 | list(APPEND CMAKE_EXTRA_INCLUDE_FILES "errno.h" "stdlib.h") 28 | check_type_size(errno_t ERRNO_T BUILTIN_TYPES_ONLY LANGUAGE C) 29 | if(NOT HAVE_ERRNO_T) 30 | add_definitions(-Derrno_t=int) 31 | endif() 32 | 33 | add_subdirectory(src) 34 | 35 | if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 36 | set(_namespace "${PROJECT_NAME}") 37 | 38 | if(BUILD_TESTING) 39 | file(WRITE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 40 | "add_library(${_namespace}::epoll-shim ALIAS epoll-shim)\n 41 | add_library(${_namespace}::epoll-shim-interpose ALIAS epoll-shim-interpose)\n") 42 | set(${PROJECT_NAME}_DIR "${PROJECT_BINARY_DIR}") 43 | add_subdirectory(test) 44 | endif() 45 | 46 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 47 | return() 48 | endif() 49 | 50 | include(GNUInstallDirs) 51 | 52 | set(CMAKE_INSTALL_PKGCONFIGDIR 53 | "libdata/pkgconfig" 54 | CACHE PATH "Installation directory for pkgconfig (.pc) files") 55 | mark_as_advanced(CMAKE_INSTALL_PKGCONFIGDIR) 56 | set(CMAKE_INSTALL_CMAKEBASEDIR 57 | "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 58 | CACHE PATH "Installation directory for CMake config (.cmake) files") 59 | mark_as_advanced(CMAKE_INSTALL_CMAKEBASEDIR) 60 | 61 | foreach(_pc_filename "${PROJECT_NAME}" "${PROJECT_NAME}-interpose") 62 | configure_file("${PROJECT_SOURCE_DIR}/${_pc_filename}.pc.cmakein" 63 | "${PROJECT_BINARY_DIR}/${_pc_filename}.pc" @ONLY) 64 | install(FILES "${PROJECT_BINARY_DIR}/${_pc_filename}.pc" 65 | DESTINATION "${CMAKE_INSTALL_PKGCONFIGDIR}") 66 | endforeach() 67 | 68 | set(CMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/libepoll-shim") 69 | install( 70 | TARGETS epoll-shim epoll-shim-interpose 71 | EXPORT ${PROJECT_NAME}-targets 72 | LIBRARY 73 | INCLUDES 74 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 75 | install(DIRECTORY "${PROJECT_BINARY_DIR}/install-include/" TYPE INCLUDE) 76 | 77 | install( 78 | EXPORT ${PROJECT_NAME}-targets 79 | NAMESPACE "${_namespace}::" 80 | DESTINATION "${CMAKE_INSTALL_CMAKEBASEDIR}") 81 | if(NOT BUILD_SHARED_LIBS) 82 | set(_deps 83 | "include(CMakeFindDependencyMacro)\n" # 84 | "set(THREADS_PREFER_PTHREAD_FLAG ON)\n" # 85 | "find_dependency(Threads)\n") 86 | endif() 87 | file( 88 | WRITE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 89 | ${_deps} 90 | "include(\"\${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME}-targets.cmake\")\n") 91 | install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 92 | DESTINATION "${CMAKE_INSTALL_CMAKEBASEDIR}") 93 | endif() 94 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Jan Kokemüller 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 deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | 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 FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # epoll-shim 2 | 3 | This is a small library that implements epoll on top of kqueue. 4 | It has been successfully used to port libinput, libevdev, Wayland and more 5 | software to FreeBSD: 6 | 7 | It may be useful for porting other software that uses epoll as well. 8 | 9 | There are some tests inside `test/`. They should also compile under Linux and 10 | can be used to verify proper epoll behavior. 11 | 12 | Sadly, this library contains some very ugly hacks and workarounds. For example: 13 | 14 | - When using `timerfd`, `signalfd` or `eventfd`, the system calls `read`, 15 | `write` and `close` are redefined as macros to internal helper functions. 16 | This is needed as there is some internal context that has to be free'd 17 | properly. This means that you shouldn't create a `timerfd`/`signalfd` in 18 | one part of a program and close it in a different part where 19 | `sys/timerfd.h` isn't included. The context would leak. Luckily, software 20 | such as libinput behaves very nicely and puts all `timerfd` related code in 21 | a single source file. 22 | 23 | Alternatively, a target/library `epoll-shim-interpose` is also provided. 24 | Instead of redefining those symbols as macros they are provided as "proper" 25 | symbols, making use of POSIX `dlsym` chaining with `RTLD_NEXT`. 26 | 27 | What approach is more suitable depends on the application: If the use of 28 | `epoll` is very localized the macro based approach is less overhead. If the 29 | use of those file descriptors is more pervasive, the interposition approach 30 | is more robust. It will be a bit less performant because all calls to 31 | `read`/`write`/`close` and so on will be routed through `epoll-shim`. 32 | 33 | - There is limited support for file descriptors that lack support for 34 | kqueue but are supported by `poll(2)`. This includes graphics or sound 35 | devices under `/dev`. Those descriptors are handled in an outer `poll(2)` 36 | loop. Edge triggering using `EPOLLET` will not work. 37 | 38 | - Shimmed file descriptors cannot be shared between processes. On `fork()` 39 | those fds are closed. When trying to pass a shimmed fd to another process the 40 | `sendmsg` call will return `EOPNOTSUPP`. In most cases sharing 41 | `epoll`/`timerfd`/`signalfd` is a bad idea anyway, but there are some 42 | legitimate use cases (for example sharing semaphore `eventfd`s, issue #23). 43 | When the OS natively supports `eventfd`s (as is the case for FreeBSD >= 13) 44 | this library won't provide `eventfd` shims or the `sys/eventfd.h` header. 45 | 46 | - There is no proper notification mechanism for changes to the system 47 | `CLOCK_REALTIME` clock on BSD systems. Also, `kevent` `EVFILT_TIMER`s use the 48 | system monotonic clock as reference. Therefore, in order to implement 49 | absolute (`TFD_TIMER_ABSTIME`) `CLOCK_REALTIME` `timerfd`s or cancellation 50 | support (`TFD_TIMER_CANCEL_ON_SET`), a thread is spawned that periodically 51 | polls the system boot time for changes to the realtime clock. 52 | 53 | The library is tested on the following operating systems: 54 | 55 | - FreeBSD 13.3 56 | - NetBSD 9.4 57 | - OpenBSD 7.5 58 | - DragonFlyBSD 6.4.0 59 | - macOS 13.7.1 60 | 61 | Be aware of some subtle kqueue bugs that may affect the emulated 62 | epoll behavior. I've marked tests that hit those behaviors as "skipped". 63 | Have a look at `atf_tc_skip()` calls in the tests. 64 | 65 | ## Installation 66 | 67 | Run the following commands to build libepoll-shim: 68 | 69 | mkdir build 70 | cd build 71 | cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo 72 | cmake --build . 73 | 74 | To run the tests: 75 | 76 | ctest --output-on-failure 77 | 78 | To install (as root): 79 | 80 | cmake --build . --target install 81 | 82 | ## Changelog 83 | 84 | ### 2024-06-08 85 | 86 | - Add support for `kqueue1` on OpenBSD. 87 | - Fix issue #46 (on FreeBSD, `O_NONBLOCK` flag of fds added to an epoll fd 88 | might change unexpectedly). 89 | - Update CI action to `vmactions/freebsd-vm@v1`. 90 | - Bump supported OS versions. 91 | 92 | ### 2023-04-11 93 | 94 | - Add support for FreeBSD's `POLLRDHUP`. 95 | - Use `CMAKE_INSTALL_FULL_*` in `*.pc` files (thanks alyssais!). 96 | 97 | ### 2023-01-28 98 | 99 | - Initial port to macOS. 100 | 101 | ### 2022-07-03 102 | 103 | - Fix crashes when `libepoll-shim.so` is placed after `libc.so` in library 104 | search order. 105 | - Test on OpenBSD 7.1. 106 | - Fix memory leak in tests (thanks arichardson!). 107 | 108 | ### 2022-06-07 109 | 110 | - Introduce `epoll-shim-interpose` library. This library provides proper 111 | wrapper symbols for `read`/`write`/`close`/`poll`/`ppoll`/`fcntl`. If for 112 | some reason the macro based approach of redefining those symbols is not 113 | appropriate, using this library instead of `epoll-shim` might be an 114 | alternative. 115 | - More faithful simulation of file descriptor semantics, including reference 116 | counting. 117 | - Faster file descriptor lookup, using an array instead of a tree data 118 | structure. 119 | - Define wrapper macros as variadic, except when ANSI C is used. 120 | 121 | ### 2021-04-18 122 | 123 | - Fix compiler warning when using shimmed `fcntl`. 124 | 125 | ### 2021-04-17 126 | 127 | - Allow setting `O_NONBLOCK` flag with `fcntl` on created file descriptors. 128 | - Implement `TFD_TIMER_CANCEL_ON_SET` for `timerfd`. 129 | - Implement correction of absolute (`TFD_TIMER_ABSTIME`) `CLOCK_REALTIME` 130 | `timerfd`s when the system time is stepped. 131 | 132 | ### 2021-03-22 133 | 134 | - Fix compilation on FreeBSD < 12 (#28). 135 | 136 | ### 2021-03-21 137 | 138 | - Add `O_CLOEXEC` handling to created file descriptors (PR #26, thanks 139 | arichardson!). Note that the shimmed file descriptors still won't work 140 | correctly after `exec(3)`. Therefore, *not* using 141 | `EPOLL_CLOEXEC`/`TFD_CLOEXEC`/`SFD_CLOEXEC`/`EFD_CLOEXEC` is strongly 142 | discouraged. 143 | 144 | ### 2021-03-10 145 | 146 | - Fix compilation on FreeBSD 12.1 (#25). 147 | 148 | ### 2021-02-13 149 | 150 | - `signalfd` now hooks into the signal disposition mechanism, just like on 151 | Linux. Note: `poll` and `ppoll` are also shimmed with macros in case 152 | `sys/signalfd.h` is included to support some use cases seen in the wild. Many 153 | more `ssi_*` fields are now set on the resulting `struct signalfd_siginfo`. 154 | - More accurate timeout calculations for `epoll_wait`/`poll`/`ppoll`. 155 | - Fix integer overflow on timerfd timeout field on 32-bit machines. 156 | - Fix re-arming of timerfd timeouts on BSDs where EV_ADD of a EVFILT_TIMER 157 | doesn't do it. 158 | 159 | ### 2020-12-29 160 | 161 | - Add support for native `eventfd`s (provided by FreeBSD >= 13). The 162 | `sys/eventfd.h` header will not be installed in this case. 163 | 164 | ### 2020-11-06 165 | 166 | - Add support for NetBSD 9.1. 167 | 168 | ### 2020-06-02 169 | 170 | - On FreeBSD, add missing `sys/signal.h` include that resulted in `sigset_t` 171 | errors (#21). 172 | 173 | ### 2020-04-25 174 | 175 | - Lift limit of 32 descriptors in `epoll_wait(2)`. 176 | - Implement `EPOLLPRI` using `EVFILT_EXCEPT`, if available. If it is not 177 | available, add logic to `EVFILT_READ` handling that will work if 178 | `SO_OOBINLINE` is set on the socket. 179 | - Implement `EPOLLONESHOT`. 180 | - Implement edge triggering with `EPOLLET`. 181 | - Add support for unlimited numbers of poll-only fds per epoll instance. 182 | - Merge `EVFILT_READ`/`EVFILT_WRITE` events together to more closely match 183 | epoll semantics. 184 | - Add support for NetBSD, OpenBSD and DragonFlyBSD. 185 | 186 | ### 2020-04-08 187 | 188 | - Implement `epoll_pwait(2)`. 189 | -------------------------------------------------------------------------------- /doc/musl/COPYRIGHT: -------------------------------------------------------------------------------- 1 | musl as a whole is licensed under the following standard MIT license: 2 | 3 | ---------------------------------------------------------------------- 4 | Copyright © 2005-2014 Rich Felker, et al. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | ---------------------------------------------------------------------- 25 | 26 | Authors/contributors include: 27 | 28 | Alex Dowad 29 | Alexander Monakov 30 | Anthony G. Basile 31 | Arvid Picciani 32 | Bobby Bingham 33 | Boris Brezillon 34 | Brent Cook 35 | Chris Spiegel 36 | Clément Vasseur 37 | Daniel Micay 38 | Denys Vlasenko 39 | Emil Renner Berthing 40 | Felix Fietkau 41 | Felix Janda 42 | Gianluca Anzolin 43 | Hauke Mehrtens 44 | Hiltjo Posthuma 45 | Isaac Dunham 46 | Jens Gustedt 47 | Jeremy Huntwork 48 | Jo-Philipp Wich 49 | Joakim Sindholt 50 | John Spencer 51 | Josiah Worcester 52 | Justin Cormack 53 | Khem Raj 54 | Kylie McClain 55 | Luca Barbato 56 | Luka Perkov 57 | M Farkas-Dyck (Strake) 58 | Michael Forney 59 | Natanael Copa 60 | Nicholas J. Kain 61 | orc 62 | Pascal Cuoq 63 | Petr Hosek 64 | Pierre Carrier 65 | Rich Felker 66 | Richard Pennington 67 | Shiz 68 | sin 69 | Solar Designer 70 | Stefan Kristiansson 71 | Szabolcs Nagy 72 | Timo Teräs 73 | Trutz Behn 74 | Valentin Ochs 75 | William Haddon 76 | 77 | Portions of this software are derived from third-party works licensed 78 | under terms compatible with the above MIT license: 79 | 80 | The TRE regular expression implementation (src/regex/reg* and 81 | src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed 82 | under a 2-clause BSD license (license text in the source files). The 83 | included version has been heavily modified by Rich Felker in 2012, in 84 | the interests of size, simplicity, and namespace cleanliness. 85 | 86 | Much of the math library code (src/math/* and src/complex/*) is 87 | Copyright © 1993,2004 Sun Microsystems or 88 | Copyright © 2003-2011 David Schultz or 89 | Copyright © 2003-2009 Steven G. Kargl or 90 | Copyright © 2003-2009 Bruce D. Evans or 91 | Copyright © 2008 Stephen L. Moshier 92 | and labelled as such in comments in the individual source files. All 93 | have been licensed under extremely permissive terms. 94 | 95 | The ARM memcpy code (src/string/armel/memcpy.s) is Copyright © 2008 96 | The Android Open Source Project and is licensed under a two-clause BSD 97 | license. It was taken from Bionic libc, used on Android. 98 | 99 | The implementation of DES for crypt (src/misc/crypt_des.c) is 100 | Copyright © 1994 David Burren. It is licensed under a BSD license. 101 | 102 | The implementation of blowfish crypt (src/misc/crypt_blowfish.c) was 103 | originally written by Solar Designer and placed into the public 104 | domain. The code also comes with a fallback permissive license for use 105 | in jurisdictions that may not recognize the public domain. 106 | 107 | The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 108 | Valentin Ochs and is licensed under an MIT-style license. 109 | 110 | The BSD PRNG implementation (src/prng/random.c) and XSI search API 111 | (src/search/*.c) functions are Copyright © 2011 Szabolcs Nagy and 112 | licensed under following terms: "Permission to use, copy, modify, 113 | and/or distribute this code for any purpose with or without fee is 114 | hereby granted. There is no warranty." 115 | 116 | The x86_64 port was written by Nicholas J. Kain. Several files (crt) 117 | were released into the public domain; others are licensed under the 118 | standard MIT license terms at the top of this file. See individual 119 | files for their copyright status. 120 | 121 | The mips and microblaze ports were originally written by Richard 122 | Pennington for use in the ellcc project. The original code was adapted 123 | by Rich Felker for build system and code conventions during upstream 124 | integration. It is licensed under the standard MIT terms. 125 | 126 | The powerpc port was also originally written by Richard Pennington, 127 | and later supplemented and integrated by John Spencer. It is licensed 128 | under the standard MIT terms. 129 | 130 | All other files which have no copyright comments are original works 131 | produced specifically for use as part of this library, written either 132 | by Rich Felker, the main author of the library, or by one or more 133 | contibutors listed above. Details on authorship of individual files 134 | can be found in the git version control history of the project. The 135 | omission of copyright and license comments in each file is in the 136 | interest of source tree size. 137 | 138 | All public header files (include/* and arch/*/bits/*) should be 139 | treated as Public Domain as they intentionally contain no content 140 | which can be covered by copyright. Some source modules may fall in 141 | this category as well. If you believe that a file is so trivial that 142 | it should be in the Public Domain, please contact the authors and 143 | request an explicit statement releasing it from copyright. 144 | 145 | The following files are trivial, believed not to be copyrightable in 146 | the first place, and hereby explicitly released to the Public Domain: 147 | 148 | All public headers: include/*, arch/*/bits/* 149 | Startup files: crt/* 150 | -------------------------------------------------------------------------------- /doc/poll.txt: -------------------------------------------------------------------------------- 1 | sockets: 2 | - POLLIN: 3 | 1. sbavail(&(so)->so_rcv) >= (so)->so_rcv.sb_lowat 4 | * more in recv buffer than low watermark 5 | 2. !TAILQ_EMPTY(&(so)->so_comp) 6 | * someone is connecting 7 | 3. (so)->so_error 8 | * temporary UDP error (?) 9 | 4. so->so_rcv.sb_state & SBS_CANTRCVMORE 10 | * can't receive any more 11 | * behind POLLINIGNEOF 12 | - POLLOUT: 13 | 5. sbspace(&(so)->so_snd) >= (so)->so_snd.sb_lowat && 14 | (((so)->so_state & SS_ISCONNECTED) || 15 | ((so)->so_proto->pr_flags & PR_CONNREQUIRED) == 0) 16 | * more space in send buffer than low watermark 17 | * socket must be connected if required by protocol 18 | 6. (so)->so_snd.sb_state & SBS_CANTSENDMORE 19 | * can't send any more 20 | 7. (so)->so_error 21 | * socket error 22 | - POLLHUP: 23 | 8. so->so_rcv.sb_state & SBS_CANTRCVMORE && 24 | so->so_snd.sb_state & SBS_CANTSENDMORE 25 | * can neither send nor receive anymore 26 | * for a reader, always returns POLLIN|POLLHUP 27 | * for a writer, always returns POLLOUT|POLLHUP 28 | 29 | - EVFILT_READ: 30 | 1. -> kn_data = sbavail(&so->so_rcv) - so->so_rcv.sb_ctl 31 | 2. -> kn_data = so->so_qlen; 32 | 3. -> kn_data = sbavail(&so->so_rcv) - so->so_rcv.sb_ctl 33 | 4. -> kn_data = sbavail(&so->so_rcv) - so->so_rcv.sb_ctl 34 | -> kn_flags |= EV_EOF 35 | -> kn_fflags = so->so_error 36 | - EVFILT_WRITE: 37 | 5. -> kn_data = sbspace(&so->so_snd) 38 | 6. -> kn_data = sbspace(&so->so_snd) 39 | -> kn_flags |= EV_EOF 40 | -> kn_fflags = so->so_error 41 | 7. -> kn_data = sbspace(&so->so_snd) 42 | - EVFILT_READ/EVFILT_WRITE: 43 | 8. when EV_EOF set in EVFILT_READ or EVFILT_WRITE: 44 | call poll() to check for POLLHUP 45 | 46 | 47 | bidirectional (named) pipes (ignoring PIPE_DIRECTW) 48 | - POLLIN: 49 | 1. rpipe->pipe_buffer.cnt > 0 50 | * some data to read 51 | 2. rpipe->pipe_state & PIPE_EOF && 52 | !(rpipe->pipe_state & PIPE_NAMED && 53 | fp->f_flag & FREAD && 54 | fp->f_seqcount == rpipe->pipe_wgen) 55 | * don't signal EOF coming from disconnected previous writers if we are 56 | a newly connected reader 57 | - POLLOUT (only if FWRITE): 58 | 3. wpipe->pipe_present != PIPE_ACTIVE 59 | 4. wpipe->pipe_state & PIPE_EOF 60 | 5. (wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt) >= PIPE_BUF 61 | 6. wpipe->pipe_buffer.size == 0 62 | - POLLHUP: 63 | 7. rpipe->pipe_state & PIPE_EOF && 64 | !(rpipe->pipe_state & PIPE_NAMED && 65 | fp->f_flag & FREAD && 66 | fp->f_seqcount == rpipe->pipe_wgen) && 67 | (wpipe->pipe_present != PIPE_ACTIVE || (wpipe->pipe_state & PIPE_EOF)) 68 | * same as with POLLIN, but writer must be disconnected 69 | * for a reader, always returns POLLIN|POLLHUP, as POLLIN condition 2. is a 70 | subset of this condition (even if rpipe->pipe_buffer.cnt == 0) 71 | 72 | - EVFILT_READ: 73 | 1. -> kn_data = rpipe->pipe_buffer.cnt 74 | 2. -> kn_data = rpipe->pipe_buffer.cnt 75 | -> kn_flags |= EV_EOF 76 | * NOTE: this actually checks for 77 | rpipe->pipe_state & PIPE_EOF || 78 | wpipe->pipe_present != PIPE_ACTIVE || 79 | wpipe->pipe_state & PIPE_EOF 80 | * this is necessary but not sufficient for conditions 2. and 7. 81 | * need to recheck with poll() if POLLIN is set 82 | - EVFILT_WRITE: 83 | 3. -> kn_data = 0 84 | -> kn_flags |= EV_EOF 85 | 4. -> kn_data = 0 86 | -> kn_flags |= EV_EOF 87 | 5. -> kn_data = wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt 88 | 6. -> kn_data = PIPE_BUF 89 | - EVFILT_READ/EVFILT_WRITE: 90 | 7. when EV_EOF set in EVFILT_READ or EVFILT_WRITE: 91 | call poll() to check for POLLHUP 92 | 93 | 94 | tty: 95 | - POLLIN: 96 | 1. ttydev_enter error 97 | 2. ttydisc_read_poll(tp) > 0 98 | - POLLOUT: 99 | 3. !(tp->t_flags & TF_ZOMBIE) && 100 | ttydisc_write_poll(tp) > 0 101 | - POLLHUP: 102 | 4. ttydev_enter error 103 | 5. tp->t_flags & TF_ZOMBIE 104 | 105 | - EVFILT_READ: 106 | 1. -> kn_flags |= EV_EOF; 107 | 2. -> kn_data = ttydisc_read_poll(tp) 108 | 4. -> kn_flags |= EV_EOF 109 | 5. -> kn_flags |= EV_EOF 110 | - EVFILT_WRITE: 111 | 3. -> kn_data = ttydisc_write_poll(tp) 112 | * NOTE: this does not check for TF_ZOMBIE! 113 | 4. -> kn_flags |= EV_EOF 114 | - EVFILT_READ/EVFILT_WRITE: 115 | 4. when EV_EOF set in EVFILT_READ or EVFILT_WRITE: 116 | call poll() to check for POLLHUP 117 | * not strictly necessary though 118 | * if reliable polling for POLLHUP is needed, EVFILT_READ must be used 119 | 120 | -------------------------------------------------------------------------------- /epoll-shim-interpose.pc.cmakein: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=@CMAKE_INSTALL_FULL_LIBDIR@ 4 | includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ 5 | 6 | Name: epoll-shim-interpose 7 | URL: https://github.com/jiixyj/epoll-shim 8 | Description: Small epoll implementation using kqueue (interposing wrapper) 9 | Version: 10 | Requires: epoll-shim 11 | Libs: -L${libdir} -lepoll-shim-interpose 12 | Cflags: -DEPOLL_SHIM_DISABLE_WRAPPER_MACROS 13 | -------------------------------------------------------------------------------- /epoll-shim.pc.cmakein: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=@CMAKE_INSTALL_FULL_LIBDIR@ 4 | includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ 5 | 6 | Name: epoll-shim 7 | URL: https://github.com/jiixyj/epoll-shim 8 | Description: Small epoll implementation using kqueue 9 | Version: 10 | Libs: -L${libdir} -lepoll-shim 11 | Libs.private: -pthread -lrt 12 | Cflags: -I${includedir}/libepoll-shim 13 | -------------------------------------------------------------------------------- /external/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(microatf) 2 | add_subdirectory(queue-macros) 3 | add_subdirectory(tree-macros) 4 | -------------------------------------------------------------------------------- /external/microatf/.clang-format: -------------------------------------------------------------------------------- 1 | # $FreeBSD$ 2 | # Basic .clang-format 3 | --- 4 | BasedOnStyle: WebKit 5 | AlignAfterOpenBracket: DontAlign 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: false 10 | AlignTrailingComments: true 11 | AllowAllArgumentsOnNextLine: false 12 | AllowAllParametersOfDeclarationOnNextLine: false 13 | AllowShortBlocksOnASingleLine: Never 14 | AllowShortCaseLabelsOnASingleLine: false 15 | AllowShortFunctionsOnASingleLine: InlineOnly 16 | AllowShortIfStatementsOnASingleLine: Never 17 | AllowShortLoopsOnASingleLine: false 18 | AlwaysBreakAfterReturnType: TopLevelDefinitions 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: MultiLine 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BreakBeforeBinaryOperators: None 24 | BreakBeforeBraces: WebKit 25 | BreakBeforeTernaryOperators: false 26 | # TODO: BreakStringLiterals can cause very strange formatting so turn it off? 27 | BreakStringLiterals: false 28 | # Prefer: 29 | # some_var = function(arg1, 30 | # arg2) 31 | # over: 32 | # some_var = 33 | # function(arg1, arg2) 34 | PenaltyBreakAssignment: 100 35 | # Prefer: 36 | # some_long_function(arg1, arg2 37 | # arg3) 38 | # over: 39 | # some_long_function( 40 | # arg1, arg2, arg3) 41 | PenaltyBreakBeforeFirstCallParameter: 100 42 | CompactNamespaces: true 43 | DerivePointerAlignment: false 44 | DisableFormat: false 45 | ForEachMacros: 46 | - ARB_ARRFOREACH 47 | - ARB_ARRFOREACH_REVWCOND 48 | - ARB_ARRFOREACH_REVERSE 49 | - ARB_FOREACH 50 | - ARB_FOREACH_FROM 51 | - ARB_FOREACH_SAFE 52 | - ARB_FOREACH_REVERSE 53 | - ARB_FOREACH_REVERSE_FROM 54 | - ARB_FOREACH_REVERSE_SAFE 55 | - CPU_FOREACH 56 | - FOREACH_THREAD_IN_PROC 57 | - FOREACH_PROC_IN_SYSTEM 58 | - FOREACH_PRISON_CHILD 59 | - FOREACH_PRISON_DESCENDANT 60 | - FOREACH_PRISON_DESCENDANT_LOCKED 61 | - FOREACH_PRISON_DESCENDANT_LOCKED_LEVEL 62 | - MNT_VNODE_FOREACH_ALL 63 | - MNT_VNODE_FOREACH_ACTIVE 64 | - RB_FOREACH 65 | - RB_FOREACH_FROM 66 | - RB_FOREACH_SAFE 67 | - RB_FOREACH_REVERSE 68 | - RB_FOREACH_REVERSE_FROM 69 | - RB_FOREACH_REVERSE_SAFE 70 | - SLIST_FOREACH 71 | - SLIST_FOREACH_FROM 72 | - SLIST_FOREACH_FROM_SAFE 73 | - SLIST_FOREACH_SAFE 74 | - SLIST_FOREACH_PREVPTR 75 | - SPLAY_FOREACH 76 | - LIST_FOREACH 77 | - LIST_FOREACH_FROM 78 | - LIST_FOREACH_FROM_SAFE 79 | - LIST_FOREACH_SAFE 80 | - STAILQ_FOREACH 81 | - STAILQ_FOREACH_FROM 82 | - STAILQ_FOREACH_FROM_SAFE 83 | - STAILQ_FOREACH_SAFE 84 | - TAILQ_FOREACH 85 | - TAILQ_FOREACH_FROM 86 | - TAILQ_FOREACH_FROM_SAFE 87 | - TAILQ_FOREACH_REVERSE 88 | - TAILQ_FOREACH_REVERSE_FROM 89 | - TAILQ_FOREACH_REVERSE_FROM_SAFE 90 | - TAILQ_FOREACH_REVERSE_SAFE 91 | - TAILQ_FOREACH_SAFE 92 | - VM_MAP_ENTRY_FOREACH 93 | - VM_PAGE_DUMP_FOREACH 94 | IndentCaseLabels: false 95 | IndentPPDirectives: None 96 | Language: Cpp 97 | NamespaceIndentation: None 98 | PointerAlignment: Right 99 | ContinuationIndentWidth: 4 100 | IndentWidth: 8 101 | TabWidth: 8 102 | ColumnLimit: 80 103 | UseTab: Always 104 | SpaceAfterCStyleCast: false 105 | # LLVM's header include ordering style is almost the exact opposite of ours. 106 | # Unfortunately, they have hard-coded their preferences into clang-format. 107 | # Clobbering this regular expression to avoid matching prevents non-system 108 | # headers from being forcibly moved to the top of the include list. 109 | # http://llvm.org/docs/CodingStandards.html#include-style 110 | IncludeIsMainRegex: 'BLAH_DONT_MATCH_ANYTHING' 111 | SortIncludes: true 112 | KeepEmptyLinesAtTheStartOfBlocks: true 113 | TypenameMacros: 114 | - ARB_ELMTYPE 115 | - ARB_HEAD 116 | - ARB8_HEAD 117 | - ARB16_HEAD 118 | - ARB32_HEAD 119 | - ARB_ENTRY 120 | - ARB8_ENTRY 121 | - ARB16_ENTRY 122 | - ARB32_ENTRY 123 | - LIST_CLASS_ENTRY 124 | - LIST_CLASS_HEAD 125 | - LIST_ENTRY 126 | - LIST_HEAD 127 | - QUEUE_TYPEOF 128 | - RB_ENTRY 129 | - RB_HEAD 130 | - SLIST_CLASS_HEAD 131 | - SLIST_CLASS_ENTRY 132 | - SLIST_HEAD 133 | - SLIST_ENTRY 134 | - SMR_POINTER 135 | - SPLAY_ENTRY 136 | - SPLAY_HEAD 137 | - STAILQ_CLASS_ENTRY 138 | - STAILQ_CLASS_HEAD 139 | - STAILQ_ENTRY 140 | - STAILQ_HEAD 141 | - TAILQ_CLASS_ENTRY 142 | - TAILQ_CLASS_HEAD 143 | - TAILQ_ENTRY 144 | - TAILQ_HEAD 145 | MaxEmptyLinesToKeep: 2 146 | -------------------------------------------------------------------------------- /external/microatf/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(microatf LANGUAGES C) 3 | 4 | # 5 | 6 | include(CTest) 7 | 8 | # 9 | 10 | set(CMAKE_C_STANDARD 11) 11 | set(CMAKE_C_EXTENSIONS ON) 12 | add_definitions(-D_GNU_SOURCE=1) 13 | 14 | # 15 | 16 | add_subdirectory(src) 17 | 18 | # 19 | 20 | set(_namespace "${PROJECT_NAME}") 21 | 22 | # 23 | 24 | set(${PROJECT_NAME}_DIR 25 | "${PROJECT_BINARY_DIR}/config/subdir" 26 | CACHE INTERNAL "") 27 | file( 28 | WRITE "${PROJECT_BINARY_DIR}/config/subdir/${PROJECT_NAME}-config.cmake" 29 | " 30 | list(APPEND CMAKE_MODULE_PATH \"${PROJECT_SOURCE_DIR}/cmake\") 31 | if(NOT TARGET ${_namespace}::microatf-c) 32 | add_library(${_namespace}::microatf-c ALIAS microatf-c) 33 | endif() 34 | if(NOT TARGET ${_namespace}::microatf-translate-signal) 35 | add_executable(${_namespace}::microatf-translate-signal ALIAS microatf-translate-signal) 36 | endif() 37 | ") 38 | 39 | if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 40 | if(BUILD_TESTING) 41 | add_subdirectory(test) 42 | endif() 43 | 44 | if(CMAKE_VERSION VERSION_LESS "3.14.0") 45 | return() 46 | endif() 47 | 48 | include(GNUInstallDirs) 49 | 50 | set(CMAKE_INSTALL_CMAKEBASEDIR 51 | "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 52 | CACHE PATH "Installation directory for CMake config (.cmake) files") 53 | mark_as_advanced(CMAKE_INSTALL_CMAKEBASEDIR) 54 | 55 | install( 56 | EXPORT ${PROJECT_NAME}-targets 57 | NAMESPACE "${_namespace}::" 58 | DESTINATION "${CMAKE_INSTALL_CMAKEBASEDIR}") 59 | file( 60 | WRITE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 61 | " 62 | list(APPEND CMAKE_MODULE_PATH \"\${CMAKE_CURRENT_LIST_DIR}\") 63 | include(\"\${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME}-targets.cmake\") 64 | ") 65 | install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 66 | DESTINATION "${CMAKE_INSTALL_CMAKEBASEDIR}") 67 | 68 | file( 69 | WRITE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 70 | " 71 | list(APPEND CMAKE_MODULE_PATH \"${PROJECT_SOURCE_DIR}/cmake\") 72 | include(\"${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake\") 73 | ") 74 | export( 75 | EXPORT ${PROJECT_NAME}-targets 76 | NAMESPACE "${_namespace}::" 77 | FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-targets.cmake") 78 | 79 | # 80 | 81 | set(CMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/microatf") 82 | install( 83 | TARGETS microatf-c 84 | EXPORT ${PROJECT_NAME}-targets 85 | INCLUDES 86 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 87 | install( 88 | TARGETS microatf-translate-signal 89 | EXPORT ${PROJECT_NAME}-targets 90 | RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") 91 | install(FILES "${PROJECT_SOURCE_DIR}/src/atf-c.h" TYPE INCLUDE) 92 | install(DIRECTORY "${PROJECT_SOURCE_DIR}/cmake/" 93 | DESTINATION "${CMAKE_INSTALL_CMAKEBASEDIR}") 94 | endif() 95 | -------------------------------------------------------------------------------- /external/microatf/cmake/ATFRunTest.cmake: -------------------------------------------------------------------------------- 1 | #[[ 2 | TEST_FOLDER_NAME 3 | TEST_EXECUTABLE 4 | TEST_NAME 5 | BINARY_DIR 6 | TIMEOUT 7 | #]] 8 | 9 | set(_wd "${BINARY_DIR}/${TEST_FOLDER_NAME}") 10 | 11 | execute_process(COMMAND "${CMAKE_COMMAND}" -E remove_directory "${_wd}") 12 | execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${_wd}/work") 13 | execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${_wd}/result") 14 | 15 | unset(_config_variables) 16 | foreach(config_var IN LISTS TEST_CONFIG_VARIABLES) 17 | list(APPEND _config_variables "-v" "${config_var}") 18 | endforeach() 19 | 20 | unset(_echo_error_variable) 21 | if(NOT CMAKE_VERSION VERSION_LESS "3.18.0") 22 | set(_echo_error_variable ECHO_ERROR_VARIABLE) 23 | endif() 24 | 25 | execute_process( 26 | COMMAND 27 | "${CMAKE_COMMAND}" -E env --unset=LANG --unset=LC_ALL --unset=LC_COLLATE 28 | --unset=LC_CTYPE --unset=LC_MESSAGES --unset=LC_MONETARY --unset=LC_NUMERIC 29 | --unset=LC_TIME "HOME=${_wd}/work" "TMPDIR=${_wd}/work" "TZ=UTC" 30 | "__RUNNING_INSIDE_ATF_RUN=internal-yes-value" ${TEST_EXECUTOR} 31 | "${TEST_EXECUTABLE}" -r "${_wd}/result" ${_config_variables} "${TEST_NAME}" 32 | WORKING_DIRECTORY "${_wd}/work" 33 | TIMEOUT "${TIMEOUT}" 34 | RESULT_VARIABLE _result 35 | ERROR_VARIABLE _stderr_lines ${_echo_error_variable}) 36 | 37 | file(STRINGS "${_wd}/result" _result_line) 38 | 39 | execute_process(COMMAND "${CMAKE_COMMAND}" -E remove_directory "${_wd}") 40 | 41 | list(LENGTH _result_line _result_line_length) 42 | if(_result_line_length GREATER 1) 43 | message(FATAL_ERROR "Result must not consist of multiple lines!") 44 | endif() 45 | 46 | string(REGEX REPLACE "\r?\n$" "" _stderr_lines "${_stderr_lines}") 47 | if(NOT _echo_error_variable AND NOT _stderr_lines STREQUAL "") 48 | message("${_stderr_lines}") 49 | endif() 50 | message(STATUS "result: ${_result}, ${_result_line}") 51 | 52 | #[[ 53 | "expected_death", -1, &formatted 54 | "expected_exit", exitcode, &formatted 55 | "expected_failure", -1, reason 56 | "expected_signal", signo, &formatted 57 | "expected_timeout", -1, &formatted 58 | "failed", -1, reason 59 | "passed", -1, NULL 60 | "skipped", -1, reason 61 | #]] 62 | 63 | if(_result_line MATCHES "^passed$") 64 | if(NOT _result EQUAL 0) 65 | message(FATAL_ERROR "") 66 | endif() 67 | 68 | elseif(_result_line MATCHES "^failed: (.*)$") 69 | message(FATAL_ERROR "${CMAKE_MATCH_1}") 70 | 71 | elseif(_result_line MATCHES "^skipped: (.*)$") 72 | if(NOT _result EQUAL 0) 73 | message(FATAL_ERROR "") 74 | endif() 75 | 76 | elseif(_result_line MATCHES "^expected_timeout: (.*)$") 77 | if(NOT _result STREQUAL "Process terminated due to timeout") 78 | message(FATAL_ERROR "") 79 | endif() 80 | 81 | elseif(_result_line MATCHES "^expected_failure: (.*)$") 82 | if(NOT _result EQUAL 0) 83 | message(FATAL_ERROR "") 84 | endif() 85 | 86 | elseif(_result_line MATCHES "^expected_death: (.*)$") 87 | 88 | elseif(_result_line MATCHES "^expected_exit\\((.*)\\): (.*)$") 89 | if(NOT _result EQUAL "${CMAKE_MATCH_1}") 90 | message(FATAL_ERROR "") 91 | endif() 92 | 93 | elseif(_result_line MATCHES "^expected_signal\\((.*)\\): (.*)$") 94 | if(NOT _result EQUAL 1) 95 | message(FATAL_ERROR "") 96 | endif() 97 | 98 | execute_process( 99 | COMMAND ${TEST_EXECUTOR} "${TRANSLATE_SIGNAL}" "${CMAKE_MATCH_1}" 100 | OUTPUT_VARIABLE _signal_translation_string 101 | RESULT_VARIABLE _signal_translation_result) 102 | 103 | if(NOT _signal_translation_result EQUAL 0) 104 | message(FATAL_ERROR "") 105 | endif() 106 | 107 | string(REGEX MATCH "([^\r\n]*)$" _signal_line "${_stderr_lines}") 108 | set(_signal_line "${CMAKE_MATCH_1}") 109 | 110 | string(REGEX REPLACE "\r?\n" ";" _signal_translations 111 | "${_signal_translation_string}") 112 | set(_signal_matched FALSE) 113 | foreach(_line ${_signal_translations}) 114 | if(_line STREQUAL "") 115 | continue() 116 | endif() 117 | if(_signal_line STREQUAL _line) 118 | set(_signal_matched TRUE) 119 | break() 120 | endif() 121 | endforeach() 122 | if(NOT _signal_matched) 123 | message(FATAL_ERROR "") 124 | endif() 125 | 126 | else() 127 | message( 128 | FATAL_ERROR 129 | "Unexpected result: \"${_result_line}\", process exited with: ${_result}") 130 | 131 | endif() 132 | -------------------------------------------------------------------------------- /external/microatf/cmake/ATFTest.cmake: -------------------------------------------------------------------------------- 1 | # This is loosely based on `GoogleTest.cmake` from CMake. 2 | # 3 | # See `Copyright.txt` for license details. 4 | 5 | set(_ATF_SCRIPT_DIR "${CMAKE_CURRENT_LIST_DIR}") 6 | 7 | function(atf_discover_tests _target) 8 | cmake_parse_arguments("" "" "" "PROPERTIES;CONFIG_VARIABLES" ${ARGN}) 9 | 10 | set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 11 | set(ctest_include_file "${ctest_file_base}_include.cmake") 12 | set(ctest_tests_file "${ctest_file_base}_tests.cmake") 13 | 14 | get_property( 15 | _test_executor 16 | TARGET "${_target}" 17 | PROPERTY CROSSCOMPILING_EMULATOR) 18 | 19 | if(CMAKE_CROSSCOMPILING AND NOT _test_executor) 20 | message(WARNING "Cannot detect tests for ${_target} without CROSSCOMPILING_EMULATOR") 21 | return() 22 | endif() 23 | 24 | add_custom_command( 25 | TARGET ${_target} 26 | POST_BUILD 27 | BYPRODUCTS "${ctest_tests_file}" 28 | COMMAND 29 | "${CMAKE_COMMAND}" # 30 | -D "TEST_TARGET=${_target}" # 31 | -D "TEST_EXECUTABLE=$" # 32 | -D "TEST_EXECUTOR=${_test_executor}" # 33 | -D "TEST_PROPERTIES=${_PROPERTIES}" # 34 | -D "TEST_CONFIG_VARIABLES=${_CONFIG_VARIABLES}" # 35 | -D "TRANSLATE_SIGNAL=$" # 36 | -D "CTEST_FILE=${ctest_tests_file}" # 37 | -D "BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}" # 38 | -D "TEST_RUN_SCRIPT=${_ATF_SCRIPT_DIR}/ATFRunTest.cmake" # 39 | -P "${_ATF_SCRIPT_DIR}/ATFTestAddTests.cmake" # 40 | VERBATIM) 41 | 42 | file( 43 | WRITE "${ctest_include_file}" 44 | "if(EXISTS \"${ctest_tests_file}\")\n" 45 | " include(\"${ctest_tests_file}\")\n" # 46 | "else()\n" # 47 | " add_test(${_target}_NOT_BUILT ${_target}_NOT_BUILT)\n" # 48 | "endif()\n") 49 | 50 | set_property( 51 | DIRECTORY 52 | APPEND 53 | PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}") 54 | 55 | if(CMAKE_VERSION VERSION_LESS "3.10") 56 | get_property( 57 | _test_includes 58 | DIRECTORY 59 | PROPERTY TEST_INCLUDE_FILES) 60 | 61 | unset(_include_content) 62 | foreach(_file IN LISTS _test_includes) 63 | set(_include_line "include(\"${_file}\")") 64 | set(_include_content "${_include_content}${_include_line}\n") 65 | endforeach() 66 | 67 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/microatf_tests_include.cmake" 68 | "${_include_content}") 69 | 70 | set_property(DIRECTORY PROPERTY TEST_INCLUDE_FILE 71 | "microatf_tests_include.cmake") 72 | endif() 73 | endfunction() 74 | -------------------------------------------------------------------------------- /external/microatf/cmake/ATFTestAddTests.cmake: -------------------------------------------------------------------------------- 1 | # This is loosely based on `GoogleTestAddTests.cmake` from CMake. 2 | # 3 | # See `Copyright.txt` for license details. 4 | 5 | set(script "") 6 | 7 | function( 8 | add_test_to_script 9 | _name 10 | _executable 11 | _executor 12 | _translate_signal 13 | _test 14 | _vars 15 | _properties 16 | _config_variables) 17 | 18 | # default timeout 19 | set(_timeout 300) 20 | set(_atf_properties "") 21 | 22 | foreach(line IN LISTS _vars) 23 | if(line MATCHES "^(.*): (.*)$") 24 | if(CMAKE_MATCH_1 STREQUAL "timeout") 25 | set(_timeout "${CMAKE_MATCH_2}") 26 | endif() 27 | if(CMAKE_MATCH_1 STREQUAL "X-ctest.properties") 28 | set(_atf_properties "${CMAKE_MATCH_2}") 29 | endif() 30 | endif() 31 | endforeach() 32 | 33 | string(REPLACE ";" " " _properties "${_properties}") 34 | 35 | set(_testscript 36 | " 37 | add_test( 38 | \"${_name}\" 39 | \"${CMAKE_COMMAND}\" 40 | -D \"TEST_FOLDER_NAME=${_name}\" 41 | -D \"TEST_EXECUTABLE=${_executable}\" 42 | -D \"TEST_EXECUTOR=${_executor}\" 43 | -D \"TRANSLATE_SIGNAL=${_translate_signal}\" 44 | -D \"TEST_CONFIG_VARIABLES=${_config_variables}\" 45 | -D \"TEST_NAME=${_test}\" 46 | -D \"BINARY_DIR=${BINARY_DIR}\" 47 | -D \"TIMEOUT=${_timeout}\" 48 | -P \"${TEST_RUN_SCRIPT}\") 49 | set_tests_properties( 50 | \"${_name}\" 51 | PROPERTIES TIMEOUT 0 52 | SKIP_REGULAR_EXPRESSION \"-- result: 0, skipped.*$\" 53 | ${_properties} 54 | ${_atf_properties} 55 | ) 56 | ") 57 | 58 | set(script 59 | "${script}${_testscript}" 60 | PARENT_SCOPE) 61 | endfunction() 62 | 63 | if(NOT EXISTS "${TEST_EXECUTABLE}") 64 | message(FATAL_ERROR "Specified test executable does not exist.\n" 65 | " Path: '${TEST_EXECUTABLE}'") 66 | endif() 67 | 68 | execute_process( 69 | COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" -l 70 | WORKING_DIRECTORY "${TEST_WORKING_DIR}" 71 | OUTPUT_VARIABLE output 72 | RESULT_VARIABLE result) 73 | 74 | if(NOT ${result} EQUAL 0) 75 | string(REPLACE "\n" "\n " output "${output}") 76 | message( 77 | FATAL_ERROR 78 | "Error running test executable.\n" # 79 | " Path: '${TEST_EXECUTABLE}'\n" # 80 | " Result: ${result}\n" # 81 | " Output:\n" # 82 | " ${output}\n") 83 | endif() 84 | 85 | string(REPLACE "\n" ";" output "${output}") 86 | 87 | macro(handle_current_tc) 88 | if(NOT _current_tc STREQUAL "") 89 | add_test_to_script( 90 | "${TEST_TARGET}.${_current_tc}" 91 | "${TEST_EXECUTABLE}" 92 | "${TEST_EXECUTOR}" 93 | "${TRANSLATE_SIGNAL}" 94 | "${_current_tc}" 95 | "${_current_tc_vars}" 96 | "${TEST_PROPERTIES}" 97 | "${TEST_CONFIG_VARIABLES}") 98 | set(_current_tc_vars "") 99 | endif() 100 | endmacro() 101 | 102 | set(_current_tc "") 103 | set(_current_tc_vars "") 104 | foreach(line ${output}) 105 | if(line MATCHES "^ident: (.*)$") 106 | handle_current_tc() 107 | set(_current_tc "${CMAKE_MATCH_1}") 108 | elseif(line MATCHES "^(.*): (.*)$") 109 | list(APPEND _current_tc_vars "${line}") 110 | endif() 111 | endforeach() 112 | 113 | handle_current_tc() 114 | 115 | file(WRITE "${CTEST_FILE}" "${script}") 116 | -------------------------------------------------------------------------------- /external/microatf/cmake/Copyright.txt: -------------------------------------------------------------------------------- 1 | CMake - Cross Platform Makefile Generator 2 | Copyright 2000-2019 Kitware, Inc. and Contributors 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | * Neither the name of Kitware, Inc. nor the names of Contributors 17 | may be used to endorse or promote products derived from this 18 | software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | ------------------------------------------------------------------------------ 33 | 34 | The following individuals and institutions are among the Contributors: 35 | 36 | * Aaron C. Meadows 37 | * Adriaan de Groot 38 | * Aleksey Avdeev 39 | * Alexander Neundorf 40 | * Alexander Smorkalov 41 | * Alexey Sokolov 42 | * Alex Merry 43 | * Alex Turbov 44 | * Andreas Pakulat 45 | * Andreas Schneider 46 | * André Rigland Brodtkorb 47 | * Axel Huebl, Helmholtz-Zentrum Dresden - Rossendorf 48 | * Benjamin Eikel 49 | * Bjoern Ricks 50 | * Brad Hards 51 | * Christopher Harvey 52 | * Christoph Grüninger 53 | * Clement Creusot 54 | * Daniel Blezek 55 | * Daniel Pfeifer 56 | * Enrico Scholz 57 | * Eran Ifrah 58 | * Esben Mose Hansen, Ange Optimization ApS 59 | * Geoffrey Viola 60 | * Google Inc 61 | * Gregor Jasny 62 | * Helio Chissini de Castro 63 | * Ilya Lavrenov 64 | * Insight Software Consortium 65 | * Jan Woetzel 66 | * Julien Schueller 67 | * Kelly Thompson 68 | * Laurent Montel 69 | * Konstantin Podsvirov 70 | * Mario Bensi 71 | * Martin Gräßlin 72 | * Mathieu Malaterre 73 | * Matthaeus G. Chajdas 74 | * Matthias Kretz 75 | * Matthias Maennich 76 | * Michael Hirsch, Ph.D. 77 | * Michael Stürmer 78 | * Miguel A. Figueroa-Villanueva 79 | * Mike Jackson 80 | * Mike McQuaid 81 | * Nicolas Bock 82 | * Nicolas Despres 83 | * Nikita Krupen'ko 84 | * NVIDIA Corporation 85 | * OpenGamma Ltd. 86 | * Patrick Stotko 87 | * Per Øyvind Karlsen 88 | * Peter Collingbourne 89 | * Petr Gotthard 90 | * Philip Lowman 91 | * Philippe Proulx 92 | * Raffi Enficiaud, Max Planck Society 93 | * Raumfeld 94 | * Roger Leigh 95 | * Rolf Eike Beer 96 | * Roman Donchenko 97 | * Roman Kharitonov 98 | * Ruslan Baratov 99 | * Sebastian Holtermann 100 | * Stephen Kelly 101 | * Sylvain Joubert 102 | * Thomas Sondergaard 103 | * Tobias Hunger 104 | * Todd Gamblin 105 | * Tristan Carel 106 | * University of Dundee 107 | * Vadim Zhukov 108 | * Will Dicharry 109 | 110 | See version control history for details of individual contributions. 111 | 112 | The above copyright and license notice applies to distributions of 113 | CMake in source and binary form. Third-party software packages supplied 114 | with CMake under compatible licenses provide their own copyright notices 115 | documented in corresponding subdirectories or source files. 116 | 117 | ------------------------------------------------------------------------------ 118 | 119 | CMake was initially developed by Kitware with the following sponsorship: 120 | 121 | * National Library of Medicine at the National Institutes of Health 122 | as part of the Insight Segmentation and Registration Toolkit (ITK). 123 | 124 | * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel 125 | Visualization Initiative. 126 | 127 | * National Alliance for Medical Image Computing (NAMIC) is funded by the 128 | National Institutes of Health through the NIH Roadmap for Medical Research, 129 | Grant U54 EB005149. 130 | 131 | * Kitware, Inc. 132 | -------------------------------------------------------------------------------- /external/microatf/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(microatf-c atf-c.c) 2 | target_include_directories(microatf-c 3 | PUBLIC $) 4 | 5 | add_executable(microatf-translate-signal translate-signal.c) 6 | -------------------------------------------------------------------------------- /external/microatf/src/atf-c.h: -------------------------------------------------------------------------------- 1 | #ifndef MICROATF_ATF_C_H_ 2 | #define MICROATF_ATF_C_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /**/ 15 | 16 | #ifdef __cplusplus 17 | #define MICROATF_ALIGNAS alignas 18 | #else 19 | #define MICROATF_ALIGNAS _Alignas 20 | #endif 21 | #define MICROATF_ATTRIBUTE_UNUSED __attribute__((__unused__)) 22 | #define MICROATF_ATTRIBUTE_FORMAT_PRINTF(a, b) \ 23 | __attribute__((__format__(__printf__, a, b))) 24 | #define MICROATF_ATTRIBUTE_NORETURN __attribute__((__noreturn__)) 25 | 26 | /**/ 27 | 28 | struct atf_error; 29 | typedef struct atf_error *atf_error_t; 30 | 31 | /**/ 32 | 33 | struct atf_tc_impl_s_; 34 | struct atf_tc_s; 35 | typedef struct atf_tc_s atf_tc_t; 36 | #pragma GCC diagnostic push 37 | #pragma GCC diagnostic ignored "-Wpragmas" 38 | #pragma GCC diagnostic ignored "-Wpedantic" 39 | struct atf_tc_s { 40 | MICROATF_ALIGNAS(8192) char const *name; 41 | void (*head)(atf_tc_t *); 42 | void (*body)(atf_tc_t const *); 43 | struct atf_tc_impl_s_ *impl_; 44 | MICROATF_ALIGNAS(max_align_t) unsigned char impl_space_[]; 45 | }; 46 | #pragma GCC diagnostic pop 47 | 48 | atf_error_t atf_tc_set_md_var(atf_tc_t *tc, /**/ 49 | char const *key, char const *value, ...); 50 | 51 | const char *atf_tc_get_md_var(atf_tc_t const *tc, const char *key); 52 | const char *atf_tc_get_config_var(atf_tc_t const *tc, const char *key); 53 | 54 | /**/ 55 | 56 | struct atf_tp_s; 57 | typedef struct atf_tp_s atf_tp_t; 58 | 59 | atf_error_t microatf_tp_add_tc(atf_tp_t *tp, atf_tc_t *tc); 60 | 61 | /**/ 62 | 63 | MICROATF_ATTRIBUTE_FORMAT_PRINTF(1, 2) 64 | void microatf_fail_check(char const *msg, ...); 65 | 66 | MICROATF_ATTRIBUTE_NORETURN 67 | MICROATF_ATTRIBUTE_FORMAT_PRINTF(1, 2) 68 | void microatf_fail_require(char const *msg, ...); 69 | 70 | /**/ 71 | 72 | MICROATF_ATTRIBUTE_FORMAT_PRINTF(1, 2) 73 | void atf_tc_expect_timeout(const char *msg, ...); 74 | 75 | MICROATF_ATTRIBUTE_FORMAT_PRINTF(2, 3) 76 | void atf_tc_expect_exit(const int exitcode, const char *msg, ...); 77 | 78 | MICROATF_ATTRIBUTE_FORMAT_PRINTF(2, 3) 79 | void atf_tc_expect_signal(const int signal, const char *msg, ...); 80 | 81 | MICROATF_ATTRIBUTE_FORMAT_PRINTF(1, 2) 82 | void atf_tc_expect_fail(const char *msg, ...); 83 | 84 | /**/ 85 | 86 | MICROATF_ATTRIBUTE_NORETURN 87 | MICROATF_ATTRIBUTE_FORMAT_PRINTF(1, 2) 88 | void atf_tc_skip(const char *reason, ...); 89 | 90 | /**/ 91 | 92 | #pragma GCC diagnostic push 93 | #pragma GCC diagnostic ignored "-Wunknown-pragmas" 94 | #pragma clang diagnostic push 95 | #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 96 | 97 | #define ATF_REQUIRE_MSG(expression, fmt, ...) \ 98 | do { \ 99 | if (!(expression)) { \ 100 | microatf_fail_require("%s:%d: " fmt, /**/ \ 101 | __FILE__, __LINE__, ##__VA_ARGS__); \ 102 | } \ 103 | } while (0) 104 | 105 | #define ATF_CHECK_MSG(expression, fmt, ...) \ 106 | do { \ 107 | if (!(expression)) { \ 108 | microatf_fail_check("%s:%d: " fmt, /**/ \ 109 | __FILE__, __LINE__, ##__VA_ARGS__); \ 110 | } \ 111 | } while (0) 112 | 113 | #pragma clang diagnostic pop 114 | #pragma GCC diagnostic pop 115 | 116 | #define ATF_REQUIRE(expression) \ 117 | ATF_REQUIRE_MSG((expression), "%s not met", #expression) 118 | 119 | #define ATF_CHECK(expression) \ 120 | ATF_CHECK_MSG((expression), "%s not met", #expression) 121 | 122 | #define ATF_REQUIRE_EQ(expected, actual) \ 123 | ATF_REQUIRE_MSG((expected) == (actual), "%s != %s", #expected, #actual) 124 | 125 | #define ATF_CHECK_EQ(expected, actual) \ 126 | ATF_CHECK_MSG((expected) == (actual), "%s != %s", #expected, #actual) 127 | 128 | #define ATF_REQUIRE_ERRNO(exp_errno, bool_expr) \ 129 | do { \ 130 | ATF_REQUIRE_MSG((bool_expr), /**/ \ 131 | "Expected true value in %s", /**/ \ 132 | #bool_expr); \ 133 | int ec = errno; \ 134 | ATF_REQUIRE_MSG(ec == (exp_errno), /**/ \ 135 | "Expected errno %d, got %d, in %s", /**/ \ 136 | (exp_errno), ec, #bool_expr); \ 137 | } while (0) 138 | 139 | #define ATF_TC_WITHOUT_HEAD(tc) \ 140 | static void microatf_tc_##tc##_body(atf_tc_t const *); \ 141 | static atf_tc_t microatf_tc_##tc = { \ 142 | #tc, \ 143 | NULL, \ 144 | microatf_tc_##tc##_body, \ 145 | NULL, \ 146 | } 147 | 148 | #define ATF_TC(tc) \ 149 | static void microatf_tc_##tc##_head(atf_tc_t *); \ 150 | static void microatf_tc_##tc##_body(atf_tc_t const *); \ 151 | static atf_tc_t microatf_tc_##tc = { \ 152 | #tc, \ 153 | microatf_tc_##tc##_head, \ 154 | microatf_tc_##tc##_body, \ 155 | NULL, \ 156 | } 157 | 158 | #define ATF_TC_HEAD(tc, tcptr) \ 159 | static void microatf_tc_##tc##_head( \ 160 | atf_tc_t *tcptr MICROATF_ATTRIBUTE_UNUSED) 161 | 162 | #define ATF_TC_BODY(tc, tcptr) \ 163 | static void microatf_tc_##tc##_body( \ 164 | atf_tc_t const *tcptr MICROATF_ATTRIBUTE_UNUSED) 165 | 166 | #define ATF_TP_ADD_TCS(tps) \ 167 | static atf_error_t microatf_tp_add_tcs(atf_tp_t *); \ 168 | int main(int argc, char **argv) \ 169 | { \ 170 | return microatf_tp_main(argc, argv, microatf_tp_add_tcs); \ 171 | } \ 172 | static atf_error_t microatf_tp_add_tcs(atf_tp_t *tps) 173 | 174 | #define ATF_TP_ADD_TC(tp, tc) \ 175 | do { \ 176 | atf_error_t ec = microatf_tp_add_tc(tp, µatf_tc_##tc); \ 177 | if (atf_is_error(ec)) { \ 178 | return ec; \ 179 | } \ 180 | } while (0) 181 | 182 | int microatf_tp_main(int argc, char **argv, 183 | atf_error_t (*add_tcs_hook)(atf_tp_t *)); 184 | 185 | atf_error_t atf_no_error(void); 186 | bool atf_is_error(atf_error_t const); 187 | 188 | #ifdef __cplusplus 189 | } 190 | #endif 191 | 192 | #endif 193 | -------------------------------------------------------------------------------- /external/microatf/src/translate-signal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char **argv) 7 | { 8 | if (argc != 2) { 9 | exit(1); 10 | } 11 | 12 | int sig = atoi(argv[1]); 13 | if (sig == 0) { 14 | exit(1); 15 | } 16 | 17 | switch (sig) { 18 | #ifdef SIGSEGV 19 | case SIGSEGV: 20 | fprintf(stdout, "Segmentation fault\n"); 21 | break; 22 | #endif 23 | #ifdef SIGBUS 24 | #if !defined(SIGSEGV) || SIGBUS != SIGSEGV 25 | case SIGBUS: 26 | fprintf(stdout, "Bus error\n"); 27 | break; 28 | #endif 29 | #endif 30 | #ifdef SIGFPE 31 | case SIGFPE: 32 | fprintf(stdout, "Floating-point exception\n"); 33 | break; 34 | #endif 35 | #ifdef SIGILL 36 | case SIGILL: 37 | fprintf(stdout, "Illegal instruction\n"); 38 | break; 39 | #endif 40 | #ifdef SIGINT 41 | case SIGINT: 42 | fprintf(stdout, "User interrupt\n"); 43 | break; 44 | #endif 45 | #ifdef SIGABRT 46 | case SIGABRT: 47 | fprintf(stdout, "Subprocess aborted\nChild aborted\n"); 48 | break; 49 | #endif 50 | #ifdef SIGKILL 51 | case SIGKILL: 52 | fprintf(stdout, "Subprocess killed\nChild killed\n"); 53 | break; 54 | #endif 55 | #ifdef SIGTERM 56 | case SIGTERM: 57 | fprintf(stdout, "Subprocess terminated\nChild terminated\n"); 58 | break; 59 | #endif 60 | #ifdef SIGHUP 61 | case SIGHUP: 62 | fprintf(stdout, "SIGHUP\n"); 63 | break; 64 | #endif 65 | #ifdef SIGQUIT 66 | case SIGQUIT: 67 | fprintf(stdout, "SIGQUIT\n"); 68 | break; 69 | #endif 70 | #ifdef SIGTRAP 71 | case SIGTRAP: 72 | fprintf(stdout, "SIGTRAP\n"); 73 | break; 74 | #endif 75 | #ifdef SIGIOT 76 | #if !defined(SIGABRT) || SIGIOT != SIGABRT 77 | case SIGIOT: 78 | fprintf(stdout, "SIGIOT\n"); 79 | break; 80 | #endif 81 | #endif 82 | #ifdef SIGUSR1 83 | case SIGUSR1: 84 | fprintf(stdout, "SIGUSR1\n"); 85 | break; 86 | #endif 87 | #ifdef SIGUSR2 88 | case SIGUSR2: 89 | fprintf(stdout, "SIGUSR2\n"); 90 | break; 91 | #endif 92 | #ifdef SIGPIPE 93 | case SIGPIPE: 94 | fprintf(stdout, "SIGPIPE\n"); 95 | break; 96 | #endif 97 | #ifdef SIGALRM 98 | case SIGALRM: 99 | fprintf(stdout, "SIGALRM\n"); 100 | break; 101 | #endif 102 | #ifdef SIGSTKFLT 103 | case SIGSTKFLT: 104 | fprintf(stdout, "SIGSTKFLT\n"); 105 | break; 106 | #endif 107 | #ifdef SIGCHLD 108 | case SIGCHLD: 109 | fprintf(stdout, "SIGCHLD\n"); 110 | break; 111 | #elif defined(SIGCLD) 112 | case SIGCLD: 113 | fprintf(stdout, "SIGCLD\n"); 114 | break; 115 | #endif 116 | #ifdef SIGCONT 117 | case SIGCONT: 118 | fprintf(stdout, "SIGCONT\n"); 119 | break; 120 | #endif 121 | #ifdef SIGSTOP 122 | case SIGSTOP: 123 | fprintf(stdout, "SIGSTOP\n"); 124 | break; 125 | #endif 126 | #ifdef SIGTSTP 127 | case SIGTSTP: 128 | fprintf(stdout, "SIGTSTP\n"); 129 | break; 130 | #endif 131 | #ifdef SIGTTIN 132 | case SIGTTIN: 133 | fprintf(stdout, "SIGTTIN\n"); 134 | break; 135 | #endif 136 | #ifdef SIGTTOU 137 | case SIGTTOU: 138 | fprintf(stdout, "SIGTTOU\n"); 139 | break; 140 | #endif 141 | #ifdef SIGURG 142 | case SIGURG: 143 | fprintf(stdout, "SIGURG\n"); 144 | break; 145 | #endif 146 | #ifdef SIGXCPU 147 | case SIGXCPU: 148 | fprintf(stdout, "SIGXCPU\n"); 149 | break; 150 | #endif 151 | #ifdef SIGXFSZ 152 | case SIGXFSZ: 153 | fprintf(stdout, "SIGXFSZ\n"); 154 | break; 155 | #endif 156 | #ifdef SIGVTALRM 157 | case SIGVTALRM: 158 | fprintf(stdout, "SIGVTALRM\n"); 159 | break; 160 | #endif 161 | #ifdef SIGPROF 162 | case SIGPROF: 163 | fprintf(stdout, "SIGPROF\n"); 164 | break; 165 | #endif 166 | #ifdef SIGWINCH 167 | case SIGWINCH: 168 | fprintf(stdout, "SIGWINCH\n"); 169 | break; 170 | #endif 171 | #ifdef SIGPOLL 172 | case SIGPOLL: 173 | fprintf(stdout, "SIGPOLL\n"); 174 | break; 175 | #endif 176 | #ifdef SIGIO 177 | #if !defined(SIGPOLL) || SIGIO != SIGPOLL 178 | case SIGIO: 179 | fprintf(stdout, "SIGIO\n"); 180 | break; 181 | #endif 182 | #endif 183 | #ifdef SIGPWR 184 | case SIGPWR: 185 | fprintf(stdout, "SIGPWR\n"); 186 | break; 187 | #endif 188 | #ifdef SIGSYS 189 | case SIGSYS: 190 | fprintf(stdout, "SIGSYS\n"); 191 | break; 192 | #endif 193 | #ifdef SIGUNUSED 194 | #if !defined(SIGSYS) || SIGUNUSED != SIGSYS 195 | case SIGUNUSED: 196 | fprintf(stdout, "SIGUNUSED\n"); 197 | break; 198 | #endif 199 | #endif 200 | default: 201 | fprintf(stdout, "Signal %d\n", sig); 202 | break; 203 | } 204 | 205 | fflush(stdout); 206 | } 207 | -------------------------------------------------------------------------------- /external/microatf/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(microatf-tests LANGUAGES C) 3 | 4 | include(CTest) 5 | 6 | find_package(microatf REQUIRED) 7 | include(ATFTest) 8 | 9 | macro(atf_test _testname) 10 | add_executable("${_testname}" "${_testname}.c") 11 | target_link_libraries("${_testname}" PRIVATE microatf::microatf-c) 12 | atf_discover_tests("${_testname}" ${ARGN}) 13 | endmacro() 14 | 15 | # 16 | 17 | atf_test(atf-test) 18 | atf_test(atf-test2) 19 | -------------------------------------------------------------------------------- /external/microatf/test/atf-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | ATF_TC_WITHOUT_HEAD(atf__environment); 11 | ATF_TC_BODY(atf__environment, tc) 12 | { 13 | DIR *cwd = opendir("."); 14 | 15 | ATF_REQUIRE(cwd != NULL); 16 | 17 | int number_ents = 0; 18 | struct dirent *de; 19 | while ((de = readdir(cwd)) != NULL) { 20 | ATF_REQUIRE(strcmp(de->d_name, ".") == 0 || 21 | strcmp(de->d_name, "..") == 0); 22 | ++number_ents; 23 | } 24 | 25 | ATF_REQUIRE(number_ents == 2); 26 | 27 | ATF_REQUIRE(getenv("LANG") == NULL); 28 | ATF_REQUIRE(getenv("LC_ALL") == NULL); 29 | ATF_REQUIRE(getenv("LC_COLLATE") == NULL); 30 | ATF_REQUIRE(getenv("LC_CTYPE") == NULL); 31 | ATF_REQUIRE(getenv("LC_MESSAGES") == NULL); 32 | ATF_REQUIRE(getenv("LC_MONETARY") == NULL); 33 | ATF_REQUIRE(getenv("LC_NUMERIC") == NULL); 34 | ATF_REQUIRE(getenv("LC_TIME") == NULL); 35 | 36 | #ifdef _WIN32 37 | atf_tc_skip("not applicable under Windows"); 38 | #endif 39 | 40 | ATF_REQUIRE(getenv("TMPDIR") != NULL); 41 | ATF_REQUIRE(getenv("HOME") != NULL); 42 | ATF_REQUIRE(strcmp(getenv("HOME"), getenv("TMPDIR")) == 0); 43 | 44 | ATF_REQUIRE(getenv("TZ") != NULL); 45 | ATF_REQUIRE(strcmp(getenv("TZ"), "UTC") == 0); 46 | } 47 | 48 | ATF_TC(atf__timeout); 49 | ATF_TC_HEAD(atf__timeout, tc) 50 | { 51 | atf_tc_set_md_var(tc, "timeout", "3"); 52 | } 53 | ATF_TC_BODY(atf__timeout, tc) 54 | { 55 | atf_tc_expect_timeout("sleep should take longer than 3s"); 56 | sleep(5); 57 | } 58 | 59 | ATF_TC_WITHOUT_HEAD(atf__checkfail); 60 | ATF_TC_BODY(atf__checkfail, tc) 61 | { 62 | atf_tc_expect_fail("this should fail"); 63 | ATF_CHECK(4 == 5); 64 | } 65 | 66 | ATF_TC_WITHOUT_HEAD(atf__exit_code); 67 | ATF_TC_BODY(atf__exit_code, tc) 68 | { 69 | atf_tc_expect_exit(42, "should exit with code 42"); 70 | sleep(1); 71 | exit(42); 72 | } 73 | 74 | ATF_TC_WITHOUT_HEAD(atf__signal_sighup); 75 | ATF_TC_BODY(atf__signal_sighup, tc) 76 | { 77 | #ifdef _WIN32 78 | atf_tc_skip("not applicable under Windows"); 79 | #else 80 | atf_tc_expect_signal(SIGHUP, "should exit by SIGHUP"); 81 | fprintf(stderr, "hello atf__signal_sighup test!\n"); 82 | kill(getpid(), SIGHUP); 83 | #endif 84 | } 85 | 86 | ATF_TC_WITHOUT_HEAD(atf__signal_sigsegv); 87 | ATF_TC_BODY(atf__signal_sigsegv, tc) 88 | { 89 | #ifdef _WIN32 90 | atf_tc_skip("not applicable under Windows"); 91 | #else 92 | atf_tc_expect_signal(SIGSEGV, "should exit by SIGSEGV"); 93 | kill(getpid(), SIGSEGV); 94 | #endif 95 | } 96 | 97 | ATF_TC_WITHOUT_HEAD(atf__signal_sigint); 98 | ATF_TC_BODY(atf__signal_sigint, tc) 99 | { 100 | #ifdef _WIN32 101 | atf_tc_skip("not applicable under Windows"); 102 | #else 103 | atf_tc_expect_signal(SIGINT, "should exit by SIGINT"); 104 | kill(getpid(), SIGINT); 105 | #endif 106 | } 107 | 108 | ATF_TC_WITHOUT_HEAD(atf__signal_sigkill); 109 | ATF_TC_BODY(atf__signal_sigkill, tc) 110 | { 111 | #ifdef _WIN32 112 | atf_tc_skip("not applicable under Windows"); 113 | #else 114 | atf_tc_expect_signal(SIGKILL, "should exit by SIGKILL"); 115 | kill(getpid(), SIGKILL); 116 | #endif 117 | } 118 | 119 | ATF_TC_WITHOUT_HEAD(atf__signal_sigabrt); 120 | ATF_TC_BODY(atf__signal_sigabrt, tc) 121 | { 122 | #ifdef _WIN32 123 | atf_tc_skip("not applicable under Windows"); 124 | #else 125 | atf_tc_expect_signal(SIGABRT, "should exit by SIGABRT"); 126 | kill(getpid(), SIGABRT); 127 | #endif 128 | } 129 | 130 | ATF_TC_WITHOUT_HEAD(atf__signal_sigterm); 131 | ATF_TC_BODY(atf__signal_sigterm, tc) 132 | { 133 | #ifdef _WIN32 134 | atf_tc_skip("not applicable under Windows"); 135 | #else 136 | atf_tc_expect_signal(SIGTERM, "should exit by SIGTERM"); 137 | kill(getpid(), SIGTERM); 138 | #endif 139 | } 140 | 141 | ATF_TC_WITHOUT_HEAD(atf__signal_sigfpe); 142 | ATF_TC_BODY(atf__signal_sigfpe, tc) 143 | { 144 | #ifdef _WIN32 145 | atf_tc_skip("not applicable under Windows"); 146 | #else 147 | atf_tc_expect_signal(SIGFPE, "should exit by SIGFPE"); 148 | 149 | volatile int d = 0; 150 | exit(100 / d); 151 | #endif 152 | } 153 | 154 | ATF_TC_WITHOUT_HEAD(atf__skip); 155 | ATF_TC_BODY(atf__skip, tc) 156 | { 157 | atf_tc_skip("this test should be skipped"); 158 | } 159 | 160 | ATF_TC_WITHOUT_HEAD(atf__stderr); 161 | ATF_TC_BODY(atf__stderr, tc) 162 | { 163 | fprintf(stderr, "line 1\n"); 164 | fprintf(stderr, "line 2\n"); 165 | fprintf(stderr, "line 3\n"); 166 | } 167 | 168 | ATF_TP_ADD_TCS(tp) 169 | { 170 | ATF_TP_ADD_TC(tp, atf__environment); 171 | ATF_TP_ADD_TC(tp, atf__timeout); 172 | ATF_TP_ADD_TC(tp, atf__checkfail); 173 | ATF_TP_ADD_TC(tp, atf__exit_code); 174 | ATF_TP_ADD_TC(tp, atf__signal_sighup); 175 | ATF_TP_ADD_TC(tp, atf__signal_sigsegv); 176 | ATF_TP_ADD_TC(tp, atf__signal_sigint); 177 | ATF_TP_ADD_TC(tp, atf__signal_sigkill); 178 | ATF_TP_ADD_TC(tp, atf__signal_sigabrt); 179 | ATF_TP_ADD_TC(tp, atf__signal_sigterm); 180 | ATF_TP_ADD_TC(tp, atf__signal_sigfpe); 181 | ATF_TP_ADD_TC(tp, atf__skip); 182 | ATF_TP_ADD_TC(tp, atf__stderr); 183 | 184 | return atf_no_error(); 185 | } 186 | -------------------------------------------------------------------------------- /external/microatf/test/atf-test2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | ATF_TC_WITHOUT_HEAD(atf__success); 4 | ATF_TC_BODY(atf__success, tc) 5 | { 6 | } 7 | 8 | ATF_TP_ADD_TCS(tp) 9 | { 10 | ATF_TP_ADD_TC(tp, atf__success); 11 | 12 | return atf_no_error(); 13 | } 14 | -------------------------------------------------------------------------------- /external/queue-macros/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(queue-macros LANGUAGES C) 3 | 4 | add_library(queue-macros INTERFACE) 5 | target_include_directories(queue-macros 6 | INTERFACE "${CMAKE_CURRENT_LIST_DIR}/include") 7 | 8 | # 9 | 10 | set(_namespace "${PROJECT_NAME}") 11 | 12 | set(${PROJECT_NAME}_DIR 13 | "${PROJECT_BINARY_DIR}/config/subdir" 14 | CACHE INTERNAL "") 15 | file( 16 | WRITE "${PROJECT_BINARY_DIR}/config/subdir/${PROJECT_NAME}-config.cmake" 17 | " 18 | if(NOT TARGET ${_namespace}::queue-macros) 19 | add_library(${_namespace}::queue-macros ALIAS queue-macros) 20 | endif() 21 | ") 22 | -------------------------------------------------------------------------------- /external/tree-macros/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(tree-macros LANGUAGES C) 3 | 4 | add_library(tree-macros INTERFACE) 5 | target_include_directories(tree-macros 6 | INTERFACE "${CMAKE_CURRENT_LIST_DIR}/include") 7 | if(APPLE) 8 | target_compile_definitions(tree-macros INTERFACE __uintptr_t=uintptr_t) 9 | endif() 10 | 11 | # 12 | 13 | set(_namespace "${PROJECT_NAME}") 14 | 15 | set(${PROJECT_NAME}_DIR 16 | "${PROJECT_BINARY_DIR}/config/subdir" 17 | CACHE INTERNAL "") 18 | file( 19 | WRITE "${PROJECT_BINARY_DIR}/config/subdir/${PROJECT_NAME}-config.cmake" 20 | " 21 | if(NOT TARGET ${_namespace}::tree-macros) 22 | add_library(${_namespace}::tree-macros ALIAS tree-macros) 23 | endif() 24 | ") 25 | -------------------------------------------------------------------------------- /include/epoll-shim/detail/common.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLL_SHIM_DETAIL_COMMON_H_ 2 | #define EPOLL_SHIM_DETAIL_COMMON_H_ 3 | 4 | #include 5 | #include 6 | 7 | #if defined(__STRICT_ANSI__) && /**/ \ 8 | !defined(__GXX_EXPERIMENTAL_CXX0X__) && \ 9 | (!defined(__cplusplus) || __cplusplus < 201103L) && \ 10 | (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) 11 | #define EPOLL_SHIM_NO_VARIADICS 12 | #endif 13 | 14 | extern int epoll_shim_close(int); 15 | #ifdef EPOLL_SHIM_NO_VARIADICS 16 | #define close(fd) epoll_shim_close((fd)) 17 | #else 18 | #define close(...) epoll_shim_close(__VA_ARGS__) 19 | #endif 20 | 21 | extern int epoll_shim_fcntl(int, int, ...); 22 | #ifdef EPOLL_SHIM_NO_VARIADICS 23 | #define fcntl epoll_shim_fcntl 24 | #else 25 | #define fcntl(...) epoll_shim_fcntl(__VA_ARGS__) 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/epoll-shim/detail/poll.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLL_SHIM_DETAIL_POLL_H_ 2 | #define EPOLL_SHIM_DETAIL_POLL_H_ 3 | 4 | #include 5 | #include 6 | 7 | extern int epoll_shim_poll(struct pollfd *, nfds_t, int); 8 | #ifdef EPOLL_SHIM_NO_VARIADICS 9 | #define poll(fds, nfds, timeout) epoll_shim_poll((fds), (nfds), (timeout)) 10 | #else 11 | #define poll(...) epoll_shim_poll(__VA_ARGS__) 12 | #endif 13 | 14 | extern int epoll_shim_ppoll(struct pollfd *, nfds_t, struct timespec const *, 15 | sigset_t const *); 16 | #ifdef EPOLL_SHIM_NO_VARIADICS 17 | #define ppoll(fds, nfds, tmo_p, sigmask) \ 18 | epoll_shim_ppoll((fds), (nfds), (tmo_p), (sigmask)) 19 | #else 20 | #define ppoll(...) epoll_shim_ppoll(__VA_ARGS__) 21 | #endif 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /include/epoll-shim/detail/read.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLL_SHIM_DETAIL_READ_H_ 2 | #define EPOLL_SHIM_DETAIL_READ_H_ 3 | 4 | #include 5 | 6 | extern ssize_t epoll_shim_read(int, void *, size_t); 7 | #ifdef EPOLL_SHIM_NO_VARIADICS 8 | #define read(fd, buf, count) epoll_shim_read((fd), (buf), (count)) 9 | #else 10 | #define read(...) epoll_shim_read(__VA_ARGS__) 11 | #endif 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/epoll-shim/detail/write.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLL_SHIM_DETAIL_WRITE_H_ 2 | #define EPOLL_SHIM_DETAIL_WRITE_H_ 3 | 4 | #include 5 | 6 | extern ssize_t epoll_shim_write(int, void const *, size_t); 7 | #ifdef EPOLL_SHIM_NO_VARIADICS 8 | #define write(fd, buf, count) epoll_shim_write((fd), (buf), (count)) 9 | #else 10 | #define write(...) epoll_shim_write(__VA_ARGS__) 11 | #endif 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/sys/epoll.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLL_SHIM_SYS_EPOLL_H_ 2 | #define EPOLL_SHIM_SYS_EPOLL_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #define EPOLL_CLOEXEC O_CLOEXEC 16 | 17 | enum EPOLL_EVENTS { __EPOLL_DUMMY }; 18 | #define EPOLLIN 0x001 19 | #define EPOLLPRI 0x002 20 | #define EPOLLOUT 0x004 21 | #define EPOLLRDNORM 0x040 22 | #define EPOLLNVAL 0x020 23 | #define EPOLLRDBAND 0x080 24 | #define EPOLLWRNORM 0x100 25 | #define EPOLLWRBAND 0x200 26 | #define EPOLLMSG 0x400 27 | #define EPOLLERR 0x008 28 | #define EPOLLHUP 0x010 29 | #define EPOLLRDHUP @POLLRDHUP_VALUE@ 30 | #define EPOLLEXCLUSIVE (1U << 28) 31 | #define EPOLLWAKEUP (1U << 29) 32 | #define EPOLLONESHOT (1U << 30) 33 | #define EPOLLET (1U << 31) 34 | 35 | #define EPOLL_CTL_ADD 1 36 | #define EPOLL_CTL_DEL 2 37 | #define EPOLL_CTL_MOD 3 38 | 39 | typedef union epoll_data { 40 | void *ptr; 41 | int fd; 42 | uint32_t u32; 43 | uint64_t u64; 44 | } epoll_data_t; 45 | 46 | struct epoll_event { 47 | uint32_t events; 48 | epoll_data_t data; 49 | } 50 | #ifdef __x86_64__ 51 | __attribute__((__packed__)) 52 | #endif 53 | ; 54 | 55 | 56 | int epoll_create(int); 57 | int epoll_create1(int); 58 | int epoll_ctl(int, int, int, struct epoll_event *); 59 | int epoll_wait(int, struct epoll_event *, int, int); 60 | int epoll_pwait(int, struct epoll_event *, int, int, sigset_t const *); 61 | 62 | 63 | #ifndef EPOLL_SHIM_DISABLE_WRAPPER_MACROS 64 | #include 65 | #endif 66 | 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif /* sys/epoll.h */ 73 | -------------------------------------------------------------------------------- /include/sys/eventfd.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLL_SHIM_SYS_EVENTFD_H_ 2 | #define EPOLL_SHIM_SYS_EVENTFD_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | #include 11 | 12 | typedef uint64_t eventfd_t; 13 | 14 | #define EFD_SEMAPHORE 1 15 | #define EFD_CLOEXEC O_CLOEXEC 16 | #define EFD_NONBLOCK O_NONBLOCK 17 | 18 | int eventfd(unsigned int, int); 19 | int eventfd_read(int, eventfd_t *); 20 | int eventfd_write(int, eventfd_t); 21 | 22 | 23 | #ifndef EPOLL_SHIM_DISABLE_WRAPPER_MACROS 24 | #include 25 | #include 26 | #include 27 | #endif 28 | 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | #endif /* sys/eventfd.h */ 35 | -------------------------------------------------------------------------------- /include/sys/signalfd.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLL_SHIM_SYS_SIGNALFD_H_ 2 | #define EPOLL_SHIM_SYS_SIGNALFD_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include /* IWYU pragma: keep */ 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #define SFD_CLOEXEC O_CLOEXEC 17 | #define SFD_NONBLOCK O_NONBLOCK 18 | 19 | int signalfd(int, sigset_t const *, int); 20 | 21 | struct signalfd_siginfo { 22 | uint32_t ssi_signo; 23 | int32_t ssi_errno; 24 | int32_t ssi_code; 25 | uint32_t ssi_pid; 26 | uint32_t ssi_uid; 27 | int32_t ssi_fd; 28 | uint32_t ssi_tid; 29 | uint32_t ssi_band; 30 | uint32_t ssi_overrun; 31 | uint32_t ssi_trapno; 32 | int32_t ssi_status; 33 | int32_t ssi_int; 34 | uint64_t ssi_ptr; 35 | uint64_t ssi_utime; 36 | uint64_t ssi_stime; 37 | uint64_t ssi_addr; 38 | uint16_t ssi_addr_lsb; 39 | uint8_t pad[128 - 12 * 4 - 4 * 8 - 2]; 40 | }; 41 | 42 | 43 | #ifndef EPOLL_SHIM_DISABLE_WRAPPER_MACROS 44 | #include 45 | #include 46 | #include 47 | #endif 48 | 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/sys/timerfd.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLL_SHIM_SYS_TIMERFD_H_ 2 | #define EPOLL_SHIM_SYS_TIMERFD_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define TFD_NONBLOCK O_NONBLOCK 12 | #define TFD_CLOEXEC O_CLOEXEC 13 | 14 | #define TFD_TIMER_ABSTIME 1 15 | #define TFD_TIMER_CANCEL_ON_SET (1 << 1) 16 | 17 | struct itimerspec; 18 | 19 | int timerfd_create(int, int); 20 | int timerfd_settime(int, int, struct itimerspec const *, struct itimerspec *); 21 | int timerfd_gettime(int, struct itimerspec *); 22 | 23 | 24 | #ifndef EPOLL_SHIM_DISABLE_WRAPPER_MACROS 25 | #include 26 | #include 27 | #include 28 | #endif 29 | 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_C_VISIBILITY_PRESET hidden) 2 | set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) 3 | 4 | set(THREADS_PREFER_PTHREAD_FLAG ON) 5 | find_package(Threads REQUIRED) 6 | 7 | find_package(tree-macros REQUIRED) 8 | find_package(queue-macros REQUIRED) 9 | 10 | add_library(rwlock OBJECT rwlock.c) 11 | set_property(TARGET rwlock PROPERTY POSITION_INDEPENDENT_CODE ON) 12 | target_link_libraries(rwlock PUBLIC Threads::Threads) 13 | target_include_directories(rwlock 14 | PUBLIC $) 15 | 16 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 17 | add_library(epoll-shim INTERFACE) 18 | add_library(epoll-shim::epoll-shim ALIAS epoll-shim) 19 | add_library(epoll-shim-interpose INTERFACE) 20 | add_library(epoll-shim::epoll-shim-interpose ALIAS epoll-shim-interpose) 21 | return() 22 | endif() 23 | 24 | add_library(wrap OBJECT wrap.c) 25 | set_property(TARGET wrap PROPERTY POSITION_INDEPENDENT_CODE ON) 26 | target_link_libraries(wrap PUBLIC Threads::Threads) 27 | target_include_directories(wrap 28 | PUBLIC $) 29 | 30 | macro(add_compat_target _name _condition) 31 | add_library(compat_${_name} OBJECT compat_${_name}.c) 32 | target_link_libraries(compat_${_name} PUBLIC wrap) 33 | set_property(TARGET compat_${_name} PROPERTY POSITION_INDEPENDENT_CODE ON) 34 | target_compile_options( 35 | compat_${_name} 36 | INTERFACE "SHELL:-include \"${CMAKE_CURRENT_LIST_DIR}/compat_${_name}.h\"") 37 | add_library(compat_enable_${_name} INTERFACE) 38 | if(${_condition}) 39 | target_sources(compat_enable_${_name} 40 | INTERFACE $) 41 | target_link_libraries(compat_enable_${_name} INTERFACE compat_${_name}) 42 | string(TOUPPER "${_name}" _upper_name) 43 | target_compile_definitions(compat_enable_${_name} 44 | INTERFACE COMPAT_ENABLE_${_upper_name}) 45 | endif() 46 | endmacro() 47 | 48 | include(CheckSymbolExists) 49 | 50 | # FreeBSD 13 and NetBSD 10 support native eventfd descriptors. NetBSD 10 51 | # supports native timerfd descriptors. Prefer them if available. 52 | check_symbol_exists(eventfd "sys/eventfd.h" HAVE_EVENTFD) 53 | check_symbol_exists(timerfd_create "sys/timerfd.h" HAVE_TIMERFD) 54 | 55 | check_symbol_exists(kqueue1 "sys/types.h;sys/event.h;sys/time.h" HAVE_KQUEUE1) 56 | add_compat_target(kqueue1 "NOT;HAVE_KQUEUE1") 57 | check_symbol_exists(sigandset "signal.h" HAVE_SIGANDSET) 58 | check_symbol_exists(sigorset "signal.h" HAVE_SIGORSET) 59 | check_symbol_exists(sigisemptyset "signal.h" HAVE_SIGISEMPTYSET) 60 | add_compat_target( 61 | sigops "NOT;HAVE_SIGANDSET;AND;NOT;HAVE_SIGORSET;AND;NOT;HAVE_SIGISEMPTYSET") 62 | include(CheckCSourceRuns) 63 | check_c_source_runs( 64 | [=[ 65 | #include 66 | #include 67 | #include 68 | int main() { 69 | struct kevent kev; 70 | EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 0, 0); 71 | int kq; 72 | return ((kq = kqueue()) >= 0 && kevent(kq, &kev, 1, NULL, 0, NULL) == 0) ? 0 : 1; 73 | } 74 | ]=] 75 | ALLOWS_ONESHOT_TIMERS_WITH_TIMEOUT_ZERO) 76 | add_library(evfilt_timer_quirks INTERFACE) 77 | if(NOT ALLOWS_ONESHOT_TIMERS_WITH_TIMEOUT_ZERO) 78 | target_compile_definitions( 79 | evfilt_timer_quirks 80 | INTERFACE QUIRK_EVFILT_TIMER_DISALLOWS_ONESHOT_TIMEOUT_ZERO) 81 | endif() 82 | add_compat_target(pipe2 "APPLE") 83 | add_compat_target(socket "APPLE") 84 | add_compat_target(socketpair "APPLE") 85 | add_compat_target(itimerspec "APPLE") 86 | add_compat_target(sem "APPLE") 87 | add_compat_target(ppoll "APPLE") 88 | 89 | target_link_libraries(rwlock PUBLIC $) 90 | 91 | add_library( 92 | epoll-shim 93 | epoll_shim_ctx.c 94 | epoll.c 95 | epollfd_ctx.c 96 | kqueue_event.c 97 | signalfd.c 98 | signalfd_ctx.c 99 | timespec_util.c) 100 | if(NOT HAVE_EVENTFD) 101 | target_sources(epoll-shim PRIVATE eventfd.c eventfd_ctx.c) 102 | endif() 103 | if(NOT HAVE_TIMERFD) 104 | target_sources(epoll-shim PRIVATE timerfd.c timerfd_ctx.c) 105 | endif() 106 | include(GenerateExportHeader) 107 | generate_export_header(epoll-shim BASE_NAME epoll_shim) 108 | target_link_libraries( 109 | epoll-shim 110 | PRIVATE Threads::Threads # 111 | $ 112 | $ # 113 | $ 114 | $ 115 | $ 116 | $ 117 | $ 118 | $ 119 | $) 120 | if(HAVE_TIMERFD) 121 | target_compile_definitions(epoll-shim PRIVATE HAVE_TIMERFD) 122 | endif() 123 | target_compile_definitions(epoll-shim PRIVATE EPOLL_SHIM_DISABLE_WRAPPER_MACROS) 124 | target_include_directories( 125 | epoll-shim 126 | PRIVATE $ 127 | PUBLIC $) 128 | 129 | include(CMakePushCheckState) 130 | cmake_push_check_state() 131 | set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) 132 | set(CMAKE_EXTRA_INCLUDE_FILES poll.h) 133 | check_type_size("((struct { char x[POLLRDHUP]\\; }*)0)->x" POLLRDHUP_VALUE) 134 | cmake_pop_check_state() 135 | 136 | if(NOT POLLRDHUP_VALUE) 137 | set(POLLRDHUP_VALUE "0x2000") 138 | else() 139 | message(STATUS "Detected value of POLLRDHUP as ${POLLRDHUP_VALUE}") 140 | endif() 141 | 142 | set(_headers 143 | "epoll-shim/detail/common.h" # 144 | "epoll-shim/detail/poll.h" # 145 | "epoll-shim/detail/read.h" # 146 | "epoll-shim/detail/write.h" # 147 | "sys/epoll.h" # 148 | "sys/signalfd.h") 149 | if(NOT HAVE_EVENTFD) 150 | list(APPEND _headers "sys/eventfd.h") 151 | endif() 152 | if(NOT HAVE_TIMERFD) 153 | list(APPEND _headers "sys/timerfd.h") 154 | endif() 155 | foreach(_header IN LISTS _headers) 156 | configure_file("${PROJECT_SOURCE_DIR}/include/${_header}" 157 | "${PROJECT_BINARY_DIR}/install-include/${_header}") 158 | endforeach() 159 | 160 | set_target_properties(epoll-shim PROPERTIES SOVERSION 0) 161 | 162 | # 163 | 164 | add_library(epoll-shim-interpose epoll_shim_interpose.c) 165 | target_link_libraries(epoll-shim-interpose PUBLIC epoll-shim) 166 | target_compile_definitions(epoll-shim-interpose 167 | PUBLIC EPOLL_SHIM_DISABLE_WRAPPER_MACROS) 168 | generate_export_header(epoll-shim-interpose BASE_NAME epoll_shim_interpose) 169 | target_include_directories( 170 | epoll-shim-interpose PRIVATE $) 171 | set_target_properties(epoll-shim-interpose PROPERTIES SOVERSION 0) 172 | -------------------------------------------------------------------------------- /src/compat_itimerspec.c: -------------------------------------------------------------------------------- 1 | #include "compat_itimerspec.h" 2 | -------------------------------------------------------------------------------- /src/compat_itimerspec.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_ITIMERSPEC_H 2 | #define COMPAT_ITIMERSPEC_H 3 | 4 | #ifdef COMPAT_ENABLE_ITIMERSPEC 5 | 6 | #include 7 | 8 | struct itimerspec { 9 | struct timespec it_interval; 10 | struct timespec it_value; 11 | }; 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/compat_kqueue1.c: -------------------------------------------------------------------------------- 1 | #include "compat_kqueue1.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "errno_return.h" 15 | #include "wrap.h" 16 | 17 | static errno_t 18 | compat_kqueue1_impl(int *fd_out, int flags) 19 | { 20 | errno_t ec; 21 | 22 | if (flags & ~(O_CLOEXEC | O_NONBLOCK)) { 23 | return EINVAL; 24 | } 25 | 26 | int fd = kqueue(); 27 | if (fd < 0) { 28 | return errno; 29 | } 30 | 31 | { 32 | int r; 33 | 34 | if (flags & O_CLOEXEC) { 35 | if ((r = real_fcntl(fd, F_GETFD)) < 0 || 36 | real_fcntl(fd, F_SETFD, r | FD_CLOEXEC) < 0) { 37 | ec = errno; 38 | goto out; 39 | } 40 | } 41 | #ifdef __APPLE__ 42 | else { 43 | if ((r = fcntl(fd, F_GETFD, 0)) < 0 || 44 | fcntl(fd, F_SETFD, r & ~FD_CLOEXEC) < 0) { 45 | ec = errno; 46 | goto out; 47 | } 48 | } 49 | #endif 50 | 51 | if (flags & O_NONBLOCK) { 52 | if ((r = real_fcntl(fd, F_GETFL)) < 0) { 53 | ec = errno; 54 | goto out; 55 | } 56 | 57 | if (real_fcntl(fd, F_SETFL, r | O_NONBLOCK) < 0 && 58 | errno != ENOTTY) { 59 | ec = errno; 60 | goto out; 61 | } 62 | } 63 | } 64 | 65 | *fd_out = fd; 66 | return 0; 67 | 68 | out: 69 | (void)real_close(fd); 70 | return ec; 71 | } 72 | 73 | int 74 | compat_kqueue1(int flags) 75 | { 76 | ERRNO_SAVE; 77 | errno_t ec; 78 | 79 | int fd; 80 | ec = compat_kqueue1_impl(&fd, flags); 81 | 82 | ERRNO_RETURN(ec, -1, fd); 83 | } 84 | -------------------------------------------------------------------------------- /src/compat_kqueue1.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_KQUEUE1_H 2 | #define COMPAT_KQUEUE1_H 3 | 4 | int compat_kqueue1(int); 5 | 6 | #ifdef COMPAT_ENABLE_KQUEUE1 7 | #define kqueue1 compat_kqueue1 8 | #endif 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/compat_pipe2.c: -------------------------------------------------------------------------------- 1 | #include "compat_pipe2.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "wrap.h" 9 | 10 | static errno_t 11 | compat_pipe2_impl(int pipefd[2], int flags) 12 | { 13 | errno_t ec; 14 | 15 | if (flags & ~(O_CLOEXEC | O_NONBLOCK)) { 16 | return EINVAL; 17 | } 18 | 19 | int p[2]; 20 | if (pipe(p) < 0) { 21 | return errno; 22 | } 23 | 24 | { 25 | int r; 26 | 27 | if (flags & O_NONBLOCK) { 28 | if ((r = fcntl(p[0], F_GETFL, 0)) < 0 || 29 | fcntl(p[0], F_SETFL, r | O_NONBLOCK) < 0 || 30 | (r = fcntl(p[1], F_GETFL, 0)) < 0 || 31 | fcntl(p[1], F_SETFL, r | O_NONBLOCK) < 0) { 32 | ec = errno; 33 | goto out; 34 | } 35 | } 36 | 37 | if (flags & O_CLOEXEC) { 38 | if ((r = fcntl(p[0], F_GETFD, 0)) < 0 || 39 | fcntl(p[0], F_SETFD, r | FD_CLOEXEC) < 0 || 40 | (r = fcntl(p[1], F_GETFD, 0)) < 0 || 41 | fcntl(p[1], F_SETFD, r | FD_CLOEXEC) < 0) { 42 | ec = errno; 43 | goto out; 44 | } 45 | } 46 | } 47 | 48 | pipefd[0] = p[0]; 49 | pipefd[1] = p[1]; 50 | 51 | return 0; 52 | 53 | out: 54 | (void)real_close(p[0]); 55 | (void)real_close(p[1]); 56 | return ec; 57 | } 58 | 59 | int 60 | compat_pipe2(int pipefd[2], int flags) 61 | { 62 | errno_t ec = compat_pipe2_impl(pipefd, flags); 63 | if (ec != 0) { 64 | errno = ec; 65 | return -1; 66 | } 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /src/compat_pipe2.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_PIPE2_H 2 | #define COMPAT_PIPE2_H 3 | 4 | #include 5 | #include 6 | 7 | int compat_pipe2(int pipefd[2], int flags); 8 | #ifdef COMPAT_ENABLE_PIPE2 9 | #define pipe2 compat_pipe2 10 | #endif 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/compat_ppoll.c: -------------------------------------------------------------------------------- 1 | #include "compat_ppoll.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "wrap.h" 14 | 15 | static errno_t 16 | compat_ppoll_impl(struct pollfd fds[], nfds_t nfds, 17 | struct timespec const *restrict timeout, sigset_t const *restrict sigmask, 18 | int *n) 19 | { 20 | errno_t ec; 21 | 22 | int timeout_ms = -1; 23 | if (timeout != NULL) { 24 | if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 || 25 | timeout->tv_nsec >= 1000000000) { 26 | return EINVAL; 27 | } 28 | 29 | timeout_ms = timeout->tv_nsec / 1000000; 30 | if (timeout->tv_nsec % 1000000) { 31 | ++timeout_ms; 32 | } 33 | 34 | int sec_ms; 35 | if (__builtin_mul_overflow(timeout->tv_sec, 1000, &sec_ms) || 36 | __builtin_add_overflow(sec_ms, timeout_ms, &timeout_ms)) { 37 | timeout_ms = -1; 38 | } 39 | } 40 | 41 | struct kevent kevs[NSIG]; 42 | int kevs_length = 0; 43 | 44 | sigset_t origmask; 45 | 46 | if (timeout_ms != 0 && sigmask != NULL) { 47 | if ((ec = pthread_sigmask(SIG_SETMASK, /**/ 48 | NULL, &origmask)) != 0) { 49 | return ec; 50 | } 51 | 52 | for (int i = 1; i < NSIG; ++i) { 53 | if (i == SIGKILL || i == SIGSTOP) { 54 | continue; 55 | } 56 | 57 | if (!sigismember(&origmask, i) || 58 | sigismember(sigmask, i)) { 59 | continue; 60 | } 61 | 62 | struct sigaction act; 63 | if (sigaction(i, NULL, &act) < 0) { 64 | return errno; 65 | } 66 | 67 | if (!(act.sa_flags & SA_SIGINFO) && 68 | (act.sa_handler == SIG_DFL || 69 | act.sa_handler == SIG_IGN)) { 70 | continue; 71 | } 72 | 73 | if (act.sa_flags & SA_RESTART) { 74 | continue; 75 | } 76 | 77 | EV_SET(&kevs[kevs_length++], i, EVFILT_SIGNAL, 78 | EV_ADD | EV_ONESHOT, 0, 0, 0); 79 | } 80 | } 81 | 82 | int kq = -1; 83 | struct pollfd *fds2 = NULL; 84 | 85 | if (kevs_length > 0) { 86 | kq = kqueue(); 87 | if (kq < 0) { 88 | return errno; 89 | } 90 | 91 | if (kevent(kq, kevs, kevs_length, NULL, 0, NULL) < 0) { 92 | ec = errno; 93 | goto out; 94 | } 95 | 96 | #ifdef EVFILT_USER 97 | sigset_t pending; 98 | if (sigpending(&pending) < 0) { 99 | ec = errno; 100 | goto out; 101 | } 102 | for (int i = 0; i < kevs_length; ++i) { 103 | if (sigismember(&pending, (int)kevs[i].ident)) { 104 | EV_SET(&kevs[0], 0, EVFILT_USER, /**/ 105 | EV_ADD | EV_ONESHOT, 0, 0, 0); 106 | EV_SET(&kevs[1], 0, EVFILT_USER, /**/ 107 | 0, NOTE_TRIGGER, 0, 0); 108 | if (kevent(kq, kevs, 2, NULL, 0, NULL) < 0) { 109 | ec = errno; 110 | goto out; 111 | } 112 | break; 113 | } 114 | } 115 | #endif 116 | 117 | size_t pfds2_bytes; 118 | if (__builtin_add_overflow(nfds, 1, &pfds2_bytes) || 119 | __builtin_mul_overflow(pfds2_bytes, sizeof(struct pollfd), 120 | &pfds2_bytes)) { 121 | ec = EINVAL; 122 | goto out; 123 | } 124 | 125 | fds2 = malloc(pfds2_bytes); 126 | if (!fds2) { 127 | ec = errno; 128 | goto out; 129 | } 130 | memcpy(fds2, fds, nfds * sizeof(struct pollfd)); 131 | fds2[nfds] = (struct pollfd) { 132 | .fd = kq, 133 | .events = POLLIN, 134 | }; 135 | } 136 | 137 | if (sigmask != NULL) { 138 | (void)pthread_sigmask(SIG_SETMASK, sigmask, &origmask); 139 | } 140 | 141 | int ready = real_poll(fds2 ? fds2 : fds, nfds + !!(fds2), timeout_ms); 142 | ec = ready < 0 ? errno : 0; 143 | 144 | if (sigmask != NULL) { 145 | (void)pthread_sigmask(SIG_SETMASK, &origmask, NULL); 146 | } 147 | 148 | if (ec == 0 && fds2 && fds2[nfds].revents) { 149 | --ready; 150 | if (ready == 0) { 151 | ec = EINTR; 152 | } 153 | } 154 | 155 | if (ec == 0) { 156 | *n = ready; 157 | if (fds2) { 158 | memcpy(fds, fds2, nfds * sizeof(struct pollfd)); 159 | } 160 | } 161 | 162 | out: 163 | free(fds2); 164 | if (kq >= 0) { 165 | real_close(kq); 166 | } 167 | return ec; 168 | } 169 | 170 | int 171 | compat_ppoll(struct pollfd fds[], nfds_t nfds, 172 | struct timespec const *restrict timeout, sigset_t const *restrict sigmask) 173 | { 174 | int n; 175 | errno_t ec = compat_ppoll_impl(fds, nfds, timeout, sigmask, &n); 176 | if (ec != 0) { 177 | errno = ec; 178 | return -1; 179 | } 180 | return n; 181 | } 182 | -------------------------------------------------------------------------------- /src/compat_ppoll.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_PPOLL_H 2 | #define COMPAT_PPOLL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | int compat_ppoll(struct pollfd fds[], nfds_t nfds, 9 | struct timespec const *restrict timeout, sigset_t const *restrict sigmask); 10 | #ifdef COMPAT_ENABLE_PPOLL 11 | #define ppoll compat_ppoll 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/compat_sem.c: -------------------------------------------------------------------------------- 1 | #include "compat_sem.h" 2 | -------------------------------------------------------------------------------- /src/compat_sem.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_SEM_H 2 | #define COMPAT_SEM_H 3 | 4 | #ifdef COMPAT_ENABLE_SEM 5 | #include 6 | 7 | #include 8 | 9 | #define sem_t dispatch_semaphore_t 10 | 11 | #define sem_init(sem, pshared, value) \ 12 | (*(sem) = dispatch_semaphore_create(value)) 13 | #define sem_post(sem) dispatch_semaphore_signal(*(sem)) 14 | #define sem_wait(sem) dispatch_semaphore_wait(*(sem), DISPATCH_TIME_FOREVER) 15 | #define sem_destroy(sem) dispatch_release(*(sem)) 16 | #endif 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/compat_sigops.c: -------------------------------------------------------------------------------- 1 | #include "compat_sigops.h" 2 | 3 | #include 4 | 5 | #if defined(__OpenBSD__) || defined(__APPLE__) 6 | int 7 | compat_sigisemptyset(sigset_t const *set) 8 | { 9 | return *set == 0; 10 | } 11 | 12 | int 13 | compat_sigandset(sigset_t *dest, sigset_t const *left, sigset_t const *right) 14 | { 15 | *dest = (*left) & (*right); 16 | return 0; 17 | } 18 | #elif defined(__NetBSD__) 19 | int 20 | compat_sigisemptyset(sigset_t const *set) 21 | { 22 | sigset_t e; 23 | __sigemptyset(&e); 24 | return __sigsetequal(set, &e); 25 | } 26 | 27 | int 28 | compat_sigandset(sigset_t *dest, sigset_t const *left, sigset_t const *right) 29 | { 30 | memcpy(dest, left, sizeof(sigset_t)); 31 | __sigandset(right, dest); 32 | return 0; 33 | } 34 | #elif defined(__DragonFly__) || defined(__FreeBSD__) 35 | int 36 | compat_sigisemptyset(sigset_t const *set) 37 | { 38 | for (int i = 0; i < _SIG_WORDS; ++i) { 39 | if (set->__bits[i] != 0) { 40 | return 0; 41 | } 42 | } 43 | return 1; 44 | } 45 | 46 | int 47 | compat_sigandset(sigset_t *dest, sigset_t const *left, sigset_t const *right) 48 | { 49 | for (int i = 0; i < _SIG_WORDS; ++i) { 50 | dest->__bits[i] = left->__bits[i] & right->__bits[i]; 51 | } 52 | return 0; 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /src/compat_sigops.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_SIGOPS_H 2 | #define COMPAT_SIGOPS_H 3 | 4 | #include 5 | 6 | int compat_sigisemptyset(sigset_t const *set); 7 | int compat_sigorset(sigset_t *dest, sigset_t const *left, 8 | sigset_t const *right); 9 | int compat_sigandset(sigset_t *dest, sigset_t const *left, 10 | sigset_t const *right); 11 | 12 | #ifdef COMPAT_ENABLE_SIGOPS 13 | #define sigisemptyset compat_sigisemptyset 14 | #define sigorset compat_sigorset 15 | #define sigandset compat_sigandset 16 | #endif 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/compat_socket.c: -------------------------------------------------------------------------------- 1 | #include "compat_socket.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "wrap.h" 10 | 11 | static errno_t 12 | compat_socket_impl(int domain, int type, int protocol, int *s) 13 | { 14 | errno_t ec; 15 | 16 | int sock = socket(domain, 17 | type & ~(COMPAT_SOCK_CLOEXEC | COMPAT_SOCK_NONBLOCK), protocol); 18 | if (sock < 0) { 19 | return errno; 20 | } 21 | 22 | { 23 | int r; 24 | 25 | if (type & COMPAT_SOCK_NONBLOCK) { 26 | if ((r = fcntl(sock, F_GETFL, 0)) < 0 || 27 | fcntl(sock, F_SETFL, r | O_NONBLOCK) < 0) { 28 | ec = errno; 29 | goto out; 30 | } 31 | } 32 | 33 | if (type & COMPAT_SOCK_CLOEXEC) { 34 | if ((r = fcntl(sock, F_GETFD, 0)) < 0 || 35 | fcntl(sock, F_SETFD, r | FD_CLOEXEC) < 0) { 36 | ec = errno; 37 | goto out; 38 | } 39 | } 40 | } 41 | 42 | *s = sock; 43 | return 0; 44 | 45 | out: 46 | (void)real_close(sock); 47 | return ec; 48 | } 49 | 50 | int 51 | compat_socket(int domain, int type, int protocol) 52 | { 53 | int s; 54 | errno_t ec = compat_socket_impl(domain, type, protocol, &s); 55 | if (ec != 0) { 56 | errno = ec; 57 | return -1; 58 | } 59 | return s; 60 | } 61 | -------------------------------------------------------------------------------- /src/compat_socket.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_SOCKET_H 2 | #define COMPAT_SOCKET_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #define COMPAT_SOCK_NONBLOCK (O_NONBLOCK) 9 | #define COMPAT_SOCK_CLOEXEC (O_CLOEXEC) 10 | int compat_socket(int domain, int type, int protocol); 11 | #ifdef COMPAT_ENABLE_SOCKET 12 | #define socket compat_socket 13 | #define SOCK_NONBLOCK COMPAT_SOCK_NONBLOCK 14 | #define SOCK_CLOEXEC COMPAT_SOCK_CLOEXEC 15 | #endif 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/compat_socketpair.c: -------------------------------------------------------------------------------- 1 | #include "compat_socketpair.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "wrap.h" 10 | 11 | static errno_t 12 | compat_socketpair_impl(int domain, int type, int protocol, int sv[2]) 13 | { 14 | errno_t ec; 15 | 16 | int p[2]; 17 | if (socketpair(domain, 18 | type & ~(COMPAT_SOCK_CLOEXEC | COMPAT_SOCK_NONBLOCK), protocol, 19 | p) < 0) { 20 | return errno; 21 | } 22 | 23 | { 24 | int r; 25 | 26 | if (type & COMPAT_SOCK_NONBLOCK) { 27 | if ((r = fcntl(p[0], F_GETFL, 0)) < 0 || 28 | fcntl(p[0], F_SETFL, r | O_NONBLOCK) < 0 || 29 | (r = fcntl(p[1], F_GETFL, 0)) < 0 || 30 | fcntl(p[1], F_SETFL, r | O_NONBLOCK) < 0) { 31 | ec = errno; 32 | goto out; 33 | } 34 | } 35 | 36 | if (type & COMPAT_SOCK_CLOEXEC) { 37 | if ((r = fcntl(p[0], F_GETFD, 0)) < 0 || 38 | fcntl(p[0], F_SETFD, r | FD_CLOEXEC) < 0 || 39 | (r = fcntl(p[1], F_GETFD, 0)) < 0 || 40 | fcntl(p[1], F_SETFD, r | FD_CLOEXEC) < 0) { 41 | ec = errno; 42 | goto out; 43 | } 44 | } 45 | } 46 | 47 | sv[0] = p[0]; 48 | sv[1] = p[1]; 49 | 50 | return 0; 51 | 52 | out: 53 | (void)real_close(p[0]); 54 | (void)real_close(p[1]); 55 | return ec; 56 | } 57 | 58 | int 59 | compat_socketpair(int domain, int type, int protocol, int sv[2]) 60 | { 61 | errno_t ec = compat_socketpair_impl(domain, type, protocol, sv); 62 | if (ec != 0) { 63 | errno = ec; 64 | return -1; 65 | } 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /src/compat_socketpair.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_SOCKETPAIR_H 2 | #define COMPAT_SOCKETPAIR_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #define COMPAT_SOCK_NONBLOCK (O_NONBLOCK) 9 | #define COMPAT_SOCK_CLOEXEC (O_CLOEXEC) 10 | int compat_socketpair(int domain, int type, int protocol, int sv[2]); 11 | #ifdef COMPAT_ENABLE_SOCKETPAIR 12 | #define socketpair compat_socketpair 13 | #define SOCK_NONBLOCK COMPAT_SOCK_NONBLOCK 14 | #define SOCK_CLOEXEC COMPAT_SOCK_CLOEXEC 15 | #endif 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/epoll.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "epoll_shim_ctx.h" 16 | #include "epoll_shim_export.h" 17 | #include "errno_return.h" 18 | #include "timespec_util.h" 19 | #include "wrap.h" 20 | 21 | void epollfd_lock(FileDescription *desc); 22 | void epollfd_unlock(FileDescription *desc); 23 | void epollfd_remove_fd(FileDescription *desc, int kq, int fd); 24 | 25 | static errno_t 26 | epollfd_close(FileDescription *desc) 27 | { 28 | return epollfd_ctx_terminate(&desc->ctx.epollfd); 29 | } 30 | 31 | static struct file_description_vtable const epollfd_vtable = { 32 | .read_fun = fd_context_default_read, 33 | .write_fun = fd_context_default_write, 34 | .close_fun = epollfd_close, 35 | }; 36 | 37 | void 38 | epollfd_lock(FileDescription *desc) 39 | { 40 | if (desc->vtable == &epollfd_vtable) { 41 | (void)pthread_mutex_lock(&desc->mutex); 42 | } 43 | } 44 | 45 | void 46 | epollfd_unlock(FileDescription *desc) 47 | { 48 | if (desc->vtable == &epollfd_vtable) { 49 | (void)pthread_mutex_unlock(&desc->mutex); 50 | } 51 | } 52 | 53 | void 54 | epollfd_remove_fd(FileDescription *desc, int kq, int fd) 55 | { 56 | if (desc->vtable == &epollfd_vtable) { 57 | epollfd_ctx_remove_fd(&desc->ctx.epollfd, kq, fd); 58 | } 59 | } 60 | 61 | static errno_t 62 | epoll_create_impl(int *fd_out, int flags) 63 | { 64 | errno_t ec; 65 | 66 | EpollShimCtx *epoll_shim_ctx; 67 | if ((ec = epoll_shim_ctx_global(&epoll_shim_ctx)) != 0) { 68 | return ec; 69 | } 70 | 71 | int fd; 72 | FileDescription *desc; 73 | ec = epoll_shim_ctx_create_desc(epoll_shim_ctx, 74 | flags & (O_CLOEXEC | O_NONBLOCK), &fd, &desc); 75 | if (ec != 0) { 76 | return ec; 77 | } 78 | 79 | desc->flags = flags & O_NONBLOCK; 80 | 81 | if ((ec = epollfd_ctx_init(&desc->ctx.epollfd)) != 0) { 82 | goto fail; 83 | } 84 | 85 | desc->vtable = &epollfd_vtable; 86 | epoll_shim_ctx_install_desc(epoll_shim_ctx, fd, desc); 87 | 88 | *fd_out = fd; 89 | return 0; 90 | 91 | fail: 92 | epoll_shim_ctx_drop_desc(epoll_shim_ctx, fd, desc); 93 | return ec; 94 | } 95 | 96 | static int 97 | epoll_create_common(int flags) 98 | { 99 | ERRNO_SAVE; 100 | errno_t ec; 101 | 102 | int fd; 103 | ec = epoll_create_impl(&fd, flags); 104 | 105 | ERRNO_RETURN(ec, -1, fd); 106 | } 107 | 108 | EPOLL_SHIM_EXPORT 109 | int 110 | epoll_create(int size) 111 | { 112 | ERRNO_SAVE; 113 | errno_t ec; 114 | 115 | ec = size <= 0 ? EINVAL : 0; 116 | 117 | ERRNO_RETURN(ec, -1, epoll_create_common(0)); 118 | } 119 | 120 | EPOLL_SHIM_EXPORT 121 | int 122 | epoll_create1(int flags) 123 | { 124 | if (flags & ~EPOLL_CLOEXEC) { 125 | errno = EINVAL; 126 | return -1; 127 | } 128 | 129 | _Static_assert(EPOLL_CLOEXEC == O_CLOEXEC, ""); 130 | 131 | return epoll_create_common(flags); 132 | } 133 | 134 | static errno_t 135 | epoll_ctl_impl(int fd, int op, int fd2, struct epoll_event *ev) 136 | { 137 | errno_t ec; 138 | 139 | if (!ev && op != EPOLL_CTL_DEL) { 140 | return EFAULT; 141 | } 142 | 143 | EpollShimCtx *epoll_shim_ctx; 144 | if ((ec = epoll_shim_ctx_global(&epoll_shim_ctx)) != 0) { 145 | return ec; 146 | } 147 | 148 | FileDescription *desc = epoll_shim_ctx_find_desc(epoll_shim_ctx, fd); 149 | if (!desc || desc->vtable != &epollfd_vtable) { 150 | struct stat sb; 151 | ec = (fd < 0 || fstat(fd, &sb) < 0) ? EBADF : EINVAL; 152 | goto out; 153 | } 154 | 155 | FileDescription *fd2_desc = (op == EPOLL_CTL_ADD) ? 156 | epoll_shim_ctx_find_desc(epoll_shim_ctx, fd2) : 157 | NULL; 158 | 159 | (void)pthread_mutex_lock(&desc->mutex); 160 | ec = epollfd_ctx_ctl(&desc->ctx.epollfd, fd, op, fd2, 161 | fd_as_pollable_desc(fd2_desc), ev); 162 | (void)pthread_mutex_unlock(&desc->mutex); 163 | 164 | if (fd2_desc) { 165 | (void)file_description_unref(&fd2_desc); 166 | } 167 | out: 168 | if (desc) { 169 | (void)file_description_unref(&desc); 170 | } 171 | return ec; 172 | } 173 | 174 | EPOLL_SHIM_EXPORT 175 | int 176 | epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev) 177 | { 178 | ERRNO_SAVE; 179 | errno_t ec; 180 | 181 | ec = epoll_ctl_impl(fd, op, fd2, ev); 182 | 183 | ERRNO_RETURN(ec, -1, 0); 184 | } 185 | 186 | static errno_t 187 | epollfd_ctx_wait_or_block(FileDescription *desc, int kq, /**/ 188 | struct epoll_event *ev, int cnt, int *actual_cnt, 189 | struct timespec const *deadline, struct timespec *timeout, 190 | sigset_t const *sigs) 191 | { 192 | errno_t ec; 193 | 194 | EpollFDCtx *epollfd = &desc->ctx.epollfd; 195 | 196 | for (;;) { 197 | (void)pthread_mutex_lock(&desc->mutex); 198 | ec = epollfd_ctx_wait(epollfd, kq, ev, cnt, actual_cnt); 199 | (void)pthread_mutex_unlock(&desc->mutex); 200 | if (ec != 0) { 201 | return ec; 202 | } 203 | 204 | if (*actual_cnt || 205 | (timeout && timeout->tv_sec == 0 && 206 | timeout->tv_nsec == 0)) { 207 | return 0; 208 | } 209 | 210 | (void)pthread_mutex_lock(&desc->mutex); 211 | 212 | nfds_t nfds = (nfds_t)(1 + epollfd->poll_fds_size); 213 | 214 | size_t size; 215 | if (__builtin_mul_overflow(nfds, sizeof(struct pollfd), 216 | &size)) { 217 | ec = ENOMEM; 218 | (void)pthread_mutex_unlock(&desc->mutex); 219 | return ec; 220 | } 221 | 222 | struct pollfd *pfds = malloc(size); 223 | if (!pfds) { 224 | ec = errno; 225 | (void)pthread_mutex_unlock(&desc->mutex); 226 | return ec; 227 | } 228 | 229 | epollfd_ctx_fill_pollfds(epollfd, kq, pfds); 230 | 231 | (void)pthread_mutex_lock(&epollfd->nr_polling_threads_mutex); 232 | ++epollfd->nr_polling_threads; 233 | (void)pthread_mutex_unlock(&epollfd->nr_polling_threads_mutex); 234 | 235 | (void)pthread_mutex_unlock(&desc->mutex); 236 | 237 | /* 238 | * This surfaced a race condition when 239 | * registering/unregistering poll-only fds. The tests should 240 | * still succeed if this is enabled. 241 | */ 242 | #if 0 243 | usleep(500000); 244 | #endif 245 | 246 | int n = real_ppoll(pfds, nfds, timeout, sigs); 247 | if (n < 0) { 248 | ec = errno; 249 | } 250 | 251 | free(pfds); 252 | 253 | (void)pthread_mutex_lock(&epollfd->nr_polling_threads_mutex); 254 | --epollfd->nr_polling_threads; 255 | if (epollfd->nr_polling_threads == 0) { 256 | (void)pthread_cond_signal( 257 | &epollfd->nr_polling_threads_cond); 258 | } 259 | (void)pthread_mutex_unlock(&epollfd->nr_polling_threads_mutex); 260 | 261 | if (n < 0) { 262 | return ec; 263 | } 264 | 265 | if (timeout) { 266 | struct timespec current_time; 267 | 268 | if (clock_gettime(CLOCK_MONOTONIC, /**/ 269 | ¤t_time) < 0) { 270 | return errno; 271 | } 272 | 273 | timespecsub(deadline, ¤t_time, timeout); 274 | if (timeout->tv_sec < 0) { 275 | timeout->tv_sec = 0; 276 | timeout->tv_nsec = 0; 277 | } 278 | } 279 | } 280 | } 281 | 282 | static errno_t 283 | timeout_to_deadline(struct timespec *deadline, struct timespec *timeout, int to) 284 | { 285 | assert(to >= 0); 286 | 287 | if (to == 0) { 288 | *deadline = *timeout = (struct timespec) { 0, 0 }; 289 | } else if (to > 0) { 290 | if (clock_gettime(CLOCK_MONOTONIC, deadline) < 0) { 291 | return errno; 292 | } 293 | *timeout = (struct timespec) { 294 | .tv_sec = to / 1000, 295 | .tv_nsec = (to % 1000) * 1000000L, 296 | }; 297 | if (!timespecadd_safe(deadline, timeout, deadline)) { 298 | return EINVAL; 299 | } 300 | } 301 | 302 | return 0; 303 | } 304 | 305 | static errno_t 306 | epoll_pwait_impl(int fd, struct epoll_event *ev, int cnt, int to, 307 | sigset_t const *sigs, int *actual_cnt) 308 | { 309 | errno_t ec; 310 | 311 | if (cnt < 1 || cnt > (int)(INT_MAX / sizeof(struct epoll_event))) { 312 | return EINVAL; 313 | } 314 | 315 | EpollShimCtx *epoll_shim_ctx; 316 | if ((ec = epoll_shim_ctx_global(&epoll_shim_ctx)) != 0) { 317 | return ec; 318 | } 319 | 320 | FileDescription *desc = epoll_shim_ctx_find_desc(epoll_shim_ctx, fd); 321 | if (!desc || desc->vtable != &epollfd_vtable) { 322 | struct stat sb; 323 | ec = (fd < 0 || fstat(fd, &sb) < 0) ? EBADF : EINVAL; 324 | goto out; 325 | } 326 | 327 | struct timespec deadline; 328 | struct timespec timeout; 329 | if (to >= 0 && 330 | (ec = timeout_to_deadline(&deadline, &timeout, to)) != 0) { 331 | goto out; 332 | } 333 | 334 | ec = epollfd_ctx_wait_or_block(desc, fd, ev, cnt, actual_cnt, /**/ 335 | (to >= 0) ? &deadline : NULL, /**/ 336 | (to >= 0) ? &timeout : NULL, /**/ 337 | sigs); 338 | 339 | out: 340 | if (desc) { 341 | (void)file_description_unref(&desc); 342 | } 343 | return ec; 344 | } 345 | 346 | EPOLL_SHIM_EXPORT 347 | int 348 | epoll_pwait(int fd, struct epoll_event *ev, int cnt, int to, 349 | sigset_t const *sigs) 350 | { 351 | ERRNO_SAVE; 352 | errno_t ec; 353 | 354 | int actual_cnt; 355 | ec = epoll_pwait_impl(fd, ev, cnt, to, sigs, &actual_cnt); 356 | 357 | ERRNO_RETURN(ec, -1, actual_cnt); 358 | } 359 | 360 | EPOLL_SHIM_EXPORT 361 | int 362 | epoll_wait(int fd, struct epoll_event *ev, int cnt, int to) 363 | { 364 | return epoll_pwait(fd, ev, cnt, to, NULL); 365 | } 366 | -------------------------------------------------------------------------------- /src/epoll_shim_ctx.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLL_SHIM_CTX_H_ 2 | #define EPOLL_SHIM_CTX_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "epollfd_ctx.h" 12 | #include "eventfd_ctx.h" 13 | #include "signalfd_ctx.h" 14 | #include "timerfd_ctx.h" 15 | 16 | #include "rwlock.h" 17 | 18 | struct file_description_vtable; 19 | typedef struct { 20 | atomic_int refcount; 21 | pthread_mutex_t mutex; 22 | int flags; /* Only for O_NONBLOCK right now. */ 23 | union { 24 | EpollFDCtx epollfd; 25 | EventFDCtx eventfd; 26 | TimerFDCtx timerfd; 27 | SignalFDCtx signalfd; 28 | } ctx; 29 | struct file_description_vtable const *vtable; 30 | } FileDescription; 31 | 32 | errno_t file_description_unref(FileDescription **desc); 33 | 34 | typedef errno_t (*fd_context_read_fun)(FileDescription *desc, int kq, /**/ 35 | void *buf, size_t nbytes, size_t *bytes_transferred); 36 | typedef errno_t (*fd_context_write_fun)(FileDescription *desc, int kq, /**/ 37 | void const *buf, size_t nbytes, size_t *bytes_transferred); 38 | typedef errno_t (*fd_context_close_fun)(FileDescription *desc); 39 | typedef void (*fd_context_poll_fun)(FileDescription *desc, int kq, /**/ 40 | uint32_t *revents); 41 | typedef void (*fd_context_realtime_change_fun)(FileDescription *desc, int kq); 42 | 43 | struct file_description_vtable { 44 | fd_context_read_fun read_fun; 45 | fd_context_write_fun write_fun; 46 | fd_context_close_fun close_fun; 47 | fd_context_poll_fun poll_fun; 48 | fd_context_realtime_change_fun realtime_change_fun; 49 | }; 50 | 51 | errno_t fd_context_default_read(FileDescription *desc, int kq, /**/ 52 | void *buf, size_t nbytes, size_t *bytes_transferred); 53 | errno_t fd_context_default_write(FileDescription *desc, int kq, /**/ 54 | void const *buf, size_t nbytes, size_t *bytes_transferred); 55 | PollableDesc fd_as_pollable_desc(FileDescription *desc); 56 | 57 | /**/ 58 | 59 | typedef struct epoll_shim_ctx EpollShimCtx; 60 | 61 | errno_t epoll_shim_ctx_global(EpollShimCtx **epoll_shim_ctx); 62 | 63 | errno_t epoll_shim_ctx_create_desc(EpollShimCtx *epoll_shim_ctx, int flags, 64 | int *fd, FileDescription **desc); 65 | void epoll_shim_ctx_install_desc(EpollShimCtx *epoll_shim_ctx, /**/ 66 | int fd, FileDescription *desc); 67 | FileDescription *epoll_shim_ctx_find_desc(EpollShimCtx *epoll_shim_ctx, int fd); 68 | void epoll_shim_ctx_drop_desc(EpollShimCtx *epoll_shim_ctx, /**/ 69 | int fd, FileDescription *desc); 70 | 71 | void 72 | epoll_shim_ctx_update_realtime_change_monitoring(EpollShimCtx *epoll_shim_ctx, 73 | int change); 74 | 75 | /**/ 76 | 77 | int epoll_shim_close(int fd); 78 | ssize_t epoll_shim_read(int fd, void *buf, size_t nbytes); 79 | ssize_t epoll_shim_write(int fd, void const *buf, size_t nbytes); 80 | 81 | int epoll_shim_poll(struct pollfd *, nfds_t, int); 82 | int epoll_shim_ppoll(struct pollfd *, nfds_t, struct timespec const *, 83 | sigset_t const *); 84 | 85 | int epoll_shim_fcntl(int fd, int cmd, ...); 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /src/epoll_shim_interpose.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "epoll_shim_interpose_export.h" 9 | 10 | int epoll_shim_close(int fd); 11 | ssize_t epoll_shim_read(int fd, void *buf, size_t nbytes); 12 | ssize_t epoll_shim_write(int fd, void const *buf, size_t nbytes); 13 | int epoll_shim_poll(struct pollfd *, nfds_t, int); 14 | int epoll_shim_ppoll(struct pollfd *, nfds_t, struct timespec const *, 15 | sigset_t const *); 16 | int epoll_shim_fcntl(int fd, int cmd, ...); 17 | 18 | EPOLL_SHIM_INTERPOSE_EXPORT 19 | ssize_t 20 | read(int fd, void *buf, size_t nbytes) 21 | { 22 | return epoll_shim_read(fd, buf, nbytes); 23 | } 24 | 25 | EPOLL_SHIM_INTERPOSE_EXPORT 26 | ssize_t 27 | write(int fd, void const *buf, size_t nbytes) 28 | { 29 | return epoll_shim_write(fd, buf, nbytes); 30 | } 31 | 32 | EPOLL_SHIM_INTERPOSE_EXPORT 33 | int 34 | close(int fd) 35 | { 36 | return epoll_shim_close(fd); 37 | } 38 | 39 | EPOLL_SHIM_INTERPOSE_EXPORT 40 | int 41 | poll(struct pollfd fds[], nfds_t nfds, int timeout) 42 | { 43 | return epoll_shim_poll(fds, nfds, timeout); 44 | } 45 | 46 | #if !defined(__APPLE__) 47 | EPOLL_SHIM_INTERPOSE_EXPORT 48 | int 49 | #ifdef __NetBSD__ 50 | __pollts50 51 | #else 52 | ppoll 53 | #endif 54 | (struct pollfd fds[], nfds_t nfds, struct timespec const *restrict timeout, 55 | sigset_t const *restrict newsigmask) 56 | { 57 | return epoll_shim_ppoll(fds, nfds, timeout, newsigmask); 58 | } 59 | #endif 60 | 61 | EPOLL_SHIM_INTERPOSE_EXPORT 62 | int 63 | fcntl(int fd, int cmd, ...) 64 | { 65 | va_list ap; 66 | 67 | va_start(ap, cmd); 68 | void *arg = va_arg(ap, void *); 69 | int rv = epoll_shim_fcntl(fd, cmd, arg); 70 | va_end(ap); 71 | 72 | return rv; 73 | } 74 | -------------------------------------------------------------------------------- /src/epollfd_ctx.h: -------------------------------------------------------------------------------- 1 | #ifndef EPOLLFD_CTX_H_ 2 | #define EPOLLFD_CTX_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "pollable_desc.h" 17 | 18 | struct registered_fds_node_; 19 | typedef struct registered_fds_node_ RegisteredFDsNode; 20 | 21 | typedef enum { 22 | EOF_STATE_READ_EOF = 0x01, 23 | EOF_STATE_WRITE_EOF = 0x02, 24 | } EOFState; 25 | 26 | typedef enum { 27 | NODE_TYPE_FIFO = 1, 28 | NODE_TYPE_SOCKET = 2, 29 | NODE_TYPE_KQUEUE = 3, 30 | NODE_TYPE_OTHER = 4, 31 | NODE_TYPE_POLL = 5, 32 | } NodeType; 33 | 34 | struct registered_fds_node_ { 35 | RB_ENTRY(registered_fds_node_) entry; 36 | TAILQ_ENTRY(registered_fds_node_) pollfd_list_entry; 37 | 38 | int fd; 39 | epoll_data_t data; 40 | 41 | bool is_registered; 42 | 43 | bool has_evfilt_read; 44 | bool has_evfilt_write; 45 | bool has_evfilt_except; 46 | 47 | bool got_evfilt_read; 48 | bool got_evfilt_write; 49 | bool got_evfilt_except; 50 | 51 | NodeType node_type; 52 | union { 53 | struct { 54 | bool readable; 55 | bool writable; 56 | } fifo; 57 | struct { 58 | PollableDesc pollable_desc; 59 | } kqueue; 60 | } node_data; 61 | int eof_state; 62 | bool pollpri_active; 63 | 64 | uint16_t events; 65 | bool needs_rdhup_translation; 66 | uint32_t revents; 67 | 68 | bool is_edge_triggered; 69 | bool is_oneshot; 70 | 71 | bool is_on_pollfd_list; 72 | int self_pipe[2]; 73 | }; 74 | 75 | typedef TAILQ_HEAD(pollfds_list_, registered_fds_node_) PollFDList; 76 | typedef RB_HEAD(registered_fds_set_, registered_fds_node_) RegisteredFDsSet; 77 | 78 | typedef struct { 79 | PollFDList poll_fds; 80 | size_t poll_fds_size; 81 | 82 | RegisteredFDsSet registered_fds; 83 | size_t registered_fds_size; 84 | 85 | struct kevent *kevs; 86 | size_t kevs_length; 87 | 88 | struct pollfd *pfds; 89 | size_t pfds_length; 90 | 91 | pthread_mutex_t nr_polling_threads_mutex; 92 | pthread_cond_t nr_polling_threads_cond; 93 | unsigned long nr_polling_threads; 94 | 95 | int self_pipe[2]; 96 | } EpollFDCtx; 97 | 98 | errno_t epollfd_ctx_init(EpollFDCtx *epollfd); 99 | errno_t epollfd_ctx_terminate(EpollFDCtx *epollfd); 100 | 101 | void epollfd_ctx_fill_pollfds(EpollFDCtx *epollfd, int kq, struct pollfd *pfds); 102 | 103 | // Called on fd2 close(). 104 | void epollfd_ctx_remove_fd(EpollFDCtx *epollfd, int kq, int fd2); 105 | 106 | errno_t epollfd_ctx_ctl(EpollFDCtx *epollfd, int kq, /**/ 107 | int op, int fd2, PollableDesc pollable_desc, struct epoll_event *ev); 108 | errno_t epollfd_ctx_wait(EpollFDCtx *epollfd, int kq, /**/ 109 | struct epoll_event *ev, int cnt, int *actual_cnt); 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /src/errno_return.h: -------------------------------------------------------------------------------- 1 | #ifndef ERRNO_RETURN_H_ 2 | #define ERRNO_RETURN_H_ 3 | 4 | #define ERRNO_SAVE int const oe = errno 5 | 6 | #define ERRNO_RETURN(ec, rc_on_error, rc_on_success) \ 7 | if ((ec) != 0) { \ 8 | errno = (ec); \ 9 | return (rc_on_error); \ 10 | } \ 11 | errno = oe; \ 12 | return (rc_on_success) 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/eventfd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "wrap.h" 18 | 19 | #include "epoll_shim_ctx.h" 20 | #include "epoll_shim_export.h" 21 | 22 | static errno_t 23 | eventfd_ctx_read_or_block(FileDescription *desc, int kq, uint64_t *value) 24 | { 25 | errno_t ec; 26 | EventFDCtx *eventfd_ctx = &desc->ctx.eventfd; 27 | 28 | for (;;) { 29 | (void)pthread_mutex_lock(&desc->mutex); 30 | ec = eventfd_ctx_read(eventfd_ctx, kq, value); 31 | bool nonblock = (desc->flags & O_NONBLOCK) != 0; 32 | (void)pthread_mutex_unlock(&desc->mutex); 33 | 34 | if (nonblock || ec != EAGAIN) { 35 | return ec; 36 | } 37 | 38 | struct pollfd pfd = { 39 | .fd = kq, 40 | .events = POLLIN, 41 | }; 42 | if (real_poll(&pfd, 1, -1) < 0) { 43 | return errno; 44 | } 45 | } 46 | } 47 | 48 | static errno_t 49 | eventfd_helper_read(FileDescription *desc, int kq, /**/ 50 | void *buf, size_t nbytes, size_t *bytes_transferred) 51 | { 52 | errno_t ec; 53 | 54 | if (nbytes != sizeof(uint64_t)) { 55 | return EINVAL; 56 | } 57 | 58 | uint64_t value; 59 | if ((ec = eventfd_ctx_read_or_block(desc, kq, &value)) != 0) { 60 | return ec; 61 | } 62 | 63 | memcpy(buf, &value, sizeof(value)); 64 | *bytes_transferred = sizeof(value); 65 | return 0; 66 | } 67 | 68 | static errno_t 69 | eventfd_helper_write(FileDescription *desc, int kq, /**/ 70 | void const *buf, size_t nbytes, size_t *bytes_transferred) 71 | { 72 | errno_t ec; 73 | 74 | if (nbytes != sizeof(uint64_t)) { 75 | return EINVAL; 76 | } 77 | 78 | uint64_t value; 79 | memcpy(&value, buf, sizeof(uint64_t)); 80 | 81 | (void)pthread_mutex_lock(&desc->mutex); 82 | ec = eventfd_ctx_write(&desc->ctx.eventfd, kq, value); 83 | (void)pthread_mutex_unlock(&desc->mutex); 84 | if (ec != 0) { 85 | return ec; 86 | } 87 | 88 | *bytes_transferred = sizeof(value); 89 | return 0; 90 | } 91 | 92 | static errno_t 93 | eventfd_close(FileDescription *desc) 94 | { 95 | return eventfd_ctx_terminate(&desc->ctx.eventfd); 96 | } 97 | 98 | static struct file_description_vtable const eventfd_vtable = { 99 | .read_fun = eventfd_helper_read, 100 | .write_fun = eventfd_helper_write, 101 | .close_fun = eventfd_close, 102 | }; 103 | 104 | static errno_t 105 | eventfd_impl(int *fd_out, unsigned int initval, int flags) 106 | { 107 | errno_t ec; 108 | 109 | if (flags & ~(EFD_SEMAPHORE | EFD_CLOEXEC | EFD_NONBLOCK)) { 110 | return EINVAL; 111 | } 112 | 113 | _Static_assert(EFD_CLOEXEC == O_CLOEXEC, ""); 114 | _Static_assert(EFD_NONBLOCK == O_NONBLOCK, ""); 115 | 116 | EpollShimCtx *epoll_shim_ctx; 117 | if ((ec = epoll_shim_ctx_global(&epoll_shim_ctx)) != 0) { 118 | return ec; 119 | } 120 | 121 | int fd; 122 | FileDescription *desc; 123 | ec = epoll_shim_ctx_create_desc(epoll_shim_ctx, 124 | flags & (O_CLOEXEC | O_NONBLOCK), &fd, &desc); 125 | if (ec != 0) { 126 | return ec; 127 | } 128 | 129 | desc->flags = flags & O_NONBLOCK; 130 | 131 | int ctx_flags = 0; 132 | if (flags & EFD_SEMAPHORE) { 133 | ctx_flags |= EVENTFD_CTX_FLAG_SEMAPHORE; 134 | } 135 | 136 | if ((ec = eventfd_ctx_init(&desc->ctx.eventfd, fd, initval, 137 | ctx_flags)) != 0) { 138 | goto fail; 139 | } 140 | 141 | desc->vtable = &eventfd_vtable; 142 | epoll_shim_ctx_install_desc(epoll_shim_ctx, fd, desc); 143 | 144 | *fd_out = fd; 145 | return 0; 146 | 147 | fail: 148 | epoll_shim_ctx_drop_desc(epoll_shim_ctx, fd, desc); 149 | return ec; 150 | } 151 | 152 | EPOLL_SHIM_EXPORT 153 | int 154 | eventfd(unsigned int initval, int flags) 155 | { 156 | errno_t ec; 157 | 158 | int fd; 159 | ec = eventfd_impl(&fd, initval, flags); 160 | if (ec != 0) { 161 | errno = ec; 162 | return -1; 163 | } 164 | 165 | return fd; 166 | } 167 | 168 | EPOLL_SHIM_EXPORT 169 | int 170 | eventfd_read(int fd, eventfd_t *value) 171 | { 172 | return (epoll_shim_read(fd, /**/ 173 | value, sizeof(*value)) == (ssize_t)sizeof(*value)) ? 174 | 0 : 175 | -1; 176 | } 177 | 178 | EPOLL_SHIM_EXPORT 179 | int 180 | eventfd_write(int fd, eventfd_t value) 181 | { 182 | return (epoll_shim_write(fd, /**/ 183 | &value, sizeof(value)) == (ssize_t)sizeof(value)) ? 184 | 0 : 185 | -1; 186 | } 187 | -------------------------------------------------------------------------------- /src/eventfd_ctx.c: -------------------------------------------------------------------------------- 1 | #include "eventfd_ctx.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | _Static_assert(sizeof(unsigned int) < sizeof(uint64_t), ""); 15 | 16 | errno_t 17 | eventfd_ctx_init(EventFDCtx *eventfd, int kq, unsigned int counter, int flags) 18 | { 19 | errno_t ec; 20 | 21 | assert((flags & ~(EVENTFD_CTX_FLAG_SEMAPHORE)) == 0); 22 | 23 | *eventfd = (EventFDCtx) { 24 | .flags_ = flags, 25 | .counter_ = counter, 26 | }; 27 | 28 | struct kevent kevs[2]; 29 | int kevs_length = 0; 30 | 31 | if ((ec = kqueue_event_init(&eventfd->kqueue_event_, /**/ 32 | kevs, &kevs_length, counter > 0)) != 0) { 33 | goto out2; 34 | } 35 | 36 | if (kevent(kq, kevs, kevs_length, NULL, 0, NULL) < 0) { 37 | ec = errno; 38 | goto out; 39 | } 40 | 41 | return 0; 42 | 43 | out: 44 | (void)kqueue_event_terminate(&eventfd->kqueue_event_); 45 | out2: 46 | return ec; 47 | } 48 | 49 | errno_t 50 | eventfd_ctx_terminate(EventFDCtx *eventfd) 51 | { 52 | errno_t ec = 0; 53 | errno_t ec_local; 54 | 55 | ec_local = kqueue_event_terminate(&eventfd->kqueue_event_); 56 | ec = ec != 0 ? ec : ec_local; 57 | 58 | return (ec); 59 | } 60 | 61 | errno_t 62 | eventfd_ctx_write(EventFDCtx *eventfd, int kq, uint64_t value) 63 | { 64 | errno_t ec; 65 | 66 | if (value == UINT64_MAX) { 67 | return EINVAL; 68 | } 69 | 70 | uint_least64_t current_value = eventfd->counter_; 71 | 72 | uint_least64_t new_value; 73 | if (__builtin_add_overflow(current_value, value, &new_value) || 74 | new_value > UINT64_MAX - 1) { 75 | return EAGAIN; 76 | } 77 | 78 | eventfd->counter_ = new_value; 79 | 80 | ec = kqueue_event_trigger(&eventfd->kqueue_event_, kq); 81 | if (ec != 0) { 82 | return ec; 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | errno_t 89 | eventfd_ctx_read(EventFDCtx *eventfd, int kq, uint64_t *value) 90 | { 91 | uint_least64_t current_value; 92 | 93 | current_value = eventfd->counter_; 94 | if (current_value == 0) { 95 | return EAGAIN; 96 | } 97 | 98 | uint_least64_t new_value = /**/ 99 | (eventfd->flags_ & EVENTFD_CTX_FLAG_SEMAPHORE) ? /**/ 100 | current_value - 1 : 101 | 0; 102 | 103 | if (new_value == 0 && 104 | kqueue_event_is_triggered(&eventfd->kqueue_event_)) { 105 | kqueue_event_clear(&eventfd->kqueue_event_, kq); 106 | } 107 | 108 | eventfd->counter_ = new_value; 109 | 110 | *value = /**/ 111 | (eventfd->flags_ & EVENTFD_CTX_FLAG_SEMAPHORE) ? /**/ 112 | 1 : 113 | current_value; 114 | return 0; 115 | } 116 | -------------------------------------------------------------------------------- /src/eventfd_ctx.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTFD_CTX_H_ 2 | #define EVENTFD_CTX_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "kqueue_event.h" 11 | 12 | #define EVENTFD_CTX_FLAG_SEMAPHORE (1 << 0) 13 | 14 | typedef struct { 15 | int flags_; 16 | 17 | KQueueEvent kqueue_event_; 18 | uint_least64_t counter_; 19 | } EventFDCtx; 20 | 21 | errno_t eventfd_ctx_init(EventFDCtx *eventfd, int kq, unsigned int counter, 22 | int flags); 23 | errno_t eventfd_ctx_terminate(EventFDCtx *eventfd); 24 | 25 | errno_t eventfd_ctx_write(EventFDCtx *eventfd, int kq, uint64_t value); 26 | errno_t eventfd_ctx_read(EventFDCtx *eventfd, int kq, uint64_t *value); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/kqueue_event.c: -------------------------------------------------------------------------------- 1 | #include "kqueue_event.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "wrap.h" 12 | 13 | errno_t 14 | kqueue_event_init(KQueueEvent *kqueue_event, struct kevent *kevs, 15 | int *kevs_length, bool should_trigger) 16 | { 17 | *kqueue_event = (KQueueEvent) { .self_pipe_ = { -1, -1 } }; 18 | 19 | #ifdef EVFILT_USER 20 | EV_SET(&kevs[(*kevs_length)++], 0, /**/ 21 | EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0); 22 | 23 | if (should_trigger) { 24 | EV_SET(&kevs[(*kevs_length)++], 0, /**/ 25 | EVFILT_USER, 0, NOTE_TRIGGER, 0, 0); 26 | kqueue_event->is_triggered_ = true; 27 | } 28 | 29 | return 0; 30 | #else 31 | errno_t ec; 32 | 33 | if (pipe2(kqueue_event->self_pipe_, O_NONBLOCK | O_CLOEXEC) < 0) { 34 | return errno; 35 | } 36 | 37 | EV_SET(&kevs[(*kevs_length)++], kqueue_event->self_pipe_[0], /**/ 38 | EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0); 39 | if (should_trigger) { 40 | if ((ec = kqueue_event_trigger(kqueue_event, -1)) != 0) { 41 | goto out; 42 | } 43 | assert(kqueue_event->is_triggered_); 44 | } 45 | 46 | return 0; 47 | 48 | out: 49 | (void)kqueue_event_terminate(kqueue_event); 50 | return ec; 51 | #endif 52 | } 53 | 54 | errno_t 55 | kqueue_event_terminate(KQueueEvent *kqueue_event) 56 | { 57 | #ifdef EVFILT_USER 58 | (void)kqueue_event; 59 | return 0; 60 | #else 61 | errno_t ec = 0; 62 | if (real_close(kqueue_event->self_pipe_[0]) < 0) { 63 | ec = ec != 0 ? ec : errno; 64 | } 65 | if (real_close(kqueue_event->self_pipe_[1]) < 0) { 66 | ec = ec != 0 ? ec : errno; 67 | } 68 | return ec; 69 | #endif 70 | } 71 | 72 | bool 73 | kqueue_event_is_triggered(KQueueEvent *kqueue_event) 74 | { 75 | return kqueue_event->is_triggered_; 76 | } 77 | 78 | errno_t 79 | kqueue_event_trigger(KQueueEvent *kqueue_event, int kq) 80 | { 81 | if (kqueue_event->is_triggered_) { 82 | return 0; 83 | } 84 | 85 | #ifdef EVFILT_USER 86 | struct kevent kevs[1]; 87 | EV_SET(&kevs[0], 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0); 88 | 89 | if (kevent(kq, kevs, 1, NULL, 0, NULL) < 0) { 90 | return errno; 91 | } 92 | #else 93 | char c = 0; 94 | if (real_write(kqueue_event->self_pipe_[1], &c, 1) < 0) { 95 | if (errno != EAGAIN && errno != EWOULDBLOCK) { 96 | return errno; 97 | } 98 | } 99 | #endif 100 | 101 | kqueue_event->is_triggered_ = true; 102 | return 0; 103 | } 104 | 105 | void 106 | kqueue_event_clear(KQueueEvent *kqueue_event, int kq) 107 | { 108 | #ifndef EVFILT_USER 109 | char c[32]; 110 | while (real_read(kqueue_event->self_pipe_[0], c, sizeof(c)) >= 0) { 111 | } 112 | #endif 113 | 114 | struct kevent kevs[32]; 115 | int n; 116 | 117 | while ((n = kevent(kq, NULL, 0, kevs, 32, 118 | &(struct timespec) { 0, 0 })) > 0) { 119 | } 120 | 121 | kqueue_event->is_triggered_ = false; 122 | } 123 | -------------------------------------------------------------------------------- /src/kqueue_event.h: -------------------------------------------------------------------------------- 1 | #ifndef KQUEUE_EVENT_H 2 | #define KQUEUE_EVENT_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | typedef struct { 13 | bool is_triggered_; 14 | int self_pipe_[2]; /* only used if EVFILT_USER is not available */ 15 | } KQueueEvent; 16 | 17 | errno_t kqueue_event_init(KQueueEvent *kqueue_event, struct kevent *kevs, 18 | int *kevs_length, bool should_trigger); 19 | errno_t kqueue_event_terminate(KQueueEvent *kqueue_event); 20 | 21 | bool kqueue_event_is_triggered(KQueueEvent *kqueue_event); 22 | 23 | errno_t kqueue_event_trigger(KQueueEvent *kqueue_event, int kq); 24 | void kqueue_event_clear(KQueueEvent *kqueue_event, int kq); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/pollable_desc.h: -------------------------------------------------------------------------------- 1 | #ifndef POLLABLE_DESC_H 2 | #define POLLABLE_DESC_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | struct pollable_desc_vtable; 11 | typedef struct { 12 | void *ptr; 13 | struct pollable_desc_vtable const *vtable; 14 | } PollableDesc; 15 | 16 | typedef void (*pollable_desc_poll_t)(void *pollable_desc, /**/ 17 | int fd, uint32_t *revents); 18 | typedef void (*pollable_desc_ref_t)(void *pollable_desc); 19 | typedef void (*pollable_desc_unref_t)(void *pollable_desc); 20 | 21 | struct pollable_desc_vtable { 22 | pollable_desc_poll_t poll_fun; 23 | pollable_desc_ref_t ref_fun; 24 | pollable_desc_unref_t unref_fun; 25 | }; 26 | 27 | static inline void 28 | pollable_desc_poll(PollableDesc pollable_desc, int fd, uint32_t *revents) 29 | { 30 | if (pollable_desc.ptr == NULL) { 31 | return; 32 | } 33 | 34 | assert(pollable_desc.vtable != NULL); 35 | assert(pollable_desc.vtable->poll_fun != NULL); 36 | 37 | pollable_desc.vtable->poll_fun(pollable_desc.ptr, fd, revents); 38 | } 39 | 40 | static inline void 41 | pollable_desc_ref(PollableDesc pollable_desc) 42 | { 43 | if (pollable_desc.ptr == NULL) { 44 | return; 45 | } 46 | 47 | assert(pollable_desc.vtable != NULL); 48 | assert(pollable_desc.vtable->ref_fun != NULL); 49 | 50 | pollable_desc.vtable->ref_fun(pollable_desc.ptr); 51 | } 52 | 53 | static inline void 54 | pollable_desc_unref(PollableDesc pollable_desc) 55 | { 56 | if (pollable_desc.ptr == NULL) { 57 | return; 58 | } 59 | 60 | assert(pollable_desc.vtable != NULL); 61 | assert(pollable_desc.vtable->unref_fun != NULL); 62 | 63 | pollable_desc.vtable->unref_fun(pollable_desc.ptr); 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/rwlock.c: -------------------------------------------------------------------------------- 1 | #include "rwlock.h" 2 | 3 | #include 4 | 5 | /* 6 | * Inspired by: 7 | * 8 | */ 9 | 10 | #define MAX_READERS (1 << 30) 11 | 12 | static int 13 | sem_wait_nointr(sem_t *sem) 14 | { 15 | int rc; 16 | int cs; 17 | (void)pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); 18 | 19 | do { 20 | rc = sem_wait(sem); 21 | } while (rc < 0 && errno == EINTR); 22 | 23 | (void)pthread_setcancelstate(cs, NULL); 24 | 25 | return rc; 26 | } 27 | 28 | errno_t 29 | rwlock_init(RWLock *rwlock) 30 | { 31 | errno_t ec; 32 | 33 | *rwlock = (RWLock) {}; 34 | 35 | if ((ec = pthread_mutex_init(&rwlock->mutex, NULL)) != 0) { 36 | goto out_mutex; 37 | } 38 | 39 | if (sem_init(&rwlock->writer_wait, 0, 0) < 0) { 40 | ec = errno; 41 | goto out_writer_wait; 42 | } 43 | 44 | if (sem_init(&rwlock->reader_wait, 0, 0) < 0) { 45 | ec = errno; 46 | goto out_reader_wait; 47 | } 48 | 49 | return 0; 50 | 51 | (void)sem_destroy(&rwlock->reader_wait); 52 | out_reader_wait: 53 | (void)sem_destroy(&rwlock->writer_wait); 54 | out_writer_wait: 55 | (void)pthread_mutex_destroy(&rwlock->mutex); 56 | out_mutex: 57 | return ec; 58 | } 59 | 60 | void 61 | rwlock_terminate(RWLock *rwlock) 62 | { 63 | (void)sem_destroy(&rwlock->reader_wait); 64 | (void)sem_destroy(&rwlock->writer_wait); 65 | (void)pthread_mutex_destroy(&rwlock->mutex); 66 | } 67 | 68 | void 69 | rwlock_lock_read(RWLock *rwlock) 70 | { 71 | if (atomic_fetch_add(&rwlock->num_pending, 1) + 1 < 0) { 72 | (void)sem_wait_nointr(&rwlock->reader_wait); 73 | } 74 | } 75 | 76 | void 77 | rwlock_unlock_read(RWLock *rwlock) 78 | { 79 | if (atomic_fetch_sub(&rwlock->num_pending, 1) - 1 < 0) { 80 | if (atomic_fetch_sub(&rwlock->readers_departing, 1) - 1 == 0) { 81 | (void)sem_post(&rwlock->writer_wait); 82 | } 83 | } 84 | } 85 | 86 | void 87 | rwlock_lock_write(RWLock *rwlock) 88 | { 89 | (void)pthread_mutex_lock(&rwlock->mutex); 90 | int_fast32_t r = atomic_fetch_sub(&rwlock->num_pending, MAX_READERS); 91 | if (r != 0 && 92 | atomic_fetch_add(&rwlock->readers_departing, r) + r != 0) { 93 | (void)sem_wait_nointr(&rwlock->writer_wait); 94 | } 95 | } 96 | 97 | static inline void 98 | rwlock_unlock_common(RWLock *rwlock, int keep_reading) 99 | { 100 | int_fast32_t r = atomic_fetch_add(&rwlock->num_pending, 101 | MAX_READERS + keep_reading) + 102 | MAX_READERS + keep_reading; 103 | for (int_fast32_t i = keep_reading; i < r; ++i) { 104 | (void)sem_post(&rwlock->reader_wait); 105 | } 106 | (void)pthread_mutex_unlock(&rwlock->mutex); 107 | } 108 | 109 | void 110 | rwlock_unlock_write(RWLock *rwlock) 111 | { 112 | rwlock_unlock_common(rwlock, 0); 113 | } 114 | 115 | void 116 | rwlock_downgrade(RWLock *rwlock) 117 | { 118 | rwlock_unlock_common(rwlock, 1); 119 | } 120 | -------------------------------------------------------------------------------- /src/rwlock.h: -------------------------------------------------------------------------------- 1 | #ifndef RWLOCK_H_ 2 | #define RWLOCK_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | typedef struct { 14 | pthread_mutex_t mutex; 15 | sem_t writer_wait; 16 | sem_t reader_wait; 17 | atomic_int_fast32_t num_pending; 18 | atomic_int_fast32_t readers_departing; 19 | } RWLock; 20 | 21 | errno_t rwlock_init(RWLock *rwlock); 22 | void rwlock_terminate(RWLock *rwlock); 23 | 24 | void rwlock_lock_read(RWLock *rwlock); 25 | void rwlock_unlock_read(RWLock *rwlock); 26 | void rwlock_lock_write(RWLock *rwlock); 27 | void rwlock_unlock_write(RWLock *rwlock); 28 | void rwlock_downgrade(RWLock *rwlock); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/signalfd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "wrap.h" 20 | 21 | #include "epoll_shim_ctx.h" 22 | #include "epoll_shim_export.h" 23 | #include "errno_return.h" 24 | 25 | static errno_t 26 | signalfd_ctx_read_or_block(FileDescription *desc, int kq, 27 | SignalFDCtxSiginfo *siginfo, bool force_nonblock) 28 | { 29 | errno_t ec; 30 | SignalFDCtx *signalfd_ctx = &desc->ctx.signalfd; 31 | 32 | for (;;) { 33 | (void)pthread_mutex_lock(&desc->mutex); 34 | ec = signalfd_ctx_read(signalfd_ctx, kq, siginfo); 35 | bool nonblock = force_nonblock || 36 | (desc->flags & O_NONBLOCK) != 0; 37 | (void)pthread_mutex_unlock(&desc->mutex); 38 | if (nonblock || (ec != EAGAIN && ec != EWOULDBLOCK)) { 39 | return ec; 40 | } 41 | 42 | struct pollfd pfd = { 43 | .fd = kq, 44 | .events = POLLIN, 45 | }; 46 | if (real_poll(&pfd, 1, -1) < 0) { 47 | return errno; 48 | } 49 | } 50 | } 51 | 52 | static errno_t 53 | signalfd_read(FileDescription *desc, int kq, void *buf, size_t nbytes, 54 | size_t *bytes_transferred) 55 | { 56 | errno_t ec; 57 | 58 | if (nbytes < sizeof(struct signalfd_siginfo)) { 59 | return EINVAL; 60 | } 61 | 62 | bool force_nonblock = false; 63 | size_t bytes_transferred_local = 0; 64 | 65 | while (nbytes >= sizeof(struct signalfd_siginfo)) { 66 | _Static_assert(sizeof(struct signalfd_siginfo) == 67 | sizeof(SignalFDCtxSiginfo), 68 | ""); 69 | 70 | SignalFDCtxSiginfo siginfo; 71 | memset(&siginfo, 0, sizeof(siginfo)); 72 | 73 | if ((ec = signalfd_ctx_read_or_block(desc, kq, &siginfo, 74 | force_nonblock)) != 0) { 75 | break; 76 | } 77 | 78 | memcpy(buf, &siginfo, sizeof(siginfo)); 79 | bytes_transferred_local += sizeof(siginfo); 80 | 81 | force_nonblock = true; 82 | nbytes -= sizeof(siginfo); 83 | buf = ((unsigned char *)buf) + sizeof(siginfo); 84 | } 85 | 86 | if (bytes_transferred_local > 0) { 87 | ec = 0; 88 | } 89 | 90 | *bytes_transferred = bytes_transferred_local; 91 | return ec; 92 | } 93 | 94 | static errno_t 95 | signalfd_close(FileDescription *desc) 96 | { 97 | return signalfd_ctx_terminate(&desc->ctx.signalfd); 98 | } 99 | 100 | static void 101 | signalfd_poll(FileDescription *desc, int kq, uint32_t *revents) 102 | { 103 | (void)pthread_mutex_lock(&desc->mutex); 104 | signalfd_ctx_poll(&desc->ctx.signalfd, kq, revents); 105 | (void)pthread_mutex_unlock(&desc->mutex); 106 | } 107 | 108 | static struct file_description_vtable const signalfd_vtable = { 109 | .read_fun = signalfd_read, 110 | .write_fun = fd_context_default_write, 111 | .close_fun = signalfd_close, 112 | .poll_fun = signalfd_poll, 113 | }; 114 | 115 | static errno_t 116 | signalfd_impl(int *sfd_out, int fd, sigset_t const *sigs, int flags) 117 | { 118 | errno_t ec; 119 | 120 | if (sigs == NULL || (flags & ~(SFD_NONBLOCK | SFD_CLOEXEC))) { 121 | return EINVAL; 122 | } 123 | 124 | if (fd != -1) { 125 | struct stat sb; 126 | return (fd < 0 || fstat(fd, &sb) < 0) ? EBADF : EINVAL; 127 | } 128 | 129 | _Static_assert(SFD_CLOEXEC == O_CLOEXEC, ""); 130 | _Static_assert(SFD_NONBLOCK == O_NONBLOCK, ""); 131 | 132 | EpollShimCtx *epoll_shim_ctx; 133 | if ((ec = epoll_shim_ctx_global(&epoll_shim_ctx)) != 0) { 134 | return ec; 135 | } 136 | 137 | int sfd; 138 | FileDescription *desc; 139 | ec = epoll_shim_ctx_create_desc(epoll_shim_ctx, 140 | flags & (O_CLOEXEC | O_NONBLOCK), &sfd, &desc); 141 | if (ec != 0) { 142 | return ec; 143 | } 144 | 145 | desc->flags = flags & O_NONBLOCK; 146 | 147 | if ((ec = signalfd_ctx_init(&desc->ctx.signalfd, sfd, sigs)) != 0) { 148 | goto fail; 149 | } 150 | 151 | desc->vtable = &signalfd_vtable; 152 | epoll_shim_ctx_install_desc(epoll_shim_ctx, sfd, desc); 153 | 154 | *sfd_out = sfd; 155 | return 0; 156 | 157 | fail: 158 | epoll_shim_ctx_drop_desc(epoll_shim_ctx, sfd, desc); 159 | return ec; 160 | } 161 | 162 | EPOLL_SHIM_EXPORT 163 | int 164 | signalfd(int fd, sigset_t const *sigs, int flags) 165 | { 166 | ERRNO_SAVE; 167 | errno_t ec; 168 | 169 | int sfd_out; 170 | ec = signalfd_impl(&sfd_out, fd, sigs, flags); 171 | 172 | ERRNO_RETURN(ec, -1, sfd_out); 173 | } 174 | -------------------------------------------------------------------------------- /src/signalfd_ctx.c: -------------------------------------------------------------------------------- 1 | #include "signalfd_ctx.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | static errno_t 17 | signalfd_has_pending(SignalFDCtx const *signalfd, bool *has_pending, 18 | sigset_t *pending) 19 | { 20 | sigset_t pending_sigs; 21 | 22 | if (sigpending(&pending_sigs) < 0 || 23 | sigandset(&pending_sigs, &pending_sigs, &signalfd->sigs) < 0) { 24 | return errno; 25 | } 26 | 27 | *has_pending = !sigisemptyset(&pending_sigs); 28 | if (pending) { 29 | *pending = pending_sigs; 30 | } 31 | return 0; 32 | } 33 | 34 | static errno_t 35 | signalfd_ctx_trigger_manually(SignalFDCtx *signalfd, int kq) 36 | { 37 | return kqueue_event_trigger(&signalfd->kqueue_event, kq); 38 | } 39 | 40 | static void 41 | signalfd_signal_handler(int signo) 42 | { 43 | (void)signo; 44 | } 45 | 46 | errno_t 47 | signalfd_ctx_init(SignalFDCtx *signalfd, int kq, sigset_t const *sigs) 48 | { 49 | errno_t ec; 50 | 51 | assert(sigs != NULL); 52 | 53 | *signalfd = (SignalFDCtx) { .sigs = *sigs }; 54 | 55 | #ifndef _SIG_MAXSIG 56 | #define _SIG_MAXSIG (8 * sizeof(sigset_t)) 57 | #endif 58 | 59 | struct kevent kevs[_SIG_MAXSIG + 2]; 60 | int n = 0; 61 | 62 | if ((ec = kqueue_event_init(&signalfd->kqueue_event, kevs, &n, 63 | false)) != 0) { 64 | goto out2; 65 | } 66 | 67 | for (int i = 1; i <= _SIG_MAXSIG; ++i) { 68 | if (sigismember(&signalfd->sigs, i)) { 69 | EV_SET(&kevs[n++], (unsigned int)i, EVFILT_SIGNAL, 70 | EV_ADD, 0, 0, 0); 71 | 72 | /* 73 | * On Linux, signals with disposition SIG_DFL and a 74 | * default action of "ignored" are returned from 75 | * sigwait. 76 | * We can emulate this by registering an empty signal 77 | * handler. 78 | */ 79 | if (i == SIGCHLD || /**/ 80 | i == SIGURG || /**/ 81 | i == SIGCONT || /**/ 82 | #ifdef SIGIO 83 | i == SIGIO || /**/ 84 | #endif 85 | #ifdef SIGWINCH 86 | i == SIGWINCH || /**/ 87 | #endif 88 | #ifdef SIGINFO 89 | i == SIGINFO || /**/ 90 | #endif 91 | #ifdef SIGPWR 92 | i == SIGPWR || /**/ 93 | #endif 94 | #ifdef SIGTHR 95 | i == SIGTHR || /**/ 96 | #endif 97 | false) { 98 | struct sigaction sa; 99 | 100 | if (sigaction(i, NULL, &sa) == 0 && 101 | !(sa.sa_flags & SA_SIGINFO) && 102 | sa.sa_handler == SIG_DFL) { 103 | sa.sa_flags |= SA_RESTART; 104 | sa.sa_handler = signalfd_signal_handler; 105 | (void)sigaction(i, &sa, NULL); 106 | } 107 | } 108 | } 109 | } 110 | 111 | n = kevent(kq, kevs, n, NULL, 0, NULL); 112 | if (n < 0) { 113 | ec = errno; 114 | goto out; 115 | } 116 | 117 | bool has_pending; 118 | if ((ec = signalfd_has_pending(signalfd, &has_pending, NULL)) != 0) { 119 | goto out; 120 | } 121 | if (has_pending) { 122 | if ((ec = signalfd_ctx_trigger_manually(signalfd, kq)) != 0) { 123 | goto out; 124 | } 125 | } 126 | 127 | return 0; 128 | 129 | out: 130 | (void)kqueue_event_terminate(&signalfd->kqueue_event); 131 | out2: 132 | return ec; 133 | } 134 | 135 | errno_t 136 | signalfd_ctx_terminate(SignalFDCtx *signalfd) 137 | { 138 | errno_t ec = 0; 139 | errno_t ec_local; 140 | 141 | ec_local = kqueue_event_terminate(&signalfd->kqueue_event); 142 | ec = ec != 0 ? ec : ec_local; 143 | 144 | return ec; 145 | } 146 | 147 | static errno_t 148 | signalfd_ctx_read_impl(SignalFDCtx *signalfd, 149 | SignalFDCtxSiginfo *signalfd_siginfo) 150 | { 151 | errno_t ec; 152 | 153 | _Static_assert(sizeof(*signalfd_siginfo) == 128, ""); 154 | 155 | /* 156 | * EVFILT_SIGNAL is an "observer". It does not hook into the 157 | * signal disposition mechanism. On the other hand, `signalfd` does. 158 | * Therefore, to properly emulate `signalfd`, `sigtimedwait` must be 159 | * called. 160 | */ 161 | 162 | siginfo_t siginfo; 163 | memset(&siginfo, 0, sizeof(siginfo)); 164 | 165 | #if defined(__OpenBSD__) || defined(__APPLE__) 166 | for (;;) { 167 | bool has_pending; 168 | sigset_t pending_sigs; 169 | if ((ec = signalfd_has_pending(signalfd, &has_pending, 170 | &pending_sigs)) != 0) { 171 | return ec; 172 | } 173 | if (!has_pending) { 174 | return EAGAIN; 175 | } 176 | 177 | #if defined(__OpenBSD__) 178 | /* 179 | * sigwait does not behave nicely when multiple signals 180 | * are pending (as of OpenBSD 6.8). So, only try to 181 | * grab one. 182 | */ 183 | int signum = __builtin_ffsll((long long)pending_sigs); 184 | sigset_t mask = sigmask(signum); 185 | 186 | extern int __thrsigdivert(sigset_t set, siginfo_t * info, 187 | struct timespec const *timeout); 188 | 189 | /* `&(struct timespec){0, 0}` returns EAGAIN but spams 190 | * the dmesg log. Let's do it with an invalid timespec 191 | * and EINVAL. */ 192 | int s = __thrsigdivert(mask, NULL, 193 | &(struct timespec) { 0, -1 }); 194 | ec = s < 0 ? errno : 0; 195 | if (ec == EINVAL || ec == EAGAIN) { 196 | /* We must retry because we only checked for 197 | * one signal. There may be others pending. */ 198 | continue; 199 | } 200 | #elif defined(__APPLE__) 201 | int s; 202 | ec = sigwait(&signalfd->sigs, &s); 203 | #else 204 | #error "" 205 | #endif 206 | if (ec != 0) { 207 | break; 208 | } 209 | 210 | siginfo.si_signo = s; 211 | 212 | break; 213 | } 214 | #else 215 | { 216 | int s = sigtimedwait(&signalfd->sigs, &siginfo, 217 | &(struct timespec) { 0, 0 }); 218 | ec = s < 0 ? errno : 0; 219 | if (ec == 0) { 220 | assert(siginfo.si_signo == s); 221 | } 222 | } 223 | #endif 224 | if (ec != 0) { 225 | return ec; 226 | } 227 | 228 | /* 229 | * First, fill the POSIX compatible fields, then anything else OS 230 | * specific we have. 231 | */ 232 | 233 | signalfd_siginfo->ssi_signo = (uint32_t)siginfo.si_signo; 234 | signalfd_siginfo->ssi_code = siginfo.si_code; 235 | signalfd_siginfo->ssi_errno = siginfo.si_errno; 236 | 237 | signalfd_siginfo->ssi_pid = (uint32_t)siginfo.si_pid; 238 | signalfd_siginfo->ssi_uid = siginfo.si_uid; 239 | 240 | signalfd_siginfo->ssi_addr = (uint64_t)(uintptr_t)siginfo.si_addr; 241 | 242 | signalfd_siginfo->ssi_status = siginfo.si_status; 243 | 244 | #ifndef __OpenBSD__ 245 | signalfd_siginfo->ssi_band = (uint32_t)siginfo.si_band; 246 | #endif 247 | 248 | signalfd_siginfo->ssi_int = siginfo.si_value.sival_int; 249 | signalfd_siginfo->ssi_ptr = (uint64_t)(uintptr_t) 250 | siginfo.si_value.sival_ptr; 251 | 252 | /* 253 | * Sane defaults for Linux specific fields, may be amended in the 254 | * future. 255 | */ 256 | signalfd_siginfo->ssi_fd = -1; 257 | signalfd_siginfo->ssi_tid = (uint32_t)-1; 258 | 259 | #ifdef __FreeBSD__ 260 | signalfd_siginfo->ssi_trapno = (uint32_t)siginfo.si_trapno; 261 | 262 | signalfd_siginfo->ssi_tid = (uint32_t)siginfo.si_timerid; 263 | signalfd_siginfo->ssi_overrun = (uint32_t)siginfo.si_overrun; 264 | 265 | if (siginfo.si_code == SI_MESGQ) { 266 | /* Re-use this field for si_mqd. */ 267 | signalfd_siginfo->ssi_fd = siginfo.si_mqd; 268 | } 269 | #elif __NetBSD__ 270 | signalfd_siginfo->ssi_utime = siginfo.si_utime; 271 | signalfd_siginfo->ssi_stime = siginfo.si_stime; 272 | 273 | signalfd_siginfo->ssi_trapno = siginfo.si_trap; 274 | /* No space for trap2/trap3 */ 275 | 276 | signalfd_siginfo->ssi_fd = siginfo.si_fd; 277 | 278 | /* No space for syscall/ptrace_state */ 279 | #endif 280 | return 0; 281 | } 282 | 283 | static bool 284 | signalfd_ctx_clear_signal(SignalFDCtx *signalfd, int kq, bool was_triggered) 285 | { 286 | if (was_triggered) { 287 | /* 288 | * When there are other signals pending we can keep the kq 289 | * readable and therefore don't need to clear it. 290 | */ 291 | bool has_pending; 292 | if (signalfd_has_pending(signalfd, &has_pending, NULL) != 0 || 293 | has_pending) { 294 | return true; 295 | } 296 | } 297 | 298 | /* 299 | * Clear the kq. Signals can arrive here, leading to a race. 300 | */ 301 | 302 | kqueue_event_clear(&signalfd->kqueue_event, kq); 303 | 304 | /* 305 | * Because of the race, we must recheck and manually trigger if 306 | * necessary. 307 | */ 308 | bool has_pending; 309 | if (signalfd_has_pending(signalfd, &has_pending, NULL) != 0 || 310 | has_pending) { 311 | (void)signalfd_ctx_trigger_manually(signalfd, kq); 312 | return true; 313 | } 314 | return false; 315 | } 316 | 317 | errno_t 318 | signalfd_ctx_read(SignalFDCtx *signalfd, int kq, SignalFDCtxSiginfo *siginfo) 319 | { 320 | errno_t ec; 321 | 322 | ec = signalfd_ctx_read_impl(signalfd, siginfo); 323 | if (ec == 0 || ec == EAGAIN || ec == EWOULDBLOCK) { 324 | (void)signalfd_ctx_clear_signal(signalfd, kq, false); 325 | } 326 | 327 | return ec; 328 | } 329 | 330 | void 331 | signalfd_ctx_poll(SignalFDCtx *signalfd, int kq, uint32_t *revents) 332 | { 333 | bool pending = signalfd_ctx_clear_signal(signalfd, kq, revents != NULL); 334 | if (revents) { 335 | *revents = pending ? POLLIN : 0; 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /src/signalfd_ctx.h: -------------------------------------------------------------------------------- 1 | #ifndef SIGNALFD_CTX_H_ 2 | #define SIGNALFD_CTX_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "kqueue_event.h" 11 | 12 | typedef struct { 13 | sigset_t sigs; 14 | KQueueEvent kqueue_event; 15 | } SignalFDCtx; 16 | 17 | typedef struct { 18 | uint32_t ssi_signo; 19 | int32_t ssi_errno; 20 | int32_t ssi_code; 21 | uint32_t ssi_pid; 22 | uint32_t ssi_uid; 23 | int32_t ssi_fd; 24 | uint32_t ssi_tid; 25 | uint32_t ssi_band; 26 | uint32_t ssi_overrun; 27 | uint32_t ssi_trapno; 28 | int32_t ssi_status; 29 | int32_t ssi_int; 30 | uint64_t ssi_ptr; 31 | uint64_t ssi_utime; 32 | uint64_t ssi_stime; 33 | uint64_t ssi_addr; 34 | uint16_t ssi_addr_lsb; 35 | uint8_t pad[46]; 36 | } SignalFDCtxSiginfo; 37 | 38 | errno_t signalfd_ctx_init(SignalFDCtx *signalfd, int kq, sigset_t const *sigs); 39 | errno_t signalfd_ctx_terminate(SignalFDCtx *signalfd); 40 | 41 | errno_t signalfd_ctx_read(SignalFDCtx *signalfd, int kq, 42 | SignalFDCtxSiginfo *siginfo); 43 | void signalfd_ctx_poll(SignalFDCtx *signalfd, int kq, uint32_t *revents); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/timerfd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "wrap.h" 18 | 19 | #include "epoll_shim_ctx.h" 20 | #include "epoll_shim_export.h" 21 | #include "errno_return.h" 22 | 23 | static errno_t 24 | timerfd_ctx_read_or_block(FileDescription *desc, int kq, uint64_t *value) 25 | { 26 | errno_t ec; 27 | TimerFDCtx *timerfd = &desc->ctx.timerfd; 28 | 29 | for (;;) { 30 | (void)pthread_mutex_lock(&desc->mutex); 31 | ec = timerfd_ctx_read(timerfd, kq, value); 32 | bool nonblock = (desc->flags & O_NONBLOCK) != 0; 33 | (void)pthread_mutex_unlock(&desc->mutex); 34 | if (nonblock && ec == 0 && *value == 0) { 35 | ec = EAGAIN; 36 | } 37 | if (nonblock || ec != EAGAIN) { 38 | return ec; 39 | } 40 | 41 | struct pollfd pfd = { 42 | .fd = kq, 43 | .events = POLLIN, 44 | }; 45 | if (real_poll(&pfd, 1, -1) < 0) { 46 | return errno; 47 | } 48 | } 49 | } 50 | 51 | static errno_t 52 | timerfd_read(FileDescription *desc, int kq, void *buf, size_t nbytes, 53 | size_t *bytes_transferred) 54 | { 55 | errno_t ec; 56 | 57 | if (nbytes < sizeof(uint64_t)) { 58 | return EINVAL; 59 | } 60 | 61 | uint64_t nr_expired; 62 | if ((ec = timerfd_ctx_read_or_block(desc, kq, &nr_expired)) != 0) { 63 | return ec; 64 | } 65 | 66 | if (nr_expired == 0) { 67 | *bytes_transferred = 0; 68 | } else { 69 | memcpy(buf, &nr_expired, sizeof(uint64_t)); 70 | *bytes_transferred = sizeof(uint64_t); 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | static errno_t 77 | timerfd_close(FileDescription *desc) 78 | { 79 | EpollShimCtx *epoll_shim_ctx; 80 | errno_t ec = epoll_shim_ctx_global(&epoll_shim_ctx); 81 | assert(ec == 0); 82 | (void)ec; 83 | 84 | int const old_can_jump = /**/ 85 | desc->ctx.timerfd.clockid == CLOCK_REALTIME && 86 | desc->ctx.timerfd.is_abstime; 87 | epoll_shim_ctx_update_realtime_change_monitoring(epoll_shim_ctx, 88 | -old_can_jump); 89 | return timerfd_ctx_terminate(&desc->ctx.timerfd); 90 | } 91 | 92 | static void 93 | timerfd_poll(FileDescription *desc, int kq, uint32_t *revents) 94 | { 95 | (void)pthread_mutex_lock(&desc->mutex); 96 | timerfd_ctx_poll(&desc->ctx.timerfd, kq, revents); 97 | (void)pthread_mutex_unlock(&desc->mutex); 98 | } 99 | 100 | static void 101 | timerfd_realtime_change(FileDescription *desc, int kq) 102 | { 103 | (void)pthread_mutex_lock(&desc->mutex); 104 | timerfd_ctx_realtime_change(&desc->ctx.timerfd, kq); 105 | (void)pthread_mutex_unlock(&desc->mutex); 106 | } 107 | 108 | static struct file_description_vtable const timerfd_vtable = { 109 | .read_fun = timerfd_read, 110 | .write_fun = fd_context_default_write, 111 | .close_fun = timerfd_close, 112 | .poll_fun = timerfd_poll, 113 | .realtime_change_fun = timerfd_realtime_change, 114 | }; 115 | 116 | static errno_t 117 | timerfd_create_impl(int *fd_out, int clockid, int flags) 118 | { 119 | errno_t ec; 120 | 121 | if (clockid != CLOCK_MONOTONIC && clockid != CLOCK_REALTIME) { 122 | return EINVAL; 123 | } 124 | 125 | if (flags & ~(TFD_CLOEXEC | TFD_NONBLOCK)) { 126 | return EINVAL; 127 | } 128 | 129 | _Static_assert(TFD_CLOEXEC == O_CLOEXEC, ""); 130 | _Static_assert(TFD_NONBLOCK == O_NONBLOCK, ""); 131 | 132 | EpollShimCtx *epoll_shim_ctx; 133 | if ((ec = epoll_shim_ctx_global(&epoll_shim_ctx)) != 0) { 134 | return ec; 135 | } 136 | 137 | int fd; 138 | FileDescription *desc; 139 | ec = epoll_shim_ctx_create_desc(epoll_shim_ctx, 140 | flags & (O_CLOEXEC | O_NONBLOCK), &fd, &desc); 141 | if (ec != 0) { 142 | return ec; 143 | } 144 | 145 | desc->flags = flags & O_NONBLOCK; 146 | 147 | if ((ec = timerfd_ctx_init(&desc->ctx.timerfd, clockid)) != 0) { 148 | goto fail; 149 | } 150 | 151 | desc->vtable = &timerfd_vtable; 152 | epoll_shim_ctx_install_desc(epoll_shim_ctx, fd, desc); 153 | 154 | *fd_out = fd; 155 | return 0; 156 | 157 | fail: 158 | epoll_shim_ctx_drop_desc(epoll_shim_ctx, fd, desc); 159 | return ec; 160 | } 161 | 162 | EPOLL_SHIM_EXPORT 163 | int 164 | timerfd_create(int clockid, int flags) 165 | { 166 | ERRNO_SAVE; 167 | errno_t ec; 168 | 169 | int fd; 170 | ec = timerfd_create_impl(&fd, clockid, flags); 171 | 172 | ERRNO_RETURN(ec, -1, fd); 173 | } 174 | 175 | static errno_t 176 | timerfd_settime_impl(int fd, int flags, const struct itimerspec *new, 177 | struct itimerspec *old) 178 | { 179 | errno_t ec; 180 | 181 | if (!new) { 182 | return EFAULT; 183 | } 184 | 185 | if (flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) { 186 | return EINVAL; 187 | } 188 | 189 | EpollShimCtx *epoll_shim_ctx; 190 | if ((ec = epoll_shim_ctx_global(&epoll_shim_ctx)) != 0) { 191 | return ec; 192 | } 193 | 194 | FileDescription *desc = epoll_shim_ctx_find_desc(epoll_shim_ctx, fd); 195 | if (!desc || desc->vtable != &timerfd_vtable) { 196 | struct stat sb; 197 | ec = (fd < 0 || fstat(fd, &sb)) ? EBADF : EINVAL; 198 | goto out; 199 | } 200 | 201 | (void)pthread_mutex_lock(&desc->mutex); 202 | { 203 | int const old_can_jump = /**/ 204 | desc->ctx.timerfd.clockid == CLOCK_REALTIME && 205 | desc->ctx.timerfd.is_abstime; 206 | 207 | ec = timerfd_ctx_settime(&desc->ctx.timerfd, fd, 208 | (flags & TFD_TIMER_ABSTIME) != 0, /**/ 209 | (flags & TFD_TIMER_CANCEL_ON_SET) != 0, /**/ 210 | new, old); 211 | 212 | if (ec == 0 || ec == ECANCELED) { 213 | int const new_can_jump = /**/ 214 | desc->ctx.timerfd.clockid == CLOCK_REALTIME && 215 | desc->ctx.timerfd.is_abstime; 216 | 217 | epoll_shim_ctx_update_realtime_change_monitoring( 218 | epoll_shim_ctx, new_can_jump - old_can_jump); 219 | } 220 | } 221 | (void)pthread_mutex_unlock(&desc->mutex); 222 | 223 | out: 224 | if (desc) { 225 | (void)file_description_unref(&desc); 226 | } 227 | return ec; 228 | } 229 | 230 | EPOLL_SHIM_EXPORT 231 | int 232 | timerfd_settime(int fd, int flags, const struct itimerspec *new, 233 | struct itimerspec *old) 234 | { 235 | ERRNO_SAVE; 236 | errno_t ec; 237 | 238 | ec = timerfd_settime_impl(fd, flags, new, old); 239 | 240 | ERRNO_RETURN(ec, -1, 0); 241 | } 242 | 243 | static errno_t 244 | timerfd_gettime_impl(int fd, struct itimerspec *cur) 245 | { 246 | errno_t ec; 247 | 248 | EpollShimCtx *epoll_shim_ctx; 249 | if ((ec = epoll_shim_ctx_global(&epoll_shim_ctx)) != 0) { 250 | return ec; 251 | } 252 | 253 | FileDescription *desc = epoll_shim_ctx_find_desc(epoll_shim_ctx, fd); 254 | if (!desc || desc->vtable != &timerfd_vtable) { 255 | struct stat sb; 256 | ec = (fd < 0 || fstat(fd, &sb)) ? EBADF : EINVAL; 257 | goto out; 258 | } 259 | 260 | (void)pthread_mutex_lock(&desc->mutex); 261 | ec = timerfd_ctx_gettime(&desc->ctx.timerfd, cur); 262 | (void)pthread_mutex_unlock(&desc->mutex); 263 | 264 | out: 265 | if (desc) { 266 | (void)file_description_unref(&desc); 267 | } 268 | return ec; 269 | } 270 | 271 | EPOLL_SHIM_EXPORT 272 | int 273 | timerfd_gettime(int fd, struct itimerspec *cur) 274 | { 275 | ERRNO_SAVE; 276 | errno_t ec; 277 | 278 | ec = timerfd_gettime_impl(fd, cur); 279 | 280 | ERRNO_RETURN(ec, -1, 0); 281 | } 282 | -------------------------------------------------------------------------------- /src/timerfd_ctx.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMERFD_CTX_H_ 2 | #define TIMERFD_CTX_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | typedef enum { 14 | TIMER_TYPE_UNSPECIFIED, 15 | TIMER_TYPE_RELATIVE, 16 | TIMER_TYPE_ABSOLUTE, 17 | } TimerType; 18 | 19 | typedef struct { 20 | bool is_abstime; 21 | 22 | clockid_t clockid; 23 | TimerType timer_type; 24 | bool is_cancel_on_set; 25 | bool force_cancel; 26 | struct timespec monotonic_offset; 27 | /* 28 | * Next expiration time, absolute (clock given by clockid). 29 | * If it_interval is != 0, it is a periodic timer. 30 | * If it_value is == 0, the timer is disarmed. 31 | */ 32 | struct itimerspec current_itimerspec; 33 | uint64_t nr_expirations; 34 | } TimerFDCtx; 35 | 36 | errno_t timerfd_ctx_init(TimerFDCtx *timerfd, int clockid); 37 | errno_t timerfd_ctx_terminate(TimerFDCtx *timerfd); 38 | 39 | errno_t timerfd_ctx_settime(TimerFDCtx *timerfd, int kq, /**/ 40 | bool is_abstime, bool is_cancel_on_set, /**/ 41 | struct itimerspec const *new, struct itimerspec *old); 42 | errno_t timerfd_ctx_gettime(TimerFDCtx *timerfd, struct itimerspec *cur); 43 | 44 | errno_t timerfd_ctx_read(TimerFDCtx *timerfd, int kq, uint64_t *value); 45 | void timerfd_ctx_poll(TimerFDCtx *timerfd, int kq, uint32_t *revents); 46 | 47 | errno_t timerfd_ctx_get_monotonic_offset(struct timespec *monotonic_offset); 48 | void timerfd_ctx_realtime_change(TimerFDCtx *timerfd, int kq); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/timespec_util.c: -------------------------------------------------------------------------------- 1 | #include "timespec_util.h" 2 | 3 | #include 4 | 5 | bool 6 | timespec_is_valid(struct timespec const *ts) 7 | { 8 | assert(ts != NULL); 9 | 10 | return ts->tv_sec >= 0 && ts->tv_nsec >= 0 && ts->tv_nsec < 1000000000; 11 | } 12 | 13 | bool 14 | itimerspec_is_valid(struct itimerspec const *its) 15 | { 16 | return timespec_is_valid(&its->it_value) && 17 | timespec_is_valid(&its->it_interval); 18 | } 19 | 20 | bool 21 | timespecadd_safe(struct timespec const *tsp, struct timespec const *usp, 22 | struct timespec *vsp) 23 | { 24 | assert(timespec_is_valid(tsp)); 25 | assert(timespec_is_valid(usp)); 26 | 27 | if (__builtin_add_overflow(tsp->tv_sec, usp->tv_sec, &vsp->tv_sec)) { 28 | return false; 29 | } 30 | vsp->tv_nsec = tsp->tv_nsec + usp->tv_nsec; 31 | 32 | if (vsp->tv_nsec >= 1000000000L) { 33 | if (__builtin_add_overflow(vsp->tv_sec, 1, &vsp->tv_sec)) { 34 | return false; 35 | } 36 | vsp->tv_nsec -= 1000000000L; 37 | } 38 | 39 | return true; 40 | } 41 | 42 | bool 43 | timespecsub_safe(struct timespec const *tsp, struct timespec const *usp, 44 | struct timespec *vsp) 45 | { 46 | assert(timespec_is_valid(tsp)); 47 | assert(timespec_is_valid(usp)); 48 | 49 | if (__builtin_sub_overflow(tsp->tv_sec, usp->tv_sec, &vsp->tv_sec)) { 50 | return false; 51 | } 52 | vsp->tv_nsec = tsp->tv_nsec - usp->tv_nsec; 53 | 54 | if (vsp->tv_nsec < 0) { 55 | if (__builtin_sub_overflow(vsp->tv_sec, 1, &vsp->tv_sec)) { 56 | return false; 57 | } 58 | vsp->tv_nsec += 1000000000L; 59 | } 60 | 61 | return true; 62 | } 63 | -------------------------------------------------------------------------------- /src/timespec_util.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMESPEC_UTIL_H 2 | #define TIMESPEC_UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | // TODO(jan): Remove this once the definition is exposed in in 11 | // all supported FreeBSD versions. 12 | #ifndef timespeccmp 13 | #define timespeccmp(tvp, uvp, cmp) \ 14 | (((tvp)->tv_sec == (uvp)->tv_sec) ? \ 15 | ((tvp)->tv_nsec cmp(uvp)->tv_nsec) : \ 16 | ((tvp)->tv_sec cmp(uvp)->tv_sec)) 17 | #endif 18 | #ifndef timespecsub 19 | #define timespecsub(tsp, usp, vsp) \ 20 | do { \ 21 | (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ 22 | (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ 23 | if ((vsp)->tv_nsec < 0) { \ 24 | (vsp)->tv_sec--; \ 25 | (vsp)->tv_nsec += 1000000000L; \ 26 | } \ 27 | } while (0) 28 | #endif 29 | 30 | bool timespec_is_valid(struct timespec const *ts); 31 | bool itimerspec_is_valid(struct itimerspec const *its); 32 | bool timespecadd_safe(struct timespec const *tsp, struct timespec const *usp, 33 | struct timespec *vsp); 34 | bool timespecsub_safe(struct timespec const *tsp, struct timespec const *usp, 35 | struct timespec *vsp); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/wrap.c: -------------------------------------------------------------------------------- 1 | #include "wrap.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "compat_ppoll.h" 15 | 16 | static struct { 17 | pthread_once_t wrap_init; 18 | 19 | typeof(read) *real_read; 20 | typeof(write) *real_write; 21 | typeof(close) *real_close; 22 | typeof(poll) *real_poll; 23 | #if !defined(__APPLE__) 24 | #ifdef __NetBSD__ 25 | typeof(pollts) *real___pollts50; 26 | #else 27 | typeof(ppoll) *real_ppoll; 28 | #endif 29 | #endif 30 | typeof(fcntl) *real_fcntl; 31 | } wrap = { .wrap_init = PTHREAD_ONCE_INIT }; 32 | 33 | static void 34 | wrap_initialize_impl(void) 35 | { 36 | /* 37 | * Try to get the "real" function with `RTLD_NEXT` first. If that 38 | * fails, it means that `libc` is before `libepoll-shim` in library 39 | * search order. This shouldn't really happen, but try with 40 | * `RTLD_DEFAULT` as a fallback anyway. 41 | */ 42 | #define WRAP(fun) \ 43 | do { \ 44 | wrap.real_##fun = dlsym(RTLD_NEXT, #fun); \ 45 | if (wrap.real_##fun == NULL) { \ 46 | wrap.real_##fun = dlsym(RTLD_DEFAULT, #fun); \ 47 | } \ 48 | if (wrap.real_##fun == NULL) { \ 49 | fprintf(stderr, \ 50 | "epoll-shim: error resolving \"%s\" " \ 51 | "with dlsym RTLD_NEXT/RTLD_DEFAULT!\n", \ 52 | #fun); \ 53 | abort(); \ 54 | } \ 55 | } while (0) 56 | 57 | WRAP(read); 58 | WRAP(write); 59 | WRAP(close); 60 | WRAP(poll); 61 | #if !defined(__APPLE__) 62 | #ifdef __NetBSD__ 63 | WRAP(__pollts50); 64 | #else 65 | WRAP(ppoll); 66 | #endif 67 | #endif 68 | WRAP(fcntl); 69 | 70 | #undef WRAP 71 | } 72 | 73 | static void 74 | wrap_initialize(void) 75 | { 76 | int const oe = errno; 77 | (void)pthread_once(&wrap.wrap_init, wrap_initialize_impl); 78 | errno = oe; 79 | } 80 | 81 | ssize_t 82 | real_read(int fd, void *buf, size_t nbytes) 83 | { 84 | wrap_initialize(); 85 | return wrap.real_read(fd, buf, nbytes); 86 | } 87 | 88 | ssize_t 89 | real_write(int fd, void const *buf, size_t nbytes) 90 | { 91 | wrap_initialize(); 92 | return wrap.real_write(fd, buf, nbytes); 93 | } 94 | 95 | int 96 | real_close(int fd) 97 | { 98 | wrap_initialize(); 99 | return wrap.real_close(fd); 100 | } 101 | 102 | int 103 | real_poll(struct pollfd fds[], nfds_t nfds, int timeout) 104 | { 105 | wrap_initialize(); 106 | return wrap.real_poll(fds, nfds, timeout); 107 | } 108 | 109 | int 110 | real_ppoll(struct pollfd fds[], nfds_t nfds, 111 | struct timespec const *restrict timeout, 112 | sigset_t const *restrict newsigmask) 113 | { 114 | #ifdef __APPLE__ 115 | return compat_ppoll(fds, nfds, timeout, newsigmask); 116 | #else 117 | wrap_initialize(); 118 | #ifdef __NetBSD__ 119 | return wrap.real___pollts50(fds, nfds, timeout, newsigmask); 120 | #else 121 | return wrap.real_ppoll(fds, nfds, timeout, newsigmask); 122 | #endif 123 | #endif 124 | } 125 | 126 | int 127 | real_fcntl(int fd, int cmd, ...) 128 | { 129 | wrap_initialize(); 130 | va_list ap; 131 | 132 | va_start(ap, cmd); 133 | void *arg = va_arg(ap, void *); 134 | int rv = wrap.real_fcntl(fd, cmd, arg); 135 | va_end(ap); 136 | 137 | return rv; 138 | } 139 | -------------------------------------------------------------------------------- /src/wrap.h: -------------------------------------------------------------------------------- 1 | #ifndef WRAP_H_ 2 | #define WRAP_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | ssize_t real_read(int fd, void *buf, size_t nbytes); 11 | ssize_t real_write(int fd, void const *buf, size_t nbytes); 12 | int real_close(int fd); 13 | int real_poll(struct pollfd fds[], nfds_t nfds, int timeout); 14 | int real_ppoll(struct pollfd fds[], nfds_t nfds, 15 | struct timespec const *restrict timeout, 16 | sigset_t const *restrict newsigmask); 17 | int real_fcntl(int fd, int cmd, ...); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(epoll-shim-tests LANGUAGES C) 3 | 4 | # 5 | 6 | include(CTest) 7 | 8 | # 9 | 10 | find_package(epoll-shim REQUIRED) 11 | 12 | find_package(microatf REQUIRED) 13 | include(ATFTest) 14 | 15 | set(THREADS_PREFER_PTHREAD_FLAG ON) 16 | find_package(Threads REQUIRED) 17 | 18 | # 19 | 20 | macro(atf_test_impl _testname _suffix) 21 | add_executable("${_testname}${_suffix}" "${_testname}.c") 22 | target_link_libraries( 23 | "${_testname}${_suffix}" PRIVATE epoll-shim::epoll-shim${_suffix} 24 | Threads::Threads microatf::microatf-c) 25 | if(APPLE) 26 | target_link_libraries( 27 | "${_testname}${_suffix}" 28 | PRIVATE wrap compat_enable_pipe2 compat_enable_ppoll 29 | compat_enable_socketpair compat_enable_socket 30 | compat_enable_itimerspec) 31 | endif() 32 | atf_discover_tests("${_testname}${_suffix}" ${ARGN}) 33 | endmacro() 34 | 35 | macro(atf_test _testname) 36 | atf_test_impl("${_testname}" "" ${ARGN}) 37 | if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") 38 | atf_test_impl("${_testname}" "-interpose" ${ARGN}) 39 | endif() 40 | endmacro() 41 | 42 | # 43 | 44 | atf_test(epoll-test) 45 | add_library(epoll-shim-rdhup-linux-def INTERFACE) 46 | target_link_libraries(epoll-shim-rdhup-linux-def INTERFACE epoll-shim::epoll-shim) 47 | target_compile_definitions(epoll-shim-rdhup-linux-def INTERFACE -DUSE_EPOLLRDHUP_LINUX_DEFINITION) 48 | add_library(epoll-shim::epoll-shim-rdhup-linux-def ALIAS epoll-shim-rdhup-linux-def) 49 | atf_test_impl(epoll-test "-rdhup-linux-def") 50 | foreach(_target epoll-test epoll-test-interpose epoll-test-rdhup-linux-def) 51 | if(TARGET ${_target}) 52 | target_sources(${_target} PRIVATE real_close.c) 53 | target_link_libraries(${_target} PRIVATE ${CMAKE_DL_LIBS}) 54 | endif() 55 | endforeach() 56 | atf_test(timerfd-test) 57 | atf_test(timerfd-root-test) 58 | atf_test(timerfd-mock-test) 59 | atf_test(signalfd-test) 60 | atf_test(perf-many-fds) 61 | atf_test(atf-test) 62 | atf_test(eventfd-ctx-test) 63 | atf_test(pipe-test) 64 | atf_test(socketpair-test) 65 | get_target_property(_target_type epoll-shim::epoll-shim TYPE) 66 | if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux" # 67 | AND NOT _target_type STREQUAL STATIC_LIBRARY) 68 | atf_test(malloc-fail-test) 69 | foreach(_target malloc-fail-test malloc-fail-test-interpose) 70 | if(TARGET ${_target}) 71 | target_sources(${_target} PRIVATE real_close.c) 72 | target_link_libraries(${_target} PRIVATE ${CMAKE_DL_LIBS}) 73 | endif() 74 | endforeach() 75 | endif() 76 | atf_test(tst-epoll) 77 | atf_test(tst-timerfd) 78 | 79 | add_executable(rwlock-test rwlock-test.c) 80 | target_link_libraries(rwlock-test PRIVATE rwlock microatf::microatf-c) 81 | atf_discover_tests(rwlock-test) 82 | 83 | add_executable(epoll-include-test epoll-include-test.c) 84 | target_link_libraries(epoll-include-test PRIVATE epoll-shim::epoll-shim) 85 | set_target_properties( 86 | epoll-include-test 87 | PROPERTIES C_STANDARD 99 # 88 | C_STANDARD_REQUIRED YES # 89 | C_EXTENSIONS NO) 90 | add_test(NAME epoll-include-test COMMAND epoll-include-test) 91 | 92 | add_executable(epoll-include-c89-test epoll-include-c89-test.c) 93 | target_link_libraries(epoll-include-c89-test PRIVATE epoll-shim::epoll-shim) 94 | set_target_properties( 95 | epoll-include-c89-test 96 | PROPERTIES C_STANDARD 90 # 97 | C_STANDARD_REQUIRED YES # 98 | C_EXTENSIONS NO) 99 | add_test(NAME epoll-include-c89-test COMMAND epoll-include-c89-test) 100 | target_compile_options(epoll-include-c89-test PRIVATE "-pedantic" 101 | "-Werror=variadic-macros") 102 | 103 | add_executable(fcntl-warning fcntl-warning.c) 104 | target_link_libraries(fcntl-warning PRIVATE epoll-shim::epoll-shim) 105 | if(CMAKE_C_COMPILER_ID STREQUAL "Clang") 106 | target_compile_options( 107 | fcntl-warning PRIVATE "-Werror" "-Wgnu-zero-variadic-macro-arguments") 108 | endif() 109 | 110 | add_executable(dlsym-fail epoll-include-test.c) 111 | target_link_libraries(dlsym-fail PRIVATE c epoll-shim::epoll-shim) 112 | add_test(NAME dlsym-fail COMMAND dlsym-fail) 113 | -------------------------------------------------------------------------------- /test/atf-c-leakcheck.h: -------------------------------------------------------------------------------- 1 | #ifndef ATF_C_LEAKCHECK_H_ 2 | #define ATF_C_LEAKCHECK_H_ 3 | 4 | #include 5 | 6 | #ifndef _GNU_SOURCE 7 | #error "Need to define _GNU_SOURCE" 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | static int fd_leak_test_a; 14 | static int fd_leak_test_b; 15 | static int fd_leak_test_c; 16 | static int fd_leak_test_d; 17 | 18 | static void 19 | init_fd_checking(void) 20 | { 21 | /* We check for fd leaks after each test. Remember fd numbers for 22 | * checking here. */ 23 | int fds_1[2]; 24 | ATF_REQUIRE(pipe2(fds_1, O_CLOEXEC) == 0); 25 | 26 | fd_leak_test_a = fds_1[0]; 27 | fd_leak_test_b = fds_1[1]; 28 | 29 | int fds_2[2]; 30 | ATF_REQUIRE(pipe2(fds_2, O_CLOEXEC) == 0); 31 | fd_leak_test_c = fds_2[0]; 32 | fd_leak_test_d = fds_2[1]; 33 | 34 | ATF_REQUIRE(close(fds_1[0]) == 0); 35 | ATF_REQUIRE(close(fds_1[1]) == 0); 36 | ATF_REQUIRE(close(fds_2[0]) == 0); 37 | ATF_REQUIRE(close(fds_2[1]) == 0); 38 | } 39 | 40 | static void 41 | check_for_fd_leaks(void) 42 | { 43 | /* Test that all fds of previous tests 44 | * have been closed successfully. */ 45 | 46 | int fds_1[2]; 47 | ATF_REQUIRE(pipe2(fds_1, O_CLOEXEC) == 0); 48 | 49 | int fds_2[2]; 50 | ATF_REQUIRE(pipe2(fds_2, O_CLOEXEC) == 0); 51 | 52 | ATF_REQUIRE(fds_1[0] == fd_leak_test_a); 53 | ATF_REQUIRE(fds_1[1] == fd_leak_test_b); 54 | ATF_REQUIRE(fds_2[0] == fd_leak_test_c); 55 | ATF_REQUIRE(fds_2[1] == fd_leak_test_d); 56 | 57 | ATF_REQUIRE(close(fds_1[0]) == 0); 58 | ATF_REQUIRE(close(fds_1[1]) == 0); 59 | ATF_REQUIRE(close(fds_2[0]) == 0); 60 | ATF_REQUIRE(close(fds_2[1]) == 0); 61 | } 62 | 63 | #define ATF_TC_BODY_FD_LEAKCHECK(tc, tcptr) \ 64 | static void fd_leakcheck_##tc##_body( \ 65 | atf_tc_t const *tcptr __attribute__((__unused__))); \ 66 | ATF_TC_BODY(tc, tcptr) \ 67 | { \ 68 | init_fd_checking(); \ 69 | fd_leakcheck_##tc##_body(tcptr); \ 70 | check_for_fd_leaks(); \ 71 | } \ 72 | static void fd_leakcheck_##tc##_body( \ 73 | atf_tc_t const *tcptr __attribute__((__unused__))) 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /test/atf-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | ATF_TC_WITHOUT_HEAD(atf__environment); 10 | ATF_TC_BODY(atf__environment, tc) 11 | { 12 | DIR *cwd = opendir("."); 13 | 14 | ATF_REQUIRE(cwd != NULL); 15 | 16 | system("pwd"); 17 | 18 | int number_ents = 0; 19 | struct dirent *de; 20 | while ((de = readdir(cwd)) != NULL) { 21 | ATF_REQUIRE(strcmp(de->d_name, ".") == 0 || 22 | strcmp(de->d_name, "..") == 0); 23 | ++number_ents; 24 | } 25 | 26 | ATF_REQUIRE(number_ents == 2); 27 | 28 | ATF_REQUIRE(getenv("LANG") == NULL); 29 | ATF_REQUIRE(getenv("LC_ALL") == NULL); 30 | ATF_REQUIRE(getenv("LC_COLLATE") == NULL); 31 | ATF_REQUIRE(getenv("LC_CTYPE") == NULL); 32 | ATF_REQUIRE(getenv("LC_MESSAGES") == NULL); 33 | ATF_REQUIRE(getenv("LC_MONETARY") == NULL); 34 | ATF_REQUIRE(getenv("LC_NUMERIC") == NULL); 35 | ATF_REQUIRE(getenv("LC_TIME") == NULL); 36 | ATF_REQUIRE(strcmp(getenv("HOME"), getenv("TMPDIR")) == 0); 37 | ATF_REQUIRE(strcmp(getenv("TZ"), "UTC") == 0); 38 | } 39 | 40 | ATF_TC(atf__timeout); 41 | ATF_TC_HEAD(atf__timeout, tc) 42 | { 43 | atf_tc_set_md_var(tc, "timeout", "3"); 44 | } 45 | ATF_TC_BODY(atf__timeout, tc) 46 | { 47 | atf_tc_expect_timeout("sleep should take longer than 3s"); 48 | sleep(5); 49 | } 50 | 51 | ATF_TC_WITHOUT_HEAD(atf__checkfail); 52 | ATF_TC_BODY(atf__checkfail, tc) 53 | { 54 | atf_tc_expect_fail("this should fail"); 55 | ATF_CHECK(4 == 5); 56 | } 57 | 58 | ATF_TC_WITHOUT_HEAD(atf__exit_code); 59 | ATF_TC_BODY(atf__exit_code, tc) 60 | { 61 | atf_tc_expect_exit(42, "should exit with code 42"); 62 | sleep(1); 63 | exit(42); 64 | } 65 | 66 | ATF_TC_WITHOUT_HEAD(atf__signal); 67 | ATF_TC_BODY(atf__signal, tc) 68 | { 69 | atf_tc_expect_signal(SIGHUP, "should exit by SIGHUP"); 70 | kill(getpid(), SIGHUP); 71 | } 72 | 73 | ATF_TP_ADD_TCS(tp) 74 | { 75 | ATF_TP_ADD_TC(tp, atf__environment); 76 | ATF_TP_ADD_TC(tp, atf__timeout); 77 | ATF_TP_ADD_TC(tp, atf__checkfail); 78 | ATF_TP_ADD_TC(tp, atf__exit_code); 79 | ATF_TP_ADD_TC(tp, atf__signal); 80 | 81 | return atf_no_error(); 82 | } 83 | -------------------------------------------------------------------------------- /test/epoll-include-c89-test.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 199506L 2 | 3 | #include 4 | 5 | #include 6 | 7 | int 8 | main(void) 9 | { 10 | int ep = epoll_create1(0); 11 | close(ep); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /test/epoll-include-test.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | 5 | #include 6 | 7 | int 8 | main(void) 9 | { 10 | int ep = epoll_create1(EPOLL_CLOEXEC); 11 | close(ep); 12 | } 13 | -------------------------------------------------------------------------------- /test/fcntl-warning.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | int 7 | main() 8 | { 9 | int ep = epoll_create1(EPOLL_CLOEXEC); 10 | int r = fcntl(ep, F_GETFL); 11 | (void)r; 12 | close(ep); 13 | } 14 | -------------------------------------------------------------------------------- /test/malloc-fail-test.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef __linux__ 13 | #include 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "atf-c-leakcheck.h" 31 | 32 | static int malloc_fail_cnt = INT_MAX; 33 | static bool need_real_calloc; 34 | 35 | static void 36 | decrement_malloc_fail_cnt(void) 37 | { 38 | --malloc_fail_cnt; 39 | // write(2, "decr\n", 5); 40 | } 41 | 42 | static void * 43 | dlsym_wrapper(void *restrict handle, char const *restrict symbol) 44 | { 45 | // write(2, "dlsymen\n", 8); 46 | 47 | need_real_calloc = true; 48 | int old_malloc_fail_cnt = malloc_fail_cnt; 49 | malloc_fail_cnt = INT_MAX; 50 | void *ret = dlsym(handle, symbol); 51 | malloc_fail_cnt = old_malloc_fail_cnt; 52 | need_real_calloc = false; 53 | 54 | // write(2, "dlsymex\n", 8); 55 | 56 | return ret; 57 | } 58 | 59 | #ifdef __FreeBSD__ 60 | 61 | int 62 | poll(struct pollfd fds[], nfds_t nfds, int timeout) 63 | { 64 | if (malloc_fail_cnt <= 0) { 65 | errno = ENOMEM; 66 | return -1; 67 | } 68 | 69 | int (*real_poll)(struct pollfd[], nfds_t, 70 | int) = (int (*)(struct pollfd[], nfds_t, 71 | int))dlsym_wrapper(RTLD_NEXT, "poll"); 72 | 73 | decrement_malloc_fail_cnt(); 74 | 75 | return real_poll(fds, nfds, timeout); 76 | } 77 | 78 | int 79 | kqueue(void) 80 | { 81 | // write(2, "kqueue\n", 7); 82 | 83 | if (malloc_fail_cnt <= 0) { 84 | errno = ENOMEM; 85 | return -1; 86 | } 87 | 88 | int (*real_kqueue)( 89 | void) = (int (*)(void))dlsym_wrapper(RTLD_NEXT, "kqueue"); 90 | 91 | decrement_malloc_fail_cnt(); 92 | 93 | return real_kqueue(); 94 | } 95 | 96 | int 97 | kevent(int kq, const struct kevent *changelist, int nchanges, 98 | struct kevent *eventlist, int nevents, const struct timespec *timeout) 99 | { 100 | // write(2, "kevent\n", 7); 101 | 102 | if (malloc_fail_cnt <= 0) { 103 | errno = ENOMEM; 104 | return -1; 105 | } 106 | 107 | int (*real_kevent)(int, const struct kevent *, int, struct kevent *, 108 | int, const struct timespec *) = (int (*)(int, const struct kevent *, 109 | int, struct kevent *, int, 110 | const struct timespec *))dlsym_wrapper(RTLD_NEXT, "kevent"); 111 | 112 | decrement_malloc_fail_cnt(); 113 | 114 | return real_kevent(kq, changelist, nchanges, eventlist, nevents, 115 | timeout); 116 | } 117 | #endif 118 | 119 | void *malloc(size_t size); 120 | void * 121 | malloc(size_t size) 122 | { 123 | // write(2, "malloc\n", 7); 124 | 125 | if (malloc_fail_cnt <= 0) { 126 | errno = ENOMEM; 127 | return NULL; 128 | } 129 | 130 | void *(*real_malloc)( 131 | size_t) = (void *(*)(size_t))dlsym_wrapper(RTLD_NEXT, "malloc"); 132 | 133 | decrement_malloc_fail_cnt(); 134 | 135 | return real_malloc(size); 136 | } 137 | 138 | void *calloc(size_t number, size_t size); 139 | void * 140 | calloc(size_t number, size_t size) 141 | { 142 | // write(2, "calloc\n", 7); 143 | 144 | #ifdef __linux__ 145 | if (need_real_calloc) { 146 | extern void *__libc_calloc(size_t, size_t); 147 | return __libc_calloc(number, size); 148 | } 149 | #endif 150 | 151 | if (malloc_fail_cnt <= 0) { 152 | errno = ENOMEM; 153 | return NULL; 154 | } 155 | 156 | void *(*real_calloc)(size_t, size_t) = (void *(*)(size_t, 157 | size_t))dlsym_wrapper(RTLD_NEXT, "calloc"); 158 | 159 | decrement_malloc_fail_cnt(); 160 | 161 | return real_calloc(number, size); 162 | } 163 | 164 | void *realloc(void *ptr, size_t size); 165 | void * 166 | realloc(void *ptr, size_t size) 167 | { 168 | // write(2, "realloc\n", 8); 169 | 170 | if (malloc_fail_cnt <= 0) { 171 | errno = ENOMEM; 172 | return NULL; 173 | } 174 | 175 | void *(*real_realloc)(void *, size_t) = (void *(*)(void *, 176 | size_t))dlsym_wrapper(RTLD_NEXT, "realloc"); 177 | 178 | decrement_malloc_fail_cnt(); 179 | 180 | return real_realloc(ptr, size); 181 | } 182 | 183 | int 184 | pthread_mutex_init(pthread_mutex_t *restrict mutex, 185 | pthread_mutexattr_t const *restrict attr) 186 | { 187 | // write(2, "mutexi\n", 7); 188 | 189 | if (malloc_fail_cnt <= 0) { 190 | return ENOMEM; 191 | } 192 | 193 | int (*real_pthread_mutex_init)(pthread_mutex_t *restrict, 194 | pthread_mutexattr_t const *restrict) = 195 | (int (*)(pthread_mutex_t *restrict, 196 | pthread_mutexattr_t const *restrict))dlsym_wrapper(RTLD_NEXT, 197 | "pthread_mutex_init"); 198 | 199 | decrement_malloc_fail_cnt(); 200 | 201 | return real_pthread_mutex_init(mutex, attr); 202 | } 203 | 204 | static void * 205 | write_to_pipe_thread_fun(void *arg) 206 | { 207 | int p = *(int *)arg; 208 | 209 | usleep(100000); 210 | 211 | char c = 0; 212 | ATF_REQUIRE(write(p, &c, 1) == 1); 213 | 214 | return NULL; 215 | } 216 | 217 | ATF_TC_WITHOUT_HEAD(malloc_fail__epoll); 218 | ATF_TC_BODY_FD_LEAKCHECK(malloc_fail__epoll, tc) 219 | { 220 | int p[2]; 221 | ATF_REQUIRE(pipe2(p, O_CLOEXEC | O_NONBLOCK) == 0); 222 | 223 | pthread_t thread; 224 | 225 | for (int fail_cnt = 0;; ++fail_cnt) { 226 | malloc_fail_cnt = INT_MAX; 227 | 228 | if (fail_cnt != 0) { 229 | ATF_REQUIRE(pthread_join(thread, NULL) == 0); 230 | } 231 | 232 | { 233 | char c; 234 | while (read(p[0], &c, 1) == 1) { 235 | } 236 | ATF_REQUIRE(errno == EAGAIN || errno == EWOULDBLOCK); 237 | } 238 | 239 | ATF_REQUIRE(pthread_create(&thread, NULL, 240 | write_to_pipe_thread_fun, &p[1]) == 0); 241 | 242 | malloc_fail_cnt = fail_cnt; 243 | 244 | int ep = epoll_create1(EPOLL_CLOEXEC); 245 | if (ep < 0) { 246 | ATF_REQUIRE_ERRNO(ENOMEM, true); 247 | continue; 248 | } 249 | 250 | struct epoll_event event = { .events = EPOLLIN }; 251 | int r = epoll_ctl(ep, EPOLL_CTL_ADD, p[0], &event); 252 | if (r < 0) { 253 | ATF_REQUIRE_ERRNO(ENOMEM, true); 254 | ATF_REQUIRE(close(ep) == 0); 255 | continue; 256 | } 257 | 258 | r = epoll_wait(ep, &event, 1, -1); 259 | if (r < 0) { 260 | ATF_REQUIRE_ERRNO(ENOMEM, true); 261 | ATF_REQUIRE(close(ep) == 0); 262 | continue; 263 | } 264 | ATF_REQUIRE(r == 1); 265 | ATF_REQUIRE(event.events == POLLIN); 266 | 267 | extern int real_close_for_test(int fd); 268 | ATF_REQUIRE(real_close_for_test(ep) == 0); 269 | ATF_REQUIRE_ERRNO(EBADF, close(ep) < 0); 270 | 271 | break; 272 | } 273 | 274 | ATF_REQUIRE(pthread_join(thread, NULL) == 0); 275 | 276 | ATF_REQUIRE(close(p[0]) == 0); 277 | ATF_REQUIRE(close(p[1]) == 0); 278 | 279 | malloc_fail_cnt = INT_MAX; 280 | } 281 | 282 | ATF_TC_WITHOUT_HEAD(malloc_fail__timerfd); 283 | ATF_TC_BODY_FD_LEAKCHECK(malloc_fail__timerfd, tc) 284 | { 285 | for (int fail_cnt = 0;; ++fail_cnt) { 286 | malloc_fail_cnt = fail_cnt; 287 | 288 | int tfd = timerfd_create(CLOCK_MONOTONIC, 289 | TFD_NONBLOCK | TFD_CLOEXEC); 290 | if (tfd < 0) { 291 | ATF_REQUIRE_ERRNO(ENOMEM, true); 292 | continue; 293 | } 294 | ATF_REQUIRE(close(tfd) == 0); 295 | 296 | break; 297 | } 298 | 299 | malloc_fail_cnt = INT_MAX; 300 | } 301 | 302 | ATF_TC_WITHOUT_HEAD(malloc_fail__eventfd); 303 | ATF_TC_BODY_FD_LEAKCHECK(malloc_fail__eventfd, tc) 304 | { 305 | for (int fail_cnt = 0;; ++fail_cnt) { 306 | malloc_fail_cnt = fail_cnt; 307 | 308 | int efd = eventfd(1, EFD_NONBLOCK | EFD_CLOEXEC); 309 | if (efd < 0) { 310 | ATF_REQUIRE_ERRNO(ENOMEM, true); 311 | continue; 312 | } 313 | 314 | eventfd_t data; 315 | if (eventfd_read(efd, &data) < 0) { 316 | ATF_REQUIRE_ERRNO(ENOMEM, true); 317 | ATF_REQUIRE(close(efd) == 0); 318 | continue; 319 | } 320 | ATF_REQUIRE(data == 1); 321 | 322 | if (eventfd_write(efd, 5) < 0) { 323 | ATF_REQUIRE_ERRNO(ENOMEM, true); 324 | ATF_REQUIRE(close(efd) == 0); 325 | continue; 326 | } 327 | 328 | if (eventfd_read(efd, &data) < 0) { 329 | ATF_REQUIRE_ERRNO(ENOMEM, true); 330 | ATF_REQUIRE(close(efd) == 0); 331 | continue; 332 | } 333 | ATF_REQUIRE(data == 5); 334 | 335 | ATF_REQUIRE(close(efd) == 0); 336 | 337 | break; 338 | } 339 | 340 | malloc_fail_cnt = INT_MAX; 341 | } 342 | 343 | ATF_TC_WITHOUT_HEAD(malloc_fail__signalfd); 344 | ATF_TC_BODY_FD_LEAKCHECK(malloc_fail__signalfd, tc) 345 | { 346 | for (int fail_cnt = 0;; ++fail_cnt) { 347 | malloc_fail_cnt = fail_cnt; 348 | 349 | sigset_t mask; 350 | sigemptyset(&mask); 351 | sigaddset(&mask, SIGINT); 352 | 353 | int sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); 354 | if (sfd < 0) { 355 | ATF_REQUIRE_ERRNO(ENOMEM, true); 356 | continue; 357 | } 358 | 359 | struct signalfd_siginfo siginfo; 360 | ssize_t s = read(sfd, &siginfo, 361 | sizeof(struct signalfd_siginfo)); 362 | ATF_REQUIRE(s < 0); 363 | ATF_REQUIRE(errno == EAGAIN || errno == ENOMEM); 364 | 365 | ATF_REQUIRE(close(sfd) == 0); 366 | 367 | break; 368 | } 369 | 370 | malloc_fail_cnt = INT_MAX; 371 | } 372 | 373 | ATF_TP_ADD_TCS(tp) 374 | { 375 | ATF_TP_ADD_TC(tp, malloc_fail__epoll); 376 | ATF_TP_ADD_TC(tp, malloc_fail__timerfd); 377 | ATF_TP_ADD_TC(tp, malloc_fail__eventfd); 378 | ATF_TP_ADD_TC(tp, malloc_fail__signalfd); 379 | 380 | return atf_no_error(); 381 | } 382 | -------------------------------------------------------------------------------- /test/perf-many-fds.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define NR_EVENTFDS (20000) 11 | 12 | /* 13 | * Those eventfd operations must not take longer than 15s! This was previously 14 | * the case with the old, naive list data structure used to manage eventfds. 15 | */ 16 | 17 | ATF_TC(perf_many_fds__perf); 18 | ATF_TC_HEAD(perf_many_fds__perf, tc) 19 | { 20 | atf_tc_set_md_var(tc, "timeout", "15"); 21 | } 22 | ATF_TC_BODY(perf_many_fds__perf, tc) 23 | { 24 | int *eventfds = malloc(NR_EVENTFDS * sizeof(int)); 25 | ATF_REQUIRE(eventfds); 26 | 27 | for (long i = 0; i < NR_EVENTFDS; ++i) { 28 | eventfds[i] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); 29 | if (eventfds[i] < 0) { 30 | atf_tc_skip("could not create eventfd: %d", errno); 31 | } 32 | } 33 | 34 | for (long i = 0; i < 2000000; ++i) { 35 | ATF_REQUIRE(eventfd_write(eventfds[0], 1) == 0); 36 | if (i % 10000 == 0) { 37 | fprintf(stderr, "."); 38 | } 39 | } 40 | free(eventfds); 41 | } 42 | 43 | ATF_TP_ADD_TCS(tp) 44 | { 45 | ATF_TP_ADD_TC(tp, perf_many_fds__perf); 46 | 47 | return atf_no_error(); 48 | } 49 | -------------------------------------------------------------------------------- /test/real_close.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #if !defined(__APPLE__) 8 | #include 9 | #include 10 | #endif 11 | 12 | #include 13 | 14 | extern int real_close_for_test(int fd); 15 | 16 | int 17 | real_close_for_test(int fd) 18 | { 19 | #if defined(__APPLE__) 20 | return close(fd); 21 | #else 22 | void *libc_handle; 23 | 24 | #if defined(__OpenBSD__) 25 | struct r_debug *r_debug = NULL; 26 | for (Elf_Dyn *dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn) { 27 | if (dyn->d_tag == DT_DEBUG) { 28 | r_debug = (struct r_debug *)dyn->d_un.d_ptr; 29 | break; 30 | } 31 | } 32 | if (!r_debug) { 33 | abort(); 34 | } 35 | struct link_map *link_map = r_debug->r_map; 36 | #else 37 | if ((libc_handle = dlopen(NULL, RTLD_NOW)) == NULL) { 38 | abort(); 39 | } 40 | 41 | #ifdef __linux__ 42 | typedef struct link_map Link_map; 43 | #endif 44 | 45 | Link_map *link_map; 46 | if (dlinfo(libc_handle, RTLD_DI_LINKMAP, &link_map) < 0) { 47 | abort(); 48 | } 49 | #endif 50 | 51 | libc_handle = NULL; 52 | for (; link_map != NULL; link_map = link_map->l_next) { 53 | char const *libname = strrchr(link_map->l_name, '/'); 54 | libname = libname == NULL ? link_map->l_name : libname + 1; 55 | 56 | if (strncmp(libname, "libc.so", strlen("libc.so")) == 0) { 57 | libc_handle = dlopen(libname, RTLD_LAZY); 58 | break; 59 | } 60 | } 61 | if (libc_handle == NULL) { 62 | abort(); 63 | } 64 | 65 | typeof(close) *real_close = dlsym(libc_handle, "close"); 66 | if (real_close == NULL) { 67 | abort(); 68 | } 69 | 70 | return real_close(fd); 71 | #endif 72 | } 73 | -------------------------------------------------------------------------------- /test/rwlock-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | // Test based on 8 | // 9 | 10 | struct stress_data { 11 | int iterations; 12 | RWLock *lock; 13 | int *data; 14 | int inc; 15 | }; 16 | 17 | static void * 18 | stress_reader(void *arg) 19 | { 20 | struct stress_data *stress_data = arg; 21 | struct timespec now; 22 | ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &now) == 0); 23 | unsigned int seed = (unsigned int)now.tv_nsec; 24 | 25 | for (int i = 0; i < stress_data->iterations; ++i) { 26 | usleep((useconds_t)(((rand_r(&seed) % 8) + 1) * 10)); 27 | 28 | rwlock_lock_read(stress_data->lock); 29 | for (int i = 1; i < 1000; ++i) { 30 | ATF_REQUIRE(stress_data->data[i] == 31 | stress_data->data[i - 1] + 1); 32 | } 33 | rwlock_unlock_read(stress_data->lock); 34 | } 35 | 36 | return NULL; 37 | } 38 | 39 | static void * 40 | stress_writer(void *arg) 41 | { 42 | struct stress_data *stress_data = arg; 43 | struct timespec now; 44 | ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &now) == 0); 45 | unsigned int seed = (unsigned int)now.tv_nsec; 46 | 47 | for (int i = 0; i < stress_data->iterations; ++i) { 48 | usleep((useconds_t)(((rand_r(&seed) % 8) + 1) * 10)); 49 | 50 | rwlock_lock_write(stress_data->lock); 51 | for (int i = 0; i < 1000; ++i) { 52 | stress_data->data[i] += stress_data->inc; 53 | } 54 | rwlock_downgrade(stress_data->lock); 55 | for (int i = 1; i < 1000; ++i) { 56 | ATF_REQUIRE(stress_data->data[i] == 57 | stress_data->data[i - 1] + 1); 58 | } 59 | rwlock_unlock_read(stress_data->lock); 60 | } 61 | 62 | return NULL; 63 | } 64 | 65 | ATF_TC_WITHOUT_HEAD(stress); 66 | ATF_TC_BODY(stress, tc) 67 | { 68 | RWLock rwlock; 69 | ATF_REQUIRE(rwlock_init(&rwlock) == 0); 70 | 71 | int data[1000]; 72 | for (int i = 0; i < 1000; ++i) { 73 | data[i] = i; 74 | } 75 | 76 | pthread_t threads[1010]; 77 | struct stress_data stress_data[1010]; 78 | 79 | for (int i = 0; i < 1000; ++i) { 80 | stress_data[i] = (struct stress_data) { 81 | .iterations = 500, 82 | .lock = &rwlock, 83 | .data = data, 84 | }; 85 | ATF_REQUIRE(pthread_create(&threads[i], NULL, /**/ 86 | stress_reader, &stress_data[i]) == 0); 87 | } 88 | 89 | for (int i = 0; i < 10; ++i) { 90 | stress_data[1000 + i] = (struct stress_data) { 91 | .iterations = 500, 92 | .lock = &rwlock, 93 | .data = data, 94 | .inc = i + 1, 95 | }; 96 | ATF_REQUIRE(pthread_create(&threads[1000 + i], NULL, 97 | stress_writer, &stress_data[1000 + i]) == 0); 98 | } 99 | 100 | for (int i = 0; i < 1010; ++i) { 101 | ATF_REQUIRE(pthread_join(threads[i], NULL) == 0); 102 | } 103 | } 104 | 105 | ATF_TP_ADD_TCS(tp) 106 | { 107 | ATF_TP_ADD_TC(tp, stress); 108 | 109 | return atf_no_error(); 110 | } 111 | -------------------------------------------------------------------------------- /test/socketpair-test.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "atf-c-leakcheck.h" 22 | 23 | #ifndef nitems 24 | #define nitems(x) (sizeof((x)) / sizeof((x)[0])) 25 | #endif 26 | 27 | ATF_TC_WITHOUT_HEAD(socketpair__simple_socketpair); 28 | ATF_TC_BODY_FD_LEAKCHECK(socketpair__simple_socketpair, tc) 29 | { 30 | int p[2] = { -1, -1 }; 31 | ATF_REQUIRE(socketpair(PF_LOCAL, SOCK_STREAM, 0, p) == 0); 32 | 33 | { 34 | struct pollfd pfd = { .fd = p[0], .events = POLLIN }; 35 | ATF_REQUIRE(poll(&pfd, 1, 0) == 0); 36 | ATF_REQUIRE(pfd.revents == 0); 37 | 38 | int ep = epoll_create1(EPOLL_CLOEXEC); 39 | ATF_REQUIRE(ep >= 0); 40 | 41 | struct epoll_event eps[32]; 42 | eps[0] = (struct epoll_event) { .events = EPOLLIN }; 43 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_ADD, p[0], &eps[0]) == 0); 44 | 45 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 46 | ATF_REQUIRE(close(ep) == 0); 47 | } 48 | 49 | { 50 | struct pollfd pfd = { .fd = p[1], .events = POLLOUT }; 51 | ATF_REQUIRE(poll(&pfd, 1, 0) == 1); 52 | ATF_REQUIRE(pfd.revents == POLLOUT); 53 | 54 | int ep = epoll_create1(EPOLL_CLOEXEC); 55 | ATF_REQUIRE(ep >= 0); 56 | 57 | struct epoll_event eps[32]; 58 | eps[0] = (struct epoll_event) { .events = EPOLLOUT }; 59 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_ADD, p[1], &eps[0]) == 0); 60 | 61 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 1); 62 | ATF_REQUIRE(eps[0].events == EPOLLOUT); 63 | ATF_REQUIRE(close(ep) == 0); 64 | } 65 | 66 | ATF_REQUIRE(close(p[0]) == 0); 67 | ATF_REQUIRE(close(p[1]) == 0); 68 | } 69 | 70 | ATF_TC_WITHOUT_HEAD(socketpair__simple_edge_triggering); 71 | ATF_TC_BODY_FD_LEAKCHECK(socketpair__simple_edge_triggering, tc) 72 | { 73 | int p[2] = { -1, -1 }; 74 | ATF_REQUIRE(socketpair(PF_LOCAL, 75 | SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, p) == 0); 76 | 77 | int ep = epoll_create1(EPOLL_CLOEXEC); 78 | ATF_REQUIRE(ep >= 0); 79 | 80 | struct epoll_event eps[32]; 81 | eps[0] = (struct epoll_event) { .events = EPOLLOUT | EPOLLET }; 82 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_ADD, p[0], &eps[0]) == 0); 83 | 84 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 1); 85 | ATF_REQUIRE(eps[0].events == EPOLLOUT); 86 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 87 | 88 | eps[0] = (struct epoll_event) { 89 | .events = EPOLLIN | EPOLLRDHUP | EPOLLOUT | EPOLLET, 90 | }; 91 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_MOD, p[0], &eps[0]) == 0); 92 | 93 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 1); 94 | ATF_REQUIRE(eps[0].events == EPOLLOUT); 95 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 96 | 97 | char c = 0; 98 | ATF_REQUIRE(write(p[1], &c, 1) == 1); 99 | 100 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 1); 101 | ATF_REQUIRE(eps[0].events == (EPOLLIN | EPOLLOUT)); 102 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 103 | 104 | ATF_REQUIRE(read(p[0], &c, 1) == 1); 105 | ATF_REQUIRE(read(p[0], &c, 1) < 0); 106 | 107 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 108 | 109 | ATF_REQUIRE(shutdown(p[1], SHUT_WR) == 0); 110 | ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); 111 | ATF_REQUIRE(eps[0].events == (EPOLLIN | EPOLLRDHUP | EPOLLOUT)); 112 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 113 | 114 | eps[0] = (struct epoll_event) { 115 | .events = EPOLLRDHUP | EPOLLOUT | EPOLLET, 116 | }; 117 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_MOD, p[0], &eps[0]) == 0); 118 | 119 | ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); 120 | ATF_REQUIRE(eps[0].events == (EPOLLRDHUP | EPOLLOUT)); 121 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 122 | 123 | #if defined(__APPLE__) 124 | ATF_REQUIRE_ERRNO(ENOTCONN, shutdown(p[0], SHUT_RD) < 0); 125 | #else 126 | ATF_REQUIRE(shutdown(p[0], SHUT_RD) == 0); 127 | 128 | ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); 129 | ATF_REQUIRE(eps[0].events == (EPOLLRDHUP | EPOLLOUT)); 130 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 131 | #endif 132 | 133 | ATF_REQUIRE(shutdown(p[0], SHUT_WR) == 0); 134 | 135 | ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); 136 | ATF_REQUIRE(eps[0].events == (EPOLLRDHUP | EPOLLOUT | EPOLLHUP)); 137 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 138 | 139 | eps[0] = (struct epoll_event) { .events = EPOLLET }; 140 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_MOD, p[0], &eps[0]) == 0); 141 | 142 | ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); 143 | ATF_REQUIRE(eps[0].events == EPOLLHUP); 144 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 145 | 146 | eps[0] = (struct epoll_event) { .events = 0 }; 147 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_MOD, p[0], &eps[0]) == 0); 148 | 149 | ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); 150 | ATF_REQUIRE(eps[0].events == EPOLLHUP); 151 | ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); 152 | ATF_REQUIRE(eps[0].events == EPOLLHUP); 153 | 154 | eps[0] = (struct epoll_event) { .events = EPOLLOUT }; 155 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_MOD, p[0], &eps[0]) == 0); 156 | 157 | ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); 158 | ATF_REQUIRE(eps[0].events == (EPOLLOUT | EPOLLHUP)); 159 | ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); 160 | ATF_REQUIRE(eps[0].events == (EPOLLOUT | EPOLLHUP)); 161 | 162 | eps[0] = (struct epoll_event) { .events = EPOLLOUT | EPOLLET }; 163 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_MOD, p[0], &eps[0]) == 0); 164 | 165 | ATF_REQUIRE(epoll_wait(ep, eps, 32, -1) == 1); 166 | ATF_REQUIRE(eps[0].events == (EPOLLOUT | EPOLLHUP)); 167 | ATF_REQUIRE(epoll_wait(ep, eps, 32, 0) == 0); 168 | 169 | ATF_REQUIRE(close(ep) == 0); 170 | 171 | ATF_REQUIRE(close(p[0]) == 0); 172 | ATF_REQUIRE(close(p[1]) == 0); 173 | } 174 | 175 | ATF_TC_WITHOUT_HEAD(socketpair__epollhup); 176 | ATF_TC_BODY_FD_LEAKCHECK(socketpair__epollhup, tc) 177 | { 178 | int p[2] = { -1, -1 }; 179 | ATF_REQUIRE(socketpair(PF_LOCAL, 180 | SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, p) == 0); 181 | 182 | int ep = epoll_create1(EPOLL_CLOEXEC); 183 | ATF_REQUIRE(ep >= 0); 184 | 185 | struct epoll_event eps[1]; 186 | eps[0] = (struct epoll_event) { .events = EPOLLET }; 187 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_ADD, p[0], &eps[0]) == 0); 188 | 189 | ATF_REQUIRE(epoll_wait(ep, eps, 1, 0) == 0); 190 | 191 | ATF_REQUIRE(shutdown(p[1], SHUT_WR) == 0); 192 | ATF_REQUIRE(epoll_wait(ep, eps, 1, 0) == 0); 193 | 194 | ATF_REQUIRE(shutdown(p[0], SHUT_WR) == 0); 195 | ATF_REQUIRE(epoll_wait(ep, eps, 1, 0) == 1); 196 | ATF_REQUIRE(eps[0].events == EPOLLHUP); 197 | ATF_REQUIRE(epoll_wait(ep, eps, 1, 0) == 0); 198 | 199 | ATF_REQUIRE(close(ep) == 0); 200 | 201 | ATF_REQUIRE(close(p[0]) == 0); 202 | ATF_REQUIRE(close(p[1]) == 0); 203 | } 204 | 205 | ATF_TC_WITHOUT_HEAD(socketpair__epollrdhup); 206 | ATF_TC_BODY_FD_LEAKCHECK(socketpair__epollrdhup, tc) 207 | { 208 | int p[2] = { -1, -1 }; 209 | ATF_REQUIRE(socketpair(PF_LOCAL, 210 | SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, p) == 0); 211 | 212 | int ep = epoll_create1(EPOLL_CLOEXEC); 213 | ATF_REQUIRE(ep >= 0); 214 | 215 | struct epoll_event eps[1]; 216 | eps[0] = (struct epoll_event) { .events = EPOLLRDHUP }; 217 | ATF_REQUIRE(epoll_ctl(ep, EPOLL_CTL_ADD, p[0], &eps[0]) == 0); 218 | 219 | ATF_REQUIRE(epoll_wait(ep, eps, 1, 0) == 0); 220 | 221 | ATF_REQUIRE(shutdown(p[1], SHUT_WR) == 0); 222 | ATF_REQUIRE(epoll_wait(ep, eps, 1, 0) == 1); 223 | ATF_REQUIRE(eps[0].events == EPOLLRDHUP); 224 | ATF_REQUIRE(epoll_wait(ep, eps, 1, 0) == 1); 225 | ATF_REQUIRE(eps[0].events == EPOLLRDHUP); 226 | 227 | ATF_REQUIRE(shutdown(p[0], SHUT_WR) == 0); 228 | ATF_REQUIRE(epoll_wait(ep, eps, 1, 0) == 1); 229 | ATF_REQUIRE(eps[0].events == (EPOLLRDHUP | EPOLLHUP)); 230 | ATF_REQUIRE(epoll_wait(ep, eps, 1, 0) == 1); 231 | ATF_REQUIRE(eps[0].events == (EPOLLRDHUP | EPOLLHUP)); 232 | 233 | ATF_REQUIRE(close(ep) == 0); 234 | 235 | ATF_REQUIRE(close(p[0]) == 0); 236 | ATF_REQUIRE(close(p[1]) == 0); 237 | } 238 | 239 | ATF_TP_ADD_TCS(tp) 240 | { 241 | ATF_TP_ADD_TC(tp, socketpair__simple_socketpair); 242 | ATF_TP_ADD_TC(tp, socketpair__simple_edge_triggering); 243 | ATF_TP_ADD_TC(tp, socketpair__epollhup); 244 | ATF_TP_ADD_TC(tp, socketpair__epollrdhup); 245 | 246 | return atf_no_error(); 247 | } 248 | -------------------------------------------------------------------------------- /test/timerfd-mock-test.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | 5 | #include 6 | 7 | #ifndef __linux__ 8 | #include 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | static unsigned int evfilt_timer_fflags; 29 | static int64_t evfilt_timer_data; 30 | static int kevent_called; 31 | 32 | #ifndef __linux__ 33 | 34 | #ifdef __NetBSD__ 35 | #define kevent_n_type size_t 36 | #else 37 | #define kevent_n_type int 38 | #endif 39 | 40 | int 41 | kevent(int kq, const struct kevent *changelist, kevent_n_type nchanges, 42 | struct kevent *eventlist, kevent_n_type nevents, 43 | const struct timespec *timeout) 44 | { 45 | (void)kq; 46 | (void)eventlist; 47 | (void)nevents; 48 | (void)timeout; 49 | 50 | ++kevent_called; 51 | 52 | if (nchanges == 1) { 53 | ATF_REQUIRE(changelist[0].filter == EVFILT_TIMER); 54 | evfilt_timer_fflags = changelist[0].fflags; 55 | evfilt_timer_data = changelist[0].data; 56 | } else if (nchanges == 2) { 57 | ATF_REQUIRE(changelist[0].filter == EVFILT_TIMER); 58 | ATF_REQUIRE(changelist[1].filter == EVFILT_TIMER); 59 | evfilt_timer_fflags = changelist[1].fflags; 60 | evfilt_timer_data = changelist[1].data; 61 | } else { 62 | ATF_REQUIRE(false); 63 | } 64 | 65 | errno = ENOSYS; 66 | return -1; 67 | } 68 | 69 | #if defined(__NetBSD__) && __NetBSD_Version__ < 999009100 70 | static unsigned int 71 | netbsd_mstohz(unsigned int ms) 72 | { 73 | return ms >= 0x20000u ? (ms / 1000u) * CLK_TCK : (ms * CLK_TCK) / 1000u; 74 | } 75 | #endif 76 | 77 | #endif 78 | 79 | ATF_TC_WITHOUT_HEAD(timerfd_mock__mocked_kevent); 80 | ATF_TC_BODY(timerfd_mock__mocked_kevent, tc) 81 | { 82 | int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); 83 | ATF_REQUIRE(tfd >= 0); 84 | 85 | for (int64_t ms = 1; ms < 3600000; ++ms) { 86 | struct itimerspec time = { 87 | .it_value.tv_sec = ms / 1000, 88 | .it_value.tv_nsec = (ms % 1000) * 1000000L, 89 | }; 90 | 91 | kevent_called = 0; 92 | 93 | int r; 94 | if ((r = timerfd_settime(tfd, 0, &time, NULL)) == 0) { 95 | atf_tc_skip("kevent could not be mocked"); 96 | } 97 | ATF_REQUIRE_ERRNO(ENOSYS, r < 0); 98 | 99 | ATF_REQUIRE(kevent_called == 1 || kevent_called == 2); 100 | 101 | #ifndef __linux__ 102 | #if defined(__FreeBSD__) || \ 103 | (defined(__NetBSD__) && __NetBSD_Version__ >= 999009100) || \ 104 | defined(__OpenBSD__) 105 | if (evfilt_timer_fflags == 0) { 106 | ATF_REQUIRE(evfilt_timer_data == ms); 107 | } else { 108 | ATF_REQUIRE(evfilt_timer_fflags == NOTE_USECONDS); 109 | ATF_REQUIRE(evfilt_timer_data == ms * 1000); 110 | } 111 | #else 112 | ATF_REQUIRE(evfilt_timer_fflags == 0); 113 | ATF_REQUIRE(evfilt_timer_data >= ms); 114 | ATF_REQUIRE(evfilt_timer_data <= UINT_MAX); 115 | 116 | #if defined(__NetBSD__) && __NetBSD_Version__ < 999009100 117 | unsigned long kernel_ms = netbsd_mstohz( 118 | (unsigned int)evfilt_timer_data) * 119 | 1000UL / CLK_TCK; 120 | #else 121 | unsigned long kernel_ms = evfilt_timer_data; 122 | #endif 123 | 124 | ATF_REQUIRE(kernel_ms >= ms); 125 | #endif 126 | #endif 127 | } 128 | 129 | ATF_REQUIRE(close(tfd) == 0); 130 | } 131 | 132 | ATF_TP_ADD_TCS(tp) 133 | { 134 | ATF_TP_ADD_TC(tp, timerfd_mock__mocked_kevent); 135 | 136 | return atf_no_error(); 137 | } 138 | -------------------------------------------------------------------------------- /test/timerfd-root-test.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "atf-c-leakcheck.h" 27 | 28 | static struct timespec current_time; 29 | static void 30 | reset_time(void) 31 | { 32 | (void)clock_settime(CLOCK_REALTIME, ¤t_time); 33 | } 34 | 35 | static void 36 | clock_settime_or_skip_test(clockid_t clockid, struct timespec const *ts) 37 | { 38 | int r = clock_settime(clockid, ts); 39 | if (r < 0 && errno == EPERM) { 40 | atf_tc_skip("root required"); 41 | } 42 | ATF_REQUIRE(r == 0); 43 | } 44 | 45 | ATF_TC(timerfd_root__zero_read_on_abs_realtime); 46 | ATF_TC_HEAD(timerfd_root__zero_read_on_abs_realtime, tc) 47 | { 48 | atf_tc_set_md_var(tc, "X-ctest.properties", 49 | "RUN_SERIAL TRUE" 50 | #ifdef __APPLE__ 51 | " ENVIRONMENT ASL_DISABLE=1" 52 | #endif 53 | ); 54 | } 55 | ATF_TC_BODY_FD_LEAKCHECK(timerfd_root__zero_read_on_abs_realtime, tc) 56 | { 57 | bool netbsd_quirks = false; 58 | 59 | int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); 60 | ATF_REQUIRE(tfd >= 0); 61 | 62 | ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); 63 | ATF_REQUIRE(atexit(reset_time) == 0); 64 | 65 | ATF_REQUIRE(timerfd_settime(tfd, TFD_TIMER_ABSTIME, 66 | &(struct itimerspec) { 67 | .it_value = current_time, 68 | .it_interval.tv_sec = 1, 69 | .it_interval.tv_nsec = 0, 70 | }, 71 | NULL) == 0); 72 | 73 | ATF_REQUIRE( 74 | poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, -1) == 1); 75 | 76 | clock_settime_or_skip_test(CLOCK_REALTIME, 77 | &(struct timespec) { 78 | .tv_sec = current_time.tv_sec - 1, 79 | .tv_nsec = current_time.tv_nsec, 80 | }); 81 | 82 | uint64_t exp; 83 | ssize_t r = read(tfd, &exp, sizeof(exp)); 84 | if ( 85 | #ifdef __NetBSD__ 86 | r == sizeof(exp) && exp == 1 87 | #else 88 | false 89 | #endif 90 | ) { 91 | netbsd_quirks = true; 92 | } else { 93 | ATF_REQUIRE_MSG(r == 0, "r: %d, errno: %d", (int)r, errno); 94 | } 95 | 96 | { 97 | int r = fcntl(tfd, F_GETFL); 98 | ATF_REQUIRE(r >= 0); 99 | r = fcntl(tfd, F_SETFL, r | O_NONBLOCK); 100 | #ifdef __NetBSD__ 101 | /* EPASSTHROUGH in userspace, should not happen. */ 102 | if (r < 0 && errno == -4) { 103 | atf_tc_skip( 104 | "NetBSD's native timerfd does not support F_SETFL."); 105 | } 106 | #endif 107 | ATF_REQUIRE(r >= 0); 108 | } 109 | 110 | r = read(tfd, &exp, sizeof(exp)); 111 | ATF_REQUIRE_ERRNO(EAGAIN, r < 0); 112 | 113 | current_time.tv_sec += 1; 114 | ATF_REQUIRE(poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, 115 | 1800) == 1); 116 | r = read(tfd, &exp, sizeof(exp)); 117 | ATF_REQUIRE(r == (ssize_t)sizeof(exp)); 118 | ATF_REQUIRE(exp == 1); 119 | 120 | ATF_REQUIRE(close(tfd) == 0); 121 | 122 | if (netbsd_quirks) { 123 | atf_tc_skip("NetBSD has some timerfd quirks"); 124 | } 125 | } 126 | 127 | ATF_TC(timerfd_root__read_on_abs_realtime_no_interval); 128 | ATF_TC_HEAD(timerfd_root__read_on_abs_realtime_no_interval, tc) 129 | { 130 | atf_tc_set_md_var(tc, "X-ctest.properties", 131 | "RUN_SERIAL TRUE" 132 | #ifdef __APPLE__ 133 | " ENVIRONMENT ASL_DISABLE=1" 134 | #endif 135 | ); 136 | } 137 | ATF_TC_BODY_FD_LEAKCHECK(timerfd_root__read_on_abs_realtime_no_interval, tc) 138 | { 139 | int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); 140 | ATF_REQUIRE(tfd >= 0); 141 | 142 | ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); 143 | ATF_REQUIRE(atexit(reset_time) == 0); 144 | 145 | ATF_REQUIRE(timerfd_settime(tfd, TFD_TIMER_ABSTIME, 146 | &(struct itimerspec) { 147 | .it_value = current_time, 148 | .it_interval.tv_sec = 0, 149 | .it_interval.tv_nsec = 0, 150 | }, 151 | NULL) == 0); 152 | 153 | ATF_REQUIRE( 154 | poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, -1) == 1); 155 | 156 | clock_settime_or_skip_test(CLOCK_REALTIME, 157 | &(struct timespec) { 158 | .tv_sec = current_time.tv_sec - 1, 159 | .tv_nsec = current_time.tv_nsec, 160 | }); 161 | 162 | uint64_t exp; 163 | ssize_t r = read(tfd, &exp, sizeof(exp)); 164 | ATF_REQUIRE(r == (ssize_t)sizeof(exp)); 165 | ATF_REQUIRE(exp == 1); 166 | 167 | ATF_REQUIRE(close(tfd) == 0); 168 | } 169 | 170 | ATF_TC(timerfd_root__cancel_on_set); 171 | ATF_TC_HEAD(timerfd_root__cancel_on_set, tc) 172 | { 173 | atf_tc_set_md_var(tc, "X-ctest.properties", 174 | "RUN_SERIAL TRUE" 175 | #ifdef __APPLE__ 176 | " ENVIRONMENT ASL_DISABLE=1" 177 | #endif 178 | ); 179 | } 180 | ATF_TC_BODY_FD_LEAKCHECK(timerfd_root__cancel_on_set, tc) 181 | { 182 | bool netbsd_quirks = false; 183 | 184 | int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); 185 | ATF_REQUIRE(tfd >= 0); 186 | 187 | ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); 188 | ATF_REQUIRE(atexit(reset_time) == 0); 189 | 190 | ATF_REQUIRE( 191 | timerfd_settime(tfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, 192 | &(struct itimerspec) { 193 | .it_value.tv_sec = current_time.tv_sec + 10, 194 | .it_value.tv_nsec = current_time.tv_nsec, 195 | .it_interval.tv_sec = 0, 196 | .it_interval.tv_nsec = 0, 197 | }, 198 | NULL) == 0); 199 | 200 | clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); 201 | 202 | ATF_REQUIRE( 203 | poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, -1) == 1); 204 | 205 | { 206 | int r = timerfd_settime(tfd, 207 | TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, 208 | &(struct itimerspec) { 209 | .it_value.tv_sec = current_time.tv_sec, 210 | .it_value.tv_nsec = current_time.tv_nsec, 211 | .it_interval.tv_sec = 0, 212 | .it_interval.tv_nsec = 0, 213 | }, 214 | NULL); 215 | if ( 216 | #ifdef __NetBSD__ 217 | r == 0 218 | #else 219 | false 220 | #endif 221 | ) { 222 | netbsd_quirks = true; 223 | } else { 224 | ATF_REQUIRE_ERRNO(ECANCELED, r < 0); 225 | } 226 | } 227 | 228 | ATF_REQUIRE(poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, 229 | 800) == 1); 230 | 231 | uint64_t exp; 232 | ssize_t r; 233 | 234 | r = read(tfd, &exp, sizeof(exp)); 235 | ATF_REQUIRE(r == (ssize_t)sizeof(exp)); 236 | ATF_REQUIRE(exp == 1); 237 | 238 | ATF_REQUIRE( 239 | timerfd_settime(tfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, 240 | &(struct itimerspec) { 241 | .it_value.tv_sec = current_time.tv_sec + 1, 242 | .it_value.tv_nsec = current_time.tv_nsec, 243 | .it_interval.tv_sec = 1, 244 | .it_interval.tv_nsec = 0, 245 | }, 246 | NULL) == 0); 247 | 248 | clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); 249 | 250 | ATF_REQUIRE( 251 | poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, -1) == 1); 252 | 253 | r = read(tfd, &exp, sizeof(exp)); 254 | ATF_REQUIRE_ERRNO(ECANCELED, r < 0); 255 | 256 | r = read(tfd, &exp, sizeof(exp)); 257 | if ( 258 | #ifdef __NetBSD__ 259 | r < 0 && errno == ECANCELED 260 | #else 261 | false 262 | #endif 263 | ) { 264 | netbsd_quirks = true; 265 | } else { 266 | current_time.tv_sec += 1; 267 | ATF_REQUIRE_MSG(r == (ssize_t)sizeof(exp), "%d %d", (int)r, 268 | errno); 269 | ATF_REQUIRE(exp == 1); 270 | } 271 | 272 | ATF_REQUIRE( 273 | timerfd_settime(tfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, 274 | &(struct itimerspec) { 275 | .it_value.tv_sec = current_time.tv_sec + 1, 276 | .it_value.tv_nsec = current_time.tv_nsec, 277 | .it_interval.tv_sec = 1, 278 | .it_interval.tv_nsec = 0, 279 | }, 280 | NULL) == 0); 281 | 282 | clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); 283 | current_time.tv_sec += 2; 284 | ATF_REQUIRE(nanosleep(&(struct timespec) { .tv_sec = 2 }, NULL) == 0); 285 | 286 | r = read(tfd, &exp, sizeof(exp)); 287 | ATF_REQUIRE_ERRNO(ECANCELED, r < 0); 288 | 289 | r = poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, 3000); 290 | if ( 291 | #ifdef __NetBSD__ 292 | r == 1 293 | #else 294 | false 295 | #endif 296 | ) { 297 | netbsd_quirks = true; 298 | } else { 299 | ATF_REQUIRE(r == 0); 300 | current_time.tv_sec += 3; 301 | } 302 | 303 | ATF_REQUIRE(close(tfd) == 0); 304 | 305 | if (netbsd_quirks) { 306 | atf_tc_skip("NetBSD has some timerfd quirks"); 307 | } 308 | } 309 | 310 | ATF_TC(timerfd_root__cancel_on_set_init); 311 | ATF_TC_HEAD(timerfd_root__cancel_on_set_init, tc) 312 | { 313 | atf_tc_set_md_var(tc, "X-ctest.properties", 314 | "RUN_SERIAL TRUE" 315 | #ifdef __APPLE__ 316 | " ENVIRONMENT ASL_DISABLE=1" 317 | #endif 318 | ); 319 | } 320 | ATF_TC_BODY_FD_LEAKCHECK(timerfd_root__cancel_on_set_init, tc) 321 | { 322 | int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); 323 | ATF_REQUIRE(tfd >= 0); 324 | 325 | ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); 326 | ATF_REQUIRE(atexit(reset_time) == 0); 327 | 328 | clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); 329 | 330 | ATF_REQUIRE( 331 | timerfd_settime(tfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, 332 | &(struct itimerspec) { 333 | .it_value.tv_sec = current_time.tv_sec + 10, 334 | .it_value.tv_nsec = current_time.tv_nsec, 335 | .it_interval.tv_sec = 0, 336 | .it_interval.tv_nsec = 0, 337 | }, 338 | NULL) == 0); 339 | 340 | clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); 341 | 342 | int r = timerfd_settime(tfd, 343 | TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, 344 | &(struct itimerspec) { 345 | .it_value.tv_sec = current_time.tv_sec + 10, 346 | .it_value.tv_nsec = current_time.tv_nsec, 347 | .it_interval.tv_sec = 0, 348 | .it_interval.tv_nsec = 0, 349 | }, 350 | NULL); 351 | if ( 352 | #ifdef __NetBSD__ 353 | r == 0 354 | #else 355 | false 356 | #endif 357 | ) { 358 | atf_tc_skip("NetBSD has some timerfd quirks"); 359 | } else { 360 | ATF_REQUIRE_ERRNO(ECANCELED, r < 0); 361 | } 362 | ATF_REQUIRE(close(tfd) == 0); 363 | } 364 | 365 | static void * 366 | clock_change_thread(void *arg) 367 | { 368 | (void)arg; 369 | 370 | fprintf(stderr, "clock change\n"); 371 | clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); 372 | 373 | current_time.tv_sec += 2; 374 | ATF_REQUIRE(nanosleep(&(struct timespec) { .tv_sec = 2 }, NULL) == 0); 375 | 376 | fprintf(stderr, "clock change\n"); 377 | clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); 378 | 379 | return NULL; 380 | } 381 | 382 | ATF_TC(timerfd_root__clock_change_notification); 383 | ATF_TC_HEAD(timerfd_root__clock_change_notification, tc) 384 | { 385 | atf_tc_set_md_var(tc, "timeout", "10"); 386 | atf_tc_set_md_var(tc, "X-ctest.properties", 387 | "RUN_SERIAL TRUE" 388 | #ifdef __APPLE__ 389 | " ENVIRONMENT ASL_DISABLE=1" 390 | #endif 391 | ); 392 | } 393 | ATF_TC_BODY_FD_LEAKCHECK(timerfd_root__clock_change_notification, tc) 394 | { 395 | ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); 396 | ATF_REQUIRE(atexit(reset_time) == 0); 397 | 398 | clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); 399 | 400 | #define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) 401 | struct itimerspec its = { 402 | .it_value.tv_sec = TIME_T_MAX, 403 | }; 404 | #undef TIME_T_MAX 405 | 406 | int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); 407 | ATF_REQUIRE(tfd >= 0); 408 | 409 | ATF_REQUIRE( 410 | timerfd_settime(tfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, 411 | &its, NULL) == 0); 412 | 413 | pthread_t clock_changer; 414 | ATF_REQUIRE(pthread_create(&clock_changer, NULL, /**/ 415 | clock_change_thread, NULL) == 0); 416 | 417 | uint64_t exp; 418 | ssize_t r; 419 | 420 | r = read(tfd, &exp, sizeof(exp)); 421 | ATF_REQUIRE_ERRNO(ECANCELED, r < 0); 422 | fprintf(stderr, "clock change detected\n"); 423 | 424 | r = read(tfd, &exp, sizeof(exp)); 425 | ATF_REQUIRE_ERRNO(ECANCELED, r < 0); 426 | fprintf(stderr, "clock change detected\n"); 427 | 428 | ATF_REQUIRE(pthread_join(clock_changer, NULL) == 0); 429 | 430 | ATF_REQUIRE(close(tfd) == 0); 431 | } 432 | 433 | ATF_TC(timerfd_root__advance_time_no_cancel); 434 | ATF_TC_HEAD(timerfd_root__advance_time_no_cancel, tc) 435 | { 436 | atf_tc_set_md_var(tc, "X-ctest.properties", 437 | "RUN_SERIAL TRUE" 438 | #ifdef __APPLE__ 439 | " ENVIRONMENT ASL_DISABLE=1" 440 | #endif 441 | ); 442 | } 443 | ATF_TC_BODY_FD_LEAKCHECK(timerfd_root__advance_time_no_cancel, tc) 444 | { 445 | int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); 446 | ATF_REQUIRE(tfd >= 0); 447 | 448 | ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); 449 | ATF_REQUIRE(atexit(reset_time) == 0); 450 | 451 | ATF_REQUIRE(timerfd_settime(tfd, TFD_TIMER_ABSTIME, 452 | &(struct itimerspec) { 453 | .it_value.tv_sec = current_time.tv_sec + 10, 454 | .it_value.tv_nsec = current_time.tv_nsec, 455 | .it_interval.tv_sec = 0, 456 | .it_interval.tv_nsec = 0, 457 | }, 458 | NULL) == 0); 459 | 460 | current_time.tv_sec += 9; 461 | clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); 462 | current_time.tv_sec -= 8; 463 | 464 | { 465 | int r = poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 466 | 1, 1800); 467 | #ifdef __NetBSD__ 468 | if (r == 0) { 469 | atf_tc_skip("NetBSD has some timerfd quirks"); 470 | } 471 | #endif 472 | ATF_REQUIRE(r == 1); 473 | } 474 | 475 | uint64_t exp; 476 | ssize_t r; 477 | 478 | r = read(tfd, &exp, sizeof(exp)); 479 | ATF_REQUIRE(r == (ssize_t)sizeof(exp)); 480 | ATF_REQUIRE(exp == 1); 481 | 482 | ATF_REQUIRE(close(tfd) == 0); 483 | } 484 | 485 | 486 | ATF_TP_ADD_TCS(tp) 487 | { 488 | ATF_TP_ADD_TC(tp, timerfd_root__zero_read_on_abs_realtime); 489 | ATF_TP_ADD_TC(tp, timerfd_root__read_on_abs_realtime_no_interval); 490 | ATF_TP_ADD_TC(tp, timerfd_root__cancel_on_set); 491 | ATF_TP_ADD_TC(tp, timerfd_root__cancel_on_set_init); 492 | ATF_TP_ADD_TC(tp, timerfd_root__clock_change_notification); 493 | ATF_TP_ADD_TC(tp, timerfd_root__advance_time_no_cancel); 494 | 495 | return atf_no_error(); 496 | } 497 | --------------------------------------------------------------------------------