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