93.75% Lines (45/48)
100.00% Functions (8/8)
| TLA | Baseline | Branch | ||||||
|---|---|---|---|---|---|---|---|---|
| Line | Hits | Code | Line | Hits | Code | |||
| 1 | // | 1 | // | |||||
| 2 | // Copyright (c) 2026 Steve Gerbino | 2 | // Copyright (c) 2026 Steve Gerbino | |||||
| 3 | // | 3 | // | |||||
| 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |||||
| 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |||||
| 6 | // | 6 | // | |||||
| 7 | // Official repository: https://github.com/cppalliance/corosio | 7 | // Official repository: https://github.com/cppalliance/corosio | |||||
| 8 | // | 8 | // | |||||
| 9 | 9 | |||||||
| 10 | #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_FD_GATE_HPP | 10 | #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_FD_GATE_HPP | |||||
| 11 | #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_FD_GATE_HPP | 11 | #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_FD_GATE_HPP | |||||
| 12 | 12 | |||||||
| 13 | #include <boost/corosio/detail/platform.hpp> | 13 | #include <boost/corosio/detail/platform.hpp> | |||||
| 14 | 14 | |||||||
| 15 | #if BOOST_COROSIO_POSIX | 15 | #if BOOST_COROSIO_POSIX | |||||
| 16 | 16 | |||||||
| 17 | #include <mutex> | 17 | #include <mutex> | |||||
| 18 | #include <vector> | 18 | #include <vector> | |||||
| 19 | 19 | |||||||
| 20 | #include <unistd.h> | 20 | #include <unistd.h> | |||||
| 21 | 21 | |||||||
| 22 | namespace boost::corosio::detail { | 22 | namespace boost::corosio::detail { | |||||
| 23 | 23 | |||||||
| 24 | /** Keep a descriptor number allocated while pool workers use it. | 24 | /** Keep a descriptor number allocated while pool workers use it. | |||||
| 25 | 25 | |||||||
| 26 | A worker copies the number when its operation is queued and calls | 26 | A worker copies the number when its operation is queued and calls | |||||
| 27 | the kernel later, on another thread. Once the object closes the | 27 | the kernel later, on another thread. Once the object closes the | |||||
| 28 | descriptor, the number can be handed to any other open in the | 28 | descriptor, the number can be handed to any other open in the | |||||
| 29 | process, and a worker still on its way to the kernel would then | 29 | process, and a worker still on its way to the kernel would then | |||||
| 30 | transfer through an unrelated file. Workers enter the gate before | 30 | transfer through an unrelated file. Workers enter the gate before | |||||
| 31 | their system call and leave it after; closing while one is inside | 31 | their system call and leave it after; closing while one is inside | |||||
| 32 | defers the `::close` to the last one out, so the number stays | 32 | defers the `::close` to the last one out, so the number stays | |||||
| 33 | allocated to the file the worker meant. | 33 | allocated to the file the worker meant. | |||||
| 34 | 34 | |||||||
| 35 | @par Thread Safety | 35 | @par Thread Safety | |||||
| 36 | Distinct objects: Safe. | 36 | Distinct objects: Safe. | |||||
| 37 | Shared objects: Safe. | 37 | Shared objects: Safe. | |||||
| 38 | */ | 38 | */ | |||||
| 39 | class posix_fd_gate | 39 | class posix_fd_gate | |||||
| 40 | { | 40 | { | |||||
| 41 | std::mutex mutex_; | 41 | std::mutex mutex_; | |||||
| 42 | int fd_ = -1; | 42 | int fd_ = -1; | |||||
| 43 | int users_ = 0; | 43 | int users_ = 0; | |||||
| 44 | std::vector<int> deferred_; | 44 | std::vector<int> deferred_; | |||||
| 45 | 45 | |||||||
| 46 | public: | 46 | public: | |||||
| 47 | /// Destroy the gate, closing any descriptor still deferred. | 47 | /// Destroy the gate, closing any descriptor still deferred. | |||||
| HITCBC | 48 | 939 | ~posix_fd_gate() | 48 | 939 | ~posix_fd_gate() | ||
| 49 | { | 49 | { | |||||
| HITCBC | 50 | 939 | for (int fd : deferred_) | 50 | 939 | for (int fd : deferred_) | ||
| MISUBC | 51 | ✗ | ::close(fd); | 51 | ✗ | ::close(fd); | ||
| HITCBC | 52 | 939 | } | 52 | 939 | } | ||
| 53 | 53 | |||||||
| HITCBC | 54 | 939 | posix_fd_gate() = default; | 54 | 939 | posix_fd_gate() = default; | ||
| 55 | posix_fd_gate(posix_fd_gate const&) = delete; | 55 | posix_fd_gate(posix_fd_gate const&) = delete; | |||||
| 56 | posix_fd_gate& operator=(posix_fd_gate const&) = delete; | 56 | posix_fd_gate& operator=(posix_fd_gate const&) = delete; | |||||
| 57 | 57 | |||||||
| 58 | /// Admit workers for @p fd, the descriptor the object now holds. | 58 | /// Admit workers for @p fd, the descriptor the object now holds. | |||||
| HITCBC | 59 | 1352 | void open(int fd) noexcept | 59 | 1352 | void open(int fd) noexcept | ||
| 60 | { | 60 | { | |||||
| HITCBC | 61 | 1352 | std::lock_guard<std::mutex> lock(mutex_); | 61 | 1352 | std::lock_guard<std::mutex> lock(mutex_); | ||
| HITCBC | 62 | 1352 | fd_ = fd; | 62 | 1352 | fd_ = fd; | ||
| HITCBC | 63 | 1352 | } | 63 | 1352 | } | ||
| 64 | 64 | |||||||
| 65 | /** Enter the gate for a system call on @p fd. | 65 | /** Enter the gate for a system call on @p fd. | |||||
| 66 | 66 | |||||||
| 67 | @return False if the object no longer holds @p fd; the caller | 67 | @return False if the object no longer holds @p fd; the caller | |||||
| 68 | must not use the number. | 68 | must not use the number. | |||||
| 69 | */ | 69 | */ | |||||
| HITCBC | 70 | 620 | bool enter(int fd) noexcept | 70 | 607 | bool enter(int fd) noexcept | ||
| 71 | { | 71 | { | |||||
| HITCBC | 72 | 620 | std::lock_guard<std::mutex> lock(mutex_); | 72 | 607 | std::lock_guard<std::mutex> lock(mutex_); | ||
| HITCBC | 73 | 620 | if (fd < 0 || fd != fd_) | 73 | 607 | if (fd < 0 || fd != fd_) | ||
| MISUBC | 74 | ✗ | return false; | 74 | ✗ | return false; | ||
| HITCBC | 75 | 620 | ++users_; | 75 | 607 | ++users_; | ||
| HITCBC | 76 | 620 | return true; | 76 | 607 | return true; | ||
| HITCBC | 77 | 620 | } | 77 | 607 | } | ||
| 78 | 78 | |||||||
| 79 | /// Leave the gate after a successful @ref enter. | 79 | /// Leave the gate after a successful @ref enter. | |||||
| HITCBC | 80 | 620 | void leave() noexcept | 80 | 607 | void leave() noexcept | ||
| 81 | { | 81 | { | |||||
| HITCBC | 82 | 620 | std::vector<int> close_now; | 82 | 607 | std::vector<int> close_now; | ||
| 83 | { | 83 | { | |||||
| HITCBC | 84 | 620 | std::lock_guard<std::mutex> lock(mutex_); | 84 | 607 | std::lock_guard<std::mutex> lock(mutex_); | ||
| HITCBC | 85 | 620 | if (--users_ == 0) | 85 | 607 | if (--users_ == 0) | ||
| HITCBC | 86 | 620 | close_now.swap(deferred_); | 86 | 607 | close_now.swap(deferred_); | ||
| HITCBC | 87 | 620 | } | 87 | 607 | } | ||
| HITCBC | 88 | 638 | for (int fd : close_now) | 88 | 623 | for (int fd : close_now) | ||
| HITCBC | 89 | 18 | ::close(fd); | 89 | 16 | ::close(fd); | ||
| HITCBC | 90 | 620 | } | 90 | 607 | } | ||
| 91 | 91 | |||||||
| 92 | /// Close the held descriptor once no worker is using it. | 92 | /// Close the held descriptor once no worker is using it. | |||||
| HITCBC | 93 | 4315 | void close() noexcept | 93 | 4315 | void close() noexcept | ||
| 94 | { | 94 | { | |||||
| HITCBC | 95 | 4315 | int fd = -1; | 95 | 4315 | int fd = -1; | ||
| 96 | { | 96 | { | |||||
| HITCBC | 97 | 4315 | std::lock_guard<std::mutex> lock(mutex_); | 97 | 4315 | std::lock_guard<std::mutex> lock(mutex_); | ||
| HITCBC | 98 | 4315 | fd = fd_; | 98 | 4315 | fd = fd_; | ||
| HITCBC | 99 | 4315 | fd_ = -1; | 99 | 4315 | fd_ = -1; | ||
| HITCBC | 100 | 4315 | if (fd >= 0 && users_ > 0) | 100 | 4315 | if (fd >= 0 && users_ > 0) | ||
| 101 | { | 101 | { | |||||
| HITCBC | 102 | 18 | deferred_.push_back(fd); | 102 | 16 | deferred_.push_back(fd); | ||
| HITCBC | 103 | 18 | fd = -1; | 103 | 16 | fd = -1; | ||
| 104 | } | 104 | } | |||||
| HITCBC | 105 | 4315 | } | 105 | 4315 | } | ||
| HITCBC | 106 | 4315 | if (fd >= 0) | 106 | 4315 | if (fd >= 0) | ||
| HITCBC | 107 | 1325 | ::close(fd); | 107 | 1327 | ::close(fd); | ||
| HITCBC | 108 | 4315 | } | 108 | 4315 | } | ||
| 109 | 109 | |||||||
| 110 | /** Stop admitting workers for the held descriptor, keeping it open. | 110 | /** Stop admitting workers for the held descriptor, keeping it open. | |||||
| 111 | 111 | |||||||
| 112 | @return False, leaving the descriptor admitted, if a worker is | 112 | @return False, leaving the descriptor admitted, if a worker is | |||||
| 113 | using it; the caller cannot take ownership of a number a | 113 | using it; the caller cannot take ownership of a number a | |||||
| 114 | worker may still pass to the kernel. | 114 | worker may still pass to the kernel. | |||||
| 115 | */ | 115 | */ | |||||
| HITCBC | 116 | 9 | bool release() noexcept | 116 | 9 | bool release() noexcept | ||
| 117 | { | 117 | { | |||||
| HITCBC | 118 | 9 | std::lock_guard<std::mutex> lock(mutex_); | 118 | 9 | std::lock_guard<std::mutex> lock(mutex_); | ||
| HITCBC | 119 | 9 | if (users_ > 0) | 119 | 9 | if (users_ > 0) | ||
| MISUBC | 120 | ✗ | return false; | 120 | ✗ | return false; | ||
| HITCBC | 121 | 9 | fd_ = -1; | 121 | 9 | fd_ = -1; | ||
| HITCBC | 122 | 9 | return true; | 122 | 9 | return true; | ||
| HITCBC | 123 | 9 | } | 123 | 9 | } | ||
| 124 | 124 | |||||||
| 125 | /// Return true if no descriptor is held and no worker is inside. | 125 | /// Return true if no descriptor is held and no worker is inside. | |||||
| HITCBC | 126 | 64 | bool idle() noexcept | 126 | 64 | bool idle() noexcept | ||
| 127 | { | 127 | { | |||||
| HITCBC | 128 | 64 | std::lock_guard<std::mutex> lock(mutex_); | 128 | 64 | std::lock_guard<std::mutex> lock(mutex_); | ||
| HITCBC | 129 | 64 | return fd_ == -1 && users_ == 0; | 129 | 64 | return fd_ == -1 && users_ == 0; | ||
| HITCBC | 130 | 64 | } | 130 | 64 | } | ||
| 131 | }; | 131 | }; | |||||
| 132 | 132 | |||||||
| 133 | } // namespace boost::corosio::detail | 133 | } // namespace boost::corosio::detail | |||||
| 134 | 134 | |||||||
| 135 | #endif // BOOST_COROSIO_POSIX | 135 | #endif // BOOST_COROSIO_POSIX | |||||
| 136 | 136 | |||||||
| 137 | #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_FD_GATE_HPP | 137 | #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_FD_GATE_HPP | |||||