/* * Copyright (c) 2017 - 2018, Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "runtime/utilities/reference_tracked_object.h" #include "gtest/gtest.h" #include namespace OCLRT { TEST(RefCounter, referenceCount) { RefCounter<> rc; ASSERT_EQ(0, rc.peek()); ASSERT_TRUE(rc.peekIsZero()); int max = 7; for (int i = 0; i < max; ++i) { rc.inc(); } ASSERT_EQ(max, rc.peek()); for (int i = 0; i < max - 1; ++i) { rc.dec(); } ASSERT_EQ(1, rc.peek()); ASSERT_FALSE(rc.peekIsZero()); rc.dec(); ASSERT_EQ(0, rc.peek()); ASSERT_TRUE(rc.peekIsZero()); } TEST(RefCounter, givenReferenceTrackedObjectWhenDecAndReturnCurrentIsCalledThenMinusOneIsReturned) { RefCounter<> rc; EXPECT_EQ(-1, rc.decAndReturnCurrent()); } TEST(unique_ptr_if_unused, InitializedWithDefaultConstructorAtQueryReturnsNullptr) { unique_ptr_if_unused uptr; ASSERT_EQ(nullptr, uptr.get()); ASSERT_FALSE(uptr.isUnused()); } TEST(unique_ptr_if_unused, deferredDeletion) { struct PrimitivObject { PrimitivObject(int v, bool *wasDeletedFlag) : memb(v), wasDeletedFlag(wasDeletedFlag) { if (wasDeletedFlag != nullptr) { *wasDeletedFlag = false; } } ~PrimitivObject() { if (wasDeletedFlag != nullptr) { *wasDeletedFlag = true; } } int memb; bool *wasDeletedFlag; }; { bool wasDeleted = false; auto a = new PrimitivObject(5, &wasDeleted); { unique_ptr_if_unused shouldNotDelete(a, false); EXPECT_FALSE(wasDeleted); EXPECT_FALSE(shouldNotDelete.isUnused()); EXPECT_EQ(a, shouldNotDelete.get()); EXPECT_EQ(a, &*shouldNotDelete); EXPECT_EQ(&a->memb, &shouldNotDelete->memb); } EXPECT_FALSE(wasDeleted); delete a; EXPECT_TRUE(wasDeleted); } { bool wasDeleted = false; auto a = new PrimitivObject(5, &wasDeleted); { unique_ptr_if_unused shouldDelete(a, true); EXPECT_FALSE(wasDeleted); EXPECT_TRUE(shouldDelete.isUnused()); EXPECT_EQ(a, shouldDelete.get()); EXPECT_EQ(a, &*shouldDelete); EXPECT_EQ(&a->memb, &shouldDelete->memb); } EXPECT_TRUE(wasDeleted); } } TEST(unique_ptr_if_unused, IntializedWithoutCustomDeleterAtDestructionUsesDefaultDeleter) { bool deleterWasCalled = false; struct DefaultDeleterTestStruct { DefaultDeleterTestStruct(bool *deleterWasCalledFlag) : deleterWasCalledFlag(deleterWasCalledFlag) {} ~DefaultDeleterTestStruct() { *deleterWasCalledFlag = true; } bool *deleterWasCalledFlag; }; { unique_ptr_if_unused uptr(new DefaultDeleterTestStruct(&deleterWasCalled), true); } ASSERT_TRUE(deleterWasCalled); } TEST(unique_ptr_if_unused, IntializedWithCustomDeleterAtDestructionUsesCustomDeleter) { struct CustomDeleterTestStruct { bool customDeleterWasCalled; static void Delete(CustomDeleterTestStruct *ptr) { ptr->customDeleterWasCalled = true; } } customDeleterObj; customDeleterObj.customDeleterWasCalled = false; { unique_ptr_if_unused uptr(&customDeleterObj, true, &CustomDeleterTestStruct::Delete); } ASSERT_TRUE(customDeleterObj.customDeleterWasCalled); } TEST(unique_ptr_if_unused, IntializedWithDerivativeOfReferenceCounterAtDestructionUsesObtainedDeleter) { struct ObtainedDeleterTestStruct : public ReferenceTrackedObject { using DeleterFuncType = void (*)(ObtainedDeleterTestStruct *); DeleterFuncType getCustomDeleter() const { return &ObtainedDeleterTestStruct::Delete; } bool obtainedDeleterWasCalled; static void Delete(ObtainedDeleterTestStruct *ptr) { ptr->obtainedDeleterWasCalled = true; } } obtainedDeleterObj; obtainedDeleterObj.obtainedDeleterWasCalled = false; { unique_ptr_if_unused uptr(&obtainedDeleterObj, true); } ASSERT_TRUE(obtainedDeleterObj.obtainedDeleterWasCalled); { // make sure that nullptr for types that declare custom deleters don't cause problems unique_ptr_if_unused{nullptr, true}; unique_ptr_if_unused{nullptr, false}; } } TEST(ReferenceTrackedObject, internalAndApiReferenceCount) { struct PrimitiveReferenceTrackedObject : ReferenceTrackedObject { PrimitiveReferenceTrackedObject(int v, bool *wasDeletedFlag) : memb(v), wasDeletedFlag(wasDeletedFlag) { if (wasDeletedFlag != nullptr) { *wasDeletedFlag = false; } } ~PrimitiveReferenceTrackedObject() override { if (wasDeletedFlag != nullptr) { *wasDeletedFlag = true; } } int memb; bool *wasDeletedFlag; }; PrimitiveReferenceTrackedObject *a = nullptr; // 1. Test simple inc/dec api ref count { bool wasDeleted = false; a = new PrimitiveReferenceTrackedObject(5, &wasDeleted); ASSERT_TRUE(a->peekHasZeroRefcounts()); a->incRefApi(); ASSERT_FALSE(a->peekHasZeroRefcounts()); { ASSERT_FALSE(wasDeleted); auto autoDeleter = a->decRefApi(); EXPECT_TRUE(autoDeleter.isUnused()); EXPECT_EQ(a, autoDeleter.get()); EXPECT_EQ(a, &*autoDeleter); EXPECT_EQ(&a->memb, &autoDeleter->memb); ASSERT_FALSE(wasDeleted); } ASSERT_TRUE(wasDeleted); } // 2. Test zero api ref count, but internal ref count > 0 { bool wasDeleted = false; a = new PrimitiveReferenceTrackedObject(5, &wasDeleted); ASSERT_TRUE(a->peekHasZeroRefcounts()); a->incRefInternal(); ASSERT_FALSE(a->peekHasZeroRefcounts()); a->incRefApi(); { { ASSERT_FALSE(wasDeleted); auto autoDeleter = a->decRefApi(); EXPECT_FALSE(autoDeleter.isUnused()); EXPECT_EQ(a, autoDeleter.get()); EXPECT_EQ(a, &*autoDeleter); EXPECT_EQ(&a->memb, &autoDeleter->memb); ASSERT_FALSE(wasDeleted); } // 3. Test api ref count 0 and dec internal ref count to 0 ASSERT_FALSE(wasDeleted); auto autoDeleter = a->decRefInternal(); EXPECT_TRUE(autoDeleter.isUnused()); EXPECT_EQ(a, autoDeleter.get()); EXPECT_EQ(a, &*autoDeleter); EXPECT_EQ(&a->memb, &autoDeleter->memb); ASSERT_FALSE(wasDeleted); } ASSERT_TRUE(wasDeleted); } // 4. Test that api refcount is not affected by internal refcount bool wasDeleted = false; a = new PrimitiveReferenceTrackedObject(5, &wasDeleted); static const int refApiTop = 7; static const int refIntTop = 13; for (int i = 0; i < refApiTop; ++i) { a->incRefApi(); } for (int i = 0; i < refIntTop; ++i) { a->incRefInternal(); } EXPECT_EQ(refApiTop, a->getRefApiCount()); EXPECT_EQ(refIntTop + refApiTop, a->getRefInternalCount()); while (a->getRefApiCount() > 0) { a->decRefApi(); ASSERT_FALSE(wasDeleted); } while (a->getRefInternalCount() > 1) { a->decRefInternal(); ASSERT_FALSE(wasDeleted); } a->decRefInternal(); ASSERT_TRUE(wasDeleted); } TEST(ReferenceTrackedObject, whenNewReferenceTrackedObjectIsCreatedRefcountsAreZero) { struct PrimitiveReferenceTrackedObject : ReferenceTrackedObject { }; PrimitiveReferenceTrackedObject obj; EXPECT_TRUE(obj.peekHasZeroRefcounts()); EXPECT_EQ(0, obj.getRefApiCount()); EXPECT_EQ(0, obj.getRefInternalCount()); } } // namespace OCLRT