include/boost/corosio/local_datagram_socket.hpp

100.0% Lines (109 / 109) 100.0% Functions (33 / 33)
local_datagram_socket.hpp
f(x) Functions (33)
Function Calls Lines Blocks
boost::corosio::local_datagram_socket::send_to_awaitable::send_to_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::detail::buffer_param, boost::corosio::local_endpoint, int) :326 88x 100.0% 100.0% boost::corosio::local_datagram_socket::send_to_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :346 84x 100.0% 80.0% boost::corosio::local_datagram_socket::recv_from_awaitable::recv_from_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::detail::buffer_param, boost::corosio::local_endpoint&, int) :363 88x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_from_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :383 84x 100.0% 80.0% boost::corosio::local_datagram_socket::connect_awaitable::connect_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::local_endpoint) :400 2x 100.0% 100.0% boost::corosio::local_datagram_socket::connect_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :413 2x 100.0% 80.0% boost::corosio::local_datagram_socket::wait_awaitable::wait_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::wait_type) :425 14x 100.0% 100.0% boost::corosio::local_datagram_socket::wait_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :437 12x 100.0% 80.0% boost::corosio::local_datagram_socket::send_awaitable::send_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::detail::buffer_param, int) :453 97x 100.0% 100.0% boost::corosio::local_datagram_socket::send_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :470 93x 100.0% 80.0% boost::corosio::local_datagram_socket::recv_awaitable::recv_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::detail::buffer_param, int) :486 97x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :503 93x 100.0% 80.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::local_datagram_socket&&) :545 2x 100.0% 100.0% boost::corosio::local_datagram_socket::operator=(boost::corosio::local_datagram_socket&&) :557 2x 100.0% 100.0% boost::corosio::local_datagram_socket::is_open() const :602 1401x 100.0% 100.0% boost::corosio::local_datagram_socket::connect(boost::corosio::local_endpoint) :642 2x 100.0% 100.0% boost::corosio::local_datagram_socket::wait(boost::corosio::wait_type) :664 14x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send_to<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::local_endpoint, boost::corosio::message_flags) :688 88x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send_to<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::local_endpoint) :701 88x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv_from<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::local_endpoint&, boost::corosio::message_flags) :728 88x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv_from<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::local_endpoint&) :742 86x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::message_flags) :763 97x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&) :773 97x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::message_flags) :794 97x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&) :804 95x 100.0% 100.0% std::error_code boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::no_delay>(boost::corosio::socket_option::no_delay const&) :873 2x 83.3% 92.0% std::error_code boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::receive_buffer_size>(boost::corosio::socket_option::receive_buffer_size const&) :873 10x 83.3% 92.0% std::error_code boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::send_buffer_size>(boost::corosio::socket_option::send_buffer_size const&) :873 12x 100.0% 92.0% std::tuple<std::error_code, boost::corosio::socket_option::no_delay> boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::no_delay>() const :895 2x 70.0% 65.0% std::tuple<std::error_code, boost::corosio::socket_option::receive_buffer_size> boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::receive_buffer_size>() const :895 2x 80.0% 73.0% std::tuple<std::error_code, boost::corosio::socket_option::send_buffer_size> boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::send_buffer_size>() const :895 4x 90.0% 73.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::io_object::handle) :955 30x 100.0% 100.0% boost::corosio::local_datagram_socket::get() const :963 1858x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_LOCAL_DATAGRAM_SOCKET_HPP
11 #define BOOST_COROSIO_LOCAL_DATAGRAM_SOCKET_HPP
12
13 #include <boost/corosio/family.hpp>
14 #include <boost/corosio/detail/config.hpp>
15 #include <boost/corosio/detail/platform.hpp>
16
17 #if BOOST_COROSIO_POSIX
18
19 #include <boost/corosio/detail/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 88x send_to_awaitable(
327 local_datagram_socket& s,
328 detail::buffer_param buf,
329 corosio::local_endpoint dest,
330 int flags = 0) noexcept
331 176x : s_(s)
332 88x , buf_(buf)
333 88x , dest_(dest)
334 88x , flags_(flags)
335 {
336 88x }
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 84x dispatch(capy::continuation& cont, capy::executor_ref ex) const
347 {
348 168x return s_.get().send_to(
349 168x 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 88x recv_from_awaitable(
364 local_datagram_socket& s,
365 detail::buffer_param buf,
366 corosio::local_endpoint& source,
367 int flags = 0) noexcept
368 176x : s_(s)
369 88x , buf_(buf)
370 88x , source_(source)
371 88x , flags_(flags)
372 {
373 88x }
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 84x dispatch(capy::continuation& cont, capy::executor_ref ex) const
384 {
385 168x return s_.get().recv_from(
386 168x 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 2x connect_awaitable(
401 local_datagram_socket& s, corosio::local_endpoint ep) noexcept
402 4x : s_(s)
403 2x , endpoint_(ep)
404 {
405 2x }
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 2x dispatch(capy::continuation& cont, capy::executor_ref ex) const
414 {
415 2x 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 14x wait_awaitable(local_datagram_socket& s, wait_type w) noexcept
426 28x : s_(s)
427 14x , w_(w)
428 {
429 14x }
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 12x dispatch(capy::continuation& cont, capy::executor_ref ex) const
438 {
439 12x 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 97x send_awaitable(
454 local_datagram_socket& s,
455 detail::buffer_param buf,
456 int flags = 0) noexcept
457 194x : s_(s)
458 97x , buf_(buf)
459 97x , flags_(flags)
460 {
461 97x }
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 93x dispatch(capy::continuation& cont, capy::executor_ref ex) const
471 {
472 93x 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 97x recv_awaitable(
487 local_datagram_socket& s,
488 detail::buffer_param buf,
489 int flags = 0) noexcept
490 194x : s_(s)
491 97x , buf_(buf)
492 97x , flags_(flags)
493 {
494 97x }
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 93x dispatch(capy::continuation& cont, capy::executor_ref ex) const
504 {
505 93x 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 2x local_datagram_socket(local_datagram_socket&& other) noexcept
546 2x : io_object(std::move(other))
547 {
548 2x }
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 2x local_datagram_socket& operator=(local_datagram_socket&& other) noexcept
558 {
559 2x if (this != &other)
560 {
561 2x close();
562 2x io_object::operator=(std::move(other));
563 }
564 2x 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 1401x 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 1401x 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 2x [[nodiscard]] auto connect(corosio::local_endpoint ep)
643 {
644 2x connect_awaitable aw(*this, ep);
645 2x if (!is_open())
646 2x aw.ec_ = open();
647 2x 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 14x [[nodiscard]] auto wait(wait_type w)
665 {
666 14x 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 88x [[nodiscard]] auto send_to(
689 Buffers const& buf,
690 corosio::local_endpoint dest,
691 corosio::message_flags flags)
692 {
693 88x send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
694 88x if (!is_open())
695 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
696 88x return aw;
697 }
698
699 /// @overload
700 template<capy::ConstBufferSequence Buffers>
701 88x [[nodiscard]] auto send_to(Buffers const& buf, corosio::local_endpoint dest)
702 {
703 88x 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 88x [[nodiscard]] auto recv_from(
729 Buffers const& buf,
730 corosio::local_endpoint& source,
731 corosio::message_flags flags)
732 {
733 88x recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
734 88x if (!is_open())
735 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
736 88x return aw;
737 }
738
739 /// @overload
740 template<capy::MutableBufferSequence Buffers>
741 [[nodiscard]] auto
742 86x recv_from(Buffers const& buf, corosio::local_endpoint& source)
743 {
744 86x 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 97x [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
764 {
765 97x send_awaitable aw(*this, buf, static_cast<int>(flags));
766 97x if (!is_open())
767 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
768 97x return aw;
769 }
770
771 /// @overload
772 template<capy::ConstBufferSequence Buffers>
773 97x [[nodiscard]] auto send(Buffers const& buf)
774 {
775 97x 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 97x [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
795 {
796 97x recv_awaitable aw(*this, buf, static_cast<int>(flags));
797 97x if (!is_open())
798 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
799 97x return aw;
800 }
801
802 /// @overload
803 template<capy::MutableBufferSequence Buffers>
804 95x [[nodiscard]] auto recv(Buffers const& buf)
805 {
806 95x 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 24x [[nodiscard]] std::error_code set_option(Option const& opt) noexcept
874 {
875 24x if (!is_open())
876 2x return make_error_code(std::errc::bad_file_descriptor);
877 22x auto const fam = get().family();
878 22x return get().set_option(
879 22x 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 8x [[nodiscard]] capy::results<Option> get_option() const noexcept
896 {
897 8x if (!is_open())
898 2x return {make_error_code(std::errc::bad_file_descriptor), Option{}};
899 6x Option opt{};
900 6x auto const fam = get().family();
901 6x std::size_t sz = opt.size(fam);
902 6x if (auto ec = get().get_option(
903 opt.level(fam), opt.name(fam), opt.data(fam), &sz))
904 2x return {ec, Option{}};
905 4x opt.resize(fam, sz);
906 4x 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 30x explicit local_datagram_socket(handle h) noexcept : io_object(std::move(h))
956 {
957 30x }
958
959 private:
960 [[nodiscard]] std::error_code
961 open_for_family(int family, int type, int protocol) noexcept;
962
963 1858x inline implementation& get() const noexcept
964 {
965 1858x 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
974