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