/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2016-2019 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 . \*---------------------------------------------------------------------------*/ #include "codedBase.H" #include "SHA1Digest.H" #include "dynamicCode.H" #include "dynamicCodeContext.H" #include "dlLibraryTable.H" #include "Pstream.H" #include "PstreamReduceOps.H" #include "OSspecific.H" #include "Ostream.H" #include "regIOobject.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(codedBase, 0); } // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // namespace Foam { //! \cond fileScope static inline void writeEntryIfPresent ( Ostream& os, const dictionary& dict, const word& key ) { const entry* eptr = dict.findEntry(key, keyType::LITERAL); if (eptr) { os.writeKeyword(key) << token::HASH << token::BEGIN_BLOCK; os.writeQuoted(string(eptr->stream()), false) << token::HASH << token::END_BLOCK << token::END_STATEMENT << nl; } } //! \endcond } // End namespace Foam // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // void Foam::codedBase::writeCodeDict(Ostream& os, const dictionary& dict) { writeEntryIfPresent(os, dict, "codeInclude"); writeEntryIfPresent(os, dict, "localCode"); writeEntryIfPresent(os, dict, "code"); writeEntryIfPresent(os, dict, "codeOptions"); writeEntryIfPresent(os, dict, "codeLibs"); } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void* Foam::codedBase::loadLibrary ( const fileName& libPath, const std::string& funcName, const dynamicCodeContext& context ) const { // Avoid compilation by loading an existing library void* handle = ( !libPath.empty() && libs().open(libPath, false) ? libs().findLibrary(libPath) : nullptr ); if (!handle) { return handle; } // Verify the loaded version and unload if needed // Manual execution of code after loading. // This is mandatory for codedBase. void* rawSymbol = dlSymFind(handle, funcName); if (rawSymbol) { loaderType fun = reinterpret_cast(rawSymbol); if (fun) { (*fun)(true); // force load } else { FatalIOErrorInFunction(context.dict()) << "Failed symbol lookup " << funcName.c_str() << nl << "from " << libPath << nl << exit(FatalIOError); } } else { FatalIOErrorInFunction(context.dict()) << "Failed symbol lookup " << funcName.c_str() << nl << "from " << libPath << nl << exit(FatalIOError); handle = nullptr; if (!libs().close(libPath, false)) { FatalIOErrorInFunction(context.dict()) << "Failed unloading library " << libPath << nl << exit(FatalIOError); } } return handle; } void Foam::codedBase::unloadLibrary ( const fileName& libPath, const std::string& funcName, const dynamicCodeContext& context ) const { void* handle = ( !libPath.empty() && libs().open(libPath, false) ? libs().findLibrary(libPath) : nullptr ); if (!handle) { return; } // Manual execution of code before unloading. // This is mandatory for codedBase. void* rawSymbol = dlSymFind(handle, funcName); if (rawSymbol) { loaderType fun = reinterpret_cast(rawSymbol); if (fun) { (*fun)(false); // force unload } else { FatalIOErrorInFunction(context.dict()) << "Failed symbol lookup " << funcName.c_str() << nl << "from " << libPath << nl << exit(FatalIOError); } } if (!libs().close(libPath, false)) { FatalIOErrorInFunction(context.dict()) << "Failed unloading library " << libPath << nl << exit(FatalIOError); } } void Foam::codedBase::createLibrary ( dynamicCode& dynCode, const dynamicCodeContext& context ) const { bool create = Pstream::master() || (regIOobject::fileModificationSkew <= 0); // not NFS if (create) { // Write files for new library if (!dynCode.upToDate(context)) { // filter with this context dynCode.reset(context); this->prepare(dynCode, context); if (!dynCode.copyOrCreateFiles(true)) { FatalIOErrorInFunction(context.dict()) << "Failed writing files for" << nl << dynCode.libRelPath() << nl << exit(FatalIOError); } } if (!dynCode.wmakeLibso()) { FatalIOErrorInFunction(context.dict()) << "Failed wmake " << dynCode.libRelPath() << nl << exit(FatalIOError); } } // all processes must wait for compile to finish if (regIOobject::fileModificationSkew > 0) { //- Since the library has only been compiled on the master the // other nodes need to pick this library up through NFS // We do this by just polling a few times using the // fileModificationSkew. const fileName libPath = dynCode.libPath(); off_t mySize = Foam::fileSize(libPath); off_t masterSize = mySize; Pstream::scatter(masterSize); for ( label iter = 0; iter < regIOobject::maxFileModificationPolls; iter++ ) { DebugPout << "on processor " << Pstream::myProcNo() << " have masterSize:" << masterSize << " and localSize:" << mySize << endl; if (mySize == masterSize) { break; } else if (mySize > masterSize) { FatalIOErrorInFunction(context.dict()) << "Excessive size when reading (NFS mounted) library " << nl << libPath << nl << "on processor " << Pstream::myProcNo() << " detected size " << mySize << " whereas master size is " << masterSize << " bytes." << nl << "If your case is NFS mounted increase" << " fileModificationSkew or maxFileModificationPolls;" << nl << "If your case is not NFS mounted" << " (so distributed) set fileModificationSkew" << " to 0" << exit(FatalIOError); } else { DebugPout << "Local file " << libPath << " not of same size (" << mySize << ") as master (" << masterSize << "). Waiting for " << regIOobject::fileModificationSkew << " seconds." << endl; Foam::sleep(regIOobject::fileModificationSkew); // Recheck local size mySize = Foam::fileSize(libPath); } } // Finished doing iterations. Do final check if (mySize != masterSize) { FatalIOErrorInFunction(context.dict()) << "Cannot read (NFS mounted) library " << nl << libPath << nl << "on processor " << Pstream::myProcNo() << " detected size " << mySize << " whereas master size is " << masterSize << " bytes." << nl << "If your case is NFS mounted increase" << " fileModificationSkew or maxFileModificationPolls;" << nl << "If your case is not NFS mounted" << " (so distributed) set fileModificationSkew" << " to 0" << exit(FatalIOError); } DebugPout << "on processor " << Pstream::myProcNo() << " after waiting: have masterSize:" << masterSize << " and localSize:" << mySize << endl; } reduce(create, orOp()); } // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // void Foam::codedBase::setCodeContext(const dictionary& dict) { context_.setCodeContext(dict); } void Foam::codedBase::append(const std::string& str) { context_.append(str); } void Foam::codedBase::updateLibrary ( const word& name, const dynamicCodeContext& context ) const { dynamicCode::checkSecurity ( "codedBase::updateLibrary()", context.dict() ); // codeName: name + _ // codeDir : name dynamicCode dynCode ( name + context.sha1().str(true), name ); const fileName libPath = dynCode.libPath(); // The correct library was already loaded => we are done if (libs().findLibrary(libPath)) { return; } DetailInfo << "Using dynamicCode for " << this->description().c_str() << " at line " << context.dict().startLineNumber() << " in " << context.dict().name() << endl; // Remove instantiation of fvPatchField provided by library this->clearRedirect(); // May need to unload old library unloadLibrary ( oldLibPath_, dynamicCode::libraryBaseName(oldLibPath_), context ); // Try loading an existing library (avoid compilation when possible) if (!loadLibrary(libPath, dynCode.codeName(), context)) { createLibrary(dynCode, context); loadLibrary(libPath, dynCode.codeName(), context); } // Retain for future reference oldLibPath_ = libPath; } void Foam::codedBase::updateLibrary ( const word& name, const dictionary& dict ) const { updateLibrary(name, dynamicCodeContext(dict)); } void Foam::codedBase::updateLibrary(const word& name) const { if (context_.valid()) { updateLibrary(name, context_); } else { updateLibrary(name, dynamicCodeContext(this->codeDict())); } } // ************************************************************************* //