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