util: Fix rescheduling and add test case.
Change-Id: Ic04a590a116083391b441338eed9a121e14852dd
diff --git a/src/util/scheduler.cpp b/src/util/scheduler.cpp
index 13a6c3a..c13e070 100644
--- a/src/util/scheduler.cpp
+++ b/src/util/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,18 @@
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_scheduledEvent = m_events.end();
+ }
}
}
@@ -142,40 +148,45 @@
{
return;
}
-
+
+ m_isEventExecuting = true;
+
// process all expired events
time::Point now = time::now();
while(!m_events.empty() && m_events.begin()->m_scheduledTime <= now)
{
EventQueue::iterator head = m_events.begin();
- head->m_event();
+ Event event = head->m_event;
if (head->m_period < 0)
{
- if(head->m_eventId->isValid())
- {
- head->m_eventId->invalidate();
- m_events.erase(head);
- }
+ head->m_eventId->invalidate();
+ m_events.erase(head);
}
else
{
- bool validity = head->m_eventId->isValid();
-
// "reschedule" and update EventId data of the event
EventInfo event(now + head->m_period, *head);
EventQueue::iterator i = m_events.insert(event);
i->m_eventId->reset(i);
- if(validity)
- m_events.erase(head);
+ m_events.erase(head);
}
+
+ event();
}
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/src/util/scheduler.hpp b/src/util/scheduler.hpp
index b1fb596..18c3004 100644
--- a/src/util/scheduler.hpp
+++ b/src/util/scheduler.hpp
@@ -92,6 +92,8 @@
EventQueue m_events;
EventQueue::iterator m_scheduledEvent;
boost::asio::monotonic_deadline_timer m_deadlineTimer;
+
+ bool m_isEventExecuting;
};
} // namespace ndn
diff --git a/src/util/time.hpp b/src/util/time.hpp
index d175964..9af95fc 100644
--- a/src/util/time.hpp
+++ b/src/util/time.hpp
@@ -160,6 +160,13 @@
int64_t m_value;
};
+inline std::ostream&
+operator<<(std::ostream &os, const Duration& duration)
+{
+ os << static_cast<int64_t>(duration) / 1000000000.0 << " s";
+ return os;
+}
+
/**
* \brief Get current time
* \return{ the current time in monotonic clock }
diff --git a/tests/test-scheduler.cpp b/tests/test-scheduler.cpp
index b63c3c0..f1c3214 100644
--- a/tests/test-scheduler.cpp
+++ b/tests/test-scheduler.cpp
@@ -80,6 +80,140 @@
BOOST_CHECK_EQUAL(count4, 4);
}
+BOOST_AUTO_TEST_CASE(CancelEmptyEvent)
+{
+ boost::asio::io_service io;
+ Scheduler scheduler(io);
+
+ EventId i;
+ 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());
+}
+
+struct SelfRescheduleFixture
+{
+ SelfRescheduleFixture()
+ : m_scheduler(m_io)
+ , m_count(0)
+ {
+ }
+
+ void
+ reschedule()
+ {
+ EventId eventId = m_scheduler.scheduleEvent(time::seconds(0.1),
+ bind(&SelfRescheduleFixture::reschedule, this));
+ m_scheduler.cancelEvent(m_selfEventId);
+ m_selfEventId = eventId;
+
+ if(m_count < 5)
+ m_count++;
+ else
+ m_scheduler.cancelEvent(m_selfEventId);
+ }
+
+ void
+ reschedule2()
+ {
+ m_scheduler.cancelEvent(m_selfEventId);
+
+
+ if(m_count < 5)
+ {
+ m_selfEventId = m_scheduler.scheduleEvent(time::seconds(0.1),
+ bind(&SelfRescheduleFixture::reschedule2, this));
+ m_count++;
+ }
+ }
+
+ void
+ doNothing()
+ {
+ m_count++;
+ }
+
+ void
+ reschedule3()
+ {
+ m_scheduler.cancelEvent(m_selfEventId);
+
+ m_scheduler.scheduleEvent(time::seconds(0.1),
+ bind(&SelfRescheduleFixture::doNothing, this));
+ m_scheduler.scheduleEvent(time::seconds(0.1),
+ bind(&SelfRescheduleFixture::doNothing, this));
+ m_scheduler.scheduleEvent(time::seconds(0.1),
+ bind(&SelfRescheduleFixture::doNothing, this));
+ m_scheduler.scheduleEvent(time::seconds(0.1),
+ bind(&SelfRescheduleFixture::doNothing, this));
+ m_scheduler.scheduleEvent(time::seconds(0.1),
+ bind(&SelfRescheduleFixture::doNothing, this));
+ m_scheduler.scheduleEvent(time::seconds(0.1),
+ bind(&SelfRescheduleFixture::doNothing, this));
+ }
+
+ boost::asio::io_service m_io;
+ Scheduler m_scheduler;
+ EventId m_selfEventId;
+ int m_count;
+
+};
+
+BOOST_FIXTURE_TEST_CASE(Reschedule, SelfRescheduleFixture)
+{
+ m_selfEventId = m_scheduler.scheduleEvent(time::seconds(0),
+ bind(&SelfRescheduleFixture::reschedule, this));
+
+ BOOST_REQUIRE_NO_THROW(m_io.run());
+
+ BOOST_CHECK_EQUAL(m_count, 5);
+}
+
+BOOST_FIXTURE_TEST_CASE(Reschedule2, SelfRescheduleFixture)
+{
+ m_selfEventId = m_scheduler.scheduleEvent(time::seconds(0),
+ bind(&SelfRescheduleFixture::reschedule2, this));
+
+ BOOST_REQUIRE_NO_THROW(m_io.run());
+
+ BOOST_CHECK_EQUAL(m_count, 5);
+}
+
+BOOST_FIXTURE_TEST_CASE(Reschedule3, SelfRescheduleFixture)
+{
+ m_selfEventId = m_scheduler.scheduleEvent(time::seconds(0),
+ bind(&SelfRescheduleFixture::reschedule3, this));
+
+ BOOST_REQUIRE_NO_THROW(m_io.run());
+
+ BOOST_CHECK_EQUAL(m_count, 6);
+}
+
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace ndn