Teach LA how to destroy

Change-Id: I57ab30b6d56370dade6987f442136ea5e5546c9b
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index c92ab91..f535afb 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -127,7 +127,7 @@
     }
 
     void tryRecycleState(DeferredDisplayState* state) {
-        mAllocator.rewindIfLastAlloc(state, sizeof(DeferredDisplayState));
+        mAllocator.rewindIfLastAlloc(state);
     }
 
     /**
diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk
index 51898d2..b6f0baf 100644
--- a/libs/hwui/tests/Android.mk
+++ b/libs/hwui/tests/Android.mk
@@ -34,16 +34,3 @@
 	tests/main.cpp
 
 include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk
-LOCAL_MODULE := hwui_unit_tests
-LOCAL_MODULE_TAGS := tests
-
-include $(LOCAL_PATH)/Android.common.mk
-
-LOCAL_SRC_FILES += \
-	tests/ClipAreaTests.cpp \
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk
new file mode 100644
index 0000000..51601b0
--- /dev/null
+++ b/libs/hwui/unit_tests/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+local_target_dir := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PATH:= $(call my-dir)/..
+
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk
+LOCAL_MODULE := hwui_unit_tests
+LOCAL_MODULE_TAGS := tests
+
+include $(LOCAL_PATH)/Android.common.mk
+
+LOCAL_SRC_FILES += \
+    unit_tests/ClipAreaTests.cpp \
+    unit_tests/LinearAllocatorTests.cpp \
+    unit_tests/main.cpp
+
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp
similarity index 100%
rename from libs/hwui/tests/ClipAreaTests.cpp
rename to libs/hwui/unit_tests/ClipAreaTests.cpp
diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
new file mode 100644
index 0000000..b3959d1
--- /dev/null
+++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <utils/LinearAllocator.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+struct SimplePair {
+    int one = 1;
+    int two = 2;
+};
+
+class SignalingDtor {
+public:
+    SignalingDtor() {
+        mDestroyed = nullptr;
+    }
+    SignalingDtor(bool* destroyedSignal) {
+        mDestroyed = destroyedSignal;
+        *mDestroyed = false;
+    }
+    virtual ~SignalingDtor() {
+        if (mDestroyed) {
+            *mDestroyed = true;
+        }
+    }
+    void setSignal(bool* destroyedSignal) {
+        mDestroyed = destroyedSignal;
+    }
+private:
+    bool* mDestroyed;
+};
+
+TEST(LinearAllocator, alloc) {
+    LinearAllocator la;
+    EXPECT_EQ(0u, la.usedSize());
+    la.alloc(64);
+    // There's some internal tracking as well as padding
+    // so the usedSize isn't strictly defined
+    EXPECT_LE(64u, la.usedSize());
+    EXPECT_GT(80u, la.usedSize());
+    auto pair = la.alloc<SimplePair>();
+    EXPECT_LE(64u + sizeof(SimplePair), la.usedSize());
+    EXPECT_GT(80u + sizeof(SimplePair), la.usedSize());
+    EXPECT_EQ(1, pair->one);
+    EXPECT_EQ(2, pair->two);
+}
+
+TEST(LinearAllocator, dtor) {
+    bool destroyed[10];
+    {
+        LinearAllocator la;
+        for (int i = 0; i < 5; i++) {
+            la.alloc<SignalingDtor>()->setSignal(destroyed + i);
+            la.alloc<SimplePair>();
+        }
+        la.alloc(100);
+        for (int i = 0; i < 5; i++) {
+            auto sd = new (la) SignalingDtor(destroyed + 5 + i);
+            la.autoDestroy(sd);
+            new (la) SimplePair();
+        }
+        la.alloc(100);
+        for (int i = 0; i < 10; i++) {
+            EXPECT_FALSE(destroyed[i]);
+        }
+    }
+    for (int i = 0; i < 10; i++) {
+        EXPECT_TRUE(destroyed[i]);
+    }
+}
+
+TEST(LinearAllocator, rewind) {
+    bool destroyed;
+    {
+        LinearAllocator la;
+        auto addr = la.alloc(100);
+        EXPECT_LE(100u, la.usedSize());
+        la.rewindIfLastAlloc(addr, 100);
+        EXPECT_GT(16u, la.usedSize());
+        size_t emptySize = la.usedSize();
+        auto sigdtor = la.alloc<SignalingDtor>();
+        sigdtor->setSignal(&destroyed);
+        EXPECT_FALSE(destroyed);
+        EXPECT_LE(emptySize, la.usedSize());
+        la.rewindIfLastAlloc(sigdtor);
+        EXPECT_TRUE(destroyed);
+        EXPECT_EQ(emptySize, la.usedSize());
+        destroyed = false;
+    }
+    // Checking for a double-destroy case
+    EXPECT_EQ(destroyed, false);
+}
diff --git a/libs/hwui/unit_tests/how_to_run.txt b/libs/hwui/unit_tests/how_to_run.txt
new file mode 100755
index 0000000..a2d6a34
--- /dev/null
+++ b/libs/hwui/unit_tests/how_to_run.txt
@@ -0,0 +1,4 @@
+mmm -j8 $ANDROID_BUILD_TOP/frameworks/base/libs/hwui/unit_tests &&
+adb push $ANDROID_PRODUCT_OUT/data/nativetest/hwui_unit_tests/hwui_unit_tests \
+    /data/nativetest/hwui_unit_tests/hwui_unit_tests &&
+adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests
diff --git a/libs/hwui/unit_tests/main.cpp b/libs/hwui/unit_tests/main.cpp
new file mode 100644
index 0000000..c9b9636
--- /dev/null
+++ b/libs/hwui/unit_tests/main.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index 31e439f..59b12cf 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -81,6 +81,10 @@
 
 #define min(x,y) (((x) < (y)) ? (x) : (y))
 
+void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) {
+    return la.alloc(size);
+}
+
 namespace android {
 namespace uirenderer {
 
@@ -120,6 +124,11 @@
     , mDedicatedPageCount(0) {}
 
 LinearAllocator::~LinearAllocator(void) {
+    while (mDtorList) {
+        auto node = mDtorList;
+        mDtorList = node->next;
+        node->dtor(node->addr);
+    }
     Page* p = mPages;
     while (p) {
         Page* next = p->next();
@@ -181,12 +190,46 @@
     return ptr;
 }
 
+void LinearAllocator::addToDestructionList(Destructor dtor, void* addr) {
+    static_assert(std::is_standard_layout<DestructorNode>::value,
+                  "DestructorNode must have standard layout");
+    static_assert(std::is_trivially_destructible<DestructorNode>::value,
+                  "DestructorNode must be trivially destructable");
+    auto node = new (*this) DestructorNode();
+    node->dtor = dtor;
+    node->addr = addr;
+    node->next = mDtorList;
+    mDtorList = node;
+}
+
+void LinearAllocator::runDestructorFor(void* addr) {
+    auto node = mDtorList;
+    DestructorNode* previous = nullptr;
+    while (node) {
+        if (node->addr == addr) {
+            if (previous) {
+                previous->next = node->next;
+            } else {
+                mDtorList = node->next;
+            }
+            node->dtor(node->addr);
+            rewindIfLastAlloc(node, sizeof(DestructorNode));
+            break;
+        }
+        previous = node;
+        node = node->next;
+    }
+}
+
 void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) {
+    // First run the destructor as running the destructor will
+    // also rewind for the DestructorNode allocation which will
+    // have been allocated after this void* if it has a destructor
+    runDestructorFor(ptr);
     // Don't bother rewinding across pages
     allocSize = ALIGN(allocSize);
     if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage)
             && ptr == ((char*)mNext - allocSize)) {
-        mTotalAllocated -= allocSize;
         mWastedSpace += allocSize;
         mNext = ptr;
     }
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index 6ca9f8d..d90dd82 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -27,6 +27,7 @@
 #define ANDROID_LINEARALLOCATOR_H
 
 #include <stddef.h>
+#include <type_traits>
 
 namespace android {
 namespace uirenderer {
@@ -53,12 +54,43 @@
     void* alloc(size_t size);
 
     /**
+     * Allocates an instance of the template type with the default constructor
+     * and adds it to the automatic destruction list.
+     */
+    template<class T>
+    T* alloc() {
+        T* ret = new (*this) T;
+        autoDestroy(ret);
+        return ret;
+    }
+
+    /**
+     * Adds the pointer to the tracking list to have its destructor called
+     * when the LinearAllocator is destroyed.
+     */
+    template<class T>
+    void autoDestroy(T* addr) {
+        if (!std::is_trivially_destructible<T>::value) {
+            auto dtor = [](void* addr) { ((T*)addr)->~T(); };
+            addToDestructionList(dtor, addr);
+        }
+    }
+
+    /**
      * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its
-     * state if possible. No destructors are called.
+     * state if possible.
      */
     void rewindIfLastAlloc(void* ptr, size_t allocSize);
 
     /**
+     * Same as rewindIfLastAlloc(void*, size_t)
+     */
+    template<class T>
+    void rewindIfLastAlloc(T* ptr) {
+        rewindIfLastAlloc((void*)ptr, sizeof(T));
+    }
+
+    /**
      * Dump memory usage statistics to the log (allocated and wasted space)
      */
     void dumpMemoryStats(const char* prefix = "");
@@ -73,7 +105,15 @@
     LinearAllocator(const LinearAllocator& other);
 
     class Page;
+    typedef void (*Destructor)(void* addr);
+    struct DestructorNode {
+        Destructor dtor;
+        void* addr;
+        DestructorNode* next = nullptr;
+    };
 
+    void addToDestructionList(Destructor, void* addr);
+    void runDestructorFor(void* addr);
     Page* newPage(size_t pageSize);
     bool fitsInCurrentPage(size_t size);
     void ensureNext(size_t size);
@@ -85,6 +125,7 @@
     void* mNext;
     Page* mCurrentPage;
     Page* mPages;
+    DestructorNode* mDtorList = nullptr;
 
     // Memory usage tracking
     size_t mTotalAllocated;
@@ -96,4 +137,6 @@
 }; // namespace uirenderer
 }; // namespace android
 
+void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la);
+
 #endif // ANDROID_LINEARALLOCATOR_H