Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions include/boost/corosio/detail/buffer_param.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2026 Michael Vandeberg
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -73,23 +74,42 @@ namespace boost::corosio {
to the buffers obtained from `copy_to`. The const-cast exists
solely to provide a uniform interface for platform I/O calls.

@note Do NOT `reinterpret_cast` the `mutable_buffer` array to
`iovec*`/`WSABUF*` and pass it to the OS, even when the layouts
match: no object of the target type exists in that storage, so the
access is undefined behavior (and `mutable_buffer` is not an
implicit-lifetime type, so `std::start_lifetime_as_array` cannot
rescue it). Copy field by field into a real platform array instead.

@code
// For write operations (const buffers):
void submit_write(buffer_param p)
{
capy::mutable_buffer bufs[8];
auto n = p.copy_to(bufs, 8);
// bufs[] may reference const data - DO NOT WRITE
writev(fd, reinterpret_cast<iovec*>(bufs), n); // OK: read-only
iovec iov[8];
for (std::size_t i = 0; i < n; ++i)
{
// bufs[] may reference const data - DO NOT WRITE through iov
iov[i].iov_base = bufs[i].data();
iov[i].iov_len = bufs[i].size();
}
writev(fd, iov, n); // read-only
}

// For read operations (mutable buffers):
void submit_read(buffer_param p)
{
capy::mutable_buffer bufs[8];
auto n = p.copy_to(bufs, 8);
// bufs[] references mutable data - safe to write
readv(fd, reinterpret_cast<iovec*>(bufs), n); // OK: writing
iovec iov[8];
for (std::size_t i = 0; i < n; ++i)
{
// bufs[] references mutable data - safe to write
iov[i].iov_base = bufs[i].data();
iov[i].iov_len = bufs[i].size();
}
readv(fd, iov, n); // writing
}
@endcode

Expand Down Expand Up @@ -131,10 +151,17 @@ namespace boost::corosio {
buffer_param p,
std::coroutine_handle<> h)
{
// CORRECT: Unroll immediately into platform structure
// CORRECT: Unroll immediately into platform structure.
// Copy field by field; do NOT reinterpret_cast the
// mutable_buffer array to iovec* (see the @note above).
capy::mutable_buffer bufs[16];
std::size_t n = p.copy_to(bufs, 16);
iovec vecs[16];
std::size_t n = p.copy_to(
reinterpret_cast<capy::mutable_buffer*>(vecs), 16);
for (std::size_t i = 0; i < n; ++i)
{
vecs[i].iov_base = bufs[i].data();
vecs[i].iov_len = bufs[i].size();
}

// CORRECT: Use unrolled buffers for system call now
submit_to_io_uring(vecs, n, h);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//
// Copyright (c) 2026 Steve Gerbino
// Copyright (c) 2026 Michael Vandeberg
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -89,10 +90,7 @@ struct uring_dgram_send_op : io_uring_op
cqe_flags = 0;
msg_flags = flags;

iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
iovec_count = copy_to_iovec(buffers, iovecs);

msg = {};
msg.msg_iov = iovecs;
Expand Down Expand Up @@ -231,10 +229,7 @@ struct uring_dgram_recv_op : io_uring_op
cqe_flags = 0;
msg_flags = flags;

iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
iovec_count = copy_to_iovec(buffers, iovecs);

msg = {};
// For the zero-iovec bypass path the caller pushes the slot
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//
// Copyright (c) 2026 Steve Gerbino
// Copyright (c) 2026 Michael Vandeberg
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -84,10 +85,7 @@ struct uring_file_read_op_base : io_uring_op
impl_ptr = std::move(impl);
res = 0;
cqe_flags = 0;
iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
iovec_count = copy_to_iovec(buffers, iovecs);
empty_buffer = (iovec_count == 0);
start(token);
}
Expand Down Expand Up @@ -225,10 +223,7 @@ struct uring_file_write_op_base : io_uring_op
impl_ptr = std::move(impl);
res = 0;
cqe_flags = 0;
iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
iovec_count = copy_to_iovec(buffers, iovecs);
empty_buffer = (iovec_count == 0);
start(token);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//
// Copyright (c) 2026 Steve Gerbino
// Copyright (c) 2026 Michael Vandeberg
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -47,6 +48,35 @@ namespace boost::corosio::detail {
/// per-op memory.
inline constexpr std::size_t io_uring_max_iov = 16;

/** Fill an `iovec` array from a buffer sequence without type-punning.

`buffer_param::copy_to` writes `capy::mutable_buffer` objects. Those
cannot legally alias `iovec` storage: no `mutable_buffer` lives at
that address, and `mutable_buffer` is not an implicit-lifetime type,
so its lifetime cannot be started in place (which also rules out
`std::start_lifetime_as_array`). We copy into a real `mutable_buffer`
scratch array, then translate field by field into the caller's
`iovec` array. Zero-size buffers are already skipped by `copy_to`.

@param buffers The source buffer sequence.
@param iovecs Destination array, filled with the non-zero buffers.
@return The number of `iovec` entries written.
*/
inline int
copy_to_iovec(
buffer_param const& buffers,
iovec (&iovecs)[io_uring_max_iov]) noexcept
{
capy::mutable_buffer bufs[io_uring_max_iov];
std::size_t const n = buffers.copy_to(bufs, io_uring_max_iov);
for (std::size_t i = 0; i < n; ++i)
{
iovecs[i].iov_base = bufs[i].data();
iovecs[i].iov_len = bufs[i].size();
}
return static_cast<int>(n);
}

/** Resolve ec_out/bytes_out from a CQE result for a completed I/O op.

Shared by read, write, and connect handlers. For reads, `res == 0`
Expand Down Expand Up @@ -117,10 +147,7 @@ struct uring_read_op : io_uring_op
spec_state = spec;
res = 0;
cqe_flags = 0;
iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
iovec_count = copy_to_iovec(buffers, iovecs);
empty_buffer = (iovec_count == 0);
start(token);
}
Expand Down Expand Up @@ -225,10 +252,7 @@ struct uring_write_op : io_uring_op
spec_state = spec;
res = 0;
cqe_flags = 0;
iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
iovec_count = copy_to_iovec(buffers, iovecs);
empty_buffer = (iovec_count == 0);
if (!empty_buffer)
{
Expand Down
41 changes: 9 additions & 32 deletions include/boost/corosio/native/detail/io_uring/io_uring_types.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//
// Copyright (c) 2026 Steve Gerbino
// Copyright (c) 2026 Michael Vandeberg
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -152,10 +153,7 @@ class BOOST_COROSIO_DECL io_uring_tcp_socket final
std::size_t* bytes) override
{
iovec iovecs[io_uring_max_iov];
int iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
int iovec_count = copy_to_iovec(buffers, iovecs);
bool stop_now = token.stop_possible() && token.stop_requested();
bool empty_buf = (iovec_count == 0);

Expand Down Expand Up @@ -232,10 +230,7 @@ class BOOST_COROSIO_DECL io_uring_tcp_socket final
std::size_t* bytes) override
{
iovec iovecs[io_uring_max_iov];
int iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
int iovec_count = copy_to_iovec(buffers, iovecs);
bool stop_now = token.stop_possible() && token.stop_requested();
bool empty_buf = (iovec_count == 0);

Expand Down Expand Up @@ -907,10 +902,7 @@ class BOOST_COROSIO_DECL io_uring_local_stream_socket final
std::size_t* bytes) override
{
iovec iovecs[io_uring_max_iov];
int iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
int iovec_count = copy_to_iovec(buffers, iovecs);
bool stop_now = token.stop_possible() && token.stop_requested();
bool empty_buf = (iovec_count == 0);

Expand Down Expand Up @@ -987,10 +979,7 @@ class BOOST_COROSIO_DECL io_uring_local_stream_socket final
std::size_t* bytes) override
{
iovec iovecs[io_uring_max_iov];
int iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
int iovec_count = copy_to_iovec(buffers, iovecs);
bool stop_now = token.stop_possible() && token.stop_requested();
bool empty_buf = (iovec_count == 0);

Expand Down Expand Up @@ -1811,10 +1800,7 @@ class BOOST_COROSIO_DECL io_uring_udp_socket final
std::size_t* bytes)
{
iovec iovecs[io_uring_max_iov];
int iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
int iovec_count = copy_to_iovec(buffers, iovecs);
bool stop_now = token.stop_possible() && token.stop_requested();
bool empty_buf = (iovec_count == 0);

Expand Down Expand Up @@ -1899,10 +1885,7 @@ class BOOST_COROSIO_DECL io_uring_udp_socket final
std::size_t* bytes)
{
iovec iovecs[io_uring_max_iov];
int iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
int iovec_count = copy_to_iovec(buffers, iovecs);
bool stop_now = token.stop_possible() && token.stop_requested();
bool empty_buf = (iovec_count == 0);

Expand Down Expand Up @@ -2365,10 +2348,7 @@ class BOOST_COROSIO_DECL io_uring_local_datagram_socket final
std::size_t* bytes)
{
iovec iovecs[io_uring_max_iov];
int iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
int iovec_count = copy_to_iovec(buffers, iovecs);
bool stop_now = token.stop_possible() && token.stop_requested();
bool empty_buf = (iovec_count == 0);

Expand Down Expand Up @@ -2453,10 +2433,7 @@ class BOOST_COROSIO_DECL io_uring_local_datagram_socket final
std::size_t* bytes)
{
iovec iovecs[io_uring_max_iov];
int iovec_count = static_cast<int>(
buffers.copy_to(
reinterpret_cast<capy::mutable_buffer*>(iovecs),
io_uring_max_iov));
int iovec_count = copy_to_iovec(buffers, iovecs);
bool stop_now = token.stop_possible() && token.stop_requested();
bool empty_buf = (iovec_count == 0);

Expand Down
Loading