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