Repository: google/filament
File: filament/src/details/Engine.cpp (C++)

Code before:
    bool const success = list.remove(ptr);

#if UTILS_HAS_RTTI
    auto typeName = CallStack::typeName<T>();
    const char * const typeNameCStr = typeName.c_str();
#else
    const char * const typeNameCStr = "<no-rtti>";
#endif

    if (ASSERT_PRECONDITION_NON_FATAL(success,
            "Object %s at %p doesn't exist (double free?)", typeNameCStr, ptr)) {

        T* p = const_cast<T*>(ptr);

        if constexpr (HasIsCreationComplete<T>::value) {
            // The presence of 'isCreationComplete' in type T implies it supports asynchronous
            // creation. For these asynchronous objects, we can terminate the backend resources
            // immediately as they are no longer referenced. However, we defer the destruction of
            // the frontend object if it's still being loaded.
            // Reason: The creation process is still active and holds a reference to the frontend
            // object to set `mCreationComplete` to true (see FTexture::FTexture). Deleting it now
            // may cause a crash, so we wait until creation completes.

            // Terminate the backend resource immediately as they're unnecessary from this point.
            p->terminate(*this);

            if (p->isCreationComplete()) {
                mHeapAllocator.destroy(p);
            } else {
                // We defer the destruction of the frontend object until the creation process
                // completes. This ensures the object remains valid while the creation process still
                // holds references to it.
                mDeferredAsyncObjectDestruction.push_back([this, p]() {
                    if (!p->isCreationComplete()) {
                        return false;
                    }
                    mHeapAllocator.destroy(p);
                    return true;
                });
            }
        } else {
            p->terminate(*this);
            mHeapAllocator.destroy(p);
        }
    }
    return success;
}

template<typename T, typename Lock>
UTILS_ALWAYS_INLINE
bool FEngine::terminateAndDestroyLocked(Lock& lock, const T* p, ResourceList<T>& list) {

Review comment (suggestion):
it might be "obvious", but I think it would be helpful to have a comment here that says that if the async mode is not enabled, `isCreationComplete()` is guaranteed to be `true`.

Code after:

#if UTILS_HAS_RTTI
    auto typeName = CallStack::typeName<T>();
    const char * const typeNameCStr = typeName.c_str();
#else
    const char * const typeNameCStr = "<no-rtti>";
#endif

    if (ASSERT_PRECONDITION_NON_FATAL(success,
            "Object %s at %p doesn't exist (double free?)", typeNameCStr, ptr)) {

        T* p = const_cast<T*>(ptr);

        if constexpr (HasIsCreationComplete<T>::value) {
            // The presence of 'isCreationComplete' in type T implies it supports asynchronous
            // creation. For these asynchronous objects, we can terminate the backend resources
            // immediately as they are no longer referenced. However, we defer the destruction of
            // the frontend object if it's still being loaded.
            // Reason: The creation process is still active and holds a reference to the frontend
            // object to set `mCreationComplete` to true (see FTexture::FTexture). Deleting it now
            // may cause a crash, so we wait until creation completes.

            // Terminate the backend resource immediately as they're unnecessary from this point.
            p->terminate(*this);

            if (p->isCreationComplete()) {
                // If creation is complete, we free the frontend object immediately. Note that in
                // regular (non-async) mode, the `isCreationComplete` method always return true.
                mHeapAllocator.destroy(p);
            } else {
                // We defer the destruction of the frontend object until the creation process
                // completes. This ensures the object remains valid while the creation process still
                // holds references to it.
                mDeferredAsyncObjectDestruction.push_back([this, p]() {
                    if (!p->isCreationComplete()) {
                        return false;
                    }
                    mHeapAllocator.destroy(p);
                    return true;
                });
            }
        } else {
            p->terminate(*this);
            mHeapAllocator.destroy(p);
        }
    }
    return success;
}

template<typename T, typename Lock>
UTILS_ALWAYS_INLINE