core: global io_service and scheduler

Scheduler and time are imported from ndn-cpp-dev.
Forwarder is using the new scheduler API.
Face system is not transitioned yet.

refs #1290

Change-Id: I5679cb50bbf9890a105f663b038f13951403c2b6
diff --git a/daemon/core/global-io.cpp b/daemon/core/global-io.cpp
new file mode 100644
index 0000000..74f1dc5
--- /dev/null
+++ b/daemon/core/global-io.cpp
@@ -0,0 +1,35 @@
+/* -*- 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 "global-io.hpp"
+
+namespace nfd {
+
+namespace scheduler {
+// defined in scheduler.cpp
+void
+resetGlobalScheduler();
+} // namespace scheduler
+
+static shared_ptr<boost::asio::io_service> g_ioService;
+
+boost::asio::io_service&
+getGlobalIoService()
+{
+  if (!static_cast<bool>(g_ioService)) {
+    g_ioService = make_shared<boost::asio::io_service>();
+  }
+  return *g_ioService;
+}
+
+void
+resetGlobalIoService()
+{
+  scheduler::resetGlobalScheduler();
+  g_ioService.reset();
+}
+
+} // namespace nfd
diff --git a/daemon/core/global-io.hpp b/daemon/core/global-io.hpp
new file mode 100644
index 0000000..7e961f1
--- /dev/null
+++ b/daemon/core/global-io.hpp
@@ -0,0 +1,30 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_CORE_GLOBAL_IO_HPP
+#define NFD_CORE_GLOBAL_IO_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+
+/** \return the global io_service instance
+ */
+boost::asio::io_service&
+getGlobalIoService();
+
+#ifdef WITH_TESTS
+/** \brief delete the global io_service instance
+ *
+ *  It will be recreated at the next invocation of getGlobalIoService.
+ */
+void
+resetGlobalIoService();
+#endif
+
+} // namespace nfd
+
+#endif // NFD_CORE_GLOBAL_IO_HPP
diff --git a/daemon/core/monotonic_deadline_timer.hpp b/daemon/core/monotonic_deadline_timer.hpp
deleted file mode 100644
index c158fe4..0000000
--- a/daemon/core/monotonic_deadline_timer.hpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (C) 2014 Named Data Networking Project
- * See COPYING for copyright and distribution information.
- */
-
-/**
- * This code is based on https://svn.boost.org/trac/boost/attachment/ticket/3504/MonotonicDeadlineTimer.h
- */
-
-#ifndef NFD_CORE_MONOTONIC_DEADLINE_TIMER_HPP
-#define NFD_CORE_MONOTONIC_DEADLINE_TIMER_HPP
-
-#include "time.hpp"
-
-namespace boost {
-namespace asio {
-
-template <> 
-struct time_traits<nfd::time::monotonic_clock>
-{
-  typedef nfd::time::Point time_type;
-  typedef nfd::time::Duration duration_type;
-
-  static time_type
-  now()
-  {
-    return nfd::time::now();
-  }
-
-  static time_type
-  add(const time_type& time, const duration_type& duration) 
-  {
-    return time + duration;
-  }
-
-  static duration_type
-  subtract(const time_type& timeLhs, const time_type& timeRhs)
-  {
-    return timeLhs - timeRhs;
-  }
-
-  static bool
-  less_than(const time_type& timeLhs, const time_type& timeRhs)
-  {
-    return timeLhs < timeRhs;
-  }
-
-  static boost::posix_time::time_duration
-  to_posix_duration(const duration_type& duration)
-  {
-    return boost::posix_time::microseconds(duration/1000);
-  }
-};
-
-typedef basic_deadline_timer<nfd::time::monotonic_clock> monotonic_deadline_timer; 
-
-} // namespace asio
-} // namespace boost
-
-#endif // NFD_CORE_MONOTONIC_DEADLINE_TIMER_HPP
diff --git a/daemon/core/scheduler.cpp b/daemon/core/scheduler.cpp
index a1759ca..439b914 100644
--- a/daemon/core/scheduler.cpp
+++ b/daemon/core/scheduler.cpp
@@ -7,204 +7,24 @@
 #include "scheduler.hpp"
 
 namespace nfd {
+namespace scheduler {
 
-struct EventIdImpl
+static shared_ptr<Scheduler> g_scheduler;
+
+Scheduler&
+getGlobalScheduler()
 {
-  EventIdImpl(const Scheduler::EventQueue::iterator& event)
-    : m_event(event)
-    , m_isValid(true)
-  {
+  if (!static_cast<bool>(g_scheduler)) {
+    g_scheduler = make_shared<Scheduler>(boost::ref(getGlobalIoService()));
   }
-
-  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<EventIdImpl>(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<bool>(eventId) || !eventId->isValid())
-    return; // event empty, already fired, or cancelled
-  
-  if (static_cast<EventQueue::iterator>(*eventId) != m_scheduledEvent) {
-    m_events.erase(*eventId);
-    eventId->invalidate();
-    return;
-  }
-  
-  m_deadlineTimer.cancel();
-  m_events.erase(static_cast<EventQueue::iterator>(*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();
-        }
-    }
+  return *g_scheduler;
 }
 
 void
-Scheduler::onEvent(const boost::system::error_code& error)
+resetGlobalScheduler()
 {
-  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;
+  g_scheduler.reset();
 }
 
-
+} // namespace scheduler
 } // namespace nfd
diff --git a/daemon/core/scheduler.hpp b/daemon/core/scheduler.hpp
index a8e60bb..6ba4f80 100644
--- a/daemon/core/scheduler.hpp
+++ b/daemon/core/scheduler.hpp
@@ -7,95 +7,46 @@
 #ifndef NFD_CORE_SCHEDULER_HPP
 #define NFD_CORE_SCHEDULER_HPP
 
-#include "common.hpp"
-#include "monotonic_deadline_timer.hpp"
+#include "global-io.hpp"
+#include "time.hpp"
+#include <ndn-cpp-dev/util/scheduler.hpp>
 
 namespace nfd {
+namespace scheduler {
 
-struct EventIdImpl; ///< \brief Private storage of information about the event
-/**
- * \brief Opaque type (shared_ptr) representing ID of the scheduled event
- */
-typedef shared_ptr<EventIdImpl> EventId;
+using ndn::Scheduler;
 
-/**
- * \brief Generic scheduler
+/** \class EventId
+ *  \brief Opaque type (shared_ptr) representing ID of a scheduled event
  */
-class Scheduler
+using ndn::EventId;
+
+} // namespace scheduler
+
+// TODO delete this after transition
+using scheduler::Scheduler;
+
+using scheduler::EventId;
+
+namespace scheduler {
+
+// TODO delete this after transition
+Scheduler&
+getGlobalScheduler();
+
+inline EventId
+schedule(const time::Duration& after, const Scheduler::Event& event)
 {
-public:
-  typedef function<void()> Event;
+  return getGlobalScheduler().scheduleEvent(after, event);
+}
 
-  Scheduler(boost::asio::io_service& ioService);
+inline void
+cancel(const EventId& eventId)
+{
+  getGlobalScheduler().cancelEvent(eventId);
+}
 
-  /**
-   * \brief Schedule one time event after the specified delay
-   * \returns EventId that can be used to cancel the scheduled event
-   */
-  EventId
-  scheduleEvent(const time::Duration& after, const Event& event);
-
-  /**
-   * \brief Schedule periodic event that should be fired every specified period.
-   *        First event will be fired after the specified delay.
-   * \returns EventId that can be used to cancel the scheduled event
-   */
-  EventId
-  schedulePeriodicEvent(const time::Duration& after,
-                        const time::Duration& period,
-                        const Event& event);
-  
-  /**
-   * \brief Cancel scheduled event
-   */
-  void
-  cancelEvent(const EventId& eventId);
-
-private:
-  void
-  onEvent(const boost::system::error_code& code);
-  
-private:
-  boost::asio::io_service& m_ioService;
-
-  struct EventInfo
-  {
-    EventInfo(const time::Duration& after,
-              const time::Duration& period,
-              const Event& event);
-    EventInfo(const time::Point& when, const EventInfo& previousEvent);
-
-    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::Duration
-    expiresFromNow() const;
-    
-    time::Point m_scheduledTime;
-    time::Duration m_period;
-    Event m_event;
-    mutable EventId m_eventId;
-  };
-
-  typedef std::multiset<EventInfo> EventQueue;
-  friend struct EventIdImpl;
-
-  EventQueue m_events;
-  EventQueue::iterator m_scheduledEvent;
-  boost::asio::monotonic_deadline_timer m_deadlineTimer;
-
-  bool m_isEventExecuting;
-};
-
+} // namespace scheduler
 } // namespace nfd
 
 #endif // NFD_CORE_SCHEDULER_HPP
