Extension of scheduler, different logic in FetchManager regarding re-requesting (exponential backoff, and flipping broadcast/forwarding hint)
diff --git a/cmd/main.cpp b/cmd/main.cpp
new file mode 100644
index 0000000..2ad4d0d
--- /dev/null
+++ b/cmd/main.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#include "dispatcher.h"
+#include "fs-watcher.h"
+#include "logging.h"
+#include "ccnx-wrapper.h"
+
+#include <boost/make_shared.hpp>
+
+using namespace boost;
+using namespace std;
+using namespace Ccnx;
+
+int main(int argc, char *argv[])
+{
+  INIT_LOGGERS ();
+
+  if (argc != 4)
+    {
+      cerr << "Usage: ./csd <username> <shared-folder> <path>" << endl;
+      return 1;
+    }
+
+  string username = argv[1];
+  string sharedFolder = argv[2];
+  string path = argv[3];
+
+  cout << "Starting ChronoShare for [" << username << "] shared-folder [" << sharedFolder << "] at [" << path << "]" << endl;
+
+  Dispatcher dispatcher (username, sharedFolder, path, make_shared<CcnxWrapper> ());
+
+  FsWatcher watcher (path.c_str (),
+                     bind (&Dispatcher::Did_LocalFile_AddOrModify, &dispatcher, _1),
+                     bind (&Dispatcher::Did_LocalFile_Delete,      &dispatcher, _1));
+
+  while (true)
+    {
+      sleep (1);
+    }
+
+  return 0;
+}
diff --git a/scheduler/scheduler.cc b/scheduler/scheduler.cc
index 227eead..02b6431 100644
--- a/scheduler/scheduler.cc
+++ b/scheduler/scheduler.cc
@@ -167,11 +167,14 @@
 }
 
 bool
-Scheduler::addTask(TaskPtr newTask)
+Scheduler::addTask(TaskPtr newTask, bool reset/* = true*/)
 {
   if (addToMap(newTask))
   {
-    newTask->reset();
+    if (reset)
+      {
+        newTask->reset();
+      }
     int res = evtimer_add(newTask->ev(), newTask->tv());
     if (res < 0)
     {
@@ -194,7 +197,7 @@
 }
 
 void
-Scheduler::rescheduleTask(const TaskPtr &task)
+Scheduler::rescheduleTask(TaskPtr task)
 {
   ScopedLock lock(m_mutex);
   TaskMapIt it = m_taskMap.find(task->tag());
@@ -231,8 +234,56 @@
   }
 }
 
+void
+Scheduler::rescheduleTaskAt (const Task::Tag &tag, double time)
+{
+  ScopedLock lock(m_mutex);
+  TaskMapIt it = m_taskMap.find (tag);
+  if (it != m_taskMap.end())
+  {
+    TaskPtr task = it->second;
+    task->reset();
+    task->setTv (time);
+
+    int res = evtimer_add(task->ev(), task->tv());
+    if (res < 0)
+    {
+      _LOG_ERROR ("evtimer_add failed for " << task->tag());
+    }
+  }
+  else
+    {
+      _LOG_ERROR ("Task for tag " << tag << " not found");
+    }
+}
+
+void
+Scheduler::rescheduleTaskAt (TaskPtr task, double time)
+{
+  ScopedLock lock(m_mutex);
+  TaskMapIt it = m_taskMap.find(task->tag());
+  if (it != m_taskMap.end())
+  {
+    TaskPtr task = it->second;
+    task->reset();
+    task->setTv (time);
+
+    int res = evtimer_add(task->ev(), task->tv());
+    if (res < 0)
+    {
+      _LOG_ERROR ("evtimer_add failed for " << task->tag());
+    }
+  }
+  else
+  {
+    task->setTv (time); // force different time
+    addTask (task, false);
+  }
+}
+
+
 bool
-Scheduler::addToMap(const TaskPtr &task)
+Scheduler::addToMap(TaskPtr task)
 {
   ScopedLock lock(m_mutex);
   if (m_taskMap.find(task->tag()) == m_taskMap.end())
diff --git a/scheduler/scheduler.h b/scheduler/scheduler.h
index 7fe5db0..18bd9bd 100644
--- a/scheduler/scheduler.h
+++ b/scheduler/scheduler.h
@@ -71,7 +71,7 @@
 
   // if task with the same tag exists, the task is not added and return false
   virtual bool
-  addTask(TaskPtr task);
+  addTask(TaskPtr task, bool reset = true);
 
   // delete task by task->tag, regardless of whether it's invoked or not
   virtual void
@@ -101,7 +101,13 @@
   // rescheduleTask(A) is called at second 4, A will be reschedule to run
   // at second 9
   virtual void
-  rescheduleTask(const TaskPtr &task);
+  rescheduleTask(TaskPtr task);
+
+  virtual void
+  rescheduleTaskAt (const Task::Tag &tag, double time);
+
+  virtual void
+  rescheduleTaskAt (TaskPtr task, double time);
 
   void
   eventLoop();
@@ -115,7 +121,7 @@
 
 protected:
   bool
-  addToMap(const TaskPtr &task);
+  addToMap(TaskPtr task);
 
 protected:
   typedef std::map<Task::Tag, TaskPtr> TaskMap;
diff --git a/src/fetch-manager.cc b/src/fetch-manager.cc
index d74f210..e185581 100644
--- a/src/fetch-manager.cc
+++ b/src/fetch-manager.cc
@@ -23,6 +23,8 @@
 #include <boost/make_shared.hpp>
 #include <boost/ref.hpp>
 #include <boost/throw_exception.hpp>
+
+#include "simple-interval-generator.h"
 #include "logging.h"
 
 INIT_LOGGER ("FetchManager");
@@ -31,21 +33,30 @@
 using namespace std;
 using namespace Ccnx;
 
-static const string BROADCAST_DOMAIN = "/ndn/broadcast/chronoshare";
+static const Name BROADCAST_DOMAIN = Name ("/ndn/broadcast/chronoshare");
 //The disposer object function
 struct fetcher_disposer { void operator() (Fetcher *delete_this) { delete delete_this; } };
 
+static const string SCHEDULE_FETCHES_TAG = "ScheduleFetches";
+
 FetchManager::FetchManager (CcnxWrapperPtr ccnx, const Mapping &mapping, uint32_t parallelFetches/* = 3*/)
   : m_ccnx (ccnx)
   , m_mapping (mapping)
   , m_maxParallelFetches (parallelFetches)
   , m_currentParallelFetches (0)
-
+  , m_scheduler (new Scheduler)
 {
+  m_scheduler->start ();
+
+  m_scheduleFetchesTask = Scheduler::schedulePeriodicTask (m_scheduler,
+                                                           make_shared<SimpleIntervalGenerator> (1),
+                                                           bind (&FetchManager::ScheduleFetches, this), SCHEDULE_FETCHES_TAG);
 }
 
 FetchManager::~FetchManager ()
 {
+  m_scheduler->shutdown ();
+
   m_fetchList.clear_and_dispose (fetcher_disposer ());
 }
 
@@ -85,7 +96,8 @@
       break;
     }
 
-  ScheduleFetches (); // will start a fetch if m_currentParallelFetches is less than max, otherwise does nothing
+  m_scheduler->rescheduleTaskAt (m_scheduleFetchesTask, 0);
+  // ScheduleFetches (); // will start a fetch if m_currentParallelFetches is less than max, otherwise does nothing
 }
 
 void
