2020-02-05 00:09:56 +00:00
|
|
|
#ifndef FISH_FD_MONITOR_H
|
|
|
|
#define FISH_FD_MONITOR_H
|
|
|
|
|
|
|
|
#include <chrono>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <functional>
|
2022-08-21 06:14:48 +00:00
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
|
2020-02-12 18:28:56 +00:00
|
|
|
// Needed for musl
|
|
|
|
#include <sys/select.h> // IWYU pragma: keep
|
2020-02-05 00:09:56 +00:00
|
|
|
|
|
|
|
#include "common.h"
|
2021-02-03 00:44:33 +00:00
|
|
|
#include "fds.h"
|
2020-02-05 00:09:56 +00:00
|
|
|
#include "maybe.h"
|
|
|
|
|
2021-01-04 00:26:28 +00:00
|
|
|
/// Each item added to fd_monitor_t is assigned a unique ID, which is not recycled.
|
|
|
|
/// Items may have their callback triggered immediately by passing the ID.
|
|
|
|
/// Zero is a sentinel.
|
|
|
|
using fd_monitor_item_id_t = uint64_t;
|
|
|
|
|
|
|
|
/// Reasons for waking an item.
|
|
|
|
enum class item_wake_reason_t {
|
|
|
|
readable, // the fd became readable
|
|
|
|
timeout, // the requested timeout was hit
|
|
|
|
poke, // the item was "poked" (woken up explicitly)
|
|
|
|
};
|
|
|
|
|
2020-02-05 00:09:56 +00:00
|
|
|
/// An item containing an fd and callback, which can be monitored to watch when it becomes readable,
|
|
|
|
/// and invoke the callback.
|
|
|
|
struct fd_monitor_item_t {
|
2021-01-04 00:26:28 +00:00
|
|
|
/// The callback type for the item. It is passed \p fd, and the reason for waking \p reason.
|
|
|
|
/// The callback may close \p fd, in which case the item is removed.
|
|
|
|
using callback_t = std::function<void(autoclose_fd_t &fd, item_wake_reason_t reason)>;
|
2020-02-05 00:09:56 +00:00
|
|
|
|
|
|
|
/// A sentinel value meaning no timeout.
|
2022-01-02 23:25:12 +00:00
|
|
|
static constexpr uint64_t kNoTimeout = fd_readable_set_t::kNoTimeout;
|
2020-02-05 00:09:56 +00:00
|
|
|
|
|
|
|
/// The fd to monitor.
|
|
|
|
autoclose_fd_t fd{};
|
|
|
|
|
|
|
|
/// A callback to be invoked when the fd is readable, or when we are timed out.
|
|
|
|
/// If we time out, then timed_out will be true.
|
|
|
|
/// If the fd is invalid on return from the function, then the item is removed.
|
|
|
|
callback_t callback{};
|
|
|
|
|
|
|
|
/// The timeout in microseconds, or kNoTimeout for none.
|
|
|
|
/// 0 timeouts are unsupported.
|
|
|
|
uint64_t timeout_usec{kNoTimeout};
|
|
|
|
|
|
|
|
/// Construct from a file, callback, and optional timeout.
|
|
|
|
fd_monitor_item_t(autoclose_fd_t fd, callback_t callback, uint64_t timeout_usec = kNoTimeout)
|
|
|
|
: fd(std::move(fd)), callback(std::move(callback)), timeout_usec(timeout_usec) {
|
|
|
|
assert(timeout_usec > 0 && "Invalid timeout");
|
|
|
|
}
|
|
|
|
|
|
|
|
fd_monitor_item_t() = default;
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Fields and methods for the private use of fd_monitor_t.
|
|
|
|
using time_point_t = std::chrono::time_point<std::chrono::steady_clock>;
|
|
|
|
|
|
|
|
// The last time we were called, or the initialization point.
|
|
|
|
maybe_t<time_point_t> last_time{};
|
|
|
|
|
2021-01-04 00:26:28 +00:00
|
|
|
// The ID for this item. This is assigned by the fd monitor.
|
|
|
|
fd_monitor_item_id_t item_id{0};
|
|
|
|
|
2020-02-05 00:09:56 +00:00
|
|
|
// \return the number of microseconds until the timeout should trigger, or kNoTimeout for none.
|
|
|
|
// A 0 return means we are at or past the timeout.
|
|
|
|
uint64_t usec_remaining(const time_point_t &now) const;
|
|
|
|
|
|
|
|
// Invoke this item's callback if its value is set in fd or has timed out.
|
|
|
|
// \return true to retain the item, false to remove it.
|
2022-01-02 23:25:12 +00:00
|
|
|
bool service_item(const fd_readable_set_t &fds, const time_point_t &now);
|
2021-01-04 00:26:28 +00:00
|
|
|
|
|
|
|
// Invoke this item's callback with a poke, if its ID is present in the (sorted) pokelist.
|
|
|
|
// \return true to retain the item, false to remove it.
|
|
|
|
using poke_list_t = std::vector<fd_monitor_item_id_t>;
|
|
|
|
bool poke_item(const poke_list_t &pokelist);
|
|
|
|
|
|
|
|
friend class fd_monitor_t;
|
2020-02-05 00:09:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/// A class which can monitor a set of fds, invoking a callback when any becomes readable, or when
|
|
|
|
/// per-item-configurable timeouts are hit.
|
|
|
|
class fd_monitor_t {
|
|
|
|
public:
|
|
|
|
using item_list_t = std::vector<fd_monitor_item_t>;
|
|
|
|
|
2021-01-04 00:26:28 +00:00
|
|
|
// A "pokelist" is a sorted list of item IDs which need explicit wakeups.
|
|
|
|
using poke_list_t = std::vector<fd_monitor_item_id_t>;
|
|
|
|
|
2020-02-05 00:09:56 +00:00
|
|
|
fd_monitor_t();
|
|
|
|
~fd_monitor_t();
|
|
|
|
|
2021-01-04 00:26:28 +00:00
|
|
|
/// Add an item to monitor. \return the ID assigned to the item.
|
|
|
|
fd_monitor_item_id_t add(fd_monitor_item_t &&item);
|
|
|
|
|
|
|
|
/// Mark that an item with a given ID needs to be explicitly woken up.
|
|
|
|
void poke_item(fd_monitor_item_id_t item_id);
|
2020-02-05 00:09:56 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
// The background thread runner.
|
|
|
|
void run_in_background();
|
|
|
|
|
2021-02-07 03:21:03 +00:00
|
|
|
// If our self-signaller is reported as ready, this reads from it and handles any changes.
|
|
|
|
// Called in the background thread.
|
|
|
|
void handle_self_signal_in_background();
|
|
|
|
|
2021-01-04 00:26:28 +00:00
|
|
|
// Poke items in the pokelist, removing any items that close their FD.
|
|
|
|
// The pokelist is consumed after this.
|
|
|
|
// This is only called in the background thread.
|
2022-04-01 03:36:29 +00:00
|
|
|
void poke_in_background(const poke_list_t &pokelist);
|
2020-02-05 00:09:56 +00:00
|
|
|
|
|
|
|
// The list of items to monitor. This is only accessed on the background thread.
|
|
|
|
item_list_t items_{};
|
|
|
|
|
|
|
|
struct data_t {
|
2021-01-04 00:26:28 +00:00
|
|
|
/// Pending items. This is set under the lock, then the background thread grabs them.
|
2020-02-05 00:09:56 +00:00
|
|
|
item_list_t pending{};
|
|
|
|
|
2021-01-04 00:26:28 +00:00
|
|
|
/// List of IDs for items that need to be poked (explicitly woken up).
|
|
|
|
poke_list_t pokelist{};
|
|
|
|
|
|
|
|
/// The last ID assigned, or if none.
|
|
|
|
fd_monitor_item_id_t last_id{0};
|
|
|
|
|
2020-02-05 00:09:56 +00:00
|
|
|
/// Whether the thread is running.
|
|
|
|
bool running{false};
|
2021-02-07 03:21:03 +00:00
|
|
|
|
|
|
|
// Set if we should terminate.
|
|
|
|
bool terminate{false};
|
2020-02-05 00:09:56 +00:00
|
|
|
};
|
|
|
|
owning_lock<data_t> data_;
|
|
|
|
|
2021-02-07 03:21:03 +00:00
|
|
|
/// Our self-signaller. When this is written to, it means there are new items pending, or new
|
|
|
|
/// items in the pokelist, or terminate is set.
|
|
|
|
fd_event_signaller_t change_signaller_;
|
2020-02-05 00:09:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|