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

98.1% Lines (51 / 52) 62.5% Branches (25 / 40) 100.0% Functions (8 / 8, 1 excl)
posix_fd_gate.hpp
f(x) Functions (9)
Line Branch 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 887x int fd_ = -1;
43 887x int users_ = 0;
44 std::vector<int> deferred_;
45
46 public:
47 /// Destroy the gate, closing any descriptor still deferred.
48 1774x ~posix_fd_gate()
49 887x {
50
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 887 times.
887x for (int fd : deferred_)
51
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ ::close(fd);
52 1774x }
53
54 2661x 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 1297x void open(int fd) noexcept
60 {
61
1/2
✓ Branch 0 taken 1297 times.
✗ Branch 1 not taken.
1297x std::lock_guard<std::mutex> lock(mutex_);
62 1297x fd_ = fd;
63 1297x }
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 578x bool enter(int fd) noexcept
71 {
72
1/2
✓ Branch 0 taken 578 times.
✗ Branch 1 not taken.
578x std::lock_guard<std::mutex> lock(mutex_);
73
3/4
✓ Branch 0 taken 578 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 577 times.
578x if (fd < 0 || fd != fd_)
74 1x return false;
75 577x ++users_;
76 577x return true;
77 578x }
78
79 /// Leave the gate after a successful @ref enter.
80 577x void leave() noexcept
81 {
82 577x std::vector<int> close_now;
83 {
84
1/2
✓ Branch 0 taken 577 times.
✗ Branch 1 not taken.
577x std::lock_guard<std::mutex> lock(mutex_);
85
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 577 times.
577x if (--users_ == 0)
86 577x close_now.swap(deferred_);
87 577x }
88
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 577 times.
607x for (int fd : close_now)
89
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30x ::close(fd);
90 577x }
91
92 /// Close the held descriptor once no worker is using it.
93 4096x void close() noexcept
94 {
95 4096x int fd = -1;
96 {
97
1/2
✓ Branch 0 taken 4096 times.
✗ Branch 1 not taken.
4096x std::lock_guard<std::mutex> lock(mutex_);
98 4096x fd = fd_;
99 4096x fd_ = -1;
100
4/4
✓ Branch 0 taken 1284 times.
✓ Branch 1 taken 2812 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 1254 times.
4096x if (fd >= 0 && users_ > 0)
101 {
102
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30x deferred_.push_back(fd);
103 30x fd = -1;
104 30x }
105 4096x }
106
2/2
✓ Branch 0 taken 1254 times.
✓ Branch 1 taken 2842 times.
4096x if (fd >= 0)
107
1/2
✓ Branch 0 taken 1254 times.
✗ Branch 1 not taken.
1254x ::close(fd);
108 4096x }
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 17x bool release() noexcept
117 {
118
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17x std::lock_guard<std::mutex> lock(mutex_);
119
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 13 times.
17x if (users_ > 0)
120 4x return false;
121 13x fd_ = -1;
122 13x return true;
123 17x }
124
125 /// Return true if no descriptor is held and no worker is inside.
126 64x bool idle() noexcept
127 {
128
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64x std::lock_guard<std::mutex> lock(mutex_);
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
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