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