@@ -93,6 +105,8 @@
 {
   unique_lock<mutex> lock (m_parellelFetchMutex);
 
+  boost::posix_time::ptime currentTime = date_time::second_clock<boost::posix_time::ptime>::universal_time ();
+
   for (FetchList::iterator item = m_fetchList.begin ();
        m_currentParallelFetches < m_maxParallelFetches && item != m_fetchList.end ();
        item++)
@@ -100,6 +114,9 @@
       if (item->IsActive ())
         continue;
 
+      if (currentTime < item->GetNextScheduledRetry ())
+        continue;
+
       _LOG_DEBUG ("Start fetching of " << item->GetName ());
 
       m_currentParallelFetches ++;
@@ -112,14 +129,38 @@
 {
   _LOG_DEBUG ("No data timeout for " << fetcher.GetName () << " with forwarding hint: " << fetcher.GetForwardingHint ());
 
-  fetcher.SetForwardingHint (Ccnx::Name (BROADCAST_DOMAIN));
   {
     unique_lock<mutex> lock (m_parellelFetchMutex);
     m_currentParallelFetches --;
     // no need to do anything with the m_fetchList
   }
 
-  ScheduleFetches ();
+  if (fetcher.GetForwardingHint () == BROADCAST_DOMAIN)
+    {
+      // try again directly (hopefully with different forwarding hint
+
+      /// @todo Handle potential exception
+      Name forwardingHint;
+      forwardingHint = m_mapping (fetcher.GetDeviceName ());
+      fetcher.SetForwardingHint (forwardingHint);
+    }
+  else
+    {
+      fetcher.SetForwardingHint (BROADCAST_DOMAIN);
+    }
+
+  double delay = fetcher.GetRetryPause ();
+  if (delay < 1) // first time
+    {
+      delay = 1;
+    }
+  else
+    {
+      delay = std::min (2*delay, 300.0); // 5 minutes max
+    }
+
+  fetcher.SetRetryPause (delay);
+  fetcher.SetNextScheduledRetry (date_time::second_clock<boost::posix_time::ptime>::universal_time () + posix_time::seconds (delay));
 }
 
 void
diff --git a/src/fetch-manager.h b/src/fetch-manager.h
index c26b78d..329b289 100644
--- a/src/fetch-manager.h
+++ b/src/fetch-manager.h
@@ -58,10 +58,6 @@
   GetCcnx ();
 
 private:
-
-  inline SchedulerPtr
-  GetScheduler ();
-
   // Fetch Events
   void
   DidDataSegmentFetched (Fetcher &fetcher, uint64_t seqno, const Ccnx::Name &basename,
@@ -90,6 +86,8 @@
   typedef boost::intrusive::list<Fetcher, MemberOption> FetchList;
 
   FetchList m_fetchList;
+  SchedulerPtr m_scheduler;
+  TaskPtr m_scheduleFetchesTask;
 };
 
 Ccnx::CcnxWrapperPtr
diff --git a/src/fetcher.cc b/src/fetcher.cc
index 3fe7c63..9c1475d 100644
--- a/src/fetcher.cc
+++ b/src/fetcher.cc
@@ -62,15 +62,13 @@
 
   , m_pipeline (6) // initial "congestion window"
   , m_activePipeline (0)
-
-  , m_executor (1)
+  , m_retryPause (0)
+  , m_nextScheduledRetry () // zero time
 {
-  m_executor.start ();
 }
 
 Fetcher::~Fetcher ()
 {
-  m_executor.shutdown ();
 }
 
 void
@@ -81,7 +79,10 @@
   // cout << "Restart: " << m_minSendSeqNo << endl;
   m_lastPositiveActivity = date_time::second_clock<boost::posix_time::ptime>::universal_time();
 
-  m_executor.execute (bind (&Fetcher::FillPipeline, this));
+  // Scheduler::scheduleOneTimeTask ();
+  // m_scheduler
+  // m_executor.execute (bind (&Fetcher::FillPipeline, this));
+  FillPipeline ();
 }
 
 void
