include/boost/corosio/native/detail/posix/posix_fd_gate.hpp

95.8% Lines (46 / 48) 100.0% Functions (8 / 8)
posix_fd_gate.hpp
f(x) Functions (8)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
3 //
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)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_FD_GATE_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_FD_GATE_HPP
12
13 #include <boost/corosio/detail/platform.hpp>
14
15 #if BOOST_COROSIO_POSIX
16
17 #include <mutex>
18 #include <vector>
19
20 #include <unistd.h>
21
22 namespace boost::corosio::detail {
23
24 /** Keep a descriptor number allocated while pool workers use it.
25
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
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
30 transfer through an unrelated file. Workers enter the gate before
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
33 allocated to the file the worker meant.
34
35 @par Thread Safety
36 Distinct objects: Safe.
37 Shared objects: Safe.
38 */
39 class posix_fd_gate
40 {
41 std::mutex mutex_;
42 int fd_ = -1;
43 int users_ = 0;
44 std::vector<int> deferred_;
45
46 public:
47 /// Destroy the gate, closing any descriptor still deferred.
48 951x ~posix_fd_gate()
49 {
50 951x for (int fd : deferred_)
51 ✗ ::close(fd);
52 951x }
53
54 951x posix_fd_gate() = default;
55 posix_fd_gate(posix_fd_gate const&) = delete;
56 posix_fd_gate& operator=(posix_fd_gate const&) = delete;
57
58 /// Admit workers for @p fd, the descriptor the object now holds.
59 1364x void open(int fd) noexcept
60 {
61 1364x std::lock_guard<std::mutex> lock(mutex_);
62 1364x fd_ = fd;
63 1364x }
64
65 /** Enter the gate for a system call on @p fd.
66
67 @return False if the object no longer holds @p fd; the caller
68 must not use the number.
69 */
70 610x bool enter(int fd) noexcept
71 {
72 610x std::lock_guard<std::mutex> lock(mutex_);
73 610x if (fd < 0 || fd != fd_)
74 ✗ return false;
75 610x ++users_;
76 610x return true;
77 610x }
78
79 /// Leave the gate after a successful @ref enter.
80 610x void leave() noexcept
81 {
82 610x std::vector<int> close_now;
83 {
84 610x std::lock_guard<std::mutex> lock(mutex_);
85 610x if (--users_ == 0)
86 610x close_now.swap(deferred_);
87 610x }
88 625x for (int fd : close_now)
89 15x ::close(fd);
90 610x }
91
92 /// Close the held descriptor once no worker is using it.
93 4355x void close() noexcept
94 {
95 4355x int fd = -1;
96 {
97 4355x std::lock_guard<std::mutex> lock(mutex_);
98 4355x fd = fd_;
99 4355x fd_ = -1;
100 4355x if (fd >= 0 && users_ > 0)
101 {
102 15x deferred_.push_back(fd);
103 15x fd = -1;
104 }
105 4355x }
106 4355x if (fd >= 0)
107 1330x ::close(fd);
108 4355x }
109
110 /** Stop admitting workers for the held descriptor, keeping it open.
111
112 @return False, leaving the descriptor admitted, if a worker is
113 using it; the caller cannot take ownership of a number a
114 worker may still pass to the kernel.
115 */
116 29x bool release() noexcept
117 {
118 29x std::lock_guard<std::mutex> lock(mutex_);
119 29x if (users_ > 0)
120 10x return false;
121 19x fd_ = -1;
122 19x return true;
123 29x }
124
125 /// Return true if no descriptor is held and no worker is inside.
126 64x bool idle() noexcept
127 {
128 64x std::lock_guard<std::mutex> lock(mutex_);
129 64x return fd_ == -1 && users_ == 0;
130 64x }
131 };
132
133 } // namespace boost::corosio::detail
134
135 #endif // BOOST_COROSIO_POSIX
136
137 #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_FD_GATE_HPP
138