Merge tag 'v0.21.18'
release v0.21.18
This commit is contained in:
+2
-2
@@ -137,7 +137,8 @@ static constexpr int
|
||||
ExportTimeoutMS(std::chrono::steady_clock::duration timeout)
|
||||
{
|
||||
return timeout >= timeout.zero()
|
||||
? int(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count())
|
||||
/* round up (+1) to avoid unnecessary wakeups */
|
||||
? int(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()) + 1
|
||||
: -1;
|
||||
}
|
||||
|
||||
@@ -220,7 +221,6 @@ EventLoop::Run() noexcept
|
||||
} while (!quit);
|
||||
|
||||
#ifndef NDEBUG
|
||||
assert(busy);
|
||||
assert(thread.IsInside());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef USE_EPOLL
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <poll.h>
|
||||
#endif
|
||||
@@ -37,17 +41,42 @@ MultiSocketMonitor::Reset() noexcept
|
||||
assert(GetEventLoop().IsInside());
|
||||
|
||||
fds.clear();
|
||||
#ifdef USE_EPOLL
|
||||
always_ready_fds.clear();
|
||||
#endif
|
||||
IdleMonitor::Cancel();
|
||||
timeout_event.Cancel();
|
||||
ready = refresh = false;
|
||||
}
|
||||
|
||||
bool
|
||||
MultiSocketMonitor::AddSocket(SocketDescriptor fd, unsigned events) noexcept
|
||||
{
|
||||
fds.emplace_front(*this, fd);
|
||||
bool success = fds.front().Schedule(events);
|
||||
if (!success) {
|
||||
fds.pop_front();
|
||||
|
||||
#ifdef USE_EPOLL
|
||||
if (errno == EPERM)
|
||||
/* not supported by epoll (e.g. "/dev/null"):
|
||||
add it to the "always ready" list */
|
||||
always_ready_fds.push_front({fd, events});
|
||||
#endif
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void
|
||||
MultiSocketMonitor::ClearSocketList() noexcept
|
||||
{
|
||||
assert(GetEventLoop().IsInside());
|
||||
|
||||
fds.clear();
|
||||
#ifdef USE_EPOLL
|
||||
always_ready_fds.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -55,6 +84,10 @@ MultiSocketMonitor::ClearSocketList() noexcept
|
||||
void
|
||||
MultiSocketMonitor::ReplaceSocketList(pollfd *pfds, unsigned n) noexcept
|
||||
{
|
||||
#ifdef USE_EPOLL
|
||||
always_ready_fds.clear();
|
||||
#endif
|
||||
|
||||
pollfd *const end = pfds + n;
|
||||
|
||||
UpdateSocketList([pfds, end](SocketDescriptor fd) -> unsigned {
|
||||
@@ -64,9 +97,7 @@ MultiSocketMonitor::ReplaceSocketList(pollfd *pfds, unsigned n) noexcept
|
||||
if (i == end)
|
||||
return 0;
|
||||
|
||||
auto events = i->events;
|
||||
i->events = 0;
|
||||
return events;
|
||||
return std::exchange(i->events, 0);
|
||||
});
|
||||
|
||||
for (auto i = pfds; i != end; ++i)
|
||||
@@ -79,7 +110,20 @@ MultiSocketMonitor::ReplaceSocketList(pollfd *pfds, unsigned n) noexcept
|
||||
void
|
||||
MultiSocketMonitor::Prepare() noexcept
|
||||
{
|
||||
const auto timeout = PrepareSockets();
|
||||
auto timeout = PrepareSockets();
|
||||
|
||||
#ifdef USE_EPOLL
|
||||
if (!always_ready_fds.empty()) {
|
||||
/* if there was at least one file descriptor not
|
||||
supported by epoll, install a very short timeout
|
||||
because we assume it's always ready */
|
||||
constexpr std::chrono::steady_clock::duration ready_timeout =
|
||||
std::chrono::milliseconds(1);
|
||||
if (timeout < timeout.zero() || timeout > ready_timeout)
|
||||
timeout = ready_timeout;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (timeout >= timeout.zero())
|
||||
timeout_event.Schedule(timeout);
|
||||
else
|
||||
|
||||
@@ -49,12 +49,10 @@ class MultiSocketMonitor : IdleMonitor
|
||||
unsigned revents;
|
||||
|
||||
public:
|
||||
SingleFD(MultiSocketMonitor &_multi, SocketDescriptor _fd,
|
||||
unsigned events) noexcept
|
||||
SingleFD(MultiSocketMonitor &_multi,
|
||||
SocketDescriptor _fd) noexcept
|
||||
:SocketMonitor(_fd, _multi.GetEventLoop()),
|
||||
multi(_multi), revents(0) {
|
||||
Schedule(events);
|
||||
}
|
||||
multi(_multi), revents(0) {}
|
||||
|
||||
SocketDescriptor GetSocket() const noexcept {
|
||||
return SocketMonitor::GetSocket();
|
||||
@@ -85,8 +83,6 @@ class MultiSocketMonitor : IdleMonitor
|
||||
}
|
||||
};
|
||||
|
||||
friend class SingleFD;
|
||||
|
||||
TimerEvent timeout_event;
|
||||
|
||||
/**
|
||||
@@ -105,6 +101,21 @@ class MultiSocketMonitor : IdleMonitor
|
||||
|
||||
std::forward_list<SingleFD> fds;
|
||||
|
||||
#ifdef USE_EPOLL
|
||||
struct AlwaysReady {
|
||||
const SocketDescriptor fd;
|
||||
const unsigned revents;
|
||||
};
|
||||
|
||||
/**
|
||||
* A list of file descriptors which are always ready. This is
|
||||
* a kludge needed because the ALSA output plugin gives us a
|
||||
* file descriptor to /dev/null, which is incompatible with
|
||||
* epoll (epoll_ctl() returns -EPERM).
|
||||
*/
|
||||
std::forward_list<AlwaysReady> always_ready_fds;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static constexpr unsigned READ = SocketMonitor::READ;
|
||||
static constexpr unsigned WRITE = SocketMonitor::WRITE;
|
||||
@@ -146,9 +157,7 @@ public:
|
||||
*
|
||||
* May only be called from PrepareSockets().
|
||||
*/
|
||||
void AddSocket(SocketDescriptor fd, unsigned events) noexcept {
|
||||
fds.emplace_front(*this, fd, events);
|
||||
}
|
||||
bool AddSocket(SocketDescriptor fd, unsigned events) noexcept;
|
||||
|
||||
/**
|
||||
* Remove all sockets.
|
||||
@@ -203,6 +212,11 @@ public:
|
||||
i.ClearReturnedEvents();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_EPOLL
|
||||
for (const auto &i : always_ready_fds)
|
||||
f(i.fd, i.revents);
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -231,7 +245,6 @@ private:
|
||||
|
||||
void OnTimeout() noexcept {
|
||||
SetReady();
|
||||
IdleMonitor::Schedule();
|
||||
}
|
||||
|
||||
virtual void OnIdle() noexcept final;
|
||||
|
||||
@@ -64,20 +64,24 @@ SocketMonitor::Close() noexcept
|
||||
Steal().Close();
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
SocketMonitor::Schedule(unsigned flags) noexcept
|
||||
{
|
||||
assert(IsDefined());
|
||||
|
||||
if (flags == GetScheduledFlags())
|
||||
return;
|
||||
return true;
|
||||
|
||||
bool success;
|
||||
if (scheduled_flags == 0)
|
||||
loop.AddFD(fd.Get(), flags, *this);
|
||||
success = loop.AddFD(fd.Get(), flags, *this);
|
||||
else if (flags == 0)
|
||||
loop.RemoveFD(fd.Get(), *this);
|
||||
success = loop.RemoveFD(fd.Get(), *this);
|
||||
else
|
||||
loop.ModifyFD(fd.Get(), flags, *this);
|
||||
success = loop.ModifyFD(fd.Get(), flags, *this);
|
||||
|
||||
scheduled_flags = flags;
|
||||
if (success)
|
||||
scheduled_flags = flags;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -98,18 +98,22 @@ public:
|
||||
return scheduled_flags;
|
||||
}
|
||||
|
||||
void Schedule(unsigned flags) noexcept;
|
||||
/**
|
||||
* @return true on success, false on error (with errno set if
|
||||
* USE_EPOLL is defined)
|
||||
*/
|
||||
bool Schedule(unsigned flags) noexcept;
|
||||
|
||||
void Cancel() noexcept {
|
||||
Schedule(0);
|
||||
}
|
||||
|
||||
void ScheduleRead() noexcept {
|
||||
Schedule(GetScheduledFlags() | READ | HANGUP | ERROR);
|
||||
bool ScheduleRead() noexcept {
|
||||
return Schedule(GetScheduledFlags() | READ | HANGUP | ERROR);
|
||||
}
|
||||
|
||||
void ScheduleWrite() noexcept {
|
||||
Schedule(GetScheduledFlags() | WRITE);
|
||||
bool ScheduleWrite() noexcept {
|
||||
return Schedule(GetScheduledFlags() | WRITE);
|
||||
}
|
||||
|
||||
void CancelRead() noexcept {
|
||||
|
||||
Reference in New Issue
Block a user