[ndnSIM] util: Redirecting the scheduler of ndn-cxx to the scheduler of NS3

Change-Id: I82fdd94582a731c2238c6645e9ace21a2cacf2de
Refs: #4509
diff --git a/src/util/scheduler.cpp b/src/util/scheduler.cpp
index 167b6e0..513348f 100644
--- a/src/util/scheduler.cpp
+++ b/src/util/scheduler.cpp
@@ -24,142 +24,82 @@
 
 #include <boost/scope_exit.hpp>
 
+namespace ns3 {
+
+/// @cond include_hidden
+
+template<>
+struct EventMemberImplObjTraits<std::function<void()>> {
+  typedef std::function<void()> T;
+  static T&
+  GetReference(T& p)
+  {
+    return p;
+  }
+};
+
+/// @endcond
+
+} // namespace ns3
+
 namespace ndn {
 namespace util {
 namespace scheduler {
 
-class EventInfo : noncopyable
-{
-public:
-  EventInfo(time::nanoseconds after, const EventCallback& callback)
-    : expireTime(time::steady_clock::now() + after)
-    , isExpired(false)
-    , callback(callback)
-  {
-  }
-
-  time::nanoseconds
-  expiresFromNow() const
-  {
-    return std::max(expireTime - time::steady_clock::now(), time::nanoseconds::zero());
-  }
-
-public:
-  time::steady_clock::TimePoint expireTime;
-  bool isExpired;
-  EventCallback callback;
-  EventQueue::const_iterator queueIt;
-};
-
-EventId::operator bool() const
-{
-  return !m_info.expired() && !m_info.lock()->isExpired;
-}
-
-bool
-EventId::operator==(const EventId& other) const
-{
-  return (!(*this) && !other) ||
-         !(m_info.owner_before(other.m_info) || other.m_info.owner_before(m_info));
-}
-
-std::ostream&
-operator<<(std::ostream& os, const EventId& eventId)
-{
-  return os << eventId.m_info.lock();
-}
-
-bool
-EventQueueCompare::operator()(const shared_ptr<EventInfo>& a, const shared_ptr<EventInfo>& b) const
-{
-  return a->expireTime < b->expireTime;
-}
-
 Scheduler::Scheduler(boost::asio::io_service& ioService)
-  : m_timer(make_unique<detail::SteadyTimer>(ioService))
-  , m_isEventExecuting(false)
+  : m_scheduledEvent(m_events.end())
 {
 }
 
-Scheduler::~Scheduler() = default;
+Scheduler::~Scheduler()
+{
+  cancelAllEvents();
+}
 
 EventId
-Scheduler::scheduleEvent(time::nanoseconds after, const EventCallback& callback)
+Scheduler::scheduleEvent(const time::nanoseconds& after, const Event& event)
 {
-  BOOST_ASSERT(callback != nullptr);
+  EventId eventId = std::make_shared<ns3::EventId>();
+  weak_ptr<ns3::EventId> eventWeak = eventId;
+  std::function<void()> eventWithCleanup = [this, event, eventWeak] () {
+    event();
+    shared_ptr<ns3::EventId> eventId = eventWeak.lock();
+    if (eventId != nullptr) {
+      this->m_events.erase(eventId); // remove the event from the set after it is executed
+    }
+  };
 
-  EventQueue::iterator i = m_queue.insert(make_shared<EventInfo>(after, callback));
-  (*i)->queueIt = i;
+  ns3::EventId id = ns3::Simulator::Schedule(ns3::NanoSeconds(after.count()),
+                                             &std::function<void()>::operator(), eventWithCleanup);
+  *eventId = std::move(id);
+  m_events.insert(eventId);
 
-  if (!m_isEventExecuting && i == m_queue.begin()) {
-    // the new event is the first one to expire
-    this->scheduleNext();
-  }
-
-  return EventId(*i);
+  return eventId;
 }
 
 void
 Scheduler::cancelEvent(const EventId& eventId)
 {
-  shared_ptr<EventInfo> info = eventId.m_info.lock();
-  if (info == nullptr || info->isExpired) {
-    return; // event already expired or cancelled
-  }
-
-  if (info->queueIt == m_queue.begin()) {
-    m_timer->cancel();
-  }
-  m_queue.erase(info->queueIt);
-
-  if (!m_isEventExecuting) {
-    this->scheduleNext();
+  if (eventId != nullptr) {
+    ns3::Simulator::Remove(*eventId);
+    m_events.erase(eventId);
+    const_cast<EventId&>(eventId).reset();
   }
 }
 
 void
 Scheduler::cancelAllEvents()
 {
-  m_queue.clear();
-  m_timer->cancel();
-}
-
-void
-Scheduler::scheduleNext()
-{
-  if (!m_queue.empty()) {
-    m_timer->expires_from_now((*m_queue.begin())->expiresFromNow());
-    m_timer->async_wait(bind(&Scheduler::executeEvent, this, _1));
-  }
-}
-
-void
-Scheduler::executeEvent(const boost::system::error_code& error)
-{
-  if (error) { // e.g., cancelled
-    return;
-  }
-
-  m_isEventExecuting = true;
-
-  BOOST_SCOPE_EXIT(this_) {
-    this_->m_isEventExecuting = false;
-    this_->scheduleNext();
-  } BOOST_SCOPE_EXIT_END
-
-  // process all expired events
-  auto now = time::steady_clock::now();
-  while (!m_queue.empty()) {
-    auto head = m_queue.begin();
-    shared_ptr<EventInfo> info = *head;
-    if (info->expireTime > now) {
-      break;
+  for (auto i = m_events.begin(); i != m_events.end(); ) {
+    auto next = i;
+    ++next; // ns3::Simulator::Remove can call cancelEvent
+    if ((*i) != nullptr) {
+      ns3::Simulator::Remove((**i));
+      const_cast<EventId&>(*i).reset();
     }
-
-    m_queue.erase(head);
-    info->isExpired = true;
-    info->callback();
+    i = next;
   }
+  m_events.clear();
 }
 
 } // namespace scheduler
diff --git a/src/util/scheduler.hpp b/src/util/scheduler.hpp
index 79fbf23..2d669b1 100644
--- a/src/util/scheduler.hpp
+++ b/src/util/scheduler.hpp
@@ -26,6 +26,12 @@
 #include "../net/asio-fwd.hpp"
 
 #include <boost/system/error_code.hpp>
+#include "../common.hpp"
+
+#include "ns3/simulator.h"
+
+#include <set>
+#include <boost/asio/io_service.hpp>
 #include <set>
 
 namespace ndn {
@@ -37,85 +43,12 @@
 
 namespace scheduler {
 
-/**
- * \brief Function to be invoked when a scheduled event expires
+typedef function<void()> EventCallback;
+
+/** \class EventId
+ *  \brief Opaque type (shared_ptr) representing ID of a scheduled event
  */
-using EventCallback = std::function<void()>;
-
-/**
- * \brief Stores internal information about a scheduled event
- */
-class EventInfo;
-
-/**
- * \brief Identifies a scheduled event
- */
-class EventId
-{
-public:
-  /**
-   * \brief Constructs an empty EventId
-   * \note EventId is implicitly convertible from nullptr.
-   */
-  EventId(std::nullptr_t = nullptr)
-  {
-  }
-
-  /**
-   * \retval true The event is valid.
-   * \retval false This EventId is empty, or the event is expired or cancelled.
-   */
-  explicit
-  operator bool() const;
-
-  /**
-   * \return whether this and other refer to the same event, or are both empty/expired/cancelled
-   */
-  bool
-  operator==(const EventId& other) const;
-
-  bool
-  operator!=(const EventId& other) const
-  {
-    return !this->operator==(other);
-  }
-
-  /**
-   * \brief clear this EventId
-   * \note This does not cancel the event.
-   * \post !(*this)
-   */
-  void
-  reset()
-  {
-    m_info.reset();
-  }
-
-private:
-  explicit
-  EventId(const weak_ptr<EventInfo>& info)
-    : m_info(info)
-  {
-  }
-
-private:
-  weak_ptr<EventInfo> m_info;
-
-  friend class Scheduler;
-  friend std::ostream& operator<<(std::ostream& os, const EventId& eventId);
-};
-
-std::ostream&
-operator<<(std::ostream& os, const EventId& eventId);
-
-class EventQueueCompare
-{
-public:
-  bool
-  operator()(const shared_ptr<EventInfo>& a, const shared_ptr<EventInfo>& b) const;
-};
-
-using EventQueue = std::multiset<shared_ptr<EventInfo>, EventQueueCompare>;
+typedef std::shared_ptr<ns3::EventId> EventId;
 
 /**
  * \brief Generic scheduler
@@ -123,6 +56,11 @@
 class Scheduler : noncopyable
 {
 public:
+  /**
+   * \deprecated use EventCallback
+   */
+  typedef EventCallback Event;
+
   explicit
   Scheduler(boost::asio::io_service& ioService);
 
@@ -133,7 +71,7 @@
    * \return EventId that can be used to cancel the scheduled event
    */
   EventId
-  scheduleEvent(time::nanoseconds after, const EventCallback& callback);
+  scheduleEvent(const time::nanoseconds& after, const Event& event);
 
   /**
    * \brief Cancel a scheduled event
@@ -148,25 +86,36 @@
   cancelAllEvents();
 
 private:
-  /**
-   * \brief Schedule the next event on the deadline timer
-   */
-  void
-  scheduleNext();
+  struct EventInfo
+  {
+    EventInfo(const time::nanoseconds& after, const Event& event);
 
-  /**
-   * \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);
+    EventInfo(const time::steady_clock::TimePoint& when, const EventInfo& previousEvent);
 
-private:
-  unique_ptr<detail::SteadyTimer> m_timer;
-  EventQueue m_queue;
-  bool m_isEventExecuting;
+    bool
+    operator <=(const EventInfo& other) const
+    {
+      return this->m_scheduledTime <= other.m_scheduledTime;
+    }
+
+    bool
+    operator <(const EventInfo& other) const
+    {
+      return this->m_scheduledTime < other.m_scheduledTime;
+    }
+
+    time::nanoseconds
+    expiresFromNow() const;
+
+    time::steady_clock::TimePoint m_scheduledTime;
+    Event m_event;
+    mutable EventId m_eventId;
+  };
+
+  typedef std::multiset<EventId> EventQueue;
+
+  EventQueue m_events;
+  EventQueue::iterator m_scheduledEvent;
 };
 
 } // namespace scheduler