diff options
Diffstat (limited to 'src/event/Loop.hxx')
-rw-r--r-- | src/event/Loop.hxx | 158 |
1 files changed, 92 insertions, 66 deletions
diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx index 62e733747..d03259fb0 100644 --- a/src/event/Loop.hxx +++ b/src/event/Loop.hxx @@ -24,33 +24,32 @@ #include "thread/Id.hxx" #include "Compiler.h" -#ifdef USE_EPOLL -#include "system/EPollFD.hxx" +#include "PollGroup.hxx" #include "thread/Mutex.hxx" #include "WakeFD.hxx" #include "SocketMonitor.hxx" -#include <functional> #include <list> #include <set> -#else -#include <glib.h> -#endif -#ifdef USE_EPOLL class TimeoutMonitor; class IdleMonitor; +class DeferredMonitor; class SocketMonitor; -#endif #include <assert.h> -class EventLoop final -#ifdef USE_EPOLL - : private SocketMonitor -#endif +/** + * 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 : SocketMonitor { -#ifdef USE_EPOLL struct TimerRecord { /** * Projected monotonic_clock_ms() value when this @@ -73,52 +72,80 @@ class EventLoop final } }; - EPollFD epoll; - WakeFD wake_fd; std::multiset<TimerRecord> timers; std::list<IdleMonitor *> idle; Mutex mutex; - std::list<std::function<void()>> calls; + std::list<DeferredMonitor *> deferred; unsigned now_ms; bool quit; - static constexpr unsigned MAX_EVENTS = 16; - unsigned n_events; - epoll_event events[MAX_EVENTS]; -#else - GMainContext *context; - GMainLoop *loop; + /** + * True when the object has been modified and another check is + * necessary before going to sleep via PollGroup::ReadEvents(). + */ + bool again; + + /** + * True when handling callbacks, false when waiting for I/O or + * timeout. + * + * Protected with #mutex. + */ + bool busy; + +#ifndef NDEBUG + /** + * True if Run() was never called. This is used for assert() + * calls. + */ + bool virgin; #endif + PollGroup poll_group; + PollResult poll_result; + /** * A reference to the thread that is currently inside Run(). */ ThreadId thread; public: -#ifdef USE_EPOLL struct Default {}; EventLoop(Default dummy=Default()); ~EventLoop(); + /** + * A caching wrapper for MonotonicClockMS(). + */ unsigned GetTimeMS() const { + assert(IsInside()); + 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); + assert(thread.IsNull() || thread.IsInside()); + + return poll_group.Add(_fd, flags, &m); } bool ModifyFD(int _fd, unsigned flags, SocketMonitor &m) { - return epoll.Modify(_fd, flags, &m); + assert(IsInside()); + + return poll_group.Modify(_fd, flags, &m); } /** @@ -126,7 +153,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); @@ -136,53 +163,38 @@ public: void AddTimer(TimeoutMonitor &t, unsigned ms); void CancelTimer(TimeoutMonitor &t); - void AddCall(std::function<void()> &&f); + /** + * Schedule a call to DeferredMonitor::RunDeferred(). + * + * This method is thread-safe. + */ + void AddDeferred(DeferredMonitor &d); + + /** + * Cancel a pending call to DeferredMonitor::RunDeferred(). + * However after returning, the call may still be running. + * + * This method is thread-safe. + */ + void RemoveDeferred(DeferredMonitor &d); + /** + * The main function of this class. It will loop until + * Break() gets called. Can be called only once. + */ void Run(); private: + /** + * Invoke all pending DeferredMonitors. + * + * Caller must lock the mutex. + */ + void HandleDeferred(); + virtual bool OnSocketReady(unsigned flags) override; public: -#else - EventLoop() - :context(g_main_context_new()), - loop(g_main_loop_new(context, false)), - thread(ThreadId::Null()) {} - - struct Default {}; - EventLoop(gcc_unused Default _dummy) - :context(g_main_context_ref(g_main_context_default())), - loop(g_main_loop_new(context, false)), - thread(ThreadId::Null()) {} - - ~EventLoop() { - g_main_loop_unref(loop); - g_main_context_unref(context); - } - - GMainContext *GetContext() { - return context; - } - - void WakeUp() { - g_main_context_wakeup(context); - } - - void Break() { - g_main_loop_quit(loop); - } - - void Run(); - - guint AddIdle(GSourceFunc function, gpointer data); - - GSource *AddTimeout(guint interval_ms, - GSourceFunc function, gpointer data); - - GSource *AddTimeoutSeconds(guint interval_s, - GSourceFunc function, gpointer data); -#endif /** * Are we currently running inside this EventLoop's thread? @@ -193,6 +205,20 @@ public: return thread.IsInside(); } + +#ifndef NDEBUG + gcc_pure + bool IsInsideOrVirgin() const { + return virgin || IsInside(); + } +#endif + +#ifndef NDEBUG + gcc_pure + bool IsInsideOrNull() const { + return thread.IsNull() || thread.IsInside(); + } +#endif }; #endif /* MAIN_NOTIFY_H */ |