HandleAllocator: Fix heap ordering using std::greater.

The default heap ordering is to return the greatest element in the
heap. Since the handle allocator expects a minimal return value on
a new allocation, this caused a bug. The bug is triggered by reserving
handles, allocating new handles, then freeing the handles and
allocating again with the same allocator. Fix the bug by using
std::greater instead of std::less, which will make the heap
return the smallest value instead of largest.

Also adds some logging debugging code for the handle allocators.

Bug: angleproject:1458
Change-Id: Ibef5dcbed0a664ccad0e0335f081e2355162584b
Reviewed-on: https://chromium-review.googlesource.com/848644
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Frank Henigman <fjhenigman@chromium.org>
diff --git a/src/libANGLE/HandleAllocator.cpp b/src/libANGLE/HandleAllocator.cpp
index c3c1842..c0c0fa2 100644
--- a/src/libANGLE/HandleAllocator.cpp
+++ b/src/libANGLE/HandleAllocator.cpp
@@ -10,6 +10,7 @@
 #include "libANGLE/HandleAllocator.h"
 
 #include <algorithm>
+#include <functional>
 
 #include "common/debug.h"
 
@@ -24,7 +25,7 @@
     }
 };
 
-HandleAllocator::HandleAllocator() : mBaseValue(1), mNextValue(1)
+HandleAllocator::HandleAllocator() : mBaseValue(1), mNextValue(1), mLoggingEnabled(false)
 {
     mUnallocatedList.push_back(HandleRange(1, std::numeric_limits<GLuint>::max()));
 }
@@ -52,9 +53,15 @@
     // Allocate from released list, logarithmic time for pop_heap.
     if (!mReleasedList.empty())
     {
-        std::pop_heap(mReleasedList.begin(), mReleasedList.end());
+        std::pop_heap(mReleasedList.begin(), mReleasedList.end(), std::greater<GLuint>());
         GLuint reusedHandle = mReleasedList.back();
         mReleasedList.pop_back();
+
+        if (mLoggingEnabled)
+        {
+            WARN() << "HandleAllocator::allocate reusing " << reusedHandle << std::endl;
+        }
+
         return reusedHandle;
     }
 
@@ -73,18 +80,33 @@
         listIt->begin++;
     }
 
+    if (mLoggingEnabled)
+    {
+        WARN() << "HandleAllocator::allocate allocating " << freeListHandle << std::endl;
+    }
+
     return freeListHandle;
 }
 
 void HandleAllocator::release(GLuint handle)
 {
+    if (mLoggingEnabled)
+    {
+        WARN() << "HandleAllocator::release releasing " << handle << std::endl;
+    }
+
     // Add to released list, logarithmic time for push_heap.
     mReleasedList.push_back(handle);
-    std::push_heap(mReleasedList.begin(), mReleasedList.end());
+    std::push_heap(mReleasedList.begin(), mReleasedList.end(), std::greater<GLuint>());
 }
 
 void HandleAllocator::reserve(GLuint handle)
 {
+    if (mLoggingEnabled)
+    {
+        WARN() << "HandleAllocator::reserve reserving " << handle << std::endl;
+    }
+
     // Clear from released list -- might be a slow operation.
     if (!mReleasedList.empty())
     {
@@ -139,4 +161,9 @@
     mNextValue = 1;
 }
 
+void HandleAllocator::enableLogging(bool enabled)
+{
+    mLoggingEnabled = enabled;
+}
+
 }  // namespace gl
diff --git a/src/libANGLE/HandleAllocator.h b/src/libANGLE/HandleAllocator.h
index cd21d68..5af5627 100644
--- a/src/libANGLE/HandleAllocator.h
+++ b/src/libANGLE/HandleAllocator.h
@@ -34,6 +34,8 @@
     void reserve(GLuint handle);
     void reset();
 
+    void enableLogging(bool enabled);
+
   private:
     GLuint mBaseValue;
     GLuint mNextValue;
@@ -56,6 +58,8 @@
     // released, stored in a heap.
     std::vector<HandleRange> mUnallocatedList;
     std::vector<GLuint> mReleasedList;
+
+    bool mLoggingEnabled;
 };
 
 }  // namespace gl
diff --git a/src/libANGLE/HandleAllocator_unittest.cpp b/src/libANGLE/HandleAllocator_unittest.cpp
index d975b4b..8597c15 100644
--- a/src/libANGLE/HandleAllocator_unittest.cpp
+++ b/src/libANGLE/HandleAllocator_unittest.cpp
@@ -150,4 +150,24 @@
     }
 }
 
+// Covers a particular bug with reserving and allocating sub ranges.
+TEST(HandleAllocatorTest, ReserveAndAllocateIterated)
+{
+    gl::HandleAllocator allocator;
+
+    for (int iteration = 0; iteration < 3; ++iteration)
+    {
+        allocator.reserve(5);
+        allocator.reserve(6);
+        GLuint a = allocator.allocate();
+        GLuint b = allocator.allocate();
+        GLuint c = allocator.allocate();
+        allocator.release(c);
+        allocator.release(a);
+        allocator.release(b);
+        allocator.release(5);
+        allocator.release(6);
+    }
+}
+
 }  // anonymous namespace
diff --git a/src/libANGLE/ResourceManager.cpp b/src/libANGLE/ResourceManager.cpp
index 79eb7e5..ebff5e5 100644
--- a/src/libANGLE/ResourceManager.cpp
+++ b/src/libANGLE/ResourceManager.cpp
@@ -260,6 +260,11 @@
     }
 }
 
+void TextureManager::enableHandleAllocatorLogging()
+{
+    mHandleAllocator.enableLogging(true);
+}
+
 // RenderbufferManager Implementation.
 
 // static
diff --git a/src/libANGLE/ResourceManager.h b/src/libANGLE/ResourceManager.h
index 2dfeff5..4dadca6 100644
--- a/src/libANGLE/ResourceManager.h
+++ b/src/libANGLE/ResourceManager.h
@@ -167,6 +167,8 @@
     static Texture *AllocateNewObject(rx::GLImplFactory *factory, GLuint handle, GLenum target);
     static void DeleteObject(const Context *context, Texture *texture);
 
+    void enableHandleAllocatorLogging();
+
   protected:
     ~TextureManager() override {}
 };