codedBase.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2016-2021 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "codedBase.H"
30 #include "SHA1Digest.H"
31 #include "dynamicCode.H"
32 #include "dynamicCodeContext.H"
33 #include "dlLibraryTable.H"
34 #include "objectRegistry.H"
35 #include "IOdictionary.H"
36 #include "Pstream.H"
37 #include "PstreamReduceOps.H"
38 #include "OSspecific.H"
39 #include "Ostream.H"
40 #include "Time.H"
41 #include "regIOobject.H"
42 
43 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
44 
45 namespace Foam
46 {
47  defineTypeNameAndDebug(codedBase, 0);
48 }
49 
50 
51 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
52 
53 namespace Foam
54 {
55 
56 static inline void writeEntryIfPresent
57 (
58  Ostream& os,
59  const dictionary& dict,
60  const word& key
61 )
62 {
63  const entry* eptr = dict.findEntry(key, keyType::LITERAL);
64  if (!eptr)
65  {
66  // Nothing to do
67  }
68  else if (eptr->isDict())
69  {
70  eptr->dict().writeEntry(os);
71  }
72  else
73  {
74  const tokenList& toks = eptr->stream();
75 
76  if (!toks.empty()) // Could also check that it is a string-type
77  {
78  os.writeEntry(key, toks[0]);
79  }
80  }
81 }
82 
83 } // End namespace Foam
84 
85 
86 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
87 
89 {
90  writeEntryIfPresent(os, dict, "codeContext");
91  writeEntryIfPresent(os, dict, "codeInclude");
92  writeEntryIfPresent(os, dict, "localCode");
93  writeEntryIfPresent(os, dict, "code");
94  writeEntryIfPresent(os, dict, "codeOptions");
95  writeEntryIfPresent(os, dict, "codeLibs");
96 }
97 
98 
99 const Foam::dictionary&
101 (
102  const objectRegistry& obr,
103  const word& dictName
104 )
105 {
107 
108  if (!dictptr)
109  {
110  dictptr = new IOdictionary
111  (
112  IOobject
113  (
114  dictName,
115  obr.time().system(),
116  obr,
119  )
120  );
121 
122  obr.store(dictptr);
123  }
124 
125  return *dictptr;
126 }
127 
128 
129 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
130 
131 void* Foam::codedBase::loadLibrary
132 (
133  const fileName& libPath,
134  const std::string& funcName,
135  const dynamicCodeContext& context
136 ) const
137 {
138  // Avoid compilation by loading an existing library
139 
140  void* handle = libs().open(libPath, false);
141 
142  if (!handle)
143  {
144  return handle;
145  }
146 
147  // Verify the loaded version and unload if needed
148 
149  // Manual execution of code after loading.
150  // This is mandatory for codedBase.
151 
152  const bool ok = libs().loadHook(handle, funcName, false);
153 
154  if (!ok)
155  {
156  FatalIOErrorInFunction(context.dict())
157  << "Failed symbol lookup " << funcName.c_str() << nl
158  << "from " << libPath << nl
159  << exit(FatalIOError);
160 
161  handle = nullptr;
162  if (!libs().close(libPath, false))
163  {
164  FatalIOErrorInFunction(context.dict())
165  << "Failed unloading library " << libPath << nl
166  << exit(FatalIOError);
167  }
168  }
169 
170  return handle;
171 }
172 
173 
174 void Foam::codedBase::unloadLibrary
175 (
176  const fileName& libPath,
177  const std::string& funcName,
178  const dynamicCodeContext& context
179 ) const
180 {
181  void* handle = libs().open(libPath, false);
182 
183  if (!handle)
184  {
185  return;
186  }
187 
188  // Manual execution of code before unloading.
189  // This is mandatory for codedBase.
190 
191  const bool ok = libs().unloadHook(handle, funcName, false);
192 
193  if (!ok)
194  {
195  IOWarningInFunction(context.dict())
196  << "Failed looking up symbol " << funcName << nl
197  << "from " << libPath << nl;
198  }
199 
200  if (!libs().close(libPath, false))
201  {
202  FatalIOErrorInFunction(context.dict())
203  << "Failed unloading library " << libPath << nl
204  << exit(FatalIOError);
205  }
206 }
207 
208 
209 void Foam::codedBase::createLibrary
210 (
211  dynamicCode& dynCode,
212  const dynamicCodeContext& context
213 ) const
214 {
215  bool create =
217  || (IOobject::fileModificationSkew <= 0); // not NFS
218 
219  if (create)
220  {
221  // Write files for new library
222  if (!dynCode.upToDate(context))
223  {
224  // filter with this context
225  dynCode.reset(context);
226 
227  this->prepare(dynCode, context);
228 
229  if (!dynCode.copyOrCreateFiles(true))
230  {
231  FatalIOErrorInFunction(context.dict())
232  << "Failed writing files for" << nl
233  << dynCode.libRelPath() << nl
234  << exit(FatalIOError);
235  }
236  }
237 
238  if (!dynCode.wmakeLibso())
239  {
240  FatalIOErrorInFunction(context.dict())
241  << "Failed wmake " << dynCode.libRelPath() << nl
242  << exit(FatalIOError);
243  }
244  }
245 
246 
247  // all processes must wait for compile to finish
249  {
250  //- Since the library has only been compiled on the master the
251  // other nodes need to pick this library up through NFS
252  // We do this by just polling a few times using the
253  // fileModificationSkew.
254 
255  const fileName libPath = dynCode.libPath();
256 
257  off_t mySize = Foam::fileSize(libPath);
258  off_t masterSize = mySize;
259  Pstream::scatter(masterSize);
260 
261  for
262  (
263  label iter = 0;
265  ++iter
266  )
267  {
268  DebugPout
269  << "on processor " << Pstream::myProcNo()
270  << " have masterSize:" << masterSize
271  << " and localSize:" << mySize
272  << endl;
273 
274  if (mySize == masterSize)
275  {
276  break;
277  }
278  else if (mySize > masterSize)
279  {
280  FatalIOErrorInFunction(context.dict())
281  << "Excessive size when reading (NFS mounted) library "
282  << nl << libPath << nl
283  << "on processor " << Pstream::myProcNo()
284  << " detected size " << mySize
285  << " whereas master size is " << masterSize
286  << " bytes." << nl
287  << "If your case is NFS mounted increase"
288  << " fileModificationSkew or maxFileModificationPolls;"
289  << nl << "If your case is not NFS mounted"
290  << " (so distributed) set fileModificationSkew"
291  << " to 0"
292  << exit(FatalIOError);
293  }
294  else
295  {
296  DebugPout
297  << "Local file " << libPath
298  << " not of same size (" << mySize
299  << ") as master ("
300  << masterSize << "). Waiting for "
302  << " seconds." << endl;
303 
305 
306  // Recheck local size
307  mySize = Foam::fileSize(libPath);
308  }
309  }
310 
311 
312  // Finished doing iterations. Do final check
313  if (mySize != masterSize)
314  {
315  FatalIOErrorInFunction(context.dict())
316  << "Cannot read (NFS mounted) library " << nl
317  << libPath << nl
318  << "on processor " << Pstream::myProcNo()
319  << " detected size " << mySize
320  << " whereas master size is " << masterSize
321  << " bytes." << nl
322  << "If your case is NFS mounted increase"
323  << " fileModificationSkew or maxFileModificationPolls;"
324  << nl << "If your case is not NFS mounted"
325  << " (so distributed) set fileModificationSkew"
326  << " to 0"
327  << exit(FatalIOError);
328  }
329 
330  DebugPout
331  << "on processor " << Pstream::myProcNo()
332  << " after waiting: have masterSize:" << masterSize
333  << " and localSize:" << mySize << endl;
334  }
335  reduce(create, orOp<bool>());
336 }
337 
338 
339 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
340 
342 {
343  context_.setCodeContext(dict);
344 }
345 
346 
347 void Foam::codedBase::append(const std::string& str)
348 {
349  context_.append(str);
350 }
351 
352 
354 (
355  const word& name,
356  const dynamicCodeContext& context
357 ) const
358 {
360  (
361  "codedBase::updateLibrary()",
362  context.dict()
363  );
364 
365  // codeName: name + _<sha1>
366  // codeDir : name
367  dynamicCode dynCode
368  (
369  name + context.sha1().str(true),
370  name
371  );
372 
373  const fileName libPath = dynCode.libPath();
374 
375 
376  // The correct library was already loaded => we are done
377  if (libs().findLibrary(libPath))
378  {
379  return;
380  }
381 
382  DetailInfo
383  << "Using dynamicCode for " << this->description().c_str()
384  << " at line " << context.dict().startLineNumber()
385  << " in " << context.dict().name() << endl;
386 
387 
388  // Remove instantiation of fvPatchField provided by library
389  this->clearRedirect();
390 
391  // May need to unload old library
392  unloadLibrary
393  (
394  oldLibPath_,
395  dlLibraryTable::basename(oldLibPath_),
396  context
397  );
398 
399  // Try loading an existing library (avoid compilation when possible)
400  if (!loadLibrary(libPath, dynCode.codeName(), context))
401  {
402  createLibrary(dynCode, context);
403 
404  loadLibrary(libPath, dynCode.codeName(), context);
405  }
406 
407  // Retain for future reference
408  oldLibPath_ = libPath;
409 }
410 
411 
413 (
414  const word& name,
415  const dictionary& dict
416 ) const
417 {
418  updateLibrary(name, dynamicCodeContext(dict));
419 }
420 
421 
423 {
424  if (context_.valid())
425  {
426  updateLibrary(name, context_);
427  }
428  else
429  {
430  updateLibrary(name, dynamicCodeContext(this->codeDict()));
431  }
432 }
433 
434 
435 // ************************************************************************* //
Foam::entry
A keyword and a list of tokens is an 'entry'.
Definition: entry.H:67
regIOobject.H
Foam::IOobject::NO_WRITE
Definition: IOobject.H:195
Foam::objectRegistry::getObjectPtr
Type * getObjectPtr(const word &name, const bool recursive=false) const
Definition: objectRegistryTemplates.C:423
Foam::IOdictionary
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:54
Foam::codedBase::append
void append(const std::string &str)
Add content to SHA1 hashing.
Definition: codedBase.C:347
Foam::BitSetOps::create
bitSet create(const label n, const labelHashSet &locations, const bool on=true)
Create a bitSet with length n with the specified on locations.
Definition: BitOps.C:123
Foam::IOobject
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:169
OSspecific.H
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:65
Foam::fileName
A class for handling file names.
Definition: fileName.H:73
Foam::entry::stream
virtual ITstream & stream() const =0
Return token stream, if entry is a primitive entry.
Foam::codedBase::writeCodeDict
static void writeCodeDict(Ostream &os, const dictionary &dict)
Write code-dictionary contents.
Definition: codedBase.C:88
Foam::codedBase::codeDict
virtual const dictionary & codeDict() const =0
Foam::SHA1::str
std::string str(const bool prefixed=false) const
The digest (40-byte) text representation, optionally with '_' prefix.
Definition: SHA1I.H:122
Foam::dynamicCode
Tools for handling dynamic code compilation.
Definition: dynamicCode.H:59
Foam::dlLibraryTable::basename
static word basename(const fileName &libPath)
Library basename without leading 'lib' or trailing '.so'.
Definition: dlLibraryTable.C:60
Foam::dynamicCodeContext
Encapsulation of dynamic code dictionaries.
Definition: dynamicCodeContext.H:53
dictName
const word dictName("faMeshDefinition")
Foam::entry::isDict
virtual bool isDict() const noexcept
Return true if this entry is a dictionary.
Definition: entry.H:233
Foam::writeEntryIfPresent
static void writeEntryIfPresent(Ostream &os, const dictionary &dict, const word &key)
Definition: codedBase.C:57
Foam::glTF::key
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:108
objectRegistry.H
Foam::UPstream::master
static bool master(const label communicator=worldComm)
Am I the master process.
Definition: UPstream.H:457
Foam::Pstream::scatter
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
Definition: gatherScatter.C:150
Foam::FatalIOError
IOerror FatalIOError
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::dynamicCodeContext::dict
const dictionary & dict() const noexcept
Return the parent dictionary context.
Definition: dynamicCodeContext.H:128
dynamicCodeContext.H
Foam::dictionary::name
const fileName & name() const noexcept
The dictionary name.
Definition: dictionaryI.H:48
Foam::regIOobject::store
bool store()
Definition: regIOobjectI.H:37
Foam::codedBase::setCodeContext
void setCodeContext(const dictionary &dict)
Set code context from a dictionary.
Definition: codedBase.C:341
Foam::dynamicCodeContext::sha1
const SHA1 & sha1() const noexcept
The SHA1 calculated from options, libs, include, code, etc.
Definition: dynamicCodeContext.H:164
Foam::objectRegistry
Registry of regIOobjects.
Definition: objectRegistry.H:60
SHA1Digest.H
Foam::reduce
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
Definition: PstreamReduceOps.H:51
Foam::fileSize
off_t fileSize(const fileName &name, const bool followLink=true)
Return size of file or -1 on failure (normally follows symbolic links).
Definition: MSwindows.C:676
Foam::dictionary::writeEntry
void writeEntry(Ostream &os) const
Write sub-dictionary with its dictName as its header.
Definition: dictionaryIO.C:164
Foam::dynamicCode::checkSecurity
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:65
Foam::dictionary::startLineNumber
label startLineNumber() const
Return line number of first token in dictionary.
Definition: dictionary.C:205
dynamicCode.H
DetailInfo
#define DetailInfo
Definition: evalEntry.C:37
Foam::IOobject::fileModificationSkew
static float fileModificationSkew
Time skew (seconds) for file modification checks.
Definition: IOobject.H:306
dict
dictionary dict
Definition: searchingEngine.H:14
Foam::dictionary
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:123
os
OBJstream os(runTime.globalPath()/outputName)
Pstream.H
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::entry::dict
virtual const dictionary & dict() const =0
Return dictionary, if entry is a dictionary.
PstreamReduceOps.H
Inter-processor communication reduction functions.
DebugPout
#define DebugPout
Report an information message using Foam::Pout.
Definition: messageStream.H:393
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Ostream.H
IOdictionary.H
Time.H
Foam::TimePaths::system
const word & system() const
Return system name.
Definition: TimePathsI.H:102
Foam::UPstream::myProcNo
static int myProcNo(const label communicator=worldComm)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:463
Foam::nl
constexpr char nl
Definition: Ostream.H:404
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:63
dlLibraryTable.H
Foam::dictionary::findEntry
entry * findEntry(const word &keyword, enum keyType::option matchOpt=keyType::REGEX)
Find for an entry (non-const access) with the given keyword.
Definition: dictionaryI.H:97
Foam::IOobject::MUST_READ_IF_MODIFIED
Definition: IOobject.H:186
Foam::Ostream::writeEntry
Ostream & writeEntry(const keyType &key, const T &value)
Write a keyword/value entry.
Definition: Ostream.H:236
Foam::name
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
codedBase.H
FatalIOErrorInFunction
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:473
Foam::keyType::LITERAL
String literal.
Definition: keyType.H:81
Foam::Ostream
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:56
IOWarningInFunction
#define IOWarningInFunction(ios)
Report an IO warning using Foam::Warning.
Definition: messageStream.H:340
Foam::codedBase::updateLibrary
void updateLibrary(const word &name, const dynamicCodeContext &context) const
Update library as required, using the given context.
Definition: codedBase.C:354
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
Foam::sleep
unsigned int sleep(const unsigned int sec)
Sleep for the specified number of seconds.
Definition: MSwindows.C:1106
Foam::objectRegistry::time
const Time & time() const noexcept
Return time registry.
Definition: objectRegistry.H:178
Foam::IOobject::maxFileModificationPolls
static int maxFileModificationPolls
Max number of times to poll for file modification changes.
Definition: IOobject.H:309