@@ -175,7 +176,8 @@
     }
   else
     {
-      m_executor.execute (bind (&Fetcher::FillPipeline, this));
+      FillPipeline ();
+      // m_executor.execute (bind (&Fetcher::FillPipeline, this));
     }
 }
 
@@ -183,7 +185,7 @@
 Fetcher::OnTimeout (uint64_t seqno, const Ccnx::Name &name)
 {
   _LOG_DEBUG (" <<< :( timeout " << name.getPartialName (0, name.size () - 1) << ", seq = " << seqno);
-  
+
   // cout << "Fetcher::OnTimeout: " << name << endl;
   // cout << "Last: " << m_lastPositiveActivity << ", config: " << m_maximumNoActivityPeriod
   //      << ", now: " << date_time::second_clock<boost::posix_time::ptime>::universal_time()
diff --git a/src/fetcher.h b/src/fetcher.h
index 1be6a27..6a31b5e 100644
--- a/src/fetcher.h
+++ b/src/fetcher.h
@@ -64,6 +64,20 @@
   const Ccnx::Name &
   GetName () const { return m_name; }
 
+  const Ccnx::Name &
+  GetDeviceName () const { return m_deviceName; }
+
+  double
+  GetRetryPause () const { return m_retryPause; }
+
+  void
+  SetRetryPause (double pause) { m_retryPause = pause; }
+
+  boost::posix_time::ptime
+  GetNextScheduledRetry () const { return m_nextScheduledRetry; }
+
+  void
+  SetNextScheduledRetry (boost::posix_time::ptime nextScheduledRetry) { m_nextScheduledRetry = nextScheduledRetry; }
 
 private:
   void
@@ -106,7 +120,9 @@
   uint32_t m_activePipeline;
 
   boost::posix_time::ptime m_lastPositiveActivity;
-  Executor m_executor;
+
+  double m_retryPause; // pause to stop trying to fetch (for fetch-manager)
+  boost::posix_time::ptime m_nextScheduledRetry;
 };
 
 typedef boost::error_info<struct tag_errmsg, std::string> errmsg_info_str;
diff --git a/wscript b/wscript
index 52d14b8..b010f9a 100644
--- a/wscript
+++ b/wscript
@@ -130,3 +130,19 @@
 	includes = "ccnx scheduler src gui src . ",
 	use = "BOOST BOOST_FILESYSTEM SQLITE3 QTCORE QTGUI LOG4CXX fs-watcher ccnx database chronoshare"
 	)
+
+    cmdline = bld (
+        target = "csd",
+	features = "qt4 cxx cxxprogram",
+	defines = "WAF",
+	source = bld.path.ant_glob(['cmd/*.cpp', 'cmd/*.cc', 'gui/fs-watcher.cc']),
+	includes = "ccnx scheduler src gui src . ",
+	use = "BOOST BOOST_FILESYSTEM SQLITE3 QTCORE QTGUI LOG4CXX fs-watcher ccnx database chronoshare"
+	)
+
+
+
+
+
+
+