diff --git a/daemon/core/time.cpp b/daemon/core/time.cpp
deleted file mode 100644
index c216df4..0000000
--- a/daemon/core/time.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- 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 "time.hpp"
-#include <time.h>
-#include <stdexcept>
-#include <sys/time.h>
-
-namespace nfd {
-namespace time {
-
-Point
-now()
-{
-#ifdef HAVE_RT
-
-  struct timespec t;
-  int res = clock_gettime(CLOCK_MONOTONIC, &t);
-  
-  if (res == -1) {
-    throw std::runtime_error("clock_gettime");
-  }
-  
-  return Point(time::seconds(t.tv_sec) + time::nanoseconds(t.tv_nsec));
-
-#else
-  // fallback to wall clock time
-
-  struct timeval tv;
-  int res = gettimeofday(&tv, 0);
-
-  if (res == -1) {
-    throw std::runtime_error("gettimeofday");
-  }
-  
-  return Point(time::seconds(tv.tv_sec) + time::microseconds(tv.tv_usec));
-  
-#endif
-}
-
-} // namespace time
-} // namespace nfd
diff --git a/daemon/core/time.hpp b/daemon/core/time.hpp
index 5efd082..2c9498f 100644
--- a/daemon/core/time.hpp
+++ b/daemon/core/time.hpp
@@ -8,152 +8,19 @@
 #define NFD_CORE_TIME_H
 
 #include "common.hpp"
