@@ -170,44 +170,96 @@ void EventLoop::quit()
170
170
wakeup ();
171
171
}
172
172
}
173
+
174
+ // The event loop needs a scope exit, so here's the simplest most limited
175
+ // C++14 scope exit available (from
176
+ // https://stackoverflow.com/a/42506763/3173540)
177
+ //
178
+ // TODO: If this is needed anywhere else, introduce a proper on_exit from, for
179
+ // example, the GSL library
180
+ namespace
181
+ {
182
+ template <typename F>
183
+ struct ScopeExit
184
+ {
185
+ ScopeExit (F &&f) : f_(std::forward<F>(f))
186
+ {
187
+ }
188
+ ~ScopeExit ()
189
+ {
190
+ f_ ();
191
+ }
192
+ F f_;
193
+ };
194
+
195
+ template <typename F>
196
+ ScopeExit<F> makeScopeExit (F &&f)
197
+ {
198
+ return ScopeExit<F>(std::forward<F>(f));
199
+ };
200
+ } // namespace
201
+
173
202
void EventLoop::loop ()
174
203
{
175
204
assert (!looping_);
176
205
assertInLoopThread ();
177
206
looping_.store (true , std::memory_order_release);
178
207
quit_.store (false , std::memory_order_release);
179
208
180
- while (!quit_.load (std::memory_order_acquire))
181
- {
182
- activeChannels_.clear ();
209
+ std::exception_ptr loopException;
210
+ try
211
+ { // Scope where the loop flag is set
212
+
213
+ auto loopFlagCleaner = makeScopeExit (
214
+ [this ]() { looping_.store (false , std::memory_order_release); });
215
+ while (!quit_.load (std::memory_order_acquire))
216
+ {
217
+ activeChannels_.clear ();
183
218
#ifdef __linux__
184
- poller_->poll (kPollTimeMs , &activeChannels_);
219
+ poller_->poll (kPollTimeMs , &activeChannels_);
185
220
#else
186
- poller_->poll (static_cast <int >(timerQueue_->getTimeout ()),
187
- &activeChannels_);
188
- timerQueue_->processTimers ();
221
+ poller_->poll (static_cast <int >(timerQueue_->getTimeout ()),
222
+ &activeChannels_);
223
+ timerQueue_->processTimers ();
189
224
#endif
190
- // TODO sort channel by priority
191
- // std::cout<<"after ->poll()"<<std::endl;
192
- eventHandling_ = true ;
193
- for (auto it = activeChannels_.begin (); it != activeChannels_.end ();
194
- ++it)
195
- {
196
- currentActiveChannel_ = *it;
197
- currentActiveChannel_->handleEvent ();
225
+ // TODO sort channel by priority
226
+ // std::cout<<"after ->poll()"<<std::endl;
227
+ eventHandling_ = true ;
228
+ for (auto it = activeChannels_.begin (); it != activeChannels_.end ();
229
+ ++it)
230
+ {
231
+ currentActiveChannel_ = *it;
232
+ currentActiveChannel_->handleEvent ();
233
+ }
234
+ currentActiveChannel_ = nullptr ;
235
+ eventHandling_ = false ;
236
+ // std::cout << "looping" << endl;
237
+ doRunInLoopFuncs ();
198
238
}
199
- currentActiveChannel_ = nullptr ;
200
- eventHandling_ = false ;
201
- // std::cout << "looping" << endl;
202
- doRunInLoopFuncs ();
239
+ // loopFlagCleaner clears the loop flag here
240
+ }
241
+ catch (std::exception &e)
242
+ {
243
+ LOG_WARN << " Exception thrown from event loop, rethrowing after "
244
+ " running functions on quit" ;
245
+ loopException = std::current_exception ();
203
246
}
204
- looping_.store (false , std::memory_order_release);
205
247
248
+ // Run the quit functions even if exceptions were thrown
249
+ // TODO: if more exceptions are thrown in the quit functions, some are left
250
+ // un-run. Can this be made exception safe?
206
251
Func f;
207
252
while (funcsOnQuit_.dequeue (f))
208
253
{
209
254
f ();
210
255
}
256
+
257
+ // Throw the exception from the end
258
+ if (loopException)
259
+ {
260
+ LOG_WARN << " Rethrowing exception from event loop" ;
261
+ std::rethrow_exception (loopException);
262
+ }
211
263
}
212
264
void EventLoop::abortNotInLoopThread ()
213
265
{
@@ -283,8 +335,14 @@ void EventLoop::doRunInLoopFuncs()
283
335
{
284
336
callingFuncs_ = true ;
285
337
{
338
+ // Assure the flag is cleared even if func throws
339
+ auto callingFlagCleaner =
340
+ makeScopeExit ([this ]() { callingFuncs_ = false ; });
286
341
// the destructor for the Func may itself insert a new entry into the
287
342
// queue
343
+ // TODO: The following is exception-unsafe. If one of the funcs throws,
344
+ // the remaining ones will not get run. The simplest fix is to catch any
345
+ // exceptions and rethrow them later, but somehow that seems fishy...
288
346
while (!funcs_.empty ())
289
347
{
290
348
Func func;
@@ -294,7 +352,6 @@ void EventLoop::doRunInLoopFuncs()
294
352
}
295
353
}
296
354
}
297
- callingFuncs_ = false ;
298
355
}
299
356
void EventLoop::wakeup ()
300
357
{
0 commit comments