95.83% Lines (46/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   951 ~posix_fd_gate() 48   951 ~posix_fd_gate()
49   { 49   {
HITCBC 50   951 for (int fd : deferred_) 50   951 for (int fd : deferred_)
MISUBC 51   ✗ ::close(fd); 51   ✗ ::close(fd);
HITCBC 52   951 } 52   951 }
53   53  
HITCBC 54   951 posix_fd_gate() = default; 54   951 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   1364 void open(int fd) noexcept 59   1364 void open(int fd) noexcept
60   { 60   {
HITCBC 61   1364 std::lock_guard<std::mutex> lock(mutex_); 61   1364 std::lock_guard<std::mutex> lock(mutex_);
HITCBC 62   1364 fd_ = fd; 62   1364 fd_ = fd;
HITCBC 63   1364 } 63   1364 }
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   624 bool enter(int fd) noexcept 70   610 bool enter(int fd) noexcept
71   { 71   {
HITCBC 72   624 std::lock_guard<std::mutex> lock(mutex_); 72   610 std::lock_guard<std::mutex> lock(mutex_);
HITCBC 73   624 if (fd < 0 || fd != fd_) 73   610 if (fd < 0 || fd != fd_)
MISUBC 74   ✗ return false; 74   ✗ return false;
HITCBC 75   624 ++users_; 75   610 ++users_;
HITCBC 76   624 return true; 76   610 return true;
HITCBC 77   624 } 77   610 }
78   78  
79   /// Leave the gate after a successful @ref enter. 79   /// Leave the gate after a successful @ref enter.
HITCBC 80   624 void leave() noexcept 80   610 void leave() noexcept
81   { 81   {
HITCBC 82   624 std::vector<int> close_now; 82   610 std::vector<int> close_now;
83   { 83   {
HITCBC 84   624 std::lock_guard<std::mutex> lock(mutex_); 84   610 std::lock_guard<std::mutex> lock(mutex_);
HITCBC 85   624 if (--users_ == 0) 85   610 if (--users_ == 0)
HITCBC 86   624 close_now.swap(deferred_); 86   610 close_now.swap(deferred_);
HITCBC 87   624 } 87   610 }
HITCBC 88   641 for (int fd : close_now) 88   625 for (int fd : close_now)
HITCBC 89   17 ::close(fd); 89   15 ::close(fd);
HITCBC 90   624 } 90   610 }
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   4355 void close() noexcept 93   4355 void close() noexcept
94   { 94   {
HITCBC 95   4355 int fd = -1; 95   4355 int fd = -1;
96   { 96   {
HITCBC 97   4355 std::lock_guard<std::mutex> lock(mutex_); 97   4355 std::lock_guard<std::mutex> lock(mutex_);
HITCBC 98   4355 fd = fd_; 98   4355 fd = fd_;
HITCBC 99   4355 fd_ = -1; 99   4355 fd_ = -1;
HITCBC 100   4355 if (fd >= 0 && users_ > 0) 100   4355 if (fd >= 0 && users_ > 0)
101   { 101   {
HITCBC 102   17 deferred_.push_back(fd); 102   15 deferred_.push_back(fd);
HITCBC 103   17 fd = -1; 103   15 fd = -1;
104   } 104   }
HITCBC 105   4355 } 105   4355 }
HITCBC 106   4355 if (fd >= 0) 106   4355 if (fd >= 0)
HITCBC 107   1328 ::close(fd); 107   1330 ::close(fd);
HITCBC 108   4355 } 108   4355 }
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   29 bool release() noexcept 116   29 bool release() noexcept
117   { 117   {
HITCBC 118   29 std::lock_guard<std::mutex> lock(mutex_); 118   29 std::lock_guard<std::mutex> lock(mutex_);
HITCBC 119   29 if (users_ > 0) 119   29 if (users_ > 0)
HITCBC 120   10 return false; 120   10 return false;
HITCBC 121   19 fd_ = -1; 121   19 fd_ = -1;
HITCBC 122   19 return true; 122   19 return true;
HITCBC 123   29 } 123   29 }
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