Linking multiple USD into the same process

We are in the unfortunate situation where we need to link two instances of USD into the same process. We have different namespaces for the two instances but it appears that the plugin loading system doesn’t namespace or distinguish things in a way where the two versions are isolated on MacOS.

Here is the part of the callstack where usd moves between the two usd instances: (libusd_ms.dylib -->bananausdplug.dylib)

    frame #18: 0x00000003ff52c164 bananausd_tf.dylib`bananaInternal_v23_2__pxrReserved__::Tf_RegistryInit::Add(libName="MFB_ALT_PACKAGE_NAME", func=(bananausd_plug.dylib`bananaInternal_v23_2__pxrReserved__::_Tf_RegistryFunction31(bananaInternal_v23_2__pxrReserved__::TfDebug*, void*) at debugCodes.cpp:32), typeName="TfDebug")(void*, void*), char const*) at registryManager.cpp:630:13
    frame #19: 0x00000003ff490834 bananausd_tf.dylib`void bananaInternal_v23_2__pxrReserved__::Tf_RegistryInit::Add<bananaInternal_v23_2__pxrReserved__::TfDebug, void>(libName="MFB_ALT_PACKAGE_NAME", func=(bananausd_plug.dylib`bananaInternal_v23_2__pxrReserved__::_Tf_RegistryFunction31(bananaInternal_v23_2__pxrReserved__::TfDebug*, void*) at debugCodes.cpp:32), typeName="TfDebug")(bananaInternal_v23_2__pxrReserved__::TfDebug*, void*), char const*) at registryManager.h:150:9
    frame #20: 0x00000003fea51f58 bananausd_plug.dylib`bananaInternal_v23_2__pxrReserved__::_Tf_RegistryAdd31((null)=0x00006000066ab7e0) at debugCodes.cpp:31:1
    frame #21: 0x00000004c5c512c8 libusd_ms.dylib`pxrInternal_v0_23__pxrReserved__::(anonymous namespace)::AddImage(mach_header const*, long) + 168
    frame #22: 0x00000004c5c511c8 libusd_ms.dylib`pxrInternal_v0_23__pxrReserved__::(anonymous namespace)::InstallDyldCallbacks() + 72
    frame #23: 0x00000001882b81d8 dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 168
    frame #24: 0x00000001882f9e94 dyld`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 340
    frame #25: 0x00000001882ed1a4 dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 528

Any thoughts on how we can make this work?

1 Like

I notice also you have got banana as non-monolithic, and libusd as monolithic.

How are you setting things up so that libusd doesn’t look at banana for the pluginfo.json files?

That is an interesting angle, maybe the solution lives in plugInfo.json somehow

We found a solution to unblock ourselves.
The issue came from the constructor entries being in a global namespace meaning they would be called regardless of which usd instance they came from even if everything else is properly namespaced:

The (arguably hacky) solution was to be able to drive the version field of Arch_ConstructorEntry through a preprocessor variable so we can inject a non-zero value into our build and it would be able to tell the difference between contructor entries from the two different builds (it’s using 0 in all entries by default).

A better solution might be a string comparison based on the namespace string but it would require the struct to change layout which would also break this logic if not both versions of USD has the same layout (if I understand it correctly). A more feasible solution might be a compile time hash based on the namespace but it might be better to just go with what I have now.

Let me know if you want a PR for the change on the public repository.

Also, the work done is only for macos, there might be dragons on other platforms too.

Created a PR for you to look at:

Thanks for reporting, @laserallan ! We’re exploring a slightly different approach, and will post comments/advice on your PR when that exploration is complete.

Thanks, Appreciate you looking at it!