From 09163a3bbc37223416a87dec39dfe2db7c06d2ba Mon Sep 17 00:00:00 2001
From: David Zafman <david.zafman@inktank.com>
Date: Tue, 7 May 2013 15:58:48 -0700
Subject: [PATCH] Add priority option to AsyncReserver class

Add priority to request_reservation()
Change to map of lists by prioriry
Add priority to queue_pointers mappped type

Signed-off-by: David Zafman <david.zafman@inktank.com>
---
 .../osd_internals/backfill_reservation.rst    |  6 +--
 src/common/AsyncReserver.h                    | 40 ++++++++++++-------
 2 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/doc/dev/osd_internals/backfill_reservation.rst b/doc/dev/osd_internals/backfill_reservation.rst
index 9918360c49d..11c92cd5472 100644
--- a/doc/dev/osd_internals/backfill_reservation.rst
+++ b/doc/dev/osd_internals/backfill_reservation.rst
@@ -10,9 +10,9 @@ incoming backfills on a single node.
 Each OSDService now has two AsyncReserver instances: one for backfills going
 from the osd (local_reserver) and one for backfills going to the osd
 (remote_reserver).  An AsyncReserver (common/AsyncReserver.h) manages a queue
-of waiting items and a set of current reservation holders.  When a slot frees
-up, the AsyncReserver queues the Context* associated with the next item in the
-finisher provided to the constructor.
+by priority of waiting items and a set of current reservation holders.  When a
+slot frees up, the AsyncReserver queues the Context* associated with the next
+item on the highest priority queue in the finisher provided to the constructor.
 
 For a primary to initiate a backfill, it must first obtain a reservation from
 its own local_reserver.  Then, it must obtain a reservation from the backfill
diff --git a/src/common/AsyncReserver.h b/src/common/AsyncReserver.h
index d61e81c6568..111db3f26af 100644
--- a/src/common/AsyncReserver.h
+++ b/src/common/AsyncReserver.h
@@ -24,6 +24,10 @@
 
 /**
  * Manages a configurable number of asyncronous reservations.
+ *
+ * Memory usage is linear with the number of items queued and
+ * linear with respect to the total number of priorities used
+ * over all time.
  */
 template <typename T>
 class AsyncReserver {
@@ -31,18 +35,23 @@ class AsyncReserver {
   unsigned max_allowed;
   Mutex lock;
 
-  list<pair<T, Context*> > queue;
-  map<T, typename list<pair<T, Context*> >::iterator > queue_pointers;
+  map<unsigned, list<pair<T, Context*> > > queues;
+  map<T, pair<unsigned, typename list<pair<T, Context*> >::iterator > > queue_pointers;
   set<T> in_progress;
 
   void do_queues() {
-    while (in_progress.size() < max_allowed &&
-           !queue.empty()) {
-      pair<T, Context*> p = queue.front();
-      queue_pointers.erase(p.first);
-      queue.pop_front();
-      f->queue(p.second);
-      in_progress.insert(p.first);
+    typename map<unsigned, list<pair<T, Context*> > >::reverse_iterator it;
+    for (it = queues.rbegin();
+         it != queues.rend() && in_progress.size() < max_allowed;
+         ++it) {
+      while (in_progress.size() < max_allowed &&
+             !it->second.empty()) {
+        pair<T, Context*> p = it->second.front();
+        queue_pointers.erase(p.first);
+        it->second.pop_front();
+        f->queue(p.second);
+        in_progress.insert(p.first);
+      }
     }
   }
 public:
@@ -67,13 +76,14 @@ public:
    */
   void request_reservation(
     T item,                   ///< [in] reservation key
-    Context *on_reserved      ///< [in] callback to be called on reservation
+    Context *on_reserved,     ///< [in] callback to be called on reservation
+    unsigned prio
     ) {
     Mutex::Locker l(lock);
     assert(!queue_pointers.count(item) &&
 	   !in_progress.count(item));
-    queue.push_back(make_pair(item, on_reserved));
-    queue_pointers.insert(make_pair(item, --queue.end()));
+    queues[prio].push_back(make_pair(item, on_reserved));
+    queue_pointers.insert(make_pair(item, make_pair(prio,--(queues[prio]).end())));
     do_queues();
   }
 
@@ -89,14 +99,16 @@ public:
     ) {
     Mutex::Locker l(lock);
     if (queue_pointers.count(item)) {
-      delete queue_pointers[item]->second;
-      queue.erase(queue_pointers[item]);
+      unsigned prio = queue_pointers[item].first;
+      delete queue_pointers[item].second->second;
+      queues[prio].erase(queue_pointers[item].second);
       queue_pointers.erase(item);
     } else {
       in_progress.erase(item);
     }
     do_queues();
   }
+  static const unsigned MAX_PRIORITY = (unsigned)-1;
 };
 
 #endif