Flow-IPC 1.0.0
Flow-IPC project: Full implementation reference.
native_handle.hpp
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
19#pragma once
20
21#include <ostream>
22#include <flow/common.hpp>
23
24namespace ipc::util
25{
26
27#ifndef FLOW_OS_LINUX
28# error "Flow-IPC supports transmitting native handles in Linux only; a core feature. Build in Linux only."
29#endif
30// From this point on (in #including .cpp files as well) POSIX is assumed; and in a few spots specifically Linux.
31
32// Types.
33
34/* Note: We lack a native_handle_fwd.hpp, as it's such a thin wrapper around an FD (int) that there's no point
35 * forward-declaring it, as it's ~always referred to by value. */
36
37/**
38 * A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.
39 * It would have been acceptable to simply use an alias
40 * to the native handle type (in POSIX, `int`), but this way is better stylistically with exactly zero performance
41 * overhead. Initialize it, e.g., `Native_handle hndl(some_fd);`, where
42 * `some_fd` might be some POSIX-y FD (network socket, Unix domain socket, file descriptor, etc.).
43 * Or initialize via no-arg construction which results in an `null() == true` object. Copy construction, assignment,
44 * equality, total ordering, and hashing all work as one would expect and essentially the same as on the underlying
45 * native handles.
46 *
47 * It is either null() or stores a native handle. In the former case, null() being `true` means explicitly
48 * that `m_native_handle == Native_handle::S_NULL_HANDLE`.
49 *
50 * ### Rationale ###
51 * Originally we tried to make it aggregate-initializable, like `Native_handle hndl = {some_fd}`; for example see
52 * standard `array<>`. This is reassuring perf-wise; and it involves less (i.e., no) code to boot.
53 * However, it really is just one light-weight scalar, and there's 0% chance explicit construction is any slower.
54 * So, perf isn't an issue. Still, why not make it aggregate-initializable though? I (ygoldfel) gave up, eventually,
55 * because I wanted *some* non-default-behavior constructors to be available, namely no-args (`Native_handle()` that
56 * makes an `null() == true` one) and move (`Native_handle(Native_handle&&)` that nullifies the source object).
57 * This isn't allowed in aggregate-initializable classes; so then I made some `static` "constructors" instead; but
58 * those aren't sufficient for containers... and on it went. This way, though verbose, just works out better for the
59 * API.
60 */
62{
63 // Types.
64
65 /// The native handle type. Much logic relies on this type being light-weight (fast to copy).
66 using handle_t = int;
67
68 // Constants.
69
70 /**
71 * The value for #m_native_handle such that `null() == true`; else it is `false`.
72 * No valid handle ever equals this.
73 */
74 static const handle_t S_NULL_HANDLE;
75
76 // Data.
77
78 /**
79 * The native handle (possibly equal to #S_NULL_HANDLE), the exact payload of this Native_handle.
80 * It can, of course, be assigned and accessed explicitly. The best way to initialize a
81 * new Native_handle is, therefore: `Native_handle{some_native_handle}`. You may also use the
82 * null() "constructor" to create a null() handle.
83 */
85
86 // Constructors/destructor.
87
88 /**
89 * Constructs with given payload; also subsumes no-args construction to mean constructing an object with
90 * `null() == true`.
91 *
92 * @param native_handle
93 * Payload.
94 */
95 Native_handle(handle_t native_handle = S_NULL_HANDLE);
96
97 /**
98 * Constructs object equal to `src`, while making `src == null()`.
99 *
100 * ### Rationale ###
101 * This is move construction, but it's not about performance at all (as this is all quite cheap anyway);
102 * more to allow for the pattern of making an object from another object without propagating more copies of the
103 * underlying handle. Won't the default move ctor take care of it? No, because it'll just copy the handle and not
104 * nullify it.
105 *
106 * @param src
107 * Source object which will be made `null() == true`.
108 */
110
111 /**
112 * Copy constructor.
113 * @param src
114 * Source object.
115 */
117
118 // Methods.
119
120 /**
121 * Move assignment; acts similarly to move ctor; but no-op if `*this == src`.
122 * @param src
123 * Source object which will be made `null() == true`, unles `*this == src`.
124 * @return `*this`.
125 */
127
128 /**
129 * Copy assignment; acts similarly to copy ctor.
130 * @param src
131 * Source object.
132 * @return `*this`.
133 */
135
136 /**
137 * Returns `true` if and only if #m_native_handle equals #S_NULL_HANDLE.
138 * @return See above.
139 */
140 bool null() const;
141}; // struct Native_handle
142
143// Free functions.
144
145/**
146 * Returns `true` if and only if the two Native_handle objects are the same underlying handle.
147 *
148 * @relatesalso Native_handle
149 *
150 * @param val1
151 * Object.
152 * @param val2
153 * Object.
154 * @return See above.
155 */
156bool operator==(Native_handle val1, Native_handle val2);
157
158/**
159 * Negation of similar `==`.
160 *
161 * @relatesalso Native_handle
162 *
163 * @param val1
164 * Object.
165 * @param val2
166 * Object.
167 * @return See above.
168 */
169bool operator!=(Native_handle val1, Native_handle val2);
170
171/**
172 * Returns a less-than comparison of two Native_handle objects, with the usual total ordering guarantees.
173 *
174 * @relatesalso Native_handle
175 *
176 * @param val1
177 * Left-hand side object.
178 * @param val2
179 * Right-hand side object.
180 * @return Whether left side is considered strictly less-than right side.
181 */
182bool operator<(Native_handle val1, Native_handle val2);
183
184/**
185 * Hasher of Native_handle for boost.unordered et al.
186 *
187 * @relatesalso Native_handle
188 *
189 * @param val
190 * Object to hash.
191 * @return See above.
192 */
193size_t hash_value(Native_handle val);
194
195/**
196 * Prints string representation of the given Native_handle to the given `ostream`.
197 *
198 * @relatesalso Native_handle
199 *
200 * @param os
201 * Stream to which to write.
202 * @param val
203 * Object to serialize.
204 * @return `os`.
205 */
206std::ostream& operator<<(std::ostream& os, const Native_handle& val);
207
208} // namespace ipc::util
Flow-IPC module containing miscellaneous general-use facilities that ubiquitously used by ~all Flow-I...
std::ostream & operator<<(std::ostream &os, const Native_handle &val)
Prints string representation of the given Native_handle to the given ostream.
bool operator<(Native_handle val1, Native_handle val2)
Returns a less-than comparison of two Native_handle objects, with the usual total ordering guarantees...
bool operator!=(Native_handle val1, Native_handle val2)
Negation of similar ==.
size_t hash_value(Native_handle val)
Hasher of Native_handle for boost.unordered et al.
bool operator==(Native_handle val1, Native_handle val2)
Returns true if and only if the two Native_handle objects are the same underlying handle.
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.
bool null() const
Returns true if and only if m_native_handle equals S_NULL_HANDLE.
Native_handle(const Native_handle &src)
Copy constructor.
Native_handle(handle_t native_handle=S_NULL_HANDLE)
Constructs with given payload; also subsumes no-args construction to mean constructing an object with...
static const handle_t S_NULL_HANDLE
The value for m_native_handle such that null() == true; else it is false.
Native_handle & operator=(Native_handle &&src)
Move assignment; acts similarly to move ctor; but no-op if *this == src.
int handle_t
The native handle type. Much logic relies on this type being light-weight (fast to copy).
handle_t m_native_handle
The native handle (possibly equal to S_NULL_HANDLE), the exact payload of this Native_handle.
Native_handle & operator=(const Native_handle &src)
Copy assignment; acts similarly to copy ctor.