mirror of
https://gitlab.com/niansa/libcrosscoro.git
synced 2025-03-06 20:53:32 +01:00
* io_scheduler inline support * add debug info for io_scheduler size issue * move poll info into its own file * cleanup for feature * Fix valgrind introduced use after free with inline processing Running the coroutines inline with event processing caused a use after free bug with valgrind detected in the inline tcp server/client benchmark code. Basically if an event and a timeout occured in the same time period because the inline processing would resume _inline_ with the event or the timeout -- if the timeout and event occured in the same epoll_wait() function call then the second one's coroutine stackframe would already be destroyed upon resuming it so the poll_info->processed check would be reading already free'ed memory. The solution to this was to introduce a vector of coroutine handles which are appended into on each epoll_wait() iteration of events and timeouts, and only then after the events and timeouts are deduplicated are the coroutine handles resumed. This new vector has elided a malloc in the timeout function, but there is still a malloc to extract the poll infos from the timeout multimap data structure. The vector is also on the class member list and is only ever cleared, it is possible with a monster set of timeouts that this vector could grow extremely large, but I think that is worth the price of not re-allocating it.
100 lines
2.9 KiB
C++
100 lines
2.9 KiB
C++
#pragma once
|
|
|
|
#include "coro/fd.hpp"
|
|
#include "coro/io_scheduler.hpp"
|
|
#include "coro/net/hostname.hpp"
|
|
#include "coro/net/ip_address.hpp"
|
|
#include "coro/task.hpp"
|
|
#include "coro/task_container.hpp"
|
|
|
|
#include <ares.h>
|
|
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <sys/epoll.h>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
namespace coro::net
|
|
{
|
|
class dns_resolver;
|
|
|
|
enum class dns_status
|
|
{
|
|
complete,
|
|
error
|
|
};
|
|
|
|
class dns_result
|
|
{
|
|
friend dns_resolver;
|
|
|
|
public:
|
|
dns_result(coro::io_scheduler& scheduler, coro::event& resume, uint64_t pending_dns_requests);
|
|
~dns_result() = default;
|
|
|
|
/**
|
|
* @return The status of the dns lookup.
|
|
*/
|
|
auto status() const -> dns_status { return m_status; }
|
|
|
|
/**
|
|
* @return If the result of the dns looked was successful then the list of ip addresses that
|
|
* were resolved from the hostname.
|
|
*/
|
|
auto ip_addresses() const -> const std::vector<coro::net::ip_address>& { return m_ip_addresses; }
|
|
|
|
private:
|
|
coro::io_scheduler& m_io_scheduler;
|
|
coro::event& m_resume;
|
|
uint64_t m_pending_dns_requests{0};
|
|
dns_status m_status{dns_status::complete};
|
|
std::vector<coro::net::ip_address> m_ip_addresses{};
|
|
|
|
friend auto ares_dns_callback(void* arg, int status, int timeouts, struct hostent* host) -> void;
|
|
};
|
|
|
|
class dns_resolver
|
|
{
|
|
public:
|
|
explicit dns_resolver(io_scheduler& scheduler, std::chrono::milliseconds timeout);
|
|
dns_resolver(const dns_resolver&) = delete;
|
|
dns_resolver(dns_resolver&&) = delete;
|
|
auto operator=(const dns_resolver&) noexcept -> dns_resolver& = delete;
|
|
auto operator=(dns_resolver&&) noexcept -> dns_resolver& = delete;
|
|
~dns_resolver();
|
|
|
|
/**
|
|
* @param hn The hostname to resolve its ip addresses.
|
|
*/
|
|
auto host_by_name(const net::hostname& hn) -> coro::task<std::unique_ptr<dns_result>>;
|
|
|
|
private:
|
|
/// The io scheduler to drive the events for dns lookups.
|
|
io_scheduler& m_io_scheduler;
|
|
|
|
/// The global timeout per dns lookup request.
|
|
std::chrono::milliseconds m_timeout{0};
|
|
|
|
/// The libc-ares channel for looking up dns entries.
|
|
ares_channel m_ares_channel{nullptr};
|
|
|
|
/// This is the set of sockets that are currently being actively polled so multiple poll tasks
|
|
/// are not setup when ares_poll() is called.
|
|
std::unordered_set<fd_t> m_active_sockets{};
|
|
|
|
task_container<io_scheduler> m_task_container;
|
|
|
|
/// Global count to track if c-ares has been initialized or cleaned up.
|
|
static uint64_t m_ares_count;
|
|
/// Critical section around the c-ares global init/cleanup to prevent heap corruption.
|
|
static std::mutex m_ares_mutex;
|
|
|
|
auto ares_poll() -> void;
|
|
auto make_poll_task(fd_t fd, poll_op ops) -> coro::task<void>;
|
|
};
|
|
|
|
} // namespace coro::net
|