util: keep Scheduler running if a callback throws
refs #3722
Change-Id: Icf19e1a59d67e351b210a60079b27b9b0a06fae6
diff --git a/src/util/scheduler.cpp b/src/util/scheduler.cpp
index 536061c..15200c9 100644
--- a/src/util/scheduler.cpp
+++ b/src/util/scheduler.cpp
@@ -19,9 +19,8 @@
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
*/
-#include "common.hpp"
-
#include "scheduler.hpp"
+#include <boost/scope_exit.hpp>
namespace ndn {
namespace util {
@@ -140,6 +139,11 @@
m_isEventExecuting = true;
+ BOOST_SCOPE_EXIT_ALL(this) {
+ m_isEventExecuting = false;
+ this->scheduleNext();
+ };
+
// process all expired events
time::steady_clock::TimePoint now = time::steady_clock::now();
while (!m_queue.empty()) {
@@ -153,9 +157,6 @@
info->isExpired = true;
info->callback();
}
-
- m_isEventExecuting = false;
- this->scheduleNext();
}
} // namespace scheduler
diff --git a/src/util/scheduler.hpp b/src/util/scheduler.hpp
index c908ec0..cad5c09 100644
--- a/src/util/scheduler.hpp
+++ b/src/util/scheduler.hpp
@@ -137,7 +137,7 @@
/**
* \brief Schedule a one-time event after the specified delay
- * \returns EventId that can be used to cancel the scheduled event
+ * \return EventId that can be used to cancel the scheduled event
*/
EventId
scheduleEvent(const time::nanoseconds& after, const EventCallback& callback);
@@ -161,6 +161,12 @@
void
scheduleNext();
+ /**
+ * \brief Execute expired events
+ * \note If an event callback throws, the exception is propagated to the thread running the
+ * io_service. In case there are other expired events, they will be processed in the next
+ * invocation of this method.
+ */
void
executeEvent(const boost::system::error_code& code);
diff --git a/tests/unit-tests/util/scheduler.t.cpp b/tests/unit-tests/util/scheduler.t.cpp
index dfc2826..72db284 100644
--- a/tests/unit-tests/util/scheduler.t.cpp
+++ b/tests/unit-tests/util/scheduler.t.cpp
@@ -78,6 +78,21 @@
BOOST_CHECK_EQUAL(count2, 1);
}
+BOOST_AUTO_TEST_CASE(CallbackException)
+{
+ class MyException : public std::exception
+ {
+ };
+ scheduler.scheduleEvent(time::milliseconds(10), [] { BOOST_THROW_EXCEPTION(MyException()); });
+
+ bool isCallbackInvoked = false;
+ scheduler.scheduleEvent(time::milliseconds(20), [&isCallbackInvoked] { isCallbackInvoked = true; });
+
+ BOOST_CHECK_THROW(this->advanceClocks(time::milliseconds(6), 2), MyException);
+ this->advanceClocks(time::milliseconds(6), 2);
+ BOOST_CHECK(isCallbackInvoked);
+}
+
BOOST_AUTO_TEST_CASE(CancelEmptyEvent)
{
EventId i;