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