diff options
Diffstat (limited to 'src/event')
27 files changed, 896 insertions, 177 deletions
diff --git a/src/event/BufferedSocket.cxx b/src/event/BufferedSocket.cxx index 92e350e85..0f71bd941 100644 --- a/src/event/BufferedSocket.cxx +++ b/src/event/BufferedSocket.cxx @@ -22,6 +22,9 @@ #include "system/SocketError.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" +#include "Compiler.h" + +#include <algorithm> BufferedSocket::ssize_t BufferedSocket::DirectRead(void *data, size_t length) diff --git a/src/event/BufferedSocket.hxx b/src/event/BufferedSocket.hxx index db920f981..9bd1b5b49 100644 --- a/src/event/BufferedSocket.hxx +++ b/src/event/BufferedSocket.hxx @@ -23,13 +23,13 @@ #include "check.h" #include "SocketMonitor.hxx" #include "util/FifoBuffer.hxx" -#include "Compiler.h" #include <assert.h> #include <stdint.h> struct fifo_buffer; class Error; +class EventLoop; /** * A #SocketMonitor specialization that adds an input buffer. diff --git a/src/event/Call.cxx b/src/event/Call.cxx index ab1d5ffbd..7767824f9 100644 --- a/src/event/Call.cxx +++ b/src/event/Call.cxx @@ -28,7 +28,7 @@ #include <assert.h> class BlockingCallMonitor final -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP : DeferredMonitor #endif { @@ -40,20 +40,22 @@ class BlockingCallMonitor final bool done; public: -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP BlockingCallMonitor(EventLoop &loop, std::function<void()> &&_f) :f(std::move(_f)), done(false) { loop.AddCall([this](){ this->DoRun(); }); } -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP BlockingCallMonitor(EventLoop &_loop, std::function<void()> &&_f) :DeferredMonitor(_loop), f(std::move(_f)), done(false) {} #endif void Run() { -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP assert(!done); Schedule(); @@ -65,13 +67,14 @@ public: mutex.unlock(); } -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP private: virtual void RunDeferred() override { DoRun(); } +#endif -#else +#ifdef USE_INTERNAL_EVENTLOOP public: #endif void DoRun() { diff --git a/src/event/DeferredMonitor.cxx b/src/event/DeferredMonitor.cxx index 4ffffaa89..40b4b0b62 100644 --- a/src/event/DeferredMonitor.cxx +++ b/src/event/DeferredMonitor.cxx @@ -24,9 +24,10 @@ void DeferredMonitor::Cancel() { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP pending = false; -#else +#endif +#ifdef USE_GLIB_EVENTLOOP const auto id = source_id.exchange(0); if (id != 0) g_source_remove(id); @@ -36,10 +37,11 @@ DeferredMonitor::Cancel() void DeferredMonitor::Schedule() { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP if (!pending.exchange(true)) fd.Write(); -#else +#endif +#ifdef USE_GLIB_EVENTLOOP const unsigned id = loop.AddIdle(Callback, this); const auto old_id = source_id.exchange(id); if (old_id != 0) @@ -47,7 +49,7 @@ DeferredMonitor::Schedule() #endif } -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP bool DeferredMonitor::OnSocketReady(unsigned) @@ -60,7 +62,9 @@ DeferredMonitor::OnSocketReady(unsigned) return true; } -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP void DeferredMonitor::Run() diff --git a/src/event/DeferredMonitor.hxx b/src/event/DeferredMonitor.hxx index 2380fb66f..d4c812e44 100644 --- a/src/event/DeferredMonitor.hxx +++ b/src/event/DeferredMonitor.hxx @@ -23,10 +23,12 @@ #include "check.h" #include "Compiler.h" -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP #include "SocketMonitor.hxx" #include "WakeFD.hxx" -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP #include <glib.h> #endif @@ -38,44 +40,51 @@ class EventLoop; * Defer execution of an event into an #EventLoop. */ class DeferredMonitor -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP : private SocketMonitor #endif { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP std::atomic_bool pending; WakeFD fd; -#else - EventLoop &loop; +#endif +#ifdef USE_GLIB_EVENTLOOP + EventLoop &loop; std::atomic<guint> source_id; #endif public: -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP DeferredMonitor(EventLoop &_loop) :SocketMonitor(_loop), pending(false) { SocketMonitor::Open(fd.Get()); SocketMonitor::Schedule(SocketMonitor::READ); } -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP DeferredMonitor(EventLoop &_loop) :loop(_loop), source_id(0) {} #endif ~DeferredMonitor() { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP /* avoid closing the WakeFD twice */ SocketMonitor::Steal(); -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP Cancel(); #endif } EventLoop &GetEventLoop() { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP return SocketMonitor::GetEventLoop(); -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP return loop; #endif } @@ -87,9 +96,11 @@ protected: virtual void RunDeferred() = 0; private: -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP virtual bool OnSocketReady(unsigned flags) override final; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP void Run(); static gboolean Callback(gpointer data); #endif diff --git a/src/event/FullyBufferedSocket.cxx b/src/event/FullyBufferedSocket.cxx index 8b57b1308..87d29296e 100644 --- a/src/event/FullyBufferedSocket.cxx +++ b/src/event/FullyBufferedSocket.cxx @@ -23,6 +23,7 @@ #include "util/fifo_buffer.h" #include "util/Error.hxx" #include "util/Domain.hxx" +#include "Compiler.h" #include <assert.h> #include <stdint.h> diff --git a/src/event/FullyBufferedSocket.hxx b/src/event/FullyBufferedSocket.hxx index c50bb5f61..083fab15e 100644 --- a/src/event/FullyBufferedSocket.hxx +++ b/src/event/FullyBufferedSocket.hxx @@ -24,7 +24,6 @@ #include "BufferedSocket.hxx" #include "IdleMonitor.hxx" #include "util/PeakBuffer.hxx" -#include "Compiler.h" /** * A #BufferedSocket specialization that adds an output buffer. diff --git a/src/event/IdleMonitor.cxx b/src/event/IdleMonitor.cxx index c99c66b26..45f533e3f 100644 --- a/src/event/IdleMonitor.cxx +++ b/src/event/IdleMonitor.cxx @@ -21,6 +21,8 @@ #include "IdleMonitor.hxx" #include "Loop.hxx" +#include <assert.h> + void IdleMonitor::Cancel() { @@ -29,10 +31,11 @@ IdleMonitor::Cancel() if (!IsActive()) return; -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP active = false; loop.RemoveIdle(*this); -#else +#endif +#ifdef USE_GLIB_EVENTLOOP g_source_remove(source_id); source_id = 0; #endif @@ -47,10 +50,11 @@ IdleMonitor::Schedule() /* already scheduled */ return; -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP active = true; loop.AddIdle(*this); -#else +#endif +#ifdef USE_GLIB_EVENTLOOP source_id = loop.AddIdle(Callback, this); #endif } @@ -60,10 +64,11 @@ IdleMonitor::Run() { assert(loop.IsInside()); -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP assert(active); active = false; -#else +#endif +#ifdef USE_GLIB_EVENTLOOP assert(source_id != 0); source_id = 0; #endif @@ -71,7 +76,7 @@ IdleMonitor::Run() OnIdle(); } -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP gboolean IdleMonitor::Callback(gpointer data) diff --git a/src/event/IdleMonitor.hxx b/src/event/IdleMonitor.hxx index c8e79eb1d..b040915e7 100644 --- a/src/event/IdleMonitor.hxx +++ b/src/event/IdleMonitor.hxx @@ -22,7 +22,7 @@ #include "check.h" -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP #include <glib.h> #endif @@ -34,23 +34,27 @@ class EventLoop; * methods must be run from EventLoop's thread. */ class IdleMonitor { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP friend class EventLoop; #endif EventLoop &loop; -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP bool active; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP guint source_id; #endif public: -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP IdleMonitor(EventLoop &_loop) :loop(_loop), active(false) {} -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP IdleMonitor(EventLoop &_loop) :loop(_loop), source_id(0) {} #endif @@ -64,9 +68,11 @@ public: } bool IsActive() const { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP return active; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP return source_id != 0; #endif } @@ -79,7 +85,7 @@ protected: private: void Run(); -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP static gboolean Callback(gpointer data); #endif }; diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx index 5aa24aea2..f7b3df022 100644 --- a/src/event/Loop.cxx +++ b/src/event/Loop.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "Loop.hxx" -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP #include "system/Clock.hxx" #include "TimeoutMonitor.hxx" @@ -33,7 +33,6 @@ EventLoop::EventLoop(Default) :SocketMonitor(*this), now_ms(::MonotonicClockMS()), quit(false), - n_events(0), thread(ThreadId::Null()) { SocketMonitor::Open(wake_fd.Get()); @@ -58,19 +57,18 @@ EventLoop::Break() AddCall([this]() { Break(); }); } -void -EventLoop::Abandon(SocketMonitor &m) +bool +EventLoop::Abandon(int _fd, SocketMonitor &m) { - for (unsigned i = 0, n = n_events; i < n; ++i) - if (events[i].data.ptr == &m) - events[i].events = 0; + poll_result.Clear(&m); + return poll_group.Abandon(_fd); } bool EventLoop::RemoveFD(int _fd, SocketMonitor &m) { - Abandon(m); - return epoll.Remove(_fd); + poll_result.Clear(&m); + return poll_group.Remove(_fd); } void @@ -115,7 +113,7 @@ EventLoop::Run() assert(thread.IsNull()); thread = ThreadId::GetCurrent(); -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP assert(!quit); do { @@ -164,37 +162,37 @@ EventLoop::Run() /* wait for new event */ - const int n = epoll.Wait(events, MAX_EVENTS, timeout_ms); - n_events = std::max(n, 0); + poll_group.ReadEvents(poll_result, timeout_ms); now_ms = ::MonotonicClockMS(); assert(!quit); /* invoke sockets */ - - for (int i = 0; i < n; ++i) { - const auto &e = events[i]; - - if (e.events != 0) { - SocketMonitor &m = *(SocketMonitor *)e.data.ptr; - m.Dispatch(e.events); + for (int i = 0; i < poll_result.GetSize(); ++i) { + auto events = poll_result.GetEvents(i); + if (events != 0) { + auto m = (SocketMonitor *)poll_result.GetObject(i); + m->Dispatch(events); if (quit) break; } } - n_events = 0; + poll_result.Reset(); + } while (!quit); -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP g_main_loop_run(loop); #endif assert(thread.IsInside()); } -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP void EventLoop::AddCall(std::function<void()> &&f) @@ -229,7 +227,9 @@ EventLoop::OnSocketReady(gcc_unused unsigned flags) return true; } -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP guint EventLoop::AddIdle(GSourceFunc function, gpointer data) diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx index 62e733747..029d01245 100644 --- a/src/event/Loop.hxx +++ b/src/event/Loop.hxx @@ -24,8 +24,8 @@ #include "thread/Id.hxx" #include "Compiler.h" -#ifdef USE_EPOLL -#include "system/EPollFD.hxx" +#ifdef USE_INTERNAL_EVENTLOOP +#include "PollGroup.hxx" #include "thread/Mutex.hxx" #include "WakeFD.hxx" #include "SocketMonitor.hxx" @@ -33,11 +33,13 @@ #include <functional> #include <list> #include <set> -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP #include <glib.h> #endif -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP class TimeoutMonitor; class IdleMonitor; class SocketMonitor; @@ -45,12 +47,21 @@ class SocketMonitor; #include <assert.h> +/** + * An event loop that polls for events on file/socket descriptors. + * + * This class is not thread-safe, all methods must be called from the + * thread that runs it, except where explicitly documented as + * thread-safe. + * + * @see SocketMonitor, MultiSocketMonitor, TimeoutMonitor, IdleMonitor + */ class EventLoop final -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP : private SocketMonitor #endif { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP struct TimerRecord { /** * Projected monotonic_clock_ms() value when this @@ -73,8 +84,6 @@ class EventLoop final } }; - EPollFD epoll; - WakeFD wake_fd; std::multiset<TimerRecord> timers; @@ -87,10 +96,11 @@ class EventLoop final bool quit; - static constexpr unsigned MAX_EVENTS = 16; - unsigned n_events; - epoll_event events[MAX_EVENTS]; -#else + PollGroup poll_group; + PollResult poll_result; +#endif + +#ifdef USE_GLIB_EVENTLOOP GMainContext *context; GMainLoop *loop; #endif @@ -101,24 +111,32 @@ class EventLoop final ThreadId thread; public: -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP struct Default {}; EventLoop(Default dummy=Default()); ~EventLoop(); + /** + * A caching wrapper for MonotonicClockMS(). + */ unsigned GetTimeMS() const { return now_ms; } + /** + * Stop execution of this #EventLoop at the next chance. This + * method is thread-safe and non-blocking: after returning, it + * is not guaranteed that the EventLoop has really stopped. + */ void Break(); bool AddFD(int _fd, unsigned flags, SocketMonitor &m) { - return epoll.Add(_fd, flags, &m); + return poll_group.Add(_fd, flags, &m); } bool ModifyFD(int _fd, unsigned flags, SocketMonitor &m) { - return epoll.Modify(_fd, flags, &m); + return poll_group.Modify(_fd, flags, &m); } /** @@ -126,7 +144,7 @@ public: * has been closed. This is like RemoveFD(), but does not * attempt to use #EPOLL_CTL_DEL. */ - void Abandon(SocketMonitor &m); + bool Abandon(int fd, SocketMonitor &m); bool RemoveFD(int fd, SocketMonitor &m); @@ -138,13 +156,19 @@ public: void AddCall(std::function<void()> &&f); + /** + * The main function of this class. It will loop until + * Break() gets called. Can be called only once. + */ void Run(); private: virtual bool OnSocketReady(unsigned flags) override; public: -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP EventLoop() :context(g_main_context_new()), loop(g_main_loop_new(context, false)), diff --git a/src/event/MultiSocketMonitor.cxx b/src/event/MultiSocketMonitor.cxx index bd1aa6fef..f26ab6c28 100644 --- a/src/event/MultiSocketMonitor.cxx +++ b/src/event/MultiSocketMonitor.cxx @@ -25,7 +25,7 @@ #include <assert.h> -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop) :IdleMonitor(_loop), TimeoutMonitor(_loop), ready(false) { @@ -65,7 +65,9 @@ MultiSocketMonitor::OnIdle() } } -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP /** * The vtable for our GSource implementation. Unfortunately, we diff --git a/src/event/MultiSocketMonitor.hxx b/src/event/MultiSocketMonitor.hxx index 8ee81a508..680930037 100644 --- a/src/event/MultiSocketMonitor.hxx +++ b/src/event/MultiSocketMonitor.hxx @@ -23,22 +23,25 @@ #include "check.h" #include "Compiler.h" -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP #include "IdleMonitor.hxx" #include "TimeoutMonitor.hxx" #include "SocketMonitor.hxx" -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP #include <glib.h> #endif #include <forward_list> +#include <iterator> #include <assert.h> #include <stdint.h> #ifdef WIN32 -/* ERRORis a WIN32 macro that poisons our namespace; this is a - kludge to allow us to use it anyway */ +/* ERROR is a WIN32 macro that poisons our namespace; this is a kludge + to allow us to use it anyway */ #ifdef ERROR #undef ERROR #endif @@ -47,14 +50,17 @@ class EventLoop; /** - * Monitor multiple sockets. + * Similar to #SocketMonitor, but monitors multiple sockets. To use + * it, implement the methods PrepareSockets() and DispatchSockets(). + * In PrepareSockets(), use UpdateSocketList() and AddSocket(). + * DispatchSockets() will be called if at least one socket is ready. */ class MultiSocketMonitor -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP : private IdleMonitor, private TimeoutMonitor #endif { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP class SingleFD final : public SocketMonitor { MultiSocketMonitor &multi; @@ -99,7 +105,9 @@ class MultiSocketMonitor friend class SingleFD; bool ready, refresh; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP struct Source { GSource base; @@ -138,12 +146,14 @@ class MultiSocketMonitor std::forward_list<SingleFD> fds; public: -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP static constexpr unsigned READ = SocketMonitor::READ; static constexpr unsigned WRITE = SocketMonitor::WRITE; static constexpr unsigned ERROR = SocketMonitor::ERROR; static constexpr unsigned HANGUP = SocketMonitor::HANGUP; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP static constexpr unsigned READ = G_IO_IN; static constexpr unsigned WRITE = G_IO_OUT; static constexpr unsigned ERROR = G_IO_ERR; @@ -153,16 +163,18 @@ public: MultiSocketMonitor(EventLoop &_loop); ~MultiSocketMonitor(); -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP using IdleMonitor::GetEventLoop; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP EventLoop &GetEventLoop() { return loop; } #endif public: -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP gcc_pure uint64_t GetTime() const { return g_source_get_time(&source->base); @@ -170,10 +182,12 @@ public: #endif void InvalidateSockets() { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP refresh = true; IdleMonitor::Schedule(); -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP /* no-op because GLib always calls the GSource's "prepare" method before each poll() anyway */ #endif @@ -181,7 +195,7 @@ public: void AddSocket(int fd, unsigned events) { fds.emplace_front(*this, fd, events); -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP g_source_add_poll(&source->base, &fds.front().pfd); #endif } @@ -198,9 +212,11 @@ public: i->SetEvents(events); prev = i; } else { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP i->Steal(); -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP g_source_remove_poll(&source->base, &i->pfd); #endif fds.erase_after(prev); @@ -215,7 +231,7 @@ protected: virtual int PrepareSockets() = 0; virtual void DispatchSockets() = 0; -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP private: void SetReady() { ready = true; @@ -231,7 +247,9 @@ private: virtual void OnIdle() final; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP public: /* GSource callbacks */ static gboolean Prepare(GSource *source, gint *timeout_r); diff --git a/src/event/PollGroup.hxx b/src/event/PollGroup.hxx new file mode 100644 index 000000000..038ffc13e --- /dev/null +++ b/src/event/PollGroup.hxx @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_EVENT_POLLGROUP_HXX +#define MPD_EVENT_POLLGROUP_HXX + +#ifdef USE_EPOLL +#include "PollGroupEPoll.hxx" +typedef PollResultEPoll PollResult; +typedef PollGroupEPoll PollGroup; +#endif + +#ifdef USE_WINSELECT +#include "PollGroupWinSelect.hxx" +typedef PollResultGeneric PollResult; +typedef PollGroupWinSelect PollGroup; +#endif + +#ifdef USE_POLL +#include "PollGroupPoll.hxx" +typedef PollResultGeneric PollResult; +typedef PollGroupPoll PollGroup; +#endif + +#endif diff --git a/src/event/PollGroupEPoll.hxx b/src/event/PollGroupEPoll.hxx new file mode 100644 index 000000000..21bb3a322 --- /dev/null +++ b/src/event/PollGroupEPoll.hxx @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_EVENT_POLLGROUP_EPOLL_HXX +#define MPD_EVENT_POLLGROUP_EPOLL_HXX + +#include "check.h" + +#include "Compiler.h" +#include "system/EPollFD.hxx" + +#include <array> +#include <algorithm> + +class PollResultEPoll +{ + friend class PollGroupEPoll; + + std::array<epoll_event, 16> events; + int n_events; +public: + PollResultEPoll() : n_events(0) { } + + int GetSize() const { return n_events; } + unsigned GetEvents(int i) const { return events[i].events; } + void *GetObject(int i) const { return events[i].data.ptr; } + void Reset() { n_events = 0; } + + void Clear(void *obj) { + for (int i = 0; i < n_events; ++i) + if (events[i].data.ptr == obj) + events[i].events = 0; + } +}; + +class PollGroupEPoll +{ + EPollFD epoll; + + PollGroupEPoll(PollGroupEPoll &) = delete; + PollGroupEPoll &operator=(PollGroupEPoll &) = delete; +public: + static constexpr unsigned READ = EPOLLIN; + static constexpr unsigned WRITE = EPOLLOUT; + static constexpr unsigned ERROR = EPOLLERR; + static constexpr unsigned HANGUP = EPOLLHUP; + + PollGroupEPoll() = default; + + void ReadEvents(PollResultEPoll &result, int timeout_ms) { + int ret = epoll.Wait(result.events.data(), result.events.size(), + timeout_ms); + result.n_events = std::max(0, ret); + } + + bool Add(int fd, unsigned events, void *obj) { + return epoll.Add(fd, events, obj); + } + + bool Modify(int fd, unsigned events, void *obj) { + return epoll.Modify(fd, events, obj); + } + + bool Remove(int fd) { + return epoll.Remove(fd); + } + + bool Abandon(gcc_unused int fd) { + // Nothing to do in this implementation. + // Closed descriptors are automatically unregistered. + return true; + } +}; + +#endif diff --git a/src/event/PollGroupPoll.cxx b/src/event/PollGroupPoll.cxx new file mode 100644 index 000000000..a2b4d1d88 --- /dev/null +++ b/src/event/PollGroupPoll.cxx @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#ifdef USE_POLL + +#include "PollGroupPoll.hxx" + +#include <assert.h> + +PollGroupPoll::PollGroupPoll() { } +PollGroupPoll::~PollGroupPoll() { } + +bool PollGroupPoll::Add(int fd, unsigned events, void *obj) +{ + assert(items.find(fd) == items.end()); + poll_events.resize(poll_events.size() + 1); + size_t index = poll_events.size() - 1; + auto &e = poll_events[index]; + e.fd = fd; + e.events = events; + e.revents = 0; + auto &item = items[fd]; + item.index = index; + item.obj = obj; + return true; +} + +bool PollGroupPoll::Modify(int fd, unsigned events, void *obj) +{ + auto item_iter = items.find(fd); + assert(item_iter != items.end()); + auto &item = item_iter->second; + item.obj = obj; + auto &e = poll_events[item.index]; + e.events = events; + e.revents &= events; + return true; +} + +bool PollGroupPoll::Remove(int fd) +{ + auto item_iter = items.find(fd); + assert(item_iter != items.end()); + auto &item = item_iter->second; + size_t index = item.index; + size_t last_index = poll_events.size() - 1; + if (index != last_index) { + std::swap(poll_events[index], poll_events[last_index]); + items[poll_events[index].fd].index = index; + } + poll_events.pop_back(); + items.erase(item_iter); + return true; +} + +void PollGroupPoll::ReadEvents(PollResultGeneric &result, int timeout_ms) +{ + int n = poll(poll_events.empty() ? nullptr : &poll_events[0], + poll_events.size(), timeout_ms); + + for (size_t i = 0; n > 0 && i < poll_events.size(); ++i) { + const auto &e = poll_events[i]; + if (e.revents != 0) { + result.Add(e.revents, items[e.fd].obj); + --n; + } + } +} + +#endif diff --git a/src/event/PollGroupPoll.hxx b/src/event/PollGroupPoll.hxx new file mode 100644 index 000000000..e8850ebb5 --- /dev/null +++ b/src/event/PollGroupPoll.hxx @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_EVENT_POLLGROUP_POLL_HXX +#define MPD_EVENT_POLLGROUP_POLL_HXX + +#include "check.h" +#include "PollResultGeneric.hxx" + +#include <string.h> // for size_t +#include <vector> +#include <unordered_map> +#include <sys/poll.h> + +class PollGroupPoll +{ + struct Item + { + size_t index; + void *obj; + }; + + std::vector<pollfd> poll_events; + std::unordered_map<int, Item> items; + + PollGroupPoll(PollGroupPoll &) = delete; + PollGroupPoll &operator=(PollGroupPoll &) = delete; +public: + static constexpr unsigned READ = POLLIN; + static constexpr unsigned WRITE = POLLOUT; + static constexpr unsigned ERROR = POLLERR; + static constexpr unsigned HANGUP = POLLHUP; + + PollGroupPoll(); + ~PollGroupPoll(); + + void ReadEvents(PollResultGeneric &result, int timeout_ms); + bool Add(int fd, unsigned events, void *obj); + bool Modify(int fd, unsigned events, void *obj); + bool Remove(int fd); + bool Abandon(int fd) { + return Remove(fd); + } +}; + +#endif diff --git a/src/event/PollGroupWinSelect.cxx b/src/event/PollGroupWinSelect.cxx new file mode 100644 index 000000000..b184ff2b2 --- /dev/null +++ b/src/event/PollGroupWinSelect.cxx @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#ifdef USE_WINSELECT + +#include "PollGroupWinSelect.hxx" + +constexpr int EVENT_READ = 0; +constexpr int EVENT_WRITE = 1; + +static inline bool HasEvent(unsigned events, int event_id) +{ + return (events & (1 << event_id)) != 0; +} + +PollGroupWinSelect::PollGroupWinSelect() { } +PollGroupWinSelect::~PollGroupWinSelect() { } + +bool PollGroupWinSelect::CanModify(PollGroupWinSelect::Item &item, + unsigned events, int event_id) +{ + if (item.index[event_id] < 0 && HasEvent(events, event_id)) + return !event_set[event_id].IsFull(); + return true; +} + +void PollGroupWinSelect::Modify(PollGroupWinSelect::Item &item, int fd, + unsigned events, int event_id) +{ + int index = item.index[event_id]; + auto &set = event_set[event_id]; + + if (index < 0 && HasEvent(events, event_id)) + item.index[event_id] = set.Add(fd); + else if (index >= 0 && !HasEvent(events, event_id)) { + if (index != set.Size() - 1) { + set.MoveToEnd(index); + items[set[index]].index[event_id] = index; + } + set.RemoveLast(); + item.index[event_id] = -1; + } +} + +bool PollGroupWinSelect::Add(int fd, unsigned events, void *obj) +{ + assert(items.find(fd) == items.end()); + auto &item = items[fd]; + + item.index[EVENT_READ] = -1; + item.index[EVENT_WRITE] = -1; + item.obj = obj; + item.events = 0; + + if (!CanModify(item, events, EVENT_READ)) { + items.erase(fd); + return false; + } + if (!CanModify(item, events, EVENT_WRITE)) { + items.erase(fd); + return false; + } + + Modify(item, fd, events, EVENT_READ); + Modify(item, fd, events, EVENT_WRITE); + return true; +} + +bool PollGroupWinSelect::Modify(int fd, unsigned events, void *obj) +{ + auto item_iter = items.find(fd); + assert(item_iter != items.end()); + auto &item = item_iter->second; + + if (!CanModify(item, events, EVENT_READ)) + return false; + if (!CanModify(item, events, EVENT_WRITE)) + return false; + + item.obj = obj; + Modify(item, fd, events, EVENT_READ); + Modify(item, fd, events, EVENT_WRITE); + return true; +} + +bool PollGroupWinSelect::Remove(int fd) +{ + auto item_iter = items.find(fd); + assert(item_iter != items.end()); + auto &item = item_iter->second; + + Modify(item, fd, 0, EVENT_READ); + Modify(item, fd, 0, EVENT_WRITE); + items.erase(item_iter); + return true; +} + +void PollGroupWinSelect::ReadEvents(PollResultGeneric &result, int timeout_ms) +{ + bool use_sleep = event_set[EVENT_READ].IsEmpty() && + event_set[EVENT_WRITE].IsEmpty(); + + if (use_sleep) { + Sleep(timeout_ms < 0 ? INFINITE : (DWORD) timeout_ms); + return; + } + + SocketSet read_set(event_set[EVENT_READ]); + SocketSet write_set(event_set[EVENT_WRITE]); + SocketSet except_set(event_set[EVENT_WRITE]); + + timeval tv; + if (timeout_ms >= 0) { + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + } + + int ret = select(0, + read_set.IsEmpty() ? nullptr : read_set.GetPtr(), + write_set.IsEmpty() ? nullptr : write_set.GetPtr(), + except_set.IsEmpty() ? nullptr : except_set.GetPtr(), + timeout_ms < 0 ? nullptr : &tv); + + if (ret == 0 || ret == SOCKET_ERROR) + return; + + for (int i = 0; i < read_set.Size(); ++i) + items[read_set[i]].events |= READ; + + for (int i = 0; i < write_set.Size(); ++i) + items[write_set[i]].events |= WRITE; + + for (int i = 0; i < except_set.Size(); ++i) + items[except_set[i]].events |= WRITE; + + for (auto i = items.begin(); i != items.end(); ++i) + if (i->second.events != 0) { + result.Add(i->second.events, i->second.obj); + i->second.events = 0; + } +} + +#endif diff --git a/src/event/PollGroupWinSelect.hxx b/src/event/PollGroupWinSelect.hxx new file mode 100644 index 000000000..7a6d12d8a --- /dev/null +++ b/src/event/PollGroupWinSelect.hxx @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_EVENT_POLLGROUP_WINSELECT_HXX +#define MPD_EVENT_POLLGROUP_WINSELECT_HXX + +#include "check.h" + +#include "PollResultGeneric.hxx" + +#include <assert.h> +#include <string.h> + +#include <unordered_map> + +#include <windows.h> +#include <winsock2.h> + +#ifdef ERROR +#undef ERROR +#endif + +class SocketSet +{ + fd_set set; +public: + SocketSet() { set.fd_count = 0; } + SocketSet(SocketSet &other) { + set.fd_count = other.set.fd_count; + memcpy(set.fd_array, + other.set.fd_array, + sizeof (SOCKET) * set.fd_count); + } + + fd_set *GetPtr() { return &set; } + int Size() { return set.fd_count; } + bool IsEmpty() { return set.fd_count == 0; } + bool IsFull() { return set.fd_count == FD_SETSIZE; } + + int operator[](int index) { + assert(index >= 0 && (u_int)index < set.fd_count); + return set.fd_array[index]; + } + + int Add(int fd) { + assert(!IsFull()); + set.fd_array[set.fd_count] = fd; + return set.fd_count++; + } + + void MoveToEnd(int index) { + assert(index >= 0 && (u_int)index < set.fd_count); + std::swap(set.fd_array[index], set.fd_array[set.fd_count - 1]); + } + + void RemoveLast() { + assert(!IsEmpty()); + --set.fd_count; + } +}; + +class PollGroupWinSelect +{ + struct Item + { + int index[2]; + void *obj; + unsigned events; + }; + + SocketSet event_set[2]; + std::unordered_map<int, Item> items; + + bool CanModify(Item &item, unsigned events, int event_id); + void Modify(Item &item, int fd, unsigned events, int event_id); + + PollGroupWinSelect(PollGroupWinSelect &) = delete; + PollGroupWinSelect &operator=(PollGroupWinSelect &) = delete; +public: + static constexpr unsigned READ = 1; + static constexpr unsigned WRITE = 2; + static constexpr unsigned ERROR = 0; + static constexpr unsigned HANGUP = 0; + + PollGroupWinSelect(); + ~PollGroupWinSelect(); + + void ReadEvents(PollResultGeneric &result, int timeout_ms); + bool Add(int fd, unsigned events, void *obj); + bool Modify(int fd, unsigned events, void *obj); + bool Remove(int fd); + bool Abandon(int fd) { return Remove(fd); } +}; + +#endif diff --git a/src/event/PollResultGeneric.hxx b/src/event/PollResultGeneric.hxx new file mode 100644 index 000000000..1c2c0d00b --- /dev/null +++ b/src/event/PollResultGeneric.hxx @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_EVENT_POLLRESULT_GENERIC_HXX +#define MPD_EVENT_POLLRESULT_GENERIC_HXX + +#include "check.h" + +#include <vector> + +class PollResultGeneric +{ + struct Item + { + unsigned events; + void *obj; + + Item() = default; + Item(unsigned _events, void *_obj) + : events(_events), obj(_obj) { } + }; + + std::vector<Item> items; +public: + int GetSize() const { return items.size(); } + unsigned GetEvents(int i) const { return items[i].events; } + void *GetObject(int i) const { return items[i].obj; } + void Reset() { items.clear(); } + + void Clear(void *obj) { + for (auto i = items.begin(); i != items.end(); ++i) + if (i->obj == obj) + i->events = 0; + } + + void Add(unsigned events, void *obj) { + items.emplace_back(events, obj); + } +}; + +#endif diff --git a/src/event/ServerSocket.cxx b/src/event/ServerSocket.cxx index 781d29181..a05d7bde2 100644 --- a/src/event/ServerSocket.cxx +++ b/src/event/ServerSocket.cxx @@ -38,6 +38,7 @@ #include <glib.h> #include <string> +#include <algorithm> #include <sys/types.h> #include <sys/stat.h> @@ -106,7 +107,10 @@ public: using SocketMonitor::IsDefined; using SocketMonitor::Close; - char *ToString() const; + gcc_pure + std::string ToString() const { + return sockaddr_to_string(address, address_length); + } void SetFD(int _fd) { SocketMonitor::Open(_fd); @@ -121,18 +125,6 @@ private: static constexpr Domain server_socket_domain("server_socket"); -/** - * Wraper for sockaddr_to_string() which never fails. - */ -char * -OneServerSocket::ToString() const -{ - char *p = sockaddr_to_string(address, address_length, IgnoreError()); - if (p == nullptr) - p = g_strdup("[unknown]"); - return p; -} - static int get_remote_uid(int fd) { @@ -242,23 +234,21 @@ ServerSocket::Open(Error &error) Error error2; if (!i.Open(error2)) { if (good != nullptr && good->GetSerial() == i.GetSerial()) { - char *address_string = i.ToString(); - char *good_string = good->ToString(); + const auto address_string = i.ToString(); + const auto good_string = good->ToString(); FormatWarning(server_socket_domain, "bind to '%s' failed: %s " "(continuing anyway, because " "binding to '%s' succeeded)", - address_string, error2.GetMessage(), - good_string); - g_free(address_string); - g_free(good_string); + address_string.c_str(), + error2.GetMessage(), + good_string.c_str()); } else if (bad == nullptr) { bad = &i; - char *address_string = i.ToString(); + const auto address_string = i.ToString(); error2.FormatPrefix("Failed to bind to '%s': ", - address_string); - g_free(address_string); + address_string.c_str()); last_error = std::move(error2); } diff --git a/src/event/ServerSocket.hxx b/src/event/ServerSocket.hxx index facb10371..05e13ff63 100644 --- a/src/event/ServerSocket.hxx +++ b/src/event/ServerSocket.hxx @@ -36,6 +36,9 @@ typedef void (*server_socket_callback_t)(int fd, class OneServerSocket; +/** + * A socket that accepts incoming stream connections (e.g. TCP). + */ class ServerSocket { friend class OneServerSocket; diff --git a/src/event/SignalMonitor.cxx b/src/event/SignalMonitor.cxx index 8c8527a77..0d96fb138 100644 --- a/src/event/SignalMonitor.cxx +++ b/src/event/SignalMonitor.cxx @@ -39,6 +39,8 @@ #include <algorithm> +#include <assert.h> + class SignalMonitor final : private SocketMonitor { #ifdef USE_SIGNALFD SignalFD fd; diff --git a/src/event/SocketMonitor.cxx b/src/event/SocketMonitor.cxx index 2b97059f7..769efba82 100644 --- a/src/event/SocketMonitor.cxx +++ b/src/event/SocketMonitor.cxx @@ -28,11 +28,10 @@ #ifdef WIN32 #include <winsock2.h> #else -#include <sys/types.h> #include <sys/socket.h> #endif -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP void SocketMonitor::Dispatch(unsigned flags) @@ -43,7 +42,9 @@ SocketMonitor::Dispatch(unsigned flags) Cancel(); } -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP /* * GSource methods @@ -113,14 +114,14 @@ void SocketMonitor::Open(int _fd) { assert(fd < 0); -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP assert(source == nullptr); #endif assert(_fd >= 0); fd = _fd; -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP poll = {fd, 0, 0}; source = (Source *)g_source_new(&socket_monitor_source_funcs, @@ -142,7 +143,7 @@ SocketMonitor::Steal() int result = fd; fd = -1; -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP g_source_destroy(&source->base); g_source_unref(&source->base); source = nullptr; @@ -156,9 +157,10 @@ SocketMonitor::Abandon() { assert(IsDefined()); -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP + int old_fd = fd; fd = -1; - loop.Abandon(*this); + loop.Abandon(old_fd, *this); #else Steal(); #endif @@ -178,7 +180,7 @@ SocketMonitor::Schedule(unsigned flags) if (flags == GetScheduledFlags()) return; -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP if (scheduled_flags == 0) loop.AddFD(fd, flags, *this); else if (flags == 0) @@ -187,7 +189,9 @@ SocketMonitor::Schedule(unsigned flags) loop.ModifyFD(fd, flags, *this); scheduled_flags = flags; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP poll.events = flags; poll.revents &= flags; diff --git a/src/event/SocketMonitor.hxx b/src/event/SocketMonitor.hxx index 5369ddb8a..f6aac2bd6 100644 --- a/src/event/SocketMonitor.hxx +++ b/src/event/SocketMonitor.hxx @@ -22,9 +22,11 @@ #include "check.h" -#ifdef USE_EPOLL -#include <sys/epoll.h> -#else +#ifdef USE_INTERNAL_EVENTLOOP +#include "PollGroup.hxx" +#endif + +#ifdef USE_GLIB_EVENTLOOP #include <glib.h> #endif @@ -34,8 +36,8 @@ #include <stddef.h> #ifdef WIN32 -/* ERRORis a WIN32 macro that poisons our namespace; this is a - kludge to allow us to use it anyway */ +/* ERROR is a WIN32 macro that poisons our namespace; this is a kludge + to allow us to use it anyway */ #ifdef ERROR #undef ERROR #endif @@ -43,9 +45,14 @@ class EventLoop; +/** + * Monitor events on a socket. Call Schedule() to announce events + * you're interested in, or Cancel() to cancel your subscription. The + * #EventLoop will invoke virtual method OnSocketReady() as soon as + * any of the subscribed events are ready. + */ class SocketMonitor { -#ifdef USE_EPOLL -#else +#ifdef USE_GLIB_EVENTLOOP struct Source { GSource base; @@ -56,38 +63,44 @@ class SocketMonitor { int fd; EventLoop &loop; -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP /** * A bit mask of events that is currently registered in the EventLoop. */ unsigned scheduled_flags; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP Source *source; GPollFD poll; #endif public: -#ifdef USE_EPOLL - static constexpr unsigned READ = EPOLLIN; - static constexpr unsigned WRITE = EPOLLOUT; - static constexpr unsigned ERROR = EPOLLERR; - static constexpr unsigned HANGUP = EPOLLHUP; -#else +#ifdef USE_INTERNAL_EVENTLOOP + static constexpr unsigned READ = PollGroup::READ; + static constexpr unsigned WRITE = PollGroup::WRITE; + static constexpr unsigned ERROR = PollGroup::ERROR; + static constexpr unsigned HANGUP = PollGroup::HANGUP; +#endif + +#ifdef USE_GLIB_EVENTLOOP static constexpr unsigned READ = G_IO_IN; static constexpr unsigned WRITE = G_IO_OUT; static constexpr unsigned ERROR = G_IO_ERR; static constexpr unsigned HANGUP = G_IO_HUP; -#endif +#endif typedef std::make_signed<size_t>::type ssize_t; -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP SocketMonitor(EventLoop &_loop) :fd(-1), loop(_loop), scheduled_flags(0) {} SocketMonitor(int _fd, EventLoop &_loop) :fd(_fd), loop(_loop), scheduled_flags(0) {} -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP SocketMonitor(EventLoop &_loop) :fd(-1), loop(_loop), source(nullptr) {} @@ -128,9 +141,11 @@ public: unsigned GetScheduledFlags() const { assert(IsDefined()); -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP return scheduled_flags; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP return poll.events; #endif } @@ -167,9 +182,11 @@ protected: virtual bool OnSocketReady(unsigned flags) = 0; public: -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP void Dispatch(unsigned flags); -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP /* GSource callbacks */ static gboolean Prepare(GSource *source, gint *timeout_r); static gboolean Check(GSource *source); diff --git a/src/event/TimeoutMonitor.cxx b/src/event/TimeoutMonitor.cxx index cffad6b92..3d7d46324 100644 --- a/src/event/TimeoutMonitor.cxx +++ b/src/event/TimeoutMonitor.cxx @@ -25,10 +25,12 @@ void TimeoutMonitor::Cancel() { if (IsActive()) { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP active = false; loop.CancelTimer(*this); -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP g_source_destroy(source); g_source_unref(source); source = nullptr; @@ -41,10 +43,12 @@ TimeoutMonitor::Schedule(unsigned ms) { Cancel(); -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP active = true; loop.AddTimer(*this, ms); -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP source = loop.AddTimeout(ms, Callback, this); #endif } @@ -54,9 +58,11 @@ TimeoutMonitor::ScheduleSeconds(unsigned s) { Cancel(); -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP Schedule(s * 1000u); -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP source = loop.AddTimeoutSeconds(s, Callback, this); #endif } @@ -64,14 +70,14 @@ TimeoutMonitor::ScheduleSeconds(unsigned s) void TimeoutMonitor::Run() { -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP Cancel(); #endif OnTimeout(); } -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP gboolean TimeoutMonitor::Callback(gpointer data) diff --git a/src/event/TimeoutMonitor.hxx b/src/event/TimeoutMonitor.hxx index 98e4e5564..568aa27ef 100644 --- a/src/event/TimeoutMonitor.hxx +++ b/src/event/TimeoutMonitor.hxx @@ -22,31 +22,39 @@ #include "check.h" -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP #include <glib.h> #endif class EventLoop; +/** + * This class monitors a timeout. Use Schedule() to begin the timeout + * or Cancel() to cancel it. + */ class TimeoutMonitor { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP friend class EventLoop; #endif EventLoop &loop; -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP bool active; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP GSource *source; #endif public: -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP TimeoutMonitor(EventLoop &_loop) :loop(_loop), active(false) { } -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP TimeoutMonitor(EventLoop &_loop) :loop(_loop), source(nullptr) {} #endif @@ -60,9 +68,11 @@ public: } bool IsActive() const { -#ifdef USE_EPOLL +#ifdef USE_INTERNAL_EVENTLOOP return active; -#else +#endif + +#ifdef USE_GLIB_EVENTLOOP return source != nullptr; #endif } @@ -77,7 +87,7 @@ protected: private: void Run(); -#ifndef USE_EPOLL +#ifdef USE_GLIB_EVENTLOOP static gboolean Callback(gpointer data); #endif }; |