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

93.8% Lines (45 / 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 939x ~posix_fd_gate()
49 {
50 939x for (int fd : deferred_)
51 ✗ ::close(fd);
52 939x }
53
54 939x 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 1352x void open(int fd) noexcept
60 {
61 1352x std::lock_guard<std::mutex> lock(mutex_);
62 1352x fd_ = fd;
63 1352x }
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 607x bool enter(int fd) noexcept
71 {
72 607x std::lock_guard<std::mutex> lock(mutex_);
73 607x if (fd < 0 || fd != fd_)
74 ✗ return false;
75 607x ++users_;
76 607x return true;
77 607x }
78
79 /// Leave the gate after a successful @ref enter.
80 607x void leave() noexcept
81 {
82 607x std::vector<int> close_now;
83 {
84 607x std::lock_guard<std::mutex> lock(mutex_);
85 607x if (--users_ == 0)
86 607x close_now.swap(deferred_);
87 607x }
88 623x for (int fd : close_now)
89 16x ::close(fd);
90 607x }
91
92 /// Close the held descriptor once no worker is using it.
93 4315x void close() noexcept
94 {
95 4315x int fd = -1;
96 {
97 4315x std::lock_guard<std::mutex> lock(mutex_);
98 4315x fd = fd_;
99 4315x fd_ = -1;
100 4315x if (fd >= 0 && users_ > 0)
101 {
102 16x deferred_.push_back(fd);
103 16x fd = -1;
104 }
105 4315x }
106 4315x if (fd >= 0)
107 1327x ::close(fd);
108 4315x }
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 9x bool release() noexcept
117 {
118 9x std::lock_guard<std::mutex> lock(mutex_);
119 9x if (users_ > 0)
120 ✗ return false;
121 9x fd_ = -1;
122 9x return true;
123 9x }
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