Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
native_handle.cpp
Go to the documentation of this file.
1/* Flow-IPC: Core
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
21#include <flow/error/error.hpp>
22#include <boost/functional/hash/hash.hpp>
23#include <cerrno>
24#include <fcntl.h>
25#include <unistd.h>
26
27namespace ipc::util
28{
29
30// Native_handle implementations.
31
32void Native_handle::close() noexcept
33{
34 if (null()) // As promised no-op in this case.
35 {
36 return;
37 }
38 // else
39
40#ifndef FLOW_OS_LINUX
41 static_assert(false, "Native ::close() tested in Linux only in this context, though anything POSIXy should be OK.");
42#endif
44
45 operator=({}); // As promised nullify it.
46
47 /* Discussion of ::close() versus alternatives:
48 *
49 * Firstly, at least in Linux (likely POSIX, but we haven't rigorously looked into it), ::close() does what we want:
50 * the FD (file descriptOR) no longer refers to the file descriptION; so in particular if it's the last FD to have
51 * been referring to it, then the descriptION goes away. So once all FDs (::dup()ed ones, socket-transmitted
52 * ones, original) are thus ::close()d, then the resource can really go away (as opposed to leaking, in addition to
53 * FD(s) leaking). So, at least it's correct/correct enough.
54 *
55 * Secondly, is there an alternative in our style? Yes but no. Yes in that we'd like to use the "portable"
56 * boost.asio for this job. Indeed, probably, making a posix_descriptor S from hndl_or_null.m_native_handle
57 * and calling S.close() would likely do the job. However, posix::descriptor's dtor is protected, so one cannot
58 * really instantiate a posix::descriptor; must instantiate a sub-class: perhaps stream::descriptor which
59 * adds generic read/write ops like write_some() and read_some(). One might be tempted to try
60 * Peer_socket<Protocol_byte_stream> (stream_protocol::socket) or Peer_socket<Protocol_pkt_stream>
61 * (seq_packet_protocol::socket). Does it matter that m_native_handle might not refer to a stream-style
62 * protocol at all (if using stream_protocol::socket or stream::descriptor) or a SEQPACKET-style one (if using
63 * seq_packet_protocol::socket)? One might guess, "no, it's just close it anyway, whatever." Turns out, actually,
64 * no: some kind of machinery in boost.asio (@todo Might be fun/educational to find out what exact machinery)
65 * seems to detect a mismatch at least with {stream|seq_packet}_protocol::socket, leading to a quite-stealthy
66 * failure to actually .close() successfully... and a leak (total failure of this function to fulfill its contract).
67 * So, in closing, no... not that we could find --
68 *
69 * whereas POSIX ::close() gets the job done.
70 *
71 * @todo Maybe revisit for boost.asio education at least. */
72} // Native_handle::close()
73
74void Native_handle::static_close(Native_handle& hndl) noexcept // Static.
75{
76 hndl.close();
77}
78
79bool Native_handle::is_open() const noexcept
80{
81#ifndef FLOW_OS_LINUX
82 static_assert(false, "F_GETFD is POSIX but tested in Linux only; look into it when porting.");
83#endif
84 return ::fcntl(m_native_handle, F_GETFD) != -1; // (-1 a/k/a null() => EBADF => false, as promised.)
85}
86
88{
89 using boost::system::system_category;
90 using ::fcntl;
91
92 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(Native_handle, dup, _1);
93 // ^-- Call ourselves and return if err_code is null. If got to present line, err_code is not null.
94
95 if (null())
96 {
98 return {};
99 }
100 // else
101
102#ifndef FLOW_OS_LINUX
103 static_assert(false, "F_DUPFD_CLOEXEC is POSIX.1-2008 but tested in Linux only; look into it when porting.");
104#endif
105
106 // (Close-on-exec set atomically, so a fork()+exec() racing us cannot leak the new descriptor into the child.)
107 const handle_t result = fcntl(m_native_handle, F_DUPFD_CLOEXEC, 0);
108 if (result == -1)
109 {
110 *err_code = Error_code{errno, system_category()};
111 return {};
112 }
113 // else
114
115 err_code->clear();
116 return Native_handle{result};
117} // Native_handle::dup()
118
120{
121 Native_handle ret{src.get()};
122 src.release();
123 return ret;
124}
125
127{
128 return Own_native_handle{src.dup(err_code)};
129}
130
131size_t hash_value(Native_handle val) noexcept
132{
133 using boost::hash;
134
135 return hash<Native_handle::handle_t>()(val.m_native_handle);
136}
137
138std::ostream& operator<<(std::ostream& os, const Native_handle& val)
139{
140 os << "native_hndl[";
141 if (val.null())
142 {
143 os << "NONE";
144 }
145 else
146 {
147 os << val.m_native_handle;
148 }
149 return os << ']';
150}
151
152} // namespace ipc::util
@ S_INVALID_ARGUMENT
User called an API with 1 or more arguments against the API spec.
Flow-IPC module containing miscellaneous general-use facilities that are ubiquitously used by ~all Fl...
Definition: util.cpp:31
std::ostream & operator<<(std::ostream &os, const Native_handle &val)
Prints string representation of the given Native_handle to the given ostream.
Native_handle disowned_native_handle(Own_native_handle &&src) noexcept
Returns Native_handle constructed by relieving an existing auto-closing Own_native_handle of its owne...
Own_native_handle duped_native_handle(Native_handle src, Error_code *err_code)
Returns Own_native_handle{src.dup(err_code)}: an auto-closing duplicate of src; on error the result i...
size_t hash_value(Native_handle val) noexcept
Hasher of Native_handle for boost.unordered et al.
boost::scope::unique_resource< Native_handle, Native_handle::Closer, Native_handle > Own_native_handle
A unique_resource wrapper for Native_handle useful to avoid descriptor leaks and double-closing; can ...
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Definition: common.hpp:313
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.
bool is_open() const noexcept
Returns whether m_native_handle is currently an open descriptor in this process; false if null().
constexpr bool null() const noexcept
Returns true if and only if m_native_handle equals S_NULL_HANDLE.
constexpr Native_handle & operator=(Native_handle &&src) noexcept
Move assignment; acts similarly to move ctor; but no-op if this == &src.
void close() noexcept
Little utility that returns m_native_handle to the OS.
int handle_t
The native handle type. Much logic relies on this type being light-weight (fast to copy).
static void static_close(Native_handle &hndl) noexcept
Simply calls hndl.close(); helpful for things like boost::core::functor<static_close>.
handle_t m_native_handle
The native handle (possibly equal to S_NULL_HANDLE), the exact payload of this Native_handle.
Native_handle dup(Error_code *err_code=nullptr) const
Returns a new handle referring to the same underlying OS-level open resource as *this,...