/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** * Copyright (C) 2014 Named Data Networking Project * See COPYING for copyright and distribution information. */ #include "scheduler.hpp" namespace nfd { struct EventIdImpl { EventIdImpl(const Scheduler::EventQueue::iterator& event) : m_event(event) , m_isValid(true) { } void invalidate() { m_isValid = false; } bool isValid() const { return m_isValid; } operator const Scheduler::EventQueue::iterator&() const { return m_event; } void reset(const Scheduler::EventQueue::iterator& newIterator) { m_event = newIterator; m_isValid = true; } private: Scheduler::EventQueue::iterator m_event; bool m_isValid; }; Scheduler::EventInfo::EventInfo(const time::Duration& after, const time::Duration& period, const Event& event) : m_scheduledTime(time::now() + after) , m_period(period) , m_event(event) { } Scheduler::EventInfo::EventInfo(const time::Point& when, const EventInfo& previousEvent) : m_scheduledTime(when) , m_period(previousEvent.m_period) , m_event(previousEvent.m_event) , m_eventId(previousEvent.m_eventId) { } time::Duration Scheduler::EventInfo::expiresFromNow() const { time::Point now = time::now(); if (now > m_scheduledTime) return time::seconds(0); // event should be scheduled ASAP else return m_scheduledTime - now; } Scheduler::Scheduler(boost::asio::io_service& ioService) : m_ioService(ioService) , m_scheduledEvent(m_events.end()) , m_deadlineTimer(ioService) , m_isEventExecuting(false) { } EventId Scheduler::scheduleEvent(const time::Duration& after, const Event& event) { return schedulePeriodicEvent(after, time::nanoseconds(-1), event); } EventId Scheduler::schedulePeriodicEvent(const time::Duration& after, const time::Duration& period, const Event& event) { EventQueue::iterator i = m_events.insert(EventInfo(after, period, event)); i->m_eventId = make_shared(boost::cref(i)); if (!m_isEventExecuting) { 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; } void Scheduler::cancelEvent(const EventId& eventId) { if (!static_cast(eventId) || !eventId->isValid()) return; // event empty, already fired, or cancelled if (static_cast(*eventId) != m_scheduledEvent) { m_events.erase(*eventId); eventId->invalidate(); return; } m_deadlineTimer.cancel(); m_events.erase(static_cast(*eventId)); eventId->invalidate(); if (!m_isEventExecuting) { 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(); } } } void Scheduler::onEvent(const boost::system::error_code& error) { if (error) // e.g., cancelled { 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(); Event event = head->m_event; if (head->m_period < 0) { head->m_eventId->invalidate(); m_events.erase(head); } else { // "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); 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; } } // namespace nfd