Commit e2021550 authored by Andrew Heather's avatar Andrew Heather
Browse files

Merge branch 'feature-dynamicLibrary' into 'develop'

Feature dynamic library - issue #1737

See merge request !375
parents 950e6672 1e7c6ea2
Test-dynamicLibrary.C
EXE = $(FOAM_USER_APPBIN)/Test-dynamicLibrary
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
Test-dynamicLibrary
Description
Test loading/unloading of libraries
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "profiling.H"
#include "DynamicList.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote("Low-level test of library load/unload");
profiling::disable(); // No profiling output
argList::noBanner();
argList::noParallel();
argList::removeOption("case");
argList::removeOption("noFunctionObjects");
argList::addBoolOption("no-close", "Skip dlclose");
argList::addBoolOption("quiet", "Disable verbosity");
argList::addArgument("lib...");
argList::noMandatoryArgs(); // Arguments are optional
argList args(argc, argv, false, true);
const bool noClose = args.found("no-close");
const bool verbose = !args.found("quiet");
//- Pointers to the loaded libraries
DynamicList<void*> libPtrs_;
//- Names of loaded libraries, or of libraries to be loaded
DynamicList<fileName> libNames_;
label nbad = 0;
wordHashSet loaded;
for (int argi = 1; argi < args.size(); ++argi)
{
const fileName libName(fileName::validate(args[argi]));
if (libName.empty())
{
continue;
}
void* ptr = Foam::dlOpen(libName, false);
if (!ptr)
{
++nbad;
}
else
{
libPtrs_.append(ptr);
libNames_.append(libName);
if (verbose)
{
const word addr(Foam::name(ptr));
if (loaded.insert(addr))
{
InfoErr << "Can load " << libName << nl;
}
else
{
InfoErr << "Already loaded " << libName << nl;
}
}
}
}
if (!noClose)
{
forAllReverse(libPtrs_, i)
{
void* ptr = libPtrs_[i];
if (ptr == nullptr)
{
libNames_[i].clear();
continue;
}
const bool ok = Foam::dlClose(ptr);
if (verbose)
{
if (ok)
{
InfoErr << "Closed ";
}
else
{
InfoErr << "Failed closing ";
}
InfoErr
<< libNames_[i]
<< " with handle " << Foam::name(ptr) << nl;
}
}
}
return 0;
}
// ************************************************************************* //
......@@ -471,7 +471,7 @@ Foam::Time::Time
if (enableLibs)
{
libs_.open(controlDict_, "libs");
libs_.open("libs", controlDict_);
}
// Explicitly set read flags on objectRegistry so anything constructed
......@@ -553,12 +553,9 @@ Foam::Time::Time
// Libraries
//
// * enable by default unless '-no-libs' option was used
if (!args.found("no-libs"))
if (enableLibs && !args.found("no-libs"))
{
if (enableLibs)
{
libs_.open(controlDict_, "libs");
}
libs_.open("libs", controlDict_);
}
// Explicitly set read flags on objectRegistry so anything constructed
......@@ -634,7 +631,7 @@ Foam::Time::Time
if (enableLibs)
{
libs_.open(controlDict_, "libs");
libs_.open("libs", controlDict_);
}
......@@ -708,7 +705,7 @@ Foam::Time::Time
if (enableLibs)
{
libs_.open(controlDict_, "libs");
libs_.open("libs", controlDict_);
}
setMonitoring(); // for profiling etc
......
......@@ -130,14 +130,14 @@ public:
private:
// Private data
// Private Data
//- Profiling trigger for time-loop (for run, loop)
mutable std::unique_ptr<profilingTrigger> loopProfiling_;
//- Any loaded dynamic libraries. Make sure to construct before
// reading controlDict.
dlLibraryTable libs_;
//- Any loaded dynamic libraries
// Construct before reading controlDict
mutable dlLibraryTable libs_;
//- The controlDict
unwatchedIOdictionary controlDict_;
......@@ -145,7 +145,7 @@ private:
protected:
// Protected data
// Protected Data
label startTimeIndex_;
......@@ -502,14 +502,8 @@ public:
return functionObjects_;
}
//- External access to the loaded libraries
const dlLibraryTable& libs() const
{
return libs_;
}
//- External access to the loaded libraries
dlLibraryTable& libs()
//- Mutable access to the loaded dynamic libraries
dlLibraryTable& libs() const
{
return libs_;
}
......
......@@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2018-2019 OpenCFD Ltd.
Copyright (C) 2018-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
......@@ -73,7 +73,7 @@ Foam::dlLibraryTable& Foam::functionEntries::codeStream::libs
(
dict.topDict()
);
return const_cast<Time&>(d.time()).libs();
return d.time().libs();
}
......@@ -145,18 +145,13 @@ Foam::functionEntries::codeStream::getFunction
if (isA<baseIOdictionary>(topDict))
{
// Cached access to dl libs. Guarantees clean up upon destruction
// of Time.
dlLibraryTable& dlLibs = libs(parentDict);
if (dlLibs.open(libPath, false))
{
lib = dlLibs.findLibrary(libPath);
}
// Cached access to libs, with cleanup upon termination
lib = libs(parentDict).open(libPath, false);
}
else
{
// Uncached opening of libPath. Do not complain if cannot be loaded
lib = dlOpen(libPath, false);
lib = Foam::dlOpen(libPath, false);
}
}
......@@ -299,14 +294,13 @@ Foam::functionEntries::codeStream::getFunction
if (isA<baseIOdictionary>(topDict))
{
// Cached access to dl libs. Guarantees clean up upon destruction
// of Time.
dlLibraryTable& dlLibs = libs(parentDict);
// Cached access to libs, with cleanup upon termination
DebugPout
<< "Opening cached dictionary:" << libPath << endl;
if (!dlLibs.open(libPath, false))
lib = libs(parentDict).open(libPath, false);
if (!lib)
{
FatalIOErrorInFunction(parentDict)
<< "Failed loading library " << libPath << nl
......@@ -314,8 +308,6 @@ Foam::functionEntries::codeStream::getFunction
<< " in system/controlDict?"
<< exit(FatalIOError);
}
lib = dlLibs.findLibrary(libPath);
}
else
{
......@@ -323,7 +315,7 @@ Foam::functionEntries::codeStream::getFunction
DebugPout
<< "Opening uncached dictionary:" << libPath << endl;
lib = dlOpen(libPath, true);
lib = Foam::dlOpen(libPath, true);
}
}
......@@ -346,7 +338,7 @@ Foam::functionEntries::codeStream::getFunction
streamingFunctionType function =
reinterpret_cast<streamingFunctionType>
(
dlSym(lib, dynCode.codeName())
Foam::dlSym(lib, dynCode.codeName())
);
......
......@@ -98,12 +98,7 @@ void* Foam::codedBase::loadLibrary
{
// Avoid compilation by loading an existing library
void* handle =
(
!libPath.empty() && libs().open(libPath, false)
? libs().findLibrary(libPath)
: nullptr
);
void* handle = libs().open(libPath, false);
if (!handle)
{
......@@ -115,25 +110,9 @@ void* Foam::codedBase::loadLibrary
// Manual execution of code after loading.
// This is mandatory for codedBase.
void* rawSymbol = dlSymFind(handle, funcName);
const bool ok = libs().loadHook(handle, funcName, false);
if (rawSymbol)
{
loaderType fun = reinterpret_cast<loaderType>(rawSymbol);
if (fun)
{
(*fun)(true); // force load
}
else
{
FatalIOErrorInFunction(context.dict())
<< "Failed symbol lookup " << funcName.c_str() << nl
<< "from " << libPath << nl
<< exit(FatalIOError);
}
}
else
if (!ok)
{
FatalIOErrorInFunction(context.dict())
<< "Failed symbol lookup " << funcName.c_str() << nl
......@@ -160,12 +139,7 @@ void Foam::codedBase::unloadLibrary
const dynamicCodeContext& context
) const
{
void* handle =
(
!libPath.empty() && libs().open(libPath, false)
? libs().findLibrary(libPath)
: nullptr
);
void* handle = libs().open(libPath, false);
if (!handle)
{
......@@ -175,23 +149,13 @@ void Foam::codedBase::unloadLibrary
// Manual execution of code before unloading.
// This is mandatory for codedBase.
void* rawSymbol = dlSymFind(handle, funcName);
const bool ok = libs().unloadHook(handle, funcName, false);
if (rawSymbol)
if (!ok)
{
loaderType fun = reinterpret_cast<loaderType>(rawSymbol);
if (fun)
{
(*fun)(false); // force unload
}
else
{
FatalIOErrorInFunction(context.dict())
<< "Failed symbol lookup " << funcName.c_str() << nl
<< "from " << libPath << nl
<< exit(FatalIOError);
}
IOWarningInFunction(context.dict())
<< "Failed looking up symbol " << funcName << nl
<< "from " << libPath << nl;
}
if (!libs().close(libPath, false))
......@@ -389,7 +353,7 @@ void Foam::codedBase::updateLibrary
unloadLibrary
(
oldLibPath_,
dynamicCode::libraryBaseName(oldLibPath_),
dlLibraryTable::basename(oldLibPath_),
context
);
......
......@@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2019 OpenCFD Ltd.
Copyright (C) 2016-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
......@@ -49,6 +49,7 @@ SourceFiles
#include "dictionary.H"
#include "dynamicCodeContext.H"
#include "fileName.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
......@@ -75,13 +76,6 @@ class codedBase
mutable fileName oldLibPath_;
// Data Types
//- Global loader/unloader function type
// Called with true on load, false on unload.
typedef void (*loaderType)(bool);
// Private Member Functions
//- Load specified library and execute funcName(true)
......@@ -107,12 +101,6 @@ class codedBase
const dynamicCodeContext& context
) const;
//- No copy construct
codedBase(const codedBase&) = delete;
//- No copy assignment
void operator=(const codedBase&) = delete;
protected:
......@@ -148,7 +136,7 @@ protected:
//- or use the codeDict() to generate one
void updateLibrary(const word& name) const;
//- Get the loaded dynamic libraries
//- Mutable access to the loaded dynamic libraries
virtual dlLibraryTable& libs() const = 0;
//- Adapt the context for the current object
......@@ -168,6 +156,13 @@ protected:
virtual const dictionary& codeDict() const = 0;
//- No copy construct
codedBase(const codedBase&) = delete;
//- No copy assignment
void operator=(const codedBase&) = delete;
public:
//- Runtime type information
......@@ -176,7 +171,7 @@ public:
// Constructors
//- Construct null
//- Default construct
codedBase() = default;
......
......@@ -29,7 +29,15 @@ License
#include "dlLibraryTable.H"
#include "OSspecific.H"
#include "IOstreams.H"
#include "int.H"
// Could be constexpr in the header if required
#ifdef __APPLE__
#define EXT_SO "dylib"
#elif defined _WIN32
#define EXT_SO "dll"
#else
#define EXT_SO "so"
#endif
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
......@@ -38,6 +46,115 @@ namespace Foam
defineTypeNameAndDebug(dlLibraryTable, 0);
}
std::unique_ptr<Foam::dlLibraryTable> Foam::dlLibraryTable::global_(nullptr);
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
Foam::word Foam::dlLibraryTable::basename(const fileName& libPath)
{
word libName(libPath.nameLessExt());
libName.removeStart("lib"); // Remove leading 'lib' from name
return libName;
}
Foam::word Foam::dlLibraryTable::fullname(word libName)
{
if (libName.empty())
{
return libName;
}
// Add leading 'lib' and trailing '.so'
return "lib" + libName.ext(EXT_SO);
}
Foam::dlLibraryTable& Foam::dlLibraryTable::libs()
{
if (!global_)
{
global_.reset(new dlLibraryTable{});
}
return *global_;
}
bool Foam::dlLibraryTable::functionHook
(
const bool load,
void* handle,
const std::string& funcName,
const bool verbose,
const std::string& context
)
{
if (!handle || funcName.empty())
{
return false;
}
bool ok = false;
void* symbol = Foam::dlSymFind(handle, funcName);
if (symbol)
{
// Execute loader/unloader code
try
{
loaderType fun = reinterpret_cast<loaderType>(symbol);
if (fun)
{
(*fun)(load);
ok = true;
}
}
catch (...)
{}
}
if (verbose && !ok)
{
auto& err = WarningInFunction
<< "Failed symbol lookup " << funcName.c_str() << nl;
if (!context.empty())
{
err << "from " << context.c_str() << nl;
}
}
return ok;
}
bool Foam::dlLibraryTable::loadHook
(
void* handle,
const std::string& funcName,
const bool verbose,
const std::string& context
)
{