tests/test.unit/omni.core/plugin/test.bar/BarImpl.cpp

File members: tests/test.unit/omni.core/plugin/test.bar/BarImpl.cpp

// Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#ifndef DOXYGEN_BUILD // Just need the full listing
#    include "IBar.h"

#    include "../test.foo/IFoo.h"

#    include <omni/core/Assert.h>
#    include <omni/core/Omni.h>

#    include <cassert>
#    include <thread>

namespace omni
{
using namespace omni::core;
}

struct IBarImpl : public omni::Implements<test::IBar>
{
    const char* getName_abi() noexcept override
    {
        return "IBarImpl";
    }
};

OMNI_MODULE_GLOBALS("test.bar.plugin", "test.IBar plugin for test.unit.");

namespace
{

omni::Result onLoad(const omni::InterfaceImplementation** out, uint32_t* outCount)
{
    // clang-format off
    static const char* interfacesImplemented[] = { "test.IBar" };
    static omni::InterfaceImplementation impls[] =
    {
        {
            "IBarImpl",
            []() {
                return static_cast<omni::IObject*>(new IBarImpl);
            },
            1, // version
            interfacesImplemented, CARB_COUNTOF32(interfacesImplemented)
        },
    };
    // clang-format on

    *out = impls;
    *outCount = CARB_COUNTOF32(impls);

    return omni::kResultSuccess;
}

void loadAndUnloadFoo(const char* fooFilename, bool mustCreate)
{
    OMNI_FATAL_UNLESS(fooFilename);

    // this will trigger a load of IFoo
    if (omniGetTypeFactoryWithoutAcquire())
    {
        {
            auto foo = omni::createType(test::IFoo::kTypeId, fooFilename);
            if (mustCreate)
            {
                OMNI_FATAL_UNLESS(foo);
            }
        }

        // unload foo (this is allowed to fail since another thread may be unloading foo)
        (void)omni::unregisterInterfaceImplementationsFromModule(fooFilename);
    }
}

void onStarted()
{
    // if carb::Framework is loading this module, it is currently locked. :(
    //
    // we're about to spawn a thread to test that we can access ITypeFactory concurrently in another (even while a
    // creation method is active).
    //
    // however, if we access carb::Framework in this thread, we'll deadlock.
    //
    // carb::Framework is _not_ threadsafe.
    //
    // getIFooModuleFilename calls acquireInterface() on IFileSystem to get the current app directory.  if we try to
    // acquireInterface on another thread we'll deadlock.
    //
    // instead, we call getIFooModuleFilename (which calls acquireInterface) here (on the same thead carb::IFramework is
    // using to load this plugin) in order to take advantage of the fact that carb::Framework's lock is recursive.
    //
    // carb::Framework is _not_ threadsafe.
    auto fooFilename = test::getIFooModuleFilename();
    OMNI_FATAL_UNLESS(!fooFilename.empty(), "test.bar.plugin must be loaded from test.unit");

    // we're looking for deadlocks here
    loadAndUnloadFoo(fooFilename.c_str(), true);

    // we're looking for deadlocks here
    auto thread = std::thread(loadAndUnloadFoo, fooFilename.c_str(), true);
    thread.join();
}

bool onCanUnload()
{
    return true;
}

void onUnload()
{
    auto filename = test::getIFooModuleFilename();
    OMNI_FATAL_UNLESS(!filename.empty(), "test.bar.plugin must be loaded from test.unit");

    // we're looking for deadlocks here
    loadAndUnloadFoo(filename.c_str(), false);

    // we're looking for deadlocks here
    auto thread = std::thread(loadAndUnloadFoo, filename.c_str(), false);
    thread.join();
}

} // end of anonymous namespace

OMNI_MODULE_API omni::Result omniModuleGetExports(omni::ModuleExports* out)
{
    OMNI_MODULE_SET_EXPORTS(out);
    OMNI_MODULE_ON_MODULE_LOAD(out, onLoad);
    OMNI_MODULE_ON_MODULE_STARTED(out, onStarted);
    OMNI_MODULE_ON_MODULE_CAN_UNLOAD(out, onCanUnload);
    OMNI_MODULE_ON_MODULE_UNLOAD(out, onUnload);

    OMNI_MODULE_REQUIRE_CARB_CLIENT_NAME(out);
    OMNI_MODULE_REQUIRE_CARB_FRAMEWORK(out);

    return omni::kResultSuccess;
}
#endif