/* * Copyright (C) 2018-2022 Intel Corporation * * SPDX-License-Identifier: MIT * */ #pragma once #include "shared/source/helpers/debug_helpers.h" #include #include namespace NEO { template class RefCounter { public: RefCounter() : val(0) { } CT peek() const { CT curr = val.load(); DEBUG_BREAK_IF(curr < 0); return curr; } void inc() { [[maybe_unused]] CT curr = ++val; DEBUG_BREAK_IF(curr < 1); } void dec() { [[maybe_unused]] CT curr = --val; DEBUG_BREAK_IF(curr < 0); } CT decAndReturnCurrent() { return --val; } protected: std::atomic val; }; template class ReferenceTrackedObject; template class unique_ptr_if_unused : // NOLINT(readability-identifier-naming) public std::unique_ptr { using DeleterFuncType = void (*)(DataType *); public: unique_ptr_if_unused() : std::unique_ptr(nullptr, dontDelete) { } unique_ptr_if_unused(DataType *ptr, bool unused, DeleterFuncType customDeleter = nullptr) : std::unique_ptr(ptr, unused ? chooseDeleter(ptr, customDeleter) : dontDelete) { } bool isUnused() const { return (this->get_deleter() != dontDelete); } private: static DeleterFuncType chooseDeleter(DataType *inPtr, DeleterFuncType customDeleter) { DeleterFuncType deleter = customDeleter; if (customDeleter == nullptr) { deleter = getObjDeleter(inPtr); } if (deleter == nullptr) { deleter = &doDelete; } return deleter; } template static typename std::enable_if_t, DT>, DeleterFuncType> getObjDeleter(DataType *inPtr) { if (inPtr != nullptr) { return inPtr->getCustomDeleter(); } return nullptr; } template static typename std::enable_if_t, DT>, DeleterFuncType> getObjDeleter(DataType *inPtr) { return nullptr; } static void doDelete(DataType *ptr) { delete ptr; // NOLINT(clang-analyzer-cplusplus.NewDelete) } static void dontDelete(DataType *ptr) { ; } }; // This class is needed for having both internal and external (api) // reference counters // Note : we need both counters because an OCL app can release an OCL object // while this object is still needed/valid (e.g. events with callbacks), // so we need to have a way of tracking internal usage of these object. // At the same time, we can't use one refcount for both internal and external // (retain/release api) usage because an OCL application can query object's // refcount (this refcount should not be contaminated by our internal usage models) // Note : internal refcount accumulates also api refcounts (i.e. incrementing/decrementing // api refcount will increment/decrement internal refcount as well) - so, object // deletion is based on single/atomic decision "if(--internalRefcount == 0)" template class ReferenceTrackedObject { public: using DeleterFuncType = void (*)(DerivedClass *); virtual ~ReferenceTrackedObject(); int32_t getRefInternalCount() const { return refInternal.peek(); } void incRefInternal() { refInternal.inc(); } unique_ptr_if_unused decRefInternal() { auto customDeleter = tryGetCustomDeleter(); auto current = refInternal.decAndReturnCurrent(); bool unused = (current == 0); UNRECOVERABLE_IF(current < 0); return unique_ptr_if_unused(static_cast(this), unused, customDeleter); } int32_t getRefApiCount() const { return refApi.peek(); } void incRefApi() { refApi.inc(); refInternal.inc(); } unique_ptr_if_unused decRefApi() { refApi.dec(); return decRefInternal(); } DeleterFuncType getCustomDeleter() const { return nullptr; } private: DeleterFuncType tryGetCustomDeleter() const { const DerivedClass *asDerivedObj = static_cast(this); return asDerivedObj->getCustomDeleter(); } RefCounter<> refInternal; RefCounter<> refApi; }; template inline ReferenceTrackedObject::~ReferenceTrackedObject() { DEBUG_BREAK_IF(refInternal.peek() > 1); } template class DecRefInternalAtScopeEnd final { public: DecRefInternalAtScopeEnd(RefTrackedObj &obj) : object{obj} { } ~DecRefInternalAtScopeEnd() { object.decRefInternal(); } DecRefInternalAtScopeEnd(const DecRefInternalAtScopeEnd &) = delete; DecRefInternalAtScopeEnd(DecRefInternalAtScopeEnd &&) = delete; DecRefInternalAtScopeEnd &operator=(const DecRefInternalAtScopeEnd &) = delete; DecRefInternalAtScopeEnd &operator=(DecRefInternalAtScopeEnd &&) = delete; private: RefTrackedObj &object; }; } // namespace NEO