Skip to content

Commit

Permalink
Registering modules by file instead of name; Ability to load module v…
Browse files Browse the repository at this point in the history
…ia explicit paths
  • Loading branch information
Epixu committed Dec 22, 2024
1 parent ee72aa2 commit 02b3e70
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 31 deletions.
89 changes: 59 additions & 30 deletions source/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
#endif

#if 0
#define VERBOSE(...) Logger::Verbose(__VA_ARGS__)
#define VERBOSE(...) Logger::Info(__VA_ARGS__)
#else
#define VERBOSE(...) LANGULUS(NOOP)
#endif
Expand All @@ -56,7 +56,7 @@
namespace Langulus::Entity
{

TUnorderedMap<Token, Runtime::SharedLibrary> Runtime::mLibraries;
TUnorderedMap<Path, Runtime::SharedLibrary> Runtime::mLibraries;

/// Close a shared library handle, unloading it
/// @param library - the library handle
Expand Down Expand Up @@ -198,6 +198,28 @@ namespace Langulus::Entity
}
}

/// Create a module instance or return an already instantiated one
/// @param path - explicit relative path
/// @param descriptor - module initialization descriptor
/// @return the new module instance
auto Runtime::InstantiateModulePath(const Path& path, const Many& descriptor) -> A::Module* {
// Load the library if not loaded yet
const auto library = LoadSharedLibraryPath(path);

// Check if module is already instantiated
auto& foundModules = GetModules(library.mModuleType);
if (foundModules)
return foundModules[0];

// A module instance doesn't exist yet, so instantiate it
VERBOSE(this, ": Module `", path, "` is not instantiated yet"
", so attempting to create it...");
const auto instance = InstantiateModule(library, descriptor);
if (not instance)
(void) UnloadSharedLibrary(library);
return instance;
}

/// Create a module instance or return an already instantiated one
/// @param name - module name
/// @param descriptor - module initialization descriptor
Expand Down Expand Up @@ -330,37 +352,19 @@ namespace Langulus::Entity
return module;
}

/// Load a shared library for a module
/// @param name - the name for the dynamic library - the filename will
/// be derived from it, by prefixing with `LangulusMod`, and
/// suffixing with `.so` or `.dll` the name also should correspond to
/// the RTTI::Boundary
/// @return the module handle (OS dependent)
/// Load a shared library for a module using explicit path
/// @param path - the relative path to the library
/// @return the shared library handle
LANGULUS(NOINLINE)
auto Runtime::LoadSharedLibrary(const Token& name) -> SharedLibrary {
auto Runtime::LoadSharedLibraryPath(Path path) -> SharedLibrary {
// Make sure string ends with terminator
path = path.Terminate();

// Check if this library is already loaded
const auto preloaded = mLibraries.FindIt(name);
const auto preloaded = mLibraries.FindIt(path);
if (preloaded)
return preloaded.GetValue();

// File prefix
Path path;
#if LANGULUS_OS(LINUX)
path += "./lib";
#endif
path += "LangulusMod";
path += name;

// File postfix
#if LANGULUS_OS(WINDOWS)
path += ".dll";
#else
path += ".so";
#endif

// Make sure string ends with terminator
path = path.Terminate();

// Load the library
#if LANGULUS_OS(WINDOWS)
const auto dll = LoadLibraryA(path.GetRaw());
Expand Down Expand Up @@ -467,7 +471,7 @@ namespace Langulus::Entity
}

// Library is properly registered
mLibraries.Insert(name, library);
mLibraries.Insert(path, library);

// Do some info logging
Logger::Info("Module `", library.mInfo()->mName,
Expand All @@ -483,7 +487,7 @@ namespace Langulus::Entity
// Make sure we end up in an invariant state
Logger::Error("Could not enter `", path, "` due to an exception");
if (UnloadSharedLibrary(library))
mLibraries.RemoveKey(name);
mLibraries.RemoveKey(path);
if (not mLibraries)
mLibraries.Reset();
return {};
Expand All @@ -493,6 +497,31 @@ namespace Langulus::Entity
Logger::Info("Module `", library.mInfo()->mName, "` loaded (", path, ')');
return library;
}

/// Load a shared library for a module by cross-platform name
/// @param name - the name for the dynamic library - the filename will
/// be derived from it, by prefixing with `LangulusMod`, and
/// suffixing with `.so` or `.dll` depending on platform
/// @attention the name should correspond to the RTTI::Boundary it uses
/// @return the shared library handle
LANGULUS(NOINLINE)
auto Runtime::LoadSharedLibrary(const Token& name) -> SharedLibrary {
// File prefix
Path path;
#if LANGULUS_OS(LINUX)
path += "./lib";
#endif
path += "LangulusMod";
path += name;

// File postfix
#if LANGULUS_OS(WINDOWS)
path += ".dll";
#else
path += ".so";
#endif
return LoadSharedLibraryPath(Abandon(path));
}

/// Unload a DLL/SO extension module
/// @param library - the library handle to unload
Expand Down
6 changes: 5 additions & 1 deletion source/Runtime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@ namespace Langulus::Entity
// Loaded shared libraries, indexed by filename
// This is a static registry - all Runtimes use the same shared
// library objects, but manage their own module instantiations
static TUnorderedMap<Token, SharedLibrary> mLibraries;
static TUnorderedMap<Path, SharedLibrary> mLibraries;
// Instantiated modules, sorted by priority
TOrderedMap<Real, ModuleList> mModules;
// Instantiated modules, indexed by type
TUnorderedMap<DMeta, ModuleList> mModulesByType;

protected:
NOD() LANGULUS_API(ENTITY)
auto LoadSharedLibraryPath(Path) -> SharedLibrary;
auto LoadSharedLibrary(const Token&) -> SharedLibrary;
NOD() bool UnloadSharedLibrary(const SharedLibrary&);

Expand All @@ -112,6 +113,9 @@ namespace Langulus::Entity

NOD() auto GetOwner() const noexcept { return mOwner; }

NOD() LANGULUS_API(ENTITY)
auto InstantiateModulePath(const Path&, const Many& = {}) -> A::Module*;

NOD() LANGULUS_API(ENTITY)
auto InstantiateModule(const Token&, const Many& = {}) -> A::Module*;

Expand Down
15 changes: 15 additions & 0 deletions source/Thing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,5 +566,20 @@ namespace Langulus::Entity
LANGULUS_ASSERT(instance, Module, "Missing module");
return instance;
}

/// Uses the current runtime to load a shared library module, and
/// instantiate it for use, if not yet instantiated
/// @attention assumes a runtime is available in the hierarchy
/// @param path - relative path to the module
/// @param descriptor - instructions for module setup
/// @return the instantiated module interface
auto Thing::LoadModPath(const Path& path, const Many& descriptor) -> A::Module* {
const auto runtime = GetRuntime();
LANGULUS_ASSUME(UserAssumes, runtime,
"No runtime available for loading a module");
const auto instance = runtime->InstantiateModulePath(path, descriptor);
LANGULUS_ASSERT(instance, Module, "Missing module");
return instance;
}

} // namespace Langulus::Entry
2 changes: 2 additions & 0 deletions source/Thing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ namespace Langulus::Entity

LANGULUS_API(ENTITY)
auto LoadMod(const Token&, const Many& = {}) -> A::Module*;
LANGULUS_API(ENTITY)
auto LoadModPath(const Path&, const Many& = {}) -> A::Module*;

NOD() LANGULUS_API(ENTITY)
auto GetOwner() const noexcept -> const Ref<Thing>&;
Expand Down

0 comments on commit 02b3e70

Please sign in to comment.