mirror of
https://github.com/fish-shell/fish-shell
synced 2024-11-10 23:24:39 +00:00
Moderize universal variable notifiers
Use some C++11 features.
This commit is contained in:
parent
a1fd9e1b85
commit
700fe4f131
2 changed files with 125 additions and 147 deletions
|
@ -1046,7 +1046,7 @@ static wcstring get_machine_identifier() {
|
|||
return result;
|
||||
}
|
||||
|
||||
class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
||||
class universal_notifier_shmem_poller_t final : public universal_notifier_t {
|
||||
#ifdef __CYGWIN__
|
||||
// This is what our shared memory looks like. Everything here is stored in network byte order
|
||||
// (big-endian).
|
||||
|
@ -1060,9 +1060,9 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
|||
#define SHMEM_VERSION_CURRENT 1000
|
||||
|
||||
private:
|
||||
long long last_change_time;
|
||||
uint32_t last_seed;
|
||||
volatile universal_notifier_shmem_t *region;
|
||||
long long last_change_time{0};
|
||||
uint32_t last_seed{0};
|
||||
volatile universal_notifier_shmem_t *region{nullptr};
|
||||
|
||||
void open_shmem() {
|
||||
assert(region == nullptr);
|
||||
|
@ -1072,52 +1072,45 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
|||
snprintf(path, sizeof path, "/%ls_shmem_%d", program_name ? program_name : L"fish",
|
||||
getuid());
|
||||
|
||||
bool errored = false;
|
||||
autoclose_fd_t fd{shm_open(path, O_RDWR | O_CREAT, 0600)};
|
||||
if (!fd.valid()) {
|
||||
const char *error = std::strerror(errno);
|
||||
FLOGF(error, _(L"Unable to open shared memory with path '%s': %s"), path, error);
|
||||
errored = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the size.
|
||||
off_t size = 0;
|
||||
if (!errored) {
|
||||
struct stat buf = {};
|
||||
if (fstat(fd.fd(), &buf) < 0) {
|
||||
const char *error = std::strerror(errno);
|
||||
FLOGF(error, _(L"Unable to fstat shared memory object with path '%s': %s"), path,
|
||||
error);
|
||||
errored = true;
|
||||
}
|
||||
size = buf.st_size;
|
||||
struct stat buf = {};
|
||||
if (fstat(fd.fd(), &buf) < 0) {
|
||||
const char *error = std::strerror(errno);
|
||||
FLOGF(error, _(L"Unable to fstat shared memory object with path '%s': %s"), path,
|
||||
error);
|
||||
return;
|
||||
}
|
||||
size = buf.st_size;
|
||||
|
||||
// Set the size, if it's too small.
|
||||
bool set_size = !errored && size < (off_t)sizeof(universal_notifier_shmem_t);
|
||||
if (set_size && ftruncate(fd.fd(), sizeof(universal_notifier_shmem_t)) < 0) {
|
||||
const char *error = std::strerror(errno);
|
||||
FLOGF(error, _(L"Unable to truncate shared memory object with path '%s': %s"), path,
|
||||
error);
|
||||
errored = true;
|
||||
if (size < (off_t)sizeof(universal_notifier_shmem_t)) {
|
||||
if (ftruncate(fd.fd(), sizeof(universal_notifier_shmem_t)) < 0) {
|
||||
const char *error = std::strerror(errno);
|
||||
FLOGF(error, _(L"Unable to truncate shared memory object with path '%s': %s"), path,
|
||||
error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Memory map the region.
|
||||
if (!errored) {
|
||||
void *addr = mmap(nullptr, sizeof(universal_notifier_shmem_t), PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, fd.fd(), 0);
|
||||
if (addr == MAP_FAILED) {
|
||||
const char *error = std::strerror(errno);
|
||||
FLOGF(error, _(L"Unable to memory map shared memory object with path '%s': %s"),
|
||||
path, error);
|
||||
this->region = nullptr;
|
||||
} else {
|
||||
this->region = static_cast<universal_notifier_shmem_t *>(addr);
|
||||
}
|
||||
void *addr = mmap(nullptr, sizeof(universal_notifier_shmem_t), PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, fd.fd(), 0);
|
||||
if (addr == MAP_FAILED) {
|
||||
const char *error = std::strerror(errno);
|
||||
FLOGF(error, _(L"Unable to memory map shared memory object with path '%s': %s"), path,
|
||||
error);
|
||||
this->region = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Close the fd, even if the mapping succeeded.
|
||||
fd.close();
|
||||
this->region = static_cast<universal_notifier_shmem_t *>(addr);
|
||||
|
||||
// Read the current seed.
|
||||
this->poll();
|
||||
|
@ -1129,7 +1122,7 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
|||
// purposes, however, it's useful to keep them in the same process, so we increment the value.
|
||||
// This isn't "safe" in the sense that multiple simultaneous increments may result in one being
|
||||
// lost, but it should always result in the value being changed, which is sufficient.
|
||||
void post_notification() {
|
||||
void post_notification() override {
|
||||
if (region != nullptr) {
|
||||
/* Read off the seed */
|
||||
uint32_t seed = ntohl(region->universal_variable_seed); //!OCLINT(constant cond op)
|
||||
|
@ -1147,13 +1140,10 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
|||
}
|
||||
}
|
||||
|
||||
universal_notifier_shmem_poller_t() : last_change_time(0), last_seed(0), region(nullptr) {
|
||||
open_shmem();
|
||||
}
|
||||
universal_notifier_shmem_poller_t() { open_shmem(); }
|
||||
|
||||
~universal_notifier_shmem_poller_t() {
|
||||
if (region != nullptr) {
|
||||
// Behold: C++ in all its glory!
|
||||
void *address = const_cast<void *>(static_cast<volatile void *>(region));
|
||||
if (munmap(address, sizeof(universal_notifier_shmem_t)) < 0) {
|
||||
wperror(L"munmap");
|
||||
|
@ -1161,7 +1151,7 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
|||
}
|
||||
}
|
||||
|
||||
bool poll() {
|
||||
bool poll() override {
|
||||
bool result = false;
|
||||
if (region != nullptr) {
|
||||
uint32_t seed = ntohl(region->universal_variable_seed); //!OCLINT(constant cond op)
|
||||
|
@ -1174,7 +1164,7 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
|||
return result;
|
||||
}
|
||||
|
||||
unsigned long usec_delay_between_polls() const {
|
||||
unsigned long usec_delay_between_polls() const override {
|
||||
// If it's been less than five seconds since the last change, we poll quickly Otherwise we
|
||||
// poll more slowly. Note that a poll is a very cheap shmem read. The bad part about making
|
||||
// this high is the process scheduling/wakeups it produces.
|
||||
|
@ -1193,11 +1183,13 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
|||
};
|
||||
|
||||
/// A notifyd-based notifier. Very straightforward.
|
||||
class universal_notifier_notifyd_t : public universal_notifier_t {
|
||||
class universal_notifier_notifyd_t final : public universal_notifier_t {
|
||||
#ifdef FISH_NOTIFYD_AVAILABLE
|
||||
int notify_fd;
|
||||
int token;
|
||||
std::string name;
|
||||
// Note that we should not use autoclose_fd_t, as notify_cancel() takes responsibility for
|
||||
// closing it.
|
||||
int notify_fd{-1};
|
||||
int token{-1}; // NOTIFY_TOKEN_INVALID
|
||||
std::string name{};
|
||||
|
||||
void setup_notifyd() {
|
||||
// Per notify(3), the user.uid.%d style is only accessible to processes with that uid.
|
||||
|
@ -1208,40 +1200,40 @@ class universal_notifier_notifyd_t : public universal_notifier_t {
|
|||
|
||||
uint32_t status =
|
||||
notify_register_file_descriptor(name.c_str(), &this->notify_fd, 0, &this->token);
|
||||
|
||||
if (status != NOTIFY_STATUS_OK) {
|
||||
FLOGF(warning, "notify_register_file_descriptor() failed with status %u.", status);
|
||||
FLOGF(warning, "Universal variable notifications may not be received.");
|
||||
}
|
||||
if (this->notify_fd >= 0) {
|
||||
if (notify_fd >= 0) {
|
||||
// Mark us for non-blocking reads, and CLO_EXEC.
|
||||
int flags = fcntl(this->notify_fd, F_GETFL, 0);
|
||||
int flags = fcntl(notify_fd, F_GETFL, 0);
|
||||
if (flags >= 0 && !(flags & O_NONBLOCK)) {
|
||||
fcntl(this->notify_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
fcntl(notify_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
|
||||
set_cloexec(this->notify_fd);
|
||||
(void)set_cloexec(notify_fd);
|
||||
// Serious hack: notify_fd is likely the read end of a pipe. The other end is owned by
|
||||
// libnotify, which does not mark it as CLO_EXEC (it should!). The next fd is probably
|
||||
// notify_fd + 1. Do it ourselves. If the implementation changes and some other FD gets
|
||||
// marked as CLO_EXEC, that's probably a good thing.
|
||||
set_cloexec(this->notify_fd + 1);
|
||||
(void)set_cloexec(notify_fd + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
universal_notifier_notifyd_t() : notify_fd(-1), token(-1 /* NOTIFY_TOKEN_INVALID */) {
|
||||
setup_notifyd();
|
||||
}
|
||||
universal_notifier_notifyd_t() { setup_notifyd(); }
|
||||
|
||||
~universal_notifier_notifyd_t() {
|
||||
if (token != -1 /* NOTIFY_TOKEN_INVALID */) {
|
||||
// Note this closes notify_fd.
|
||||
notify_cancel(token);
|
||||
}
|
||||
}
|
||||
|
||||
int notification_fd() const { return notify_fd; }
|
||||
int notification_fd() const override { return notify_fd; }
|
||||
|
||||
bool notification_fd_became_readable(int fd) {
|
||||
bool notification_fd_became_readable(int fd) override {
|
||||
// notifyd notifications come in as 32 bit values. We don't care about the value. We set
|
||||
// ourselves as non-blocking, so just read until we can't read any more.
|
||||
assert(fd == notify_fd);
|
||||
|
@ -1255,7 +1247,7 @@ class universal_notifier_notifyd_t : public universal_notifier_t {
|
|||
return read_something;
|
||||
}
|
||||
|
||||
void post_notification() {
|
||||
void post_notification() override {
|
||||
uint32_t status = notify_post(name.c_str());
|
||||
if (status != NOTIFY_STATUS_OK) {
|
||||
FLOGF(warning,
|
||||
|
@ -1271,10 +1263,40 @@ class universal_notifier_notifyd_t : public universal_notifier_t {
|
|||
#endif
|
||||
};
|
||||
|
||||
#if !defined(__APPLE__) && !defined(__CYGWIN__)
|
||||
#define NAMED_PIPE_FLASH_DURATION_USEC (1e5)
|
||||
#define SUSTAINED_READABILITY_CLEANUP_DURATION_USEC (5 * 1e6)
|
||||
#endif
|
||||
/// Returns a "variables" file in the appropriate runtime directory. This is called infrequently and
|
||||
/// so does not need to be cached.
|
||||
static wcstring default_named_pipe_path() {
|
||||
wcstring result = env_get_runtime_path();
|
||||
if (!result.empty()) {
|
||||
result.append(L"/fish_universal_variables");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Create a fifo (named pipe) at \p test_path if non-null, or a default runtime path if null.
|
||||
/// Open the fifo for both reading and writing, in non-blocking mode.
|
||||
/// \return the fifo, or an invalid fd on failure.
|
||||
static autoclose_fd_t make_fifo(const wchar_t *test_path, const wchar_t *suffix) {
|
||||
wcstring vars_path = test_path ? wcstring(test_path) : default_named_pipe_path();
|
||||
vars_path.append(suffix);
|
||||
const std::string narrow_path = wcs2string(vars_path);
|
||||
|
||||
int mkfifo_status = mkfifo(narrow_path.c_str(), 0600);
|
||||
if (mkfifo_status == -1 && errno != EEXIST) {
|
||||
const char *error = std::strerror(errno);
|
||||
const wchar_t *errmsg = _(L"Unable to make a pipe for universal variables using '%ls': %s");
|
||||
FLOGF(error, errmsg, vars_path.c_str(), error);
|
||||
return autoclose_fd_t{};
|
||||
}
|
||||
|
||||
autoclose_fd_t res{wopen_cloexec(vars_path, O_RDWR | O_NONBLOCK, 0600)};
|
||||
if (!res.valid()) {
|
||||
const char *error = std::strerror(errno);
|
||||
const wchar_t *errmsg = _(L"Unable to open a pipe for universal variables using '%ls': %s");
|
||||
FLOGF(error, errmsg, vars_path.c_str(), error);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Named-pipe based notifier. All clients open the same named pipe for reading and writing. The
|
||||
// pipe's readability status is a trigger to enter polling mode.
|
||||
|
@ -1285,42 +1307,34 @@ class universal_notifier_notifyd_t : public universal_notifier_t {
|
|||
// mode until the pipe is no longer readable. To guard against the possibility of a shell exiting
|
||||
// when there is data remaining in the pipe, if the pipe is kept readable too long, clients will
|
||||
// attempt to read data out of it (to render it no longer readable).
|
||||
class universal_notifier_named_pipe_t : public universal_notifier_t {
|
||||
#if !defined(__APPLE__) && !defined(__CYGWIN__)
|
||||
int pipe_fd;
|
||||
long long readback_time_usec;
|
||||
size_t readback_amount;
|
||||
class universal_notifier_named_pipe_t final : public universal_notifier_t {
|
||||
#if !defined(__CYGWIN__)
|
||||
autoclose_fd_t pipe_fd;
|
||||
long long readback_time_usec{0};
|
||||
size_t readback_amount{0};
|
||||
|
||||
bool polling_due_to_readable_fd;
|
||||
long long drain_if_still_readable_time_usec;
|
||||
// We "flash" the pipe to make it briefly readable, for this many usec.
|
||||
static constexpr long long k_flash_duration_usec = 1e5;
|
||||
|
||||
void make_pipe(const wchar_t *test_path);
|
||||
// If the pipe remains readable for this many usec, we drain it.
|
||||
static constexpr long long k_readable_too_long_duration_usec = 5e6;
|
||||
|
||||
bool polling_due_to_readable_fd{false};
|
||||
long long drain_if_still_readable_time_usec{0};
|
||||
|
||||
void drain_excessive_data() const {
|
||||
// The pipe seems to have data on it, that won't go away. Read a big chunk out of it. We
|
||||
// don't read until it's exhausted, because if someone were to pipe say /dev/null, that
|
||||
// would cause us to hang!
|
||||
size_t read_amt = 64 * 1024;
|
||||
void *buff = malloc(read_amt);
|
||||
ignore_result(read(this->pipe_fd, buff, read_amt));
|
||||
free(buff);
|
||||
char buff[512];
|
||||
ignore_result(read(pipe_fd.fd(), buff, sizeof buff));
|
||||
}
|
||||
|
||||
public:
|
||||
explicit universal_notifier_named_pipe_t(const wchar_t *test_path)
|
||||
: pipe_fd(-1),
|
||||
readback_time_usec(0),
|
||||
readback_amount(0),
|
||||
polling_due_to_readable_fd(false),
|
||||
drain_if_still_readable_time_usec(0) {
|
||||
make_pipe(test_path);
|
||||
}
|
||||
: pipe_fd(make_fifo(test_path, L".notifier")) {}
|
||||
|
||||
~universal_notifier_named_pipe_t() override {
|
||||
if (pipe_fd >= 0) {
|
||||
close(pipe_fd);
|
||||
}
|
||||
}
|
||||
~universal_notifier_named_pipe_t() override = default;
|
||||
|
||||
int notification_fd() const override {
|
||||
if (polling_due_to_readable_fd) {
|
||||
|
@ -1329,7 +1343,7 @@ class universal_notifier_named_pipe_t : public universal_notifier_t {
|
|||
return -1;
|
||||
}
|
||||
// We are not in polling mode. Return the fd so it can be watched.
|
||||
return pipe_fd;
|
||||
return pipe_fd.fd();
|
||||
}
|
||||
|
||||
bool notification_fd_became_readable(int fd) override {
|
||||
|
@ -1342,28 +1356,26 @@ class universal_notifier_named_pipe_t : public universal_notifier_t {
|
|||
bool should_sync = false;
|
||||
if (readback_time_usec == 0) {
|
||||
polling_due_to_readable_fd = true;
|
||||
drain_if_still_readable_time_usec =
|
||||
get_time() + SUSTAINED_READABILITY_CLEANUP_DURATION_USEC;
|
||||
drain_if_still_readable_time_usec = get_time() + k_readable_too_long_duration_usec;
|
||||
should_sync = true;
|
||||
}
|
||||
return should_sync;
|
||||
}
|
||||
|
||||
void post_notification() override {
|
||||
if (pipe_fd >= 0) {
|
||||
// We need to write some data (any data) to the pipe, then wait for a while, then read
|
||||
// it back. Nobody is expected to read it except us.
|
||||
int pid_nbo = htonl(getpid()); //!OCLINT(constant cond op)
|
||||
ssize_t amt_written = write(this->pipe_fd, &pid_nbo, sizeof pid_nbo);
|
||||
if (amt_written < 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
|
||||
// Very unsual: the pipe is full!
|
||||
drain_excessive_data();
|
||||
}
|
||||
|
||||
// Now schedule a read for some time in the future.
|
||||
this->readback_time_usec = get_time() + NAMED_PIPE_FLASH_DURATION_USEC;
|
||||
this->readback_amount += sizeof pid_nbo;
|
||||
if (!pipe_fd.valid()) return;
|
||||
// We need to write some data (any data) to the pipe, then wait for a while, then read
|
||||
// it back. Nobody is expected to read it except us.
|
||||
char c[1] = {'\0'};
|
||||
ssize_t amt_written = write(pipe_fd.fd(), c, sizeof c);
|
||||
if (amt_written < 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
|
||||
// Very unsual: the pipe is full!
|
||||
drain_excessive_data();
|
||||
}
|
||||
|
||||
// Now schedule a read for some time in the future.
|
||||
this->readback_time_usec = get_time() + k_flash_duration_usec;
|
||||
this->readback_amount += sizeof c;
|
||||
}
|
||||
|
||||
unsigned long usec_delay_between_polls() const override {
|
||||
|
@ -1382,7 +1394,7 @@ class universal_notifier_named_pipe_t : public universal_notifier_t {
|
|||
unsigned long polling_delay = ULONG_MAX;
|
||||
if (polling_due_to_readable_fd) {
|
||||
// We're in polling mode. Don't return a value less than our polling interval.
|
||||
polling_delay = NAMED_PIPE_FLASH_DURATION_USEC;
|
||||
polling_delay = k_flash_duration_usec;
|
||||
}
|
||||
|
||||
// Now return the smaller of the two values. If we get ULONG_MAX, it means there's no more
|
||||
|
@ -1395,13 +1407,15 @@ class universal_notifier_named_pipe_t : public universal_notifier_t {
|
|||
}
|
||||
|
||||
bool poll() override {
|
||||
if (!pipe_fd.valid()) return false;
|
||||
|
||||
// Check if we are past the readback time.
|
||||
if (this->readback_time_usec > 0 && get_time() >= this->readback_time_usec) {
|
||||
// Read back what we wrote. We do nothing with the value.
|
||||
while (this->readback_amount > 0) {
|
||||
char buff[64];
|
||||
size_t amt_to_read = std::min(this->readback_amount, sizeof(buff));
|
||||
ignore_result(read(this->pipe_fd, buff, amt_to_read));
|
||||
ignore_result(read(this->pipe_fd.fd(), buff, amt_to_read));
|
||||
this->readback_amount -= amt_to_read;
|
||||
}
|
||||
assert(this->readback_amount == 0);
|
||||
|
@ -1409,7 +1423,7 @@ class universal_notifier_named_pipe_t : public universal_notifier_t {
|
|||
}
|
||||
|
||||
// Check to see if we are doing readability polling.
|
||||
if (!polling_due_to_readable_fd || pipe_fd < 0) {
|
||||
if (!polling_due_to_readable_fd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1417,10 +1431,10 @@ class universal_notifier_named_pipe_t : public universal_notifier_t {
|
|||
// See if this is still readable.
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(this->pipe_fd, &fds);
|
||||
FD_SET(pipe_fd.fd(), &fds);
|
||||
struct timeval timeout = {};
|
||||
select(this->pipe_fd + 1, &fds, nullptr, nullptr, &timeout);
|
||||
if (!FD_ISSET(this->pipe_fd, &fds)) {
|
||||
select(pipe_fd.fd() + 1, &fds, nullptr, nullptr, &timeout);
|
||||
if (!FD_ISSET(pipe_fd.fd(), &fds)) {
|
||||
// No longer readable, no longer polling.
|
||||
polling_due_to_readable_fd = false;
|
||||
drain_if_still_readable_time_usec = 0;
|
||||
|
@ -1482,51 +1496,13 @@ universal_notifier_t::~universal_notifier_t() = default;
|
|||
|
||||
int universal_notifier_t::notification_fd() const { return -1; }
|
||||
|
||||
void universal_notifier_t::post_notification() {}
|
||||
|
||||
bool universal_notifier_t::poll() { return false; }
|
||||
|
||||
void universal_notifier_t::post_notification() {}
|
||||
|
||||
unsigned long universal_notifier_t::usec_delay_between_polls() const { return 0; }
|
||||
|
||||
bool universal_notifier_t::notification_fd_became_readable(int fd) {
|
||||
UNUSED(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(__APPLE__) && !defined(__CYGWIN__)
|
||||
/// Returns a "variables" file in the appropriate runtime directory. This is called infrequently and
|
||||
/// so does not need to be cached.
|
||||
static wcstring default_named_pipe_path() {
|
||||
wcstring result = env_get_runtime_path();
|
||||
if (!result.empty()) {
|
||||
result.append(L"/fish_universal_variables");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void universal_notifier_named_pipe_t::make_pipe(const wchar_t *test_path) {
|
||||
wcstring vars_path = test_path ? wcstring(test_path) : default_named_pipe_path();
|
||||
vars_path.append(L".notifier");
|
||||
const std::string narrow_path = wcs2string(vars_path);
|
||||
|
||||
int mkfifo_status = mkfifo(narrow_path.c_str(), 0600);
|
||||
if (mkfifo_status == -1 && errno != EEXIST) {
|
||||
const char *error = std::strerror(errno);
|
||||
const wchar_t *errmsg = _(L"Unable to make a pipe for universal variables using '%ls': %s");
|
||||
FLOGF(error, errmsg, vars_path.c_str(), error);
|
||||
pipe_fd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
int fd = wopen_cloexec(vars_path, O_RDWR | O_NONBLOCK, 0600);
|
||||
if (fd < 0) {
|
||||
const char *error = std::strerror(errno);
|
||||
const wchar_t *errmsg = _(L"Unable to open a pipe for universal variables using '%ls': %s");
|
||||
FLOGF(error, errmsg, vars_path.c_str(), error);
|
||||
pipe_fd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
pipe_fd = fd;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -162,8 +162,10 @@ class universal_notifier_t {
|
|||
// Use a value in shared memory. Simple, but requires polling and therefore semi-frequent
|
||||
// wakeups.
|
||||
strategy_shmem_polling,
|
||||
|
||||
// Strategy that uses notify(3). Simple and efficient, but OS X/macOS only.
|
||||
strategy_notifyd,
|
||||
|
||||
// Strategy that uses a named pipe. Somewhat complex, but portable and doesn't require
|
||||
// polling most of the time.
|
||||
strategy_named_pipe,
|
||||
|
|
Loading…
Reference in a new issue