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