core: Fixing scheduler, so it doesn't crash when an event is cancelled from the event invocation
Change-Id: I219df36a4ed19d75e97e676bedeba023fd53326e
diff --git a/daemon/core/scheduler.cpp b/daemon/core/scheduler.cpp
index c72e279..a1759ca 100644
--- a/daemon/core/scheduler.cpp
+++ b/daemon/core/scheduler.cpp
@@ -77,6 +77,7 @@
: m_ioService(ioService)
, m_scheduledEvent(m_events.end())
, m_deadlineTimer(ioService)
+ , m_isEventExecuting(false)
{
}
@@ -94,13 +95,16 @@
{
EventQueue::iterator i = m_events.insert(EventInfo(after, period, event));
i->m_eventId = make_shared<EventIdImpl>(boost::cref(i));
-
- if (m_scheduledEvent == m_events.end() ||
- *i < *m_scheduledEvent)
+
+ if (!m_isEventExecuting)
{
- m_deadlineTimer.expires_from_now(after);
- m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
- m_scheduledEvent = i;
+ if (m_scheduledEvent == m_events.end() ||
+ *i < *m_scheduledEvent)
+ {
+ m_deadlineTimer.expires_from_now(after);
+ m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
+ m_scheduledEvent = i;
+ }
}
return i->m_eventId;
@@ -122,16 +126,19 @@
m_events.erase(static_cast<EventQueue::iterator>(*eventId));
eventId->invalidate();
- if (!m_events.empty())
+ if (!m_isEventExecuting)
{
- m_deadlineTimer.expires_from_now(m_events.begin()->expiresFromNow());
- m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
- m_scheduledEvent = m_events.begin();
- }
- else
- {
- m_deadlineTimer.cancel();
- m_scheduledEvent = m_events.end();
+ if (!m_events.empty())
+ {
+ m_deadlineTimer.expires_from_now(m_events.begin()->expiresFromNow());
+ m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
+ m_scheduledEvent = m_events.begin();
+ }
+ else
+ {
+ m_deadlineTimer.cancel();
+ m_scheduledEvent = m_events.end();
+ }
}
}
@@ -142,6 +149,8 @@
{
return;
}
+
+ m_isEventExecuting = true;
// process all expired events
time::Point now = time::now();
@@ -149,7 +158,7 @@
{
EventQueue::iterator head = m_events.begin();
- head->m_event();
+ Event event = head->m_event;
if (head->m_period < 0)
{
head->m_eventId->invalidate();
@@ -163,13 +172,38 @@
i->m_eventId->reset(i);
m_events.erase(head);
}
+
+ try
+ {
+ event();
+ }
+ catch(...)
+ {
+ m_isEventExecuting = false;
+ for (EventQueue::iterator i = m_events.begin();
+ i != m_events.end();
+ ++i)
+ {
+ i->m_eventId->invalidate();
+ }
+ m_events.clear();
+
+ throw;
+ }
}
if (!m_events.empty())
{
m_deadlineTimer.expires_from_now(m_events.begin()->m_scheduledTime - now);
m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
+ m_scheduledEvent = m_events.begin();
}
+ else
+ {
+ m_scheduledEvent = m_events.end();
+ }
+
+ m_isEventExecuting = false;
}
diff --git a/daemon/core/scheduler.hpp b/daemon/core/scheduler.hpp
index 4b2e2d4..a8e60bb 100644
--- a/daemon/core/scheduler.hpp
+++ b/daemon/core/scheduler.hpp
@@ -92,6 +92,8 @@
EventQueue m_events;
EventQueue::iterator m_scheduledEvent;
boost::asio::monotonic_deadline_timer m_deadlineTimer;
+
+ bool m_isEventExecuting;
};
} // namespace nfd
diff --git a/tests/core/scheduler.cpp b/tests/core/scheduler.cpp
index 593744b..9e673a0 100644
--- a/tests/core/scheduler.cpp
+++ b/tests/core/scheduler.cpp
@@ -89,6 +89,32 @@
scheduler.cancelEvent(i);
}
+struct SelfCancelFixture
+{
+ SelfCancelFixture()
+ : m_scheduler(m_io)
+ {
+ }
+
+ void
+ cancelSelf()
+ {
+ m_scheduler.cancelEvent(m_selfEventId);
+ }
+
+ boost::asio::io_service m_io;
+ Scheduler m_scheduler;
+ EventId m_selfEventId;
+};
+
+BOOST_FIXTURE_TEST_CASE(SelfCancel, SelfCancelFixture)
+{
+ m_selfEventId = m_scheduler.scheduleEvent(time::seconds(0.1),
+ bind(&SelfCancelFixture::cancelSelf, this));
+
+ BOOST_REQUIRE_NO_THROW(m_io.run());
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace nfd