+#include <ndn-cpp-dev/util/time.hpp>
 
 namespace nfd {
 namespace time {
 
-class monotonic_clock;
-
-/** \class Duration
- *  \brief represents a time interval
- *  Time unit is nanosecond.
- */
-class Duration
-{
-public:
-  Duration()
-    : m_value(0)
-  {
-  }
-
-  explicit
-  Duration(int64_t value)
-    : m_value(value)
-  {
-  }
-  
-  operator int64_t&()
-  {
-    return m_value;
-  }
-
-  operator const int64_t&() const
-  {
-    return m_value;
-  }
-
-  Duration
-  operator+(const Duration& other) const
-  {
-    return Duration(this->m_value + other.m_value);
-  }
-  
-  Duration
-  operator-(const Duration& other) const
-  {
-    return Duration(this->m_value - other.m_value);
-  }
-
-private:
-  int64_t m_value;
-};
-
-/** \class Point
- *  \brief represents a point in time
- *  This uses monotonic clock.
- */
-class Point
-{
-public:
-  Point()
-    : m_value(0)
-  {
-  }
-
-  explicit
-  Point(int64_t value)
-    : m_value(value)
-  {
-  }
-  
-  operator int64_t&()
-  {
-    return m_value;
-  }
-
-  operator const int64_t&() const
-  {
-    return m_value;
-  }
-
-  Point
-  operator+(const Duration& other) const
-  {
-    return Point(this->m_value + static_cast<int64_t>(other));
-  }
-  
-  Duration
-  operator-(const Point& other) const
-  {
-    return Duration(this->m_value - other.m_value);
-  }
-
-  Point
-  operator-(const Duration& other) const
-  {
-    return Point(this->m_value  - static_cast<int64_t>(other));
-  }
-  
-private:
-  int64_t m_value;
-};
-
-/**
- * \brief Get current time
- * \return{ the current time in monotonic clock }
- */
-Point
-now();
-
-/**
- * \brief Get time::Duration for the specified number of seconds
- */
-template<class T>
-inline Duration
-seconds(T value)
-{
-  return Duration(value * static_cast<int64_t>(1000000000));
-}
-
-/**
- * \brief Get time::Duration for the specified number of milliseconds
- */
-template<class T>
-inline Duration
-milliseconds(T value)
-{
-  return Duration(value * static_cast<int64_t>(1000000));
-}
-
-/**
- * \brief Get time::Duration for the specified number of microseconds
- */
-template<class T>
-inline Duration
-microseconds(T value)
-{
-  return Duration(value * static_cast<int64_t>(1000));
-}
-
-/**
- * \brief Get time::Duration for the specified number of nanoseconds
- */
-inline Duration
-nanoseconds(int64_t value)
-{
-  return Duration(value);
-}
-
+using ndn::time::Duration;
+using ndn::time::Point;
+using ndn::time::monotonic_clock;
+using ndn::time::now;
+using ndn::time::seconds;
+using ndn::time::milliseconds;
+using ndn::time::microseconds;
+using ndn::time::nanoseconds;
 
 } // namespace time
 } // namespace nfd