Skip to content

Commit

Permalink
networking: Prototype ip::resolve() and poll().
Browse files Browse the repository at this point in the history
  • Loading branch information
ned14 committed Dec 23, 2021
1 parent 09d4c0b commit d5717dc
Show file tree
Hide file tree
Showing 25 changed files with 368 additions and 81 deletions.
91 changes: 78 additions & 13 deletions include/llfio/v2.0/byte_io_handle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ class LLFIO_DECL byte_io_handle : public handle
//! The virtualised implementation of `max_buffers()` used if no multiplexer has been set.
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC size_t _do_max_buffers() const noexcept;
//! The virtualised implementation of `allocate_registered_buffer()` used if no multiplexer has been set.
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<registered_buffer_type> _do_allocate_registered_buffer(size_t &bytes) noexcept; // default implementation is in map_handle.hpp
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<registered_buffer_type>
_do_allocate_registered_buffer(size_t &bytes) noexcept; // default implementation is in map_handle.hpp
//! The virtualised implementation of `read()` used if no multiplexer has been set.
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> _do_read(io_request<buffers_type> reqs, deadline d) noexcept;
//! The virtualised implementation of `read()` used if no multiplexer has been set.
Expand Down Expand Up @@ -188,7 +189,8 @@ class LLFIO_DECL byte_io_handle : public handle
state->~io_operation_state();
return ret;
}
io_result<const_buffers_type> _do_multiplexer_barrier(registered_buffer_type &&base, io_request<const_buffers_type> reqs, barrier_kind kind, deadline d) noexcept
io_result<const_buffers_type> _do_multiplexer_barrier(registered_buffer_type &&base, io_request<const_buffers_type> reqs, barrier_kind kind,
deadline d) noexcept
{
LLFIO_DEADLINE_TO_SLEEP_INIT(d);
const auto state_reqs = _ctx->io_state_requirements();
Expand Down Expand Up @@ -292,10 +294,16 @@ class LLFIO_DECL byte_io_handle : public handle
\mallocs The default synchronous implementation in file_handle performs no memory allocation.
*/
LLFIO_MAKE_FREE_FUNCTION
io_result<buffers_type> read(io_request<buffers_type> reqs, deadline d = deadline()) noexcept { return (_ctx == nullptr) ? _do_read(reqs, d) : _do_multiplexer_read({}, reqs, d); }
io_result<buffers_type> read(io_request<buffers_type> reqs, deadline d = deadline()) noexcept
{
return (_ctx == nullptr) ? _do_read(reqs, d) : _do_multiplexer_read({}, reqs, d);
}
//! \overload Registered buffer overload, scatter list **must** be wholly within the registered buffer
LLFIO_MAKE_FREE_FUNCTION
io_result<buffers_type> read(registered_buffer_type base, io_request<buffers_type> reqs, deadline d = deadline()) noexcept { return (_ctx == nullptr) ? _do_read(std::move(base), reqs, d) : _do_multiplexer_read(std::move(base), reqs, d); }
io_result<buffers_type> read(registered_buffer_type base, io_request<buffers_type> reqs, deadline d = deadline()) noexcept
{
return (_ctx == nullptr) ? _do_read(std::move(base), reqs, d) : _do_multiplexer_read(std::move(base), reqs, d);
}
//! \overload Convenience initialiser list based overload for `read()`
LLFIO_MAKE_FREE_FUNCTION
io_result<size_type> read(extent_type offset, std::initializer_list<buffer_type> lst, deadline d = deadline()) noexcept
Expand Down Expand Up @@ -334,10 +342,16 @@ class LLFIO_DECL byte_io_handle : public handle
\mallocs The default synchronous implementation in file_handle performs no memory allocation.
*/
LLFIO_MAKE_FREE_FUNCTION
io_result<const_buffers_type> write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept { return (_ctx == nullptr) ? _do_write(reqs, d) : _do_multiplexer_write({}, std::move(reqs), d); }
io_result<const_buffers_type> write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept
{
return (_ctx == nullptr) ? _do_write(reqs, d) : _do_multiplexer_write({}, std::move(reqs), d);
}
//! \overload Registered buffer overload, gather list **must** be wholly within the registered buffer
LLFIO_MAKE_FREE_FUNCTION
io_result<const_buffers_type> write(registered_buffer_type base, io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept { return (_ctx == nullptr) ? _do_write(std::move(base), reqs, d) : _do_multiplexer_write(std::move(base), std::move(reqs), d); }
io_result<const_buffers_type> write(registered_buffer_type base, io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept
{
return (_ctx == nullptr) ? _do_write(std::move(base), reqs, d) : _do_multiplexer_write(std::move(base), std::move(reqs), d);
}
//! \overload Convenience initialiser list based overload for `write()`
LLFIO_MAKE_FREE_FUNCTION
io_result<size_type> write(extent_type offset, std::initializer_list<const_buffer_type> lst, deadline d = deadline()) noexcept
Expand Down Expand Up @@ -385,7 +399,8 @@ class LLFIO_DECL byte_io_handle : public handle
\mallocs None.
*/
LLFIO_MAKE_FREE_FUNCTION
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(), barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(),
barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept
{
return (_ctx == nullptr) ? _do_barrier(reqs, kind, d) : _do_multiplexer_barrier({}, std::move(reqs), kind, d);
}
Expand Down Expand Up @@ -466,7 +481,8 @@ class LLFIO_DECL byte_io_handle : public handle
and finishes immediately, no coroutine suspension occurs.
*/
LLFIO_MAKE_FREE_FUNCTION
awaitable<io_result<const_buffers_type>> co_barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(), barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept
awaitable<io_result<const_buffers_type>> co_barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(),
barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept
{
if(_ctx == nullptr)
{
Expand All @@ -477,7 +493,8 @@ class LLFIO_DECL byte_io_handle : public handle
return ret;
}
};
static_assert((sizeof(void *) == 4 && sizeof(byte_io_handle) == 20) || (sizeof(void *) == 8 && sizeof(byte_io_handle) == 32), "byte_io_handle is not 20 or 32 bytes in size!");
static_assert((sizeof(void *) == 4 && sizeof(byte_io_handle) == 20) || (sizeof(void *) == 8 && sizeof(byte_io_handle) == 32),
"byte_io_handle is not 20 or 32 bytes in size!");

// Out of line definition purely to work around a bug in GCC where if marked inline,
// its visibility is hidden and links fail
Expand Down Expand Up @@ -517,7 +534,8 @@ inline size_t byte_io_multiplexer::do_byte_io_handle_max_buffers(const byte_io_h
{
return h->_do_max_buffers();
}
inline result<byte_io_multiplexer::registered_buffer_type> byte_io_multiplexer::do_byte_io_handle_allocate_registered_buffer(byte_io_handle *h, size_t &bytes) noexcept
inline result<byte_io_multiplexer::registered_buffer_type> byte_io_multiplexer::do_byte_io_handle_allocate_registered_buffer(byte_io_handle *h,
size_t &bytes) noexcept
{
return h->_do_allocate_registered_buffer(bytes);
}
Expand Down Expand Up @@ -560,6 +578,50 @@ template <class T> inline byte_io_multiplexer::awaitable<T>::~awaitable()
}
}

/*! \class pollable_handle
\brief A handle type which can be supplied to `poll()`.
*/
class pollable_handle
{
virtual const handle &_get_handle() const noexcept = 0;

public:
virtual ~pollable_handle() {}
};

//! What to poll
QUICKCPPLIB_BITFIELD_BEGIN_T(poll_what, uint8_t) //
{
none = 0U, //!< Query nothing for this handle.

is_readable = (1U << 0U), //!< If this handle is readable.
is_writable = (1U << 1U), //!< If this handle is writable.
is_errored = (1U << 2U), //!< If this handle is errored.
is_closed = (1U << 3U), //!< If this handle is closed/hung up.

not_pollable = (1U << 7U) //!< This handle is not pollable.
} //
QUICKCPPLIB_BITFIELD_END(poll_what)

/*! \brief Polls a list of pollable handles awaiting a change in state.
\return The number of handles with changed state. Handles not `is_kernel_handle()`
receive `poll_what::not_pollable`.
\param out An array of `poll_what` set with the results of the poll.
\param handles An array of pointers to `handle`. Individual pointers can be null if you want to skip them.
\param query An array of `poll_what` to check.
\param d An optional timeout.
\errors Whatever POSIX `poll()` or Windows `select()` can return.
Note that the maximum number of handles which can be passed to this function is 1024
(the platform syscall may refuse even that many). Note that this function is `O(N)` to
handle count, so more than a few hundred is a bad idea in any case.
If you need to wait on more handles than this, you need to implement a `byte_io_multiplexer`
for your platform.
The sizes of `out`, `handles` and `query` must be the same, or an error is returned.
*/
LLFIO_HEADERS_ONLY_FUNC_SPEC result<size_t> poll(span<poll_what> out, span<pollable_handle *> handles, span<const poll_what> query, deadline d = {}) noexcept;

// BEGIN make_free_functions.py
/*! \brief Read data from the open handle.
Expand All @@ -580,7 +642,8 @@ returned if deadline i/o is not possible with this particular handle configurati
reading from regular files on POSIX or reading from a non-overlapped HANDLE on Windows).
\mallocs The default synchronous implementation in file_handle performs no memory allocation.
*/
inline byte_io_handle::io_result<byte_io_handle::buffers_type> read(byte_io_handle &self, byte_io_handle::io_request<byte_io_handle::buffers_type> reqs, deadline d = deadline()) noexcept
inline byte_io_handle::io_result<byte_io_handle::buffers_type> read(byte_io_handle &self, byte_io_handle::io_request<byte_io_handle::buffers_type> reqs,
deadline d = deadline()) noexcept
{
return self.read(std::forward<decltype(reqs)>(reqs), std::forward<decltype(d)>(d));
}
Expand All @@ -605,12 +668,14 @@ returned if deadline i/o is not possible with this particular handle configurati
writing to regular files on POSIX or writing to a non-overlapped HANDLE on Windows).
\mallocs The default synchronous implementation in file_handle performs no memory allocation.
*/
inline byte_io_handle::io_result<byte_io_handle::const_buffers_type> write(byte_io_handle &self, byte_io_handle::io_request<byte_io_handle::const_buffers_type> reqs, deadline d = deadline()) noexcept
inline byte_io_handle::io_result<byte_io_handle::const_buffers_type>
write(byte_io_handle &self, byte_io_handle::io_request<byte_io_handle::const_buffers_type> reqs, deadline d = deadline()) noexcept
{
return self.write(std::forward<decltype(reqs)>(reqs), std::forward<decltype(d)>(d));
}
//! \overload
inline byte_io_handle::io_result<byte_io_handle::size_type> write(byte_io_handle &self, byte_io_handle::extent_type offset, std::initializer_list<byte_io_handle::const_buffer_type> lst, deadline d = deadline()) noexcept
inline byte_io_handle::io_result<byte_io_handle::size_type>
write(byte_io_handle &self, byte_io_handle::extent_type offset, std::initializer_list<byte_io_handle::const_buffer_type> lst, deadline d = deadline()) noexcept
{
return self.write(std::forward<decltype(offset)>(offset), std::forward<decltype(lst)>(lst), std::forward<decltype(d)>(d));
}
Expand Down
42 changes: 42 additions & 0 deletions include/llfio/v2.0/byte_io_multiplexer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,48 @@ class LLFIO_DECL byte_io_multiplexer : public handle
byte_io_multiplexer &operator=(const byte_io_multiplexer &) = delete;
~byte_io_multiplexer() = default;

struct implementation_information_t
{
string_view name; //!< The name of the underlying implementation e.g. "OpenSSL", "IOCP, "io_uring", "Windows RIO" etc.
struct
{
uint16_t major{0}, minor{0}, patch{0};
} version; //!< Version of the underlying implementation. Could be a kernel version if appropriate.
string_view postfix; //!< The build config or other disambiguator from others with the same name and version.
struct multiplexes_t
{
struct kernel_t
{
uint16_t file_handle : 1; //!< This i/o multiplexer can register plain kernel `file_handle`.
uint16_t pipe_handle : 1; //!< This i/o multiplexer can register plain kernel `pipe_handle`.
uint16_t byte_socket_handle : 1; //!< This i/o multiplexer can register plain kernel `byte_socket_handle`.
uint16_t listening_socket_handle : 1; //!< This i/o multiplexer can register plain kernel `listening_socket_handle`.

constexpr kernel_t()
: file_handle(false)
, pipe_handle(false)
, byte_socket_handle(false)
, listening_socket_handle(false)
{
}
} kernel;
uint16_t registered_io_buffers : 1; //!< This i/o multiplexer implements registered i/o buffers.
uint16_t secure_byte_socket_source : 1; //!< This i/o multiplexer can register a `byte_socket_handle` obtained from a `secure_byte_socket_source`.
uint16_t http_byte_socket_source : 1; //!< This i/o multiplexer can register a `http_byte_socket_handle` obtained from a `http_byte_socket_source`.

constexpr multiplexes_t()
: registered_io_buffers(false)
, secure_byte_socket_source(false)
, http_byte_socket_source(false)
{
}
} multiplexes;

constexpr implementation_information_t() {}
};
//! Returns implementation information about an i/o multiplexer
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC implementation_information_t implementation_information() const noexcept = 0;

public:
//! Implements `byte_io_handle` registration. The bottom two bits of the returned value are set into `_v.behaviour`'s `_multiplexer_state_bit0` and
//! `_multiplexer_state_bit`
Expand Down
82 changes: 78 additions & 4 deletions include/llfio/v2.0/byte_socket_handle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct sockaddr_in6;
#pragma warning(push)
#pragma warning(disable : 4201) // nameless struct/union
#pragma warning(disable : 4251) // dll interface
#pragma warning(disable : 4275) // dll interface
#endif

LLFIO_V2_NAMESPACE_EXPORT_BEGIN
Expand Down Expand Up @@ -68,7 +69,8 @@ namespace ip
{
unknown,
v4, //!< IP version 4
v6 //!< IP version 6
v6, //!< IP version 6
any //!< Either v4 or v6
};
/*! \class address
\brief A version independent IP address.
Expand Down Expand Up @@ -165,6 +167,74 @@ namespace ip
};
//! Write address to stream
LLFIO_HEADERS_ONLY_FUNC_SPEC std::ostream &operator<<(std::ostream &s, const address &v);

class resolver;
namespace detail
{
struct LLFIO_DECL resolver_deleter
{
void operator()(resolver *p) const;
};
} // namespace detail
//! Returned by `resolve()` as a handle to the asynchronous name resolution operation.
class LLFIO_DECL resolver
{
public:
//! Returns true if the deadline expired, and the returned list of addresses is incomplete. Until `get()` is called, always is true.
LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool incomplete() const noexcept;
//! Returns the array of addresses, blocking until completion if necessary, returning any error if one occurred.
LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<span<address>> get() const noexcept;
//! Wait for up the deadline for the array of addresses to be retrieved.
LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> wait(deadline d = {}) const noexcept;
//! \overload
template <class Rep, class Period> result<bool> wait_for(const std::chrono::duration<Rep, Period> &duration) const noexcept
{
auto r = wait(duration);
if(!r && r.error() == errc::timed_out)
{
return false;
}
OUTCOME_TRY(std::move(r));
return true;
}
//! \overload
template <class Clock, class Duration> result<bool> wait_until(const std::chrono::time_point<Clock, Duration> &timeout) const noexcept
{
auto r = wait(timeout);
if(!r && r.error() == errc::timed_out)
{
return false;
}
OUTCOME_TRY(std::move(r));
return true;
}
};
//! A pointer to a resolver
using resolver_ptr = std::unique_ptr<resolver, detail::resolver_deleter>;

/*! \brief Retrieve a list of potential `address` for a given name and service e.g.
"www.google.com" and "https" optionally within a bounded deadline.
The object returned by this function can take many seconds to become ready as multiple network requests may
need to be made. The deadline can be used to bound execution times -- like in a few
other places in LLFIO, this deadline does not cause timed out errors, rather it aborts
any remaining name resolution after the deadline expires and returns whatever addresses
have been resolved by the deadline.
This function has a future-like API as several major platforms provide native asynchronous
name resolution (currently: Linux, Windows). On other platforms, `std::async` with
`getaddrinfo()` is used as an emulation, and therefore deadline expiry means no partial
list of addresses are returned.
If you become no longer interested in the results, simply reset or delete the pointer
and the resolution will be aborted asynchronously.
\mallocs This is one of those very few APIs in LLFIO where dynamic memory allocation
is unbounded and uncontrollable thanks to how the platform APIs are implemented.
*/
LLFIO_HEADERS_ONLY_FUNC_SPEC result<resolver_ptr> resolve(string_view name, string_view service, family _family = family::any, deadline d = {},
bool blocking = false) noexcept;

//! Make an `address_v4`
LLFIO_HEADERS_ONLY_FUNC_SPEC result<address_v4> make_address_v4(string_view str) noexcept;
/*! \class address_v4
Expand Down Expand Up @@ -201,6 +271,8 @@ namespace ip
inline result<address_v4> make_address_v4(const address_v4::bytes_type &bytes, uint16_t port = 0) noexcept { return address_v4(bytes, port); }
//! Make an `address_v4`
inline result<address_v4> make_address_v4(const address_v4::uint_type &bytes, uint16_t port = 0) noexcept { return address_v4(bytes, port); }

//! Make an `address_v6`
LLFIO_HEADERS_ONLY_FUNC_SPEC result<address_v6> make_address_v6(string_view str) noexcept;
/*! \class address_v6
\brief A v6 IP address.
Expand Down Expand Up @@ -248,6 +320,7 @@ namespace ip
}
} // namespace ip


/*! \class byte_socket_handle
\brief A handle to a byte-orientated socket-like entity.
Expand Down Expand Up @@ -310,7 +383,7 @@ socket option is full of gotchas.
If you don't wish to have this operation occur during close, you
can call `shutdown_and_close()` manually instead.
*/
class LLFIO_DECL byte_socket_handle : public byte_io_handle
class LLFIO_DECL byte_socket_handle : public byte_io_handle, public pollable_handle
{
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC const handle &_get_handle() const noexcept final { return *this; }

Expand Down Expand Up @@ -534,8 +607,10 @@ template <> struct construct<byte_socket_handle>
/* \class listening_socket_handle
\brief A handle to a socket-like entity able to receive incoming connections.
*/
class LLFIO_DECL listening_socket_handle : public handle
class LLFIO_DECL listening_socket_handle : public handle, public pollable_handle
{
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC const handle &_get_handle() const noexcept final { return *this; }

protected:
byte_io_multiplexer *_ctx{nullptr}; // +4 or +8 bytes
public:
Expand Down Expand Up @@ -878,7 +953,6 @@ template <> struct construct<listening_socket_handle>
result<listening_socket_handle> operator()() const noexcept { return listening_socket_handle::listening_socket(family, _mode, _caching, flags); }
};


// BEGIN make_free_functions.py
// END make_free_functions.py

Expand Down
Loading

0 comments on commit d5717dc

Please sign in to comment.