include/boost/corosio/local_datagram_socket.hpp

100.0% Lines (114 / 114) 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::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::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::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 :467 93x 100.0% 80.0% boost::corosio::local_datagram_socket::recv_awaitable::recv_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::buffer_param, int) :483 97x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :498 93x 100.0% 80.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::local_datagram_socket&&) :540 2x 100.0% 100.0% boost::corosio::local_datagram_socket::operator=(boost::corosio::local_datagram_socket&&) :552 2x 100.0% 100.0% boost::corosio::local_datagram_socket::is_open() const :597 1401x 100.0% 100.0% boost::corosio::local_datagram_socket::connect(boost::corosio::local_endpoint) :637 2x 100.0% 100.0% boost::corosio::local_datagram_socket::wait(boost::corosio::wait_type) :659 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) :683 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) :696 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) :723 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&) :737 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) :758 97x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&) :768 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) :789 97x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&) :799 95x 100.0% 100.0% void boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::no_delay>(boost::corosio::socket_option::no_delay const&) :865 2x 66.7% 78.0% void boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::receive_buffer_size>(boost::corosio::socket_option::receive_buffer_size const&) :865 10x 66.7% 78.0% void boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::send_buffer_size>(boost::corosio::socket_option::send_buffer_size const&) :865 12x 88.9% 78.0% boost::corosio::socket_option::no_delay boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::no_delay>() const :890 2x 66.7% 70.0% boost::corosio::socket_option::receive_buffer_size boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::receive_buffer_size>() const :890 2x 75.0% 80.0% boost::corosio::socket_option::send_buffer_size boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::send_buffer_size>() const :890 4x 91.7% 80.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::io_object::handle) :953 30x 100.0% 100.0% boost::corosio::local_datagram_socket::get() const :961 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/except.hpp>
20 #include <boost/corosio/detail/native_handle.hpp>
21 #include <boost/corosio/detail/op_base.hpp>
22 #include <boost/corosio/io/io_object.hpp>
23 #include <boost/capy/results.hpp>
24 #include <boost/corosio/detail/buffer_param.hpp>
25 #include <boost/corosio/error.hpp>
26 #include <boost/corosio/local_endpoint.hpp>
27 #include <boost/corosio/message_flags.hpp>
28 #include <boost/corosio/shutdown_type.hpp>
29 #include <boost/corosio/wait_type.hpp>
30 #include <boost/capy/ex/executor_ref.hpp>
31 #include <boost/capy/ex/execution_context.hpp>
32 #include <boost/capy/ex/await_context.hpp>
33 #include <boost/capy/concept/executor.hpp>
34
35 #include <system_error>
36
37 #include <concepts>
38 #include <boost/capy/continuation.hpp>
39
40 #include <coroutine>
41 #include <cstddef>
42 #include <stop_token>
43 #include <type_traits>
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 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 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 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 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 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 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 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 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, buffer_param buf, int flags = 0) noexcept
454 194x : s_(s)
455 97x , buf_(buf)
456 97x , flags_(flags)
457 {
458 97x }
459
460 friend detail::bytes_op_base<send_awaitable>;
461
462 local_datagram_socket& s_;
463 buffer_param buf_;
464 int flags_;
465
466 std::coroutine_handle<>
467 93x dispatch(capy::continuation& cont, capy::executor_ref ex) const
468 {
469 93x return s_.get().send(cont, ex, buf_, flags_, token_, &ec_, &bytes_);
470 }
471 };
472
473 /** Represent the awaitable returned by @ref recv.
474
475 Captures the buffer, then dispatches to the backend
476 implementation on suspension. Requires a prior connect().
477 */
478 struct recv_awaitable : detail::bytes_op_base<recv_awaitable>
479 {
480 private:
481 friend local_datagram_socket;
482
483 97x recv_awaitable(
484 local_datagram_socket& s, buffer_param buf, int flags = 0) noexcept
485 194x : s_(s)
486 97x , buf_(buf)
487 97x , flags_(flags)
488 {
489 97x }
490
491 friend detail::bytes_op_base<recv_awaitable>;
492
493 local_datagram_socket& s_;
494 buffer_param buf_;
495 int flags_;
496
497 std::coroutine_handle<>
498 93x dispatch(capy::continuation& cont, capy::executor_ref ex) const
499 {
500 93x return s_.get().recv(cont, ex, buf_, flags_, token_, &ec_, &bytes_);
501 }
502 };
503
504 public:
505 /** Destructor.
506
507 Closes the socket if open, cancelling any pending operations.
508 */
509 ~local_datagram_socket() override;
510
511 /** Construct a socket from an execution context.
512
513 @param ctx The execution context that owns this socket.
514 */
515 explicit local_datagram_socket(capy::execution_context& ctx);
516
517 /** Construct a socket from an executor.
518
519 The socket is associated with the executor's context.
520
521 @tparam Ex A type satisfying capy::Executor.
522
523 @param ex The executor whose context owns the socket.
524 */
525 template<class Ex>
526 requires(!std::
527 same_as<std::remove_cvref_t<Ex>, local_datagram_socket>) &&
528 capy::Executor<Ex>
529 explicit local_datagram_socket(Ex const& ex)
530 : local_datagram_socket(ex.context())
531 {
532 }
533
534 /** Move constructor.
535
536 Transfers ownership of the socket resources.
537
538 @param other The socket to move from.
539 */
540 2x local_datagram_socket(local_datagram_socket&& other) noexcept
541 2x : io_object(std::move(other))
542 {
543 2x }
544
545 /** Move assignment operator.
546
547 Closes any existing socket and transfers ownership.
548
549 @param other The socket to move from.
550 @return Reference to this socket.
551 */
552 2x local_datagram_socket& operator=(local_datagram_socket&& other) noexcept
553 {
554 2x if (this != &other)
555 {
556 2x close();
557 2x io_object::operator=(std::move(other));
558 }
559 2x return *this;
560 }
561
562 /// Copy construction is disabled; the handle is uniquely owned.
563 local_datagram_socket(local_datagram_socket const&) = delete;
564 /// Copy assignment is disabled; the handle is uniquely owned.
565 local_datagram_socket& operator=(local_datagram_socket const&) = delete;
566
567 /** Open the socket.
568
569 Creates a Unix datagram socket and associates it with
570 the platform reactor.
571
572 Failures such as descriptor exhaustion are normal runtime
573 conditions and are reported through the returned error code.
574 Opening an already-open socket is a no-op that reports
575 success.
576
577
578 @return The error code, empty on success.
579 */
580 [[nodiscard]] std::error_code open() noexcept;
581
582 /** Close the socket.
583
584 Cancels any pending asynchronous operations and releases
585 the underlying file descriptor. Has no effect if the
586 socket is not open.
587
588 @post is_open() == false
589 */
590 void close() noexcept;
591
592 /** Check if the socket is open.
593
594 @return `true` if the socket holds a valid file descriptor,
595 `false` otherwise.
596 */
597 1401x bool is_open() const noexcept
598 {
599 #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
600 return h_ && get().native_handle() != ~native_handle_type(0);
601 #else
602 1401x return h_ && get().native_handle() >= 0;
603 #endif
604 }
605
606 /** Bind the socket to a local endpoint.
607
608 Associates the socket with a local address (filesystem path).
609 Required before calling `recv_from` in connectionless mode.
610
611 @param ep The local endpoint to bind to.
612
613 @return Error code on failure, empty on success.
614
615 A closed socket reports `errc::bad_file_descriptor`.
616 */
617 [[nodiscard]] std::error_code bind(corosio::local_endpoint ep) noexcept;
618
619 /** Initiate an asynchronous connect to set the default peer.
620
621 If the socket is not already open, it is opened automatically.
622 After successful completion, send()/recv() may be used
623 without specifying an endpoint.
624
625 @param ep The remote endpoint to connect to.
626
627 @par Cancellation
628 Supports cancellation via the awaitable's stop_token or by
629 calling cancel(). On cancellation, yields
630 `capy::cond::canceled`.
631
632 @return An awaitable that completes with `results<>`.
633
634 If the socket needs to be opened and the open fails, the
635 awaitable completes immediately with that error.
636 */
637 2x [[nodiscard]] auto connect(corosio::local_endpoint ep)
638 {
639 2x connect_awaitable aw(*this, ep);
640 2x if (!is_open())
641 2x aw.ec_ = open();
642 2x return aw;
643 }
644
645 /** Wait for the socket to become ready in a given direction.
646
647 Suspends until the socket is ready for the requested
648 direction, or an error condition is reported. No bytes
649 are transferred.
650
651 @param w The wait direction (read, write, or error).
652
653 @return An awaitable that completes with `results<>`.
654
655 A closed socket completes with `errc::bad_file_descriptor`.
656
657 @pre This socket must outlive the returned awaitable.
658 */
659 14x [[nodiscard]] auto wait(wait_type w)
660 {
661 14x return wait_awaitable(*this, w);
662 }
663
664 /** Send a datagram to the specified destination.
665
666 Completes when the transport accepts the entire datagram
667 by the kernel. The `bytes_transferred` value equals the
668 datagram size on success.
669
670 @param buf The buffer containing data to send.
671 @param dest The destination endpoint.
672 @param flags Message flags (e.g. message_flags::do_not_route).
673
674 @par Cancellation
675 Supports cancellation via stop_token or cancel().
676
677 @return An awaitable that completes with
678 results<std::size_t>.
679
680 A closed socket reports `errc::bad_file_descriptor`.
681 */
682 template<capy::ConstBufferSequence Buffers>
683 88x [[nodiscard]] auto send_to(
684 Buffers const& buf,
685 corosio::local_endpoint dest,
686 corosio::message_flags flags)
687 {
688 88x send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
689 88x if (!is_open())
690 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
691 88x return aw;
692 }
693
694 /// @overload
695 template<capy::ConstBufferSequence Buffers>
696 88x [[nodiscard]] auto send_to(Buffers const& buf, corosio::local_endpoint dest)
697 {
698 88x return send_to(buf, dest, corosio::message_flags::none);
699 }
700
701 /** Receive a datagram and capture the sender's endpoint.
702
703 Completes when one datagram arrives. The
704 `bytes_transferred` value is the number of bytes copied
705 into the buffer. If the buffer is smaller than the
706 datagram, excess bytes are discarded (datagram
707 semantics).
708
709 @param buf The buffer to receive data into.
710 @param source Reference to an endpoint that receives
711 the sender's address on successful completion.
712 @param flags Message flags (e.g. message_flags::peek).
713
714 @par Cancellation
715 Supports cancellation via stop_token or cancel().
716
717 @return An awaitable that completes with
718 results<std::size_t>.
719
720 A closed socket reports `errc::bad_file_descriptor`.
721 */
722 template<capy::MutableBufferSequence Buffers>
723 88x [[nodiscard]] auto recv_from(
724 Buffers const& buf,
725 corosio::local_endpoint& source,
726 corosio::message_flags flags)
727 {
728 88x recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
729 88x if (!is_open())
730 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
731 88x return aw;
732 }
733
734 /// @overload
735 template<capy::MutableBufferSequence Buffers>
736 [[nodiscard]] auto
737 86x recv_from(Buffers const& buf, corosio::local_endpoint& source)
738 {
739 86x return recv_from(buf, source, corosio::message_flags::none);
740 }
741
742 /** Send a datagram to the connected peer.
743
744 @pre connect() succeeded.
745
746 @param buf The buffer containing data to send.
747 @param flags Message flags.
748
749 @par Cancellation
750 Supports cancellation via stop_token or cancel().
751
752 @return An awaitable that completes with
753 results<std::size_t>.
754
755 A closed socket reports `errc::bad_file_descriptor`.
756 */
757 template<capy::ConstBufferSequence Buffers>
758 97x [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
759 {
760 97x send_awaitable aw(*this, buf, static_cast<int>(flags));
761 97x if (!is_open())
762 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
763 97x return aw;
764 }
765
766 /// @overload
767 template<capy::ConstBufferSequence Buffers>
768 97x [[nodiscard]] auto send(Buffers const& buf)
769 {
770 97x return send(buf, corosio::message_flags::none);
771 }
772
773 /** Receive a datagram from the connected peer.
774
775 @pre connect() succeeded.
776
777 @param buf The buffer to receive data into.
778 @param flags Message flags (e.g. message_flags::peek).
779
780 @par Cancellation
781 Supports cancellation via stop_token or cancel().
782
783 @return An awaitable that completes with
784 results<std::size_t>.
785
786 A closed socket reports `errc::bad_file_descriptor`.
787 */
788 template<capy::MutableBufferSequence Buffers>
789 97x [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
790 {
791 97x recv_awaitable aw(*this, buf, static_cast<int>(flags));
792 97x if (!is_open())
793 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
794 97x return aw;
795 }
796
797 /// @overload
798 template<capy::MutableBufferSequence Buffers>
799 95x [[nodiscard]] auto recv(Buffers const& buf)
800 {
801 95x return recv(buf, corosio::message_flags::none);
802 }
803
804 /** Cancel any pending asynchronous operations.
805
806 Operations still in flight complete with
807 `errc::operation_canceled`; an operation whose result is
808 already decided reports that result. Check
809 `ec == cond::canceled` for portable comparison.
810 */
811 void cancel() noexcept;
812
813 /** Get the native socket handle.
814
815 @return The native socket handle, or -1 if not open.
816 */
817 native_handle_type native_handle() const noexcept;
818
819 /** Release ownership of the native socket handle.
820
821 Deregisters the socket from the reactor and cancels pending
822 operations without closing the fd. The caller takes ownership
823 of the returned descriptor.
824
825 @return The native handle.
826
827 @throws std::system_error `errc::bad_file_descriptor` if the
828 socket is not open.
829 */
830 native_handle_type release();
831
832 /** Query the number of bytes available for reading.
833
834 @return The number of bytes that can be read without blocking.
835
836 @throws std::system_error `errc::bad_file_descriptor` if the
837 socket is not open; otherwise thrown on ioctl failure.
838 */
839 std::size_t available() const;
840
841 /** Shut down part or all of the socket.
842
843 Failures such as an unconnected socket are normal runtime
844 conditions and are reported through the returned error
845 code. A closed socket reports `errc::bad_file_descriptor`.
846
847 @param what Which direction to shut down.
848
849 @return The error code, empty on success.
850 */
851 [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
852
853 /** Set a socket option.
854
855 @tparam Option A socket option type that provides static
856 `level()` and `name()` members, and `data()` / `size()`
857 accessors for the option value.
858
859 @param opt The option to set.
860
861 @throws std::system_error `errc::bad_file_descriptor` if the
862 socket is not open; otherwise thrown on failure.
863 */
864 template<class Option>
865 24x void set_option(Option const& opt)
866 {
867 24x if (!is_open())
868 2x detail::throw_system_error(
869 4x make_error_code(std::errc::bad_file_descriptor),
870 "local_datagram_socket::set_option");
871 22x auto const fam = get().family();
872 22x std::error_code ec = get().set_option(
873 opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
874 22x if (ec)
875 2x detail::throw_system_error(ec, "local_datagram_socket::set_option");
876 20x }
877
878 /** Get a socket option.
879
880 @tparam Option A socket option type that provides static
881 `level()` and `name()` members, `data()` / `size()`
882 accessors, and a `resize()` member.
883
884 @return The current option value.
885
886 @throws std::system_error `errc::bad_file_descriptor` if the
887 socket is not open; otherwise thrown on failure.
888 */
889 template<class Option>
890 8x Option get_option() const
891 {
892 8x if (!is_open())
893 2x detail::throw_system_error(
894 4x make_error_code(std::errc::bad_file_descriptor),
895 "local_datagram_socket::get_option");
896 6x Option opt{};
897 6x auto const fam = get().family();
898 6x std::size_t sz = opt.size(fam);
899 std::error_code ec =
900 6x get().get_option(opt.level(fam), opt.name(fam), opt.data(fam), &sz);
901 6x if (ec)
902 2x detail::throw_system_error(ec, "local_datagram_socket::get_option");
903 4x opt.resize(fam, sz);
904 4x return opt;
905 }
906
907 /** Assign an existing native socket to this object.
908
909 Adopts a Unix domain datagram socket created outside the
910 library — from `socketpair()`, received over `SCM_RIGHTS`,
911 or made natively — and registers it with the backend. The
912 socket must be a datagram socket in the `AF_UNIX` family.
913 Adoption never alters the descriptor's flags or options.
914 The fd must already be non-blocking.
915
916 The object must be closed. To replace a held socket, `close()`
917 or `release()` it first.
918
919 @par Exception Safety
920 Throws nothing. On failure the object is unchanged and the
921 caller retains ownership of `fd`.
922
923 @param fd The native socket to adopt. On success the object
924 owns it and closes it.
925
926 @return `error::already_open` if this object is open.
927 Otherwise the error code, empty on success. Validation and
928 registration failures are normal runtime conditions when
929 adopting foreign descriptors.
930 */
931 [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
932
933 /** Get the local endpoint of the socket.
934
935 @return The local endpoint, or a default endpoint if not bound.
936 */
937 corosio::local_endpoint local_endpoint() const noexcept;
938
939 /** Get the remote endpoint of the socket.
940
941 Returns the address of the connected peer.
942
943 @return The remote endpoint, or a default endpoint if
944 not connected.
945 */
946 corosio::local_endpoint remote_endpoint() const noexcept;
947
948 protected:
949 /// Default-construct (for derived types).
950 local_datagram_socket() noexcept = default;
951
952 /// Construct from a pre-built handle.
953 30x explicit local_datagram_socket(handle h) noexcept : io_object(std::move(h))
954 {
955 30x }
956
957 private:
958 [[nodiscard]] std::error_code
959 open_for_family(int family, int type, int protocol) noexcept;
960
961 1858x inline implementation& get() const noexcept
962 {
963 1858x return *static_cast<implementation*>(h_.get());
964 }
965 };
966
967 } // namespace boost::corosio
968
969 #endif // BOOST_COROSIO_POSIX
970
971 #endif // BOOST_COROSIO_LOCAL_DATAGRAM_SOCKET_HPP
972