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