├── .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