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