include/boost/corosio/local_datagram_socket.hpp

100.0% Lines (109 / 109) 100.0% Functions (33 / 33)
local_datagram_socket.hpp
f(x) Functions (33)
Function Calls Lines Blocks
boost::corosio::local_datagram_socket::send_to_awaitable::send_to_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::detail::buffer_param, boost::corosio::local_endpoint, int) :325 88x 100.0% 100.0% boost::corosio::local_datagram_socket::send_to_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :345 84x 100.0% 80.0% boost::corosio::local_datagram_socket::recv_from_awaitable::recv_from_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::detail::buffer_param, boost::corosio::local_endpoint&, int) :362 88x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_from_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :382 84x 100.0% 80.0% boost::corosio::local_datagram_socket::connect_awaitable::connect_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::local_endpoint) :399 2x 100.0% 100.0% boost::corosio::local_datagram_socket::connect_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :412 2x 100.0% 80.0% boost::corosio::local_datagram_socket::wait_awaitable::wait_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::wait_type) :424 14x 100.0% 100.0% boost::corosio::local_datagram_socket::wait_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :436 12x 100.0% 80.0% boost::corosio::local_datagram_socket::send_awaitable::send_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::detail::buffer_param, int) :452 97x 100.0% 100.0% boost::corosio::local_datagram_socket::send_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :469 93x 100.0% 80.0% boost::corosio::local_datagram_socket::recv_awaitable::recv_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::detail::buffer_param, int) :485 97x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :502 93x 100.0% 80.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::local_datagram_socket&&) :544 2x 100.0% 100.0% boost::corosio::local_datagram_socket::operator=(boost::corosio::local_datagram_socket&&) :556 2x 100.0% 100.0% boost::corosio::local_datagram_socket::is_open() const :601 1401x 100.0% 100.0% boost::corosio::local_datagram_socket::connect(boost::corosio::local_endpoint) :641 2x 100.0% 100.0% boost::corosio::local_datagram_socket::wait(boost::corosio::wait_type) :663 14x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send_to<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::local_endpoint, boost::corosio::message_flags) :687 88x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send_to<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::local_endpoint) :700 88x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv_from<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::local_endpoint&, boost::corosio::message_flags) :727 88x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv_from<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::local_endpoint&) :741 86x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::message_flags) :762 97x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&) :772 97x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::message_flags) :793 97x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&) :803 95x 100.0% 100.0% std::error_code boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::no_delay>(boost::corosio::socket_option::no_delay const&) :872 2x 83.3% 92.0% std::error_code boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::receive_buffer_size>(boost::corosio::socket_option::receive_buffer_size const&) :872 10x 83.3% 92.0% std::error_code boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::send_buffer_size>(boost::corosio::socket_option::send_buffer_size const&) :872 12x 100.0% 92.0% std::tuple<std::error_code, boost::corosio::socket_option::no_delay> boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::no_delay>() const :894 2x 70.0% 65.0% std::tuple<std::error_code, boost::corosio::socket_option::receive_buffer_size> boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::receive_buffer_size>() const :894 2x 80.0% 73.0% std::tuple<std::error_code, boost::corosio::socket_option::send_buffer_size> boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::send_buffer_size>() const :894 4x 90.0% 73.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::io_object::handle) :954 30x 100.0% 100.0% boost::corosio::local_datagram_socket::get() const :962 1858x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
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_LOCAL_DATAGRAM_SOCKET_HPP
11 #define BOOST_COROSIO_LOCAL_DATAGRAM_SOCKET_HPP
12
13 #include <boost/corosio/family.hpp>
14 #include <boost/corosio/detail/config.hpp>
15 #include <boost/corosio/detail/platform.hpp>
16
17 #if BOOST_COROSIO_POSIX
18
19 #include <boost/corosio/detail/native_handle.hpp>
20 #include <boost/corosio/detail/op_base.hpp>
21 #include <boost/corosio/io/io_object.hpp>
22 #include <boost/capy/results.hpp>
23 #include <boost/corosio/detail/buffer_param.hpp>
24 #include <boost/corosio/error.hpp>
25 #include <boost/corosio/local_endpoint.hpp>
26 #include <boost/corosio/message_flags.hpp>
27 #include <boost/corosio/shutdown_type.hpp>
28 #include <boost/corosio/wait_type.hpp>
29 #include <boost/capy/ex/executor_ref.hpp>
30 #include <boost/capy/ex/execution_context.hpp>
31 #include <boost/capy/ex/await_context.hpp>
32 #include <boost/capy/concept/executor.hpp>
33
34 #include <system_error>
35
36 #include <concepts>
37 #include <boost/capy/continuation.hpp>
38
39 #include <coroutine>
40 #include <cstddef>
41 #include <stop_token>
42 #include <type_traits>
43 #include <utility>
44
45 namespace boost::corosio {
46
47 /** Sends and receives datagrams over a Unix domain socket, from a coroutine.
48
49 This class provides asynchronous Unix domain datagram socket
50 operations that return awaitable types. Each operation
51 participates in the affine awaitable protocol, ensuring
52 coroutines resume on the correct executor.
53
54 Supports two modes of operation:
55
56 @li **Connectionless:** each send_to() specifies a destination
57 endpoint, and each recv_from() captures the source. The
58 socket must be opened (and optionally bound) before I/O.
59
60 @li **Connected:** call connect() to set a default peer,
61 then use send()/recv() without endpoint arguments. The
62 kernel filters incoming datagrams to those from the
63 connected peer.
64
65 @note Not available on Windows. Windows does not support
66 AF_UNIX datagram sockets (SOCK_DGRAM). Attempting to
67 open this socket on Windows fails.
68
69 @par Cancellation
70 All asynchronous operations support cancellation through
71 `std::stop_token` via the affine protocol, or explicitly
72 through cancel(). Cancelled operations complete with
73 `capy::cond::canceled`. Datagram sends and receives are
74 atomic — there is no partial progress on cancellation.
75
76 @par Thread Safety
77 Distinct objects: Safe.@n
78 Shared objects: Unsafe. A socket must not have concurrent
79 operations of the same type (e.g., two simultaneous
80 `recv_from`). One send and one `recv` may be in flight
81 simultaneously. Both `recv` and `recv_from` share the
82 same internal read slot, so they must not overlap. Likewise,
83 send and `send_to` share the write slot.
84
85 @par Example
86 @par !example connectionless_and_connected
87 */
88 class BOOST_COROSIO_DECL local_datagram_socket : public io_object
89 {
90 public:
91 /// The shutdown direction type used by shutdown().
92 using shutdown_type = corosio::shutdown_type;
93 using enum corosio::shutdown_type;
94
95 /** Define backend hooks for local datagram socket operations.
96
97 Platform backends (epoll, kqueue, select) derive from this
98 to implement datagram I/O, connection, and option management.
99 */
100 struct implementation : io_object::implementation
101 {
102 /** Initiate an asynchronous `send_to` operation.
103
104 @param cont Continuation to resume on completion; it lives in
105 the awaiting frame until then.
106 @param ex Executor for dispatching the completion.
107 @param buf The buffer data to send.
108 @param dest The destination endpoint.
109 @param flags Message flags (e.g. `message_flags::do_not_route`).
110 @param token Stop token for cancellation.
111 @param ec Output error code.
112 @param bytes_out Output bytes transferred.
113
114 @return Coroutine handle to resume immediately.
115 */
116 virtual std::coroutine_handle<> send_to(
117 capy::continuation& cont,
118 capy::executor_ref ex,
119 detail::buffer_param buf,
120 corosio::local_endpoint dest,
121 int flags,
122 std::stop_token token,
123 std::error_code* ec,
124 std::size_t* bytes_out) = 0;
125
126 /** Initiate an asynchronous `recv_from` operation.
127
128 @param cont Continuation to resume on completion; it lives in
129 the awaiting frame until then.
130 @param ex Executor for dispatching the completion.
131 @param buf The buffer to receive into.
132 @param source Output endpoint for the sender's address.
133 @param flags Message flags (e.g. `message_flags::peek`).
134 @param token Stop token for cancellation.
135 @param ec Output error code.
136 @param bytes_out Output bytes transferred.
137
138 @return Coroutine handle to resume immediately.
139 */
140 virtual std::coroutine_handle<> recv_from(
141 capy::continuation& cont,
142 capy::executor_ref ex,
143 detail::buffer_param buf,
144 corosio::local_endpoint* source,
145 int flags,
146 std::stop_token token,
147 std::error_code* ec,
148 std::size_t* bytes_out) = 0;
149
150 /** Initiate an asynchronous connect to set the default peer.
151
152 @param cont Continuation to resume on completion; it lives in
153 the awaiting frame until then.
154 @param ex Executor for dispatching the completion.
155 @param ep The remote endpoint to connect to.
156 @param token Stop token for cancellation.
157 @param ec Output error code.
158
159 @return Coroutine handle to resume immediately.
160 */
161 virtual std::coroutine_handle<> connect(
162 capy::continuation& cont,
163 capy::executor_ref ex,
164 corosio::local_endpoint ep,
165 std::stop_token token,
166 std::error_code* ec) = 0;
167
168 /** Initiate an asynchronous connected send operation.
169
170 @param cont Continuation to resume on completion; it lives in
171 the awaiting frame until then.
172 @param ex Executor for dispatching the completion.
173 @param buf The buffer data to send.
174 @param flags Message flags (e.g. `message_flags::do_not_route`).
175 @param token Stop token for cancellation.
176 @param ec Output error code.
177 @param bytes_out Output bytes transferred.
178
179 @return Coroutine handle to resume immediately.
180 */
181 virtual std::coroutine_handle<> send(
182 capy::continuation& cont,
183 capy::executor_ref ex,
184 detail::buffer_param buf,
185 int flags,
186 std::stop_token token,
187 std::error_code* ec,
188 std::size_t* bytes_out) = 0;
189
190 /** Initiate an asynchronous connected `recv` operation.
191
192 @param cont Continuation to resume on completion; it lives in
193 the awaiting frame until then.
194 @param ex Executor for dispatching the completion.
195 @param buf The buffer to receive into.
196 @param flags Message flags (e.g. `message_flags::peek`).
197 @param token Stop token for cancellation.
198 @param ec Output error code.
199 @param bytes_out Output bytes transferred.
200
201 @return Coroutine handle to resume immediately.
202 */
203 virtual std::coroutine_handle<> recv(
204 capy::continuation& cont,
205 capy::executor_ref ex,
206 detail::buffer_param buf,
207 int flags,
208 std::stop_token token,
209 std::error_code* ec,
210 std::size_t* bytes_out) = 0;
211
212 /** Initiate an asynchronous wait for socket readiness.
213
214 Completes when the socket becomes ready for the
215 specified direction, or an error condition is
216 reported. No bytes are transferred.
217
218 @param cont Continuation to resume on completion; it lives in
219 the awaiting frame until then.
220 @param ex Executor for dispatching the completion.
221 @param w The direction to wait on.
222 @param token Stop token for cancellation.
223 @param ec Output error code.
224
225 @return Coroutine handle to resume immediately.
226 */
227 virtual std::coroutine_handle<> wait(
228 capy::continuation& cont,
229 capy::executor_ref ex,
230 wait_type w,
231 std::stop_token token,
232 std::error_code* ec) = 0;
233
234 /** Shut down part or all of the socket.
235
236 @param what Which directions to disable.
237
238 @return The error code, empty on success.
239 */
240 virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
241
242 /// Return the platform socket descriptor.
243 virtual native_handle_type native_handle() const noexcept = 0;
244
245 /** Return the socket's address family.
246
247 Local sockets have no IP family; implementations return
248 `v4`, which the family-neutral options applicable to them
249 ignore.
250
251 @return The address family for option rendering.
252 */
253 virtual corosio::family family() const noexcept = 0;
254
255 /** Release ownership of the socket descriptor.
256
257 The implementation deregisters from the reactor and cancels
258 pending operations. The caller takes ownership of the
259 returned descriptor.
260
261 @return The native handle, or an invalid sentinel if
262 not open.
263 */
264 virtual native_handle_type release_socket() noexcept = 0;
265
266 /** Request cancellation of pending asynchronous operations.
267
268 Operations still in flight complete with `operation_canceled`;
269 an operation whose result is already decided reports that
270 result. Check `ec == cond::canceled` for portable comparison.
271 */
272 virtual void cancel() noexcept = 0;
273
274 /** Set a socket option.
275
276 @param level The protocol level (e.g. SOL_SOCKET).
277 @param optname The option name.
278 @param data Pointer to the option value.
279 @param size Size of the option value in bytes.
280 @return Error code on failure, empty on success.
281 */
282 virtual std::error_code set_option(
283 int level,
284 int optname,
285 void const* data,
286 std::size_t size) noexcept = 0;
287
288 /** Get a socket option.
289
290 @param level The protocol level (e.g. SOL_SOCKET).
291 @param optname The option name.
292 @param data Pointer to receive the option value.
293 @param size On entry, the size of the buffer. On exit,
294 the size of the option value.
295 @return Error code on failure, empty on success.
296 */
297 virtual std::error_code
298 get_option(int level, int optname, void* data, std::size_t* size)
299 const noexcept = 0;
300
301 /// Return the cached local endpoint.
302 virtual corosio::local_endpoint local_endpoint() const noexcept = 0;
303
304 /// Return the cached remote endpoint (connected mode).
305 virtual corosio::local_endpoint remote_endpoint() const noexcept = 0;
306
307 /** Bind the socket to a local endpoint.
308
309 @param ep The local endpoint to bind to.
310 @return Error code on failure, empty on success.
311 */
312 virtual std::error_code bind(corosio::local_endpoint ep) noexcept = 0;
313 };
314
315 /** Represent the awaitable returned by @ref send_to.
316
317 Captures the destination endpoint and buffer, then dispatches
318 to the backend implementation on suspension.
319 */
320 struct send_to_awaitable : detail::bytes_op_base<send_to_awaitable>
321 {
322 private:
323 friend local_datagram_socket;
324
325 88x send_to_awaitable(
326 local_datagram_socket& s,
327 detail::buffer_param buf,
328 corosio::local_endpoint dest,
329 int flags = 0) noexcept
330 176x : s_(s)
331 88x , buf_(buf)
332 88x , dest_(dest)
333 88x , flags_(flags)
334 {
335 88x }
336
337 friend detail::bytes_op_base<send_to_awaitable>;
338
339 local_datagram_socket& s_;
340 detail::buffer_param buf_;
341 corosio::local_endpoint dest_;
342 int flags_;
343
344 std::coroutine_handle<>
345 84x dispatch(capy::continuation& cont, capy::executor_ref ex) const
346 {
347 168x return s_.get().send_to(
348 168x cont, ex, buf_, dest_, flags_, token_, &ec_, &bytes_);
349 }
350 };
351
352 /** Represent the awaitable returned by @ref recv_from.
353
354 Captures the source endpoint reference and buffer, then
355 dispatches to the backend implementation on suspension.
356 */
357 struct recv_from_awaitable : detail::bytes_op_base<recv_from_awaitable>
358 {
359 private:
360 friend local_datagram_socket;
361
362 88x recv_from_awaitable(
363 local_datagram_socket& s,
364 detail::buffer_param buf,
365 corosio::local_endpoint& source,
366 int flags = 0) noexcept
367 176x : s_(s)
368 88x , buf_(buf)
369 88x , source_(source)
370 88x , flags_(flags)
371 {
372 88x }
373
374 friend detail::bytes_op_base<recv_from_awaitable>;
375
376 local_datagram_socket& s_;
377 detail::buffer_param buf_;
378 corosio::local_endpoint& source_;
379 int flags_;
380
381 std::coroutine_handle<>
382 84x dispatch(capy::continuation& cont, capy::executor_ref ex) const
383 {
384 168x return s_.get().recv_from(
385 168x cont, ex, buf_, &source_, flags_, token_, &ec_, &bytes_);
386 }
387 };
388
389 /** Represent the awaitable returned by @ref connect.
390
391 Captures the target endpoint, then dispatches to the
392 backend implementation on suspension.
393 */
394 struct connect_awaitable : detail::void_op_base<connect_awaitable>
395 {
396 private:
397 friend local_datagram_socket;
398
399 2x connect_awaitable(
400 local_datagram_socket& s, corosio::local_endpoint ep) noexcept
401 4x : s_(s)
402 2x , endpoint_(ep)
403 {
404 2x }
405
406 friend detail::void_op_base<connect_awaitable>;
407
408 local_datagram_socket& s_;
409 corosio::local_endpoint endpoint_;
410
411 std::coroutine_handle<>
412 2x dispatch(capy::continuation& cont, capy::executor_ref ex) const
413 {
414 2x return s_.get().connect(cont, ex, endpoint_, token_, &ec_);
415 }
416 };
417
418 /// Represent the awaitable returned by @ref wait.
419 struct wait_awaitable : detail::void_op_base<wait_awaitable>
420 {
421 private:
422 friend local_datagram_socket;
423
424 14x wait_awaitable(local_datagram_socket& s, wait_type w) noexcept
425 28x : s_(s)
426 14x , w_(w)
427 {
428 14x }
429
430 friend detail::void_op_base<wait_awaitable>;
431
432 local_datagram_socket& s_;
433 wait_type w_;
434
435 std::coroutine_handle<>
436 12x dispatch(capy::continuation& cont, capy::executor_ref ex) const
437 {
438 12x return s_.get().wait(cont, ex, w_, token_, &ec_);
439 }
440 };
441
442 /** Represent the awaitable returned by @ref send.
443
444 Captures the buffer, then dispatches to the backend
445 implementation on suspension. Requires a prior connect().
446 */
447 struct send_awaitable : detail::bytes_op_base<send_awaitable>
448 {
449 private:
450 friend local_datagram_socket;
451
452 97x send_awaitable(
453 local_datagram_socket& s,
454 detail::buffer_param buf,
455 int flags = 0) noexcept
456 194x : s_(s)
457 97x , buf_(buf)
458 97x , flags_(flags)
459 {
460 97x }
461
462 friend detail::bytes_op_base<send_awaitable>;
463
464 local_datagram_socket& s_;
465 detail::buffer_param buf_;
466 int flags_;
467
468 std::coroutine_handle<>
469 93x dispatch(capy::continuation& cont, capy::executor_ref ex) const
470 {
471 93x return s_.get().send(cont, ex, buf_, flags_, token_, &ec_, &bytes_);
472 }
473 };
474
475 /** Represent the awaitable returned by @ref recv.
476
477 Captures the buffer, then dispatches to the backend
478 implementation on suspension. Requires a prior connect().
479 */
480 struct recv_awaitable : detail::bytes_op_base<recv_awaitable>
481 {
482 private:
483 friend local_datagram_socket;
484
485 97x recv_awaitable(
486 local_datagram_socket& s,
487 detail::buffer_param buf,
488 int flags = 0) noexcept
489 194x : s_(s)
490 97x , buf_(buf)
491 97x , flags_(flags)
492 {
493 97x }
494
495 friend detail::bytes_op_base<recv_awaitable>;
496
497 local_datagram_socket& s_;
498 detail::buffer_param buf_;
499 int flags_;
500
501 std::coroutine_handle<>
502 93x dispatch(capy::continuation& cont, capy::executor_ref ex) const
503 {
504 93x return s_.get().recv(cont, ex, buf_, flags_, token_, &ec_, &bytes_);
505 }
506 };
507
508 public:
509 /** Destructor.
510
511 Closes the socket if open, cancelling any pending operations.
512 */
513 ~local_datagram_socket() override;
514
515 /** Construct a socket from an execution context.
516
517 @param ctx The execution context that owns this socket.
518 */
519 explicit local_datagram_socket(capy::execution_context& ctx);
520
521 /** Construct a socket from an executor.
522
523 The socket is associated with the executor's context.
524
525 @tparam Ex A type satisfying capy::Executor.
526
527 @param ex The executor whose context owns the socket.
528 */
529 template<class Ex>
530 requires(!std::
531 same_as<std::remove_cvref_t<Ex>, local_datagram_socket>) &&
532 capy::Executor<Ex>
533 explicit local_datagram_socket(Ex const& ex)
534 : local_datagram_socket(ex.context())
535 {
536 }
537
538 /** Move constructor.
539
540 Transfers ownership of the socket resources.
541
542 @param other The socket to move from.
543 */
544 2x local_datagram_socket(local_datagram_socket&& other) noexcept
545 2x : io_object(std::move(other))
546 {
547 2x }
548
549 /** Move assignment operator.
550
551 Closes any existing socket and transfers ownership.
552
553 @param other The socket to move from.
554 @return Reference to this socket.
555 */
556 2x local_datagram_socket& operator=(local_datagram_socket&& other) noexcept
557 {
558 2x if (this != &other)
559 {
560 2x close();
561 2x io_object::operator=(std::move(other));
562 }
563 2x return *this;
564 }
565
566 /// Copy construction is disabled; the handle is uniquely owned.
567 local_datagram_socket(local_datagram_socket const&) = delete;
568 /// Copy assignment is disabled; the handle is uniquely owned.
569 local_datagram_socket& operator=(local_datagram_socket const&) = delete;
570
571 /** Open the socket.
572
573 Creates a Unix datagram socket and associates it with
574 the platform reactor.
575
576 Failures such as descriptor exhaustion are normal runtime
577 conditions and are reported through the returned error code.
578 Opening an already-open socket is a no-op that reports
579 success.
580
581
582 @return The error code, empty on success.
583 */
584 [[nodiscard]] std::error_code open() noexcept;
585
586 /** Close the socket.
587
588 Cancels any pending asynchronous operations and releases
589 the underlying file descriptor. Has no effect if the
590 socket is not open.
591
592 @post is_open() == false
593 */
594 void close() noexcept;
595
596 /** Check if the socket is open.
597
598 @return `true` if the socket holds a valid file descriptor,
599 `false` otherwise.
600 */
601 1401x bool is_open() const noexcept
602 {
603 #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
604 return h_ && get().native_handle() != ~native_handle_type(0);
605 #else
606 1401x return h_ && get().native_handle() >= 0;
607 #endif
608 }
609
610 /** Bind the socket to a local endpoint.
611
612 Associates the socket with a local address (filesystem path).
613 Required before calling `recv_from` in connectionless mode.
614
615 @param ep The local endpoint to bind to.
616
617 @return Error code on failure, empty on success.
618
619 A closed socket reports `errc::bad_file_descriptor`.
620 */
621 [[nodiscard]] std::error_code bind(corosio::local_endpoint ep) noexcept;
622
623 /** Initiate an asynchronous connect to set the default peer.
624
625 If the socket is not already open, it is opened automatically.
626 After successful completion, send()/recv() may be used
627 without specifying an endpoint.
628
629 @param ep The remote endpoint to connect to.
630
631 @par Cancellation
632 Supports cancellation via the awaitable's stop_token or by
633 calling cancel(). On cancellation, yields
634 `capy::cond::canceled`.
635
636 @return An awaitable that completes with `results<>`.
637
638 If the socket needs to be opened and the open fails, the
639 awaitable completes immediately with that error.
640 */
641 2x [[nodiscard]] auto connect(corosio::local_endpoint ep)
642 {
643 2x connect_awaitable aw(*this, ep);
644 2x if (!is_open())
645 2x aw.ec_ = open();
646 2x return aw;
647 }
648
649 /** Wait for the socket to become ready in a given direction.
650
651 Suspends until the socket is ready for the requested
652 direction, or an error condition is reported. No bytes
653 are transferred.
654
655 @param w The wait direction (read, write, or error).
656
657 @return An awaitable that completes with `results<>`.
658
659 A closed socket completes with `errc::bad_file_descriptor`.
660
661 @pre This socket must outlive the returned awaitable.
662 */
663 14x [[nodiscard]] auto wait(wait_type w)
664 {
665 14x return wait_awaitable(*this, w);
666 }
667
668 /** Send a datagram to the specified destination.
669
670 Completes when the transport accepts the entire datagram
671 by the kernel. The `bytes_transferred` value equals the
672 datagram size on success.
673
674 @param buf The buffer containing data to send.
675 @param dest The destination endpoint.
676 @param flags Message flags (e.g. message_flags::do_not_route).
677
678 @par Cancellation
679 Supports cancellation via stop_token or cancel().
680
681 @return An awaitable that completes with
682 results<std::size_t>.
683
684 A closed socket reports `errc::bad_file_descriptor`.
685 */
686 template<capy::ConstBufferSequence Buffers>
687 88x [[nodiscard]] auto send_to(
688 Buffers const& buf,
689 corosio::local_endpoint dest,
690 corosio::message_flags flags)
691 {
692 88x send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
693 88x if (!is_open())
694 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
695 88x return aw;
696 }
697
698 /// @overload
699 template<capy::ConstBufferSequence Buffers>
700 88x [[nodiscard]] auto send_to(Buffers const& buf, corosio::local_endpoint dest)
701 {
702 88x return send_to(buf, dest, corosio::message_flags::none);
703 }
704
705 /** Receive a datagram and capture the sender's endpoint.
706
707 Completes when one datagram arrives. The
708 `bytes_transferred` value is the number of bytes copied
709 into the buffer. If the buffer is smaller than the
710 datagram, excess bytes are discarded (datagram
711 semantics).
712
713 @param buf The buffer to receive data into.
714 @param source Reference to an endpoint that receives
715 the sender's address on successful completion.
716 @param flags Message flags (e.g. message_flags::peek).
717
718 @par Cancellation
719 Supports cancellation via stop_token or cancel().
720
721 @return An awaitable that completes with
722 results<std::size_t>.
723
724 A closed socket reports `errc::bad_file_descriptor`.
725 */
726 template<capy::MutableBufferSequence Buffers>
727 88x [[nodiscard]] auto recv_from(
728 Buffers const& buf,
729 corosio::local_endpoint& source,
730 corosio::message_flags flags)
731 {
732 88x recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
733 88x if (!is_open())
734 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
735 88x return aw;
736 }
737
738 /// @overload
739 template<capy::MutableBufferSequence Buffers>
740 [[nodiscard]] auto
741 86x recv_from(Buffers const& buf, corosio::local_endpoint& source)
742 {
743 86x return recv_from(buf, source, corosio::message_flags::none);
744 }
745
746 /** Send a datagram to the connected peer.
747
748 @pre connect() succeeded.
749
750 @param buf The buffer containing data to send.
751 @param flags Message flags.
752
753 @par Cancellation
754 Supports cancellation via stop_token or cancel().
755
756 @return An awaitable that completes with
757 results<std::size_t>.
758
759 A closed socket reports `errc::bad_file_descriptor`.
760 */
761 template<capy::ConstBufferSequence Buffers>
762 97x [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
763 {
764 97x send_awaitable aw(*this, buf, static_cast<int>(flags));
765 97x if (!is_open())
766 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
767 97x return aw;
768 }
769
770 /// @overload
771 template<capy::ConstBufferSequence Buffers>
772 97x [[nodiscard]] auto send(Buffers const& buf)
773 {
774 97x return send(buf, corosio::message_flags::none);
775 }
776
777 /** Receive a datagram from the connected peer.
778
779 @pre connect() succeeded.
780
781 @param buf The buffer to receive data into.
782 @param flags Message flags (e.g. message_flags::peek).
783
784 @par Cancellation
785 Supports cancellation via stop_token or cancel().
786
787 @return An awaitable that completes with
788 results<std::size_t>.
789
790 A closed socket reports `errc::bad_file_descriptor`.
791 */
792 template<capy::MutableBufferSequence Buffers>
793 97x [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
794 {
795 97x recv_awaitable aw(*this, buf, static_cast<int>(flags));
796 97x if (!is_open())
797 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
798 97x return aw;
799 }
800
801 /// @overload
802 template<capy::MutableBufferSequence Buffers>
803 95x [[nodiscard]] auto recv(Buffers const& buf)
804 {
805 95x return recv(buf, corosio::message_flags::none);
806 }
807
808 /** Cancel any pending asynchronous operations.
809
810 Operations still in flight complete with
811 `errc::operation_canceled`; an operation whose result is
812 already decided reports that result. Check
813 `ec == cond::canceled` for portable comparison.
814 */
815 void cancel() noexcept;
816
817 /** Get the native socket handle.
818
819 @return The native socket handle, or -1 if not open.
820 */
821 native_handle_type native_handle() const noexcept;
822
823 /** Release ownership of the native socket handle.
824
825 Deregisters the socket from the reactor and cancels pending
826 operations without closing the fd. The caller takes ownership
827 of the returned descriptor.
828
829 A closed object reports `errc::bad_file_descriptor` and the
830 returned handle is the invalid sentinel ( -1 ), never zero.
831
832 @return The error code and the native handle.
833 */
834 [[nodiscard]] capy::results<native_handle_type> release() noexcept;
835
836 /** Query the number of bytes available for reading.
837
838 A closed socket reports `errc::bad_file_descriptor`. Other
839 failures carry the error code from the operating system. On
840 failure the count is zero.
841
842 @return The error code and the number of readable bytes.
843 */
844 [[nodiscard]] capy::results<std::size_t> available() const noexcept;
845
846 /** Shut down part or all of the socket.
847
848 Failures such as an unconnected socket are normal runtime
849 conditions and are reported through the returned error
850 code. A closed socket reports `errc::bad_file_descriptor`.
851
852 @param what Which direction to shut down.
853
854 @return The error code, empty on success.
855 */
856 [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
857
858 /** Set a socket option.
859
860 Fails with `errc::bad_file_descriptor` if the socket is not
861 open, or with the error the operating system reports.
862
863 @tparam Option A socket option type that provides static
864 `level()` and `name()` members, and `data()` / `size()`
865 accessors for the option value.
866
867 @param opt The option to set.
868
869 @return The error code, empty on success.
870 */
871 template<class Option>
872 24x [[nodiscard]] std::error_code set_option(Option const& opt) noexcept
873 {
874 24x if (!is_open())
875 2x return make_error_code(std::errc::bad_file_descriptor);
876 22x auto const fam = get().family();
877 22x return get().set_option(
878 22x opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
879 }
880
881 /** Get a socket option.
882
883 Fails with `errc::bad_file_descriptor` if the socket is not
884 open, or with the error the operating system reports. On
885 failure the option is value-initialized.
886
887 @tparam Option A socket option type that provides static
888 `level()` and `name()` members, `data()` / `size()`
889 accessors, and a `resize()` member.
890
891 @return The error code and the option value.
892 */
893 template<class Option>
894 8x [[nodiscard]] capy::results<Option> get_option() const noexcept
895 {
896 8x if (!is_open())
897 2x return {make_error_code(std::errc::bad_file_descriptor), Option{}};
898 6x Option opt{};
899 6x auto const fam = get().family();
900 6x std::size_t sz = opt.size(fam);
901 6x if (auto ec = get().get_option(
902 opt.level(fam), opt.name(fam), opt.data(fam), &sz))
903 2x return {ec, Option{}};
904 4x opt.resize(fam, sz);
905 4x return {std::error_code{}, std::move(opt)};
906 }
907
908 /** Assign an existing native socket to this object.
909
910 Adopts a Unix domain datagram socket created outside the
911 library — from `socketpair()`, received over `SCM_RIGHTS`,
912 or made natively — and registers it with the backend. The
913 socket must be a datagram socket in the `AF_UNIX` family.
914 Adoption never alters the descriptor's flags or options.
915 The fd must already be non-blocking.
916
917 The object must be closed. To replace a held socket, `close()`
918 or `release()` it first.
919
920 @par Exception Safety
921 Throws nothing. On failure the object is unchanged and the
922 caller retains ownership of `fd`.
923
924 @param fd The native socket to adopt. On success the object
925 owns it and closes it.
926
927 @return `error::already_open` if this object is open.
928 Otherwise the error code, empty on success. Validation and
929 registration failures are normal runtime conditions when
930 adopting foreign descriptors.
931 */
932 [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
933
934 /** Get the local endpoint of the socket.
935
936 @return The local endpoint, or a default endpoint if not bound.
937 */
938 corosio::local_endpoint local_endpoint() const noexcept;
939
940 /** Get the remote endpoint of the socket.
941
942 Returns the address of the connected peer.
943
944 @return The remote endpoint, or a default endpoint if
945 not connected.
946 */
947 corosio::local_endpoint remote_endpoint() const noexcept;
948
949 protected:
950 /// Default-construct (for derived types).
951 local_datagram_socket() noexcept = default;
952
953 /// Construct from a pre-built handle.
954 30x explicit local_datagram_socket(handle h) noexcept : io_object(std::move(h))
955 {
956 30x }
957
958 private:
959 [[nodiscard]] std::error_code
960 open_for_family(int family, int type, int protocol) noexcept;
961
962 1858x inline implementation& get() const noexcept
963 {
964 1858x return *static_cast<implementation*>(h_.get());
965 }
966 };
967
968 } // namespace boost::corosio
969
970 #endif // BOOST_COROSIO_POSIX
971
972 #endif // BOOST_COROSIO_LOCAL_DATAGRAM_SOCKET_HPP
973