1 /* 2 * Copyright 2003-2021 The Music Player Daemon Project 3 * http://www.musicpd.org 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 #ifndef EVENT_LOOP_HXX 21 #define EVENT_LOOP_HXX 22 23 #include "Chrono.hxx" 24 #include "TimerWheel.hxx" 25 #include "TimerList.hxx" 26 #include "Backend.hxx" 27 #include "SocketEvent.hxx" 28 #include "event/Features.h" 29 #include "time/ClockCache.hxx" 30 #include "util/IntrusiveList.hxx" 31 32 #ifdef HAVE_THREADED_EVENT_LOOP 33 #include "WakeFD.hxx" 34 #include "thread/Id.hxx" 35 #include "thread/Mutex.hxx" 36 37 #include <boost/intrusive/list.hpp> 38 #endif 39 40 #include <atomic> 41 #include <cassert> 42 43 #include "io/uring/Features.h" 44 #ifdef HAVE_URING 45 #include <memory> 46 namespace Uring { class Queue; class Manager; } 47 #endif 48 49 class DeferEvent; 50 class InjectEvent; 51 52 /** 53 * An event loop that polls for events on file/socket descriptors. 54 * 55 * This class is not thread-safe, all methods must be called from the 56 * thread that runs it, except where explicitly documented as 57 * thread-safe. 58 * 59 * @see SocketEvent, MultiSocketMonitor, TimerEvent, DeferEvent, InjectEvent 60 */ 61 class EventLoop final 62 { 63 #ifdef HAVE_THREADED_EVENT_LOOP 64 WakeFD wake_fd; 65 SocketEvent wake_event{*this, BIND_THIS_METHOD(OnSocketReady), wake_fd.GetSocket()}; 66 #endif 67 68 TimerWheel coarse_timers; 69 TimerList timers; 70 71 using DeferList = IntrusiveList<DeferEvent>; 72 73 DeferList defer; 74 75 /** 76 * This is like #defer, but gets invoked when the loop is idle. 77 */ 78 DeferList idle; 79 80 #ifdef HAVE_THREADED_EVENT_LOOP 81 Mutex mutex; 82 83 using InjectList = 84 boost::intrusive::list<InjectEvent, 85 boost::intrusive::base_hook<boost::intrusive::list_base_hook<>>, 86 boost::intrusive::constant_time_size<false>>; 87 InjectList inject; 88 #endif 89 90 using SocketList = IntrusiveList<SocketEvent>; 91 92 /** 93 * A list of scheduled #SocketEvent instances, without those 94 * which are ready (these are in #ready_sockets). 95 */ 96 SocketList sockets; 97 98 /** 99 * A linked list of #SocketEvent instances which have a 100 * non-zero "ready_flags" field, and need to be dispatched. 101 */ 102 SocketList ready_sockets; 103 104 #ifdef HAVE_URING 105 std::unique_ptr<Uring::Manager> uring; 106 #endif 107 108 #ifdef HAVE_THREADED_EVENT_LOOP 109 /** 110 * A reference to the thread that is currently inside Run(). 111 */ 112 ThreadId thread = ThreadId::Null(); 113 114 /** 115 * Is this #EventLoop alive, i.e. can events be scheduled? 116 * This is used by BlockingCall() to determine whether 117 * schedule in the #EventThread or to call directly (if 118 * there's no #EventThread yet/anymore). 119 */ 120 bool alive; 121 #endif 122 123 std::atomic_bool quit{false}; 124 125 /** 126 * True when the object has been modified and another check is 127 * necessary before going to sleep via EventPollBackend::ReadEvents(). 128 */ 129 bool again; 130 131 #ifdef HAVE_THREADED_EVENT_LOOP 132 /** 133 * True when handling callbacks, false when waiting for I/O or 134 * timeout. 135 * 136 * Protected with #mutex. 137 */ 138 bool busy = true; 139 #endif 140 141 #ifdef HAVE_URING 142 bool uring_initialized = false; 143 #endif 144 145 EventPollBackend poll_backend; 146 147 ClockCache<std::chrono::steady_clock> steady_clock_cache; 148 149 public: 150 /** 151 * Throws on error. 152 */ 153 #ifdef HAVE_THREADED_EVENT_LOOP 154 explicit EventLoop(ThreadId _thread); 155 EventLoop()156 EventLoop():EventLoop(ThreadId::GetCurrent()) {} 157 #else 158 EventLoop(); 159 #endif 160 161 ~EventLoop() noexcept; 162 163 EventLoop(const EventLoop &other) = delete; 164 EventLoop &operator=(const EventLoop &other) = delete; 165 GetSteadyClockCache() const166 const auto &GetSteadyClockCache() const noexcept { 167 return steady_clock_cache; 168 } 169 170 /** 171 * Caching wrapper for std::chrono::steady_clock::now(). The 172 * real clock is queried at most once per event loop 173 * iteration, because it is assumed that the event loop runs 174 * for a negligible duration. 175 */ 176 [[gnu::pure]] SteadyNow() const177 const auto &SteadyNow() const noexcept { 178 #ifdef HAVE_THREADED_EVENT_LOOP 179 assert(IsInside()); 180 #endif 181 182 return steady_clock_cache.now(); 183 } 184 185 #ifdef HAVE_URING 186 [[gnu::pure]] 187 Uring::Queue *GetUring() noexcept; 188 #endif 189 190 /** 191 * Stop execution of this #EventLoop at the next chance. This 192 * method is thread-safe and non-blocking: after returning, it 193 * is not guaranteed that the EventLoop has really stopped. 194 */ 195 void Break() noexcept; 196 197 bool AddFD(int fd, unsigned events, SocketEvent &event) noexcept; 198 bool ModifyFD(int fd, unsigned events, SocketEvent &event) noexcept; 199 bool RemoveFD(int fd, SocketEvent &event) noexcept; 200 201 /** 202 * Remove the given #SocketEvent after the file descriptor 203 * has been closed. This is like RemoveFD(), but does not 204 * attempt to use #EPOLL_CTL_DEL. 205 */ 206 bool AbandonFD(SocketEvent &event) noexcept; 207 208 void Insert(CoarseTimerEvent &t) noexcept; 209 void Insert(FineTimerEvent &t) noexcept; 210 211 /** 212 * Schedule a call to DeferEvent::RunDeferred(). 213 */ 214 void AddDefer(DeferEvent &d) noexcept; 215 void AddIdle(DeferEvent &e) noexcept; 216 217 #ifdef HAVE_THREADED_EVENT_LOOP 218 /** 219 * Schedule a call to the InjectEvent. 220 * 221 * This method is thread-safe. 222 */ 223 void AddInject(InjectEvent &d) noexcept; 224 225 /** 226 * Cancel a pending call to the InjectEvent. 227 * However after returning, the call may still be running. 228 * 229 * This method is thread-safe. 230 */ 231 void RemoveInject(InjectEvent &d) noexcept; 232 #endif 233 234 /** 235 * The main function of this class. It will loop until 236 * Break() gets called. Can be called only once. 237 */ 238 void Run() noexcept; 239 240 private: 241 void RunDeferred() noexcept; 242 243 /** 244 * Invoke one "idle" #DeferEvent. 245 * 246 * @return false if there was no such event 247 */ 248 bool RunOneIdle() noexcept; 249 250 #ifdef HAVE_THREADED_EVENT_LOOP 251 /** 252 * Invoke all pending InjectEvents. 253 * 254 * Caller must lock the mutex. 255 */ 256 void HandleInject() noexcept; 257 #endif 258 259 /** 260 * Invoke all expired #TimerEvent instances and return the 261 * duration until the next timer expires. Returns a negative 262 * duration if there is no timeout. 263 */ 264 Event::Duration HandleTimers() noexcept; 265 266 /** 267 * Call epoll_wait() and pass all returned events to 268 * SocketEvent::SetReadyFlags(). 269 * 270 * @return true if one or more sockets have become ready 271 */ 272 bool Wait(Event::Duration timeout) noexcept; 273 274 #ifdef HAVE_THREADED_EVENT_LOOP 275 void OnSocketReady(unsigned flags) noexcept; 276 #endif 277 278 public: 279 #ifdef HAVE_THREADED_EVENT_LOOP SetAlive(bool _alive)280 void SetAlive(bool _alive) noexcept { 281 alive = _alive; 282 } 283 IsAlive() const284 bool IsAlive() const noexcept { 285 return alive; 286 } 287 #endif 288 289 /** 290 * Are we currently running inside this EventLoop's thread? 291 */ 292 [[gnu::pure]] IsInside() const293 bool IsInside() const noexcept { 294 #ifdef HAVE_THREADED_EVENT_LOOP 295 return thread.IsInside(); 296 #else 297 return true; 298 #endif 299 } 300 }; 301 302 #endif /* MAIN_NOTIFY_H */ 303