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-2022 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 
88 void Foam::codedBase::writeCodeDict(Ostream& os, const dictionary& dict)
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::broadcast(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 
336  Pstream::reduceOr(create); // MPI barrier
337 }
339 
340 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
341 
343 {
344  context_.setCodeContext(dict);
345 }
346 
347 
348 void Foam::codedBase::append(const std::string& str)
349 {
350  context_.append(str);
351 }
352 
353 
355 (
356  const word& name,
357  const dynamicCodeContext& context
358 ) const
359 {
361  (
362  "codedBase::updateLibrary()",
363  context.dict()
364  );
365 
366  // codeName: name + _<sha1>
367  // codeDir : name
368  dynamicCode dynCode
369  (
370  name + context.sha1().str(true),
371  name
372  );
373 
374  const fileName libPath = dynCode.libPath();
375 
376 
377  // The correct library was already loaded => we are done
378  if (libs().findLibrary(libPath))
379  {
380  return;
381  }
382 
383  DetailInfo
384  << "Using dynamicCode for " << this->description().c_str()
385  << " at line " << context.dict().startLineNumber()
386  << " in " << context.dict().name() << endl;
387 
388 
389  // Remove instantiation of fvPatchField provided by library
390  this->clearRedirect();
391 
392  // May need to unload old library
393  unloadLibrary
394  (
395  oldLibPath_,
396  dlLibraryTable::basename(oldLibPath_),
397  context
398  );
399 
400  // Try loading an existing library (avoid compilation when possible)
401  if (!loadLibrary(libPath, dynCode.codeName(), context))
402  {
403  createLibrary(dynCode, context);
404 
405  loadLibrary(libPath, dynCode.codeName(), context);
406  }
407 
408  // Retain for future reference
409  oldLibPath_ = libPath;
410 }
411 
412 
414 (
415  const word& name,
416  const dictionary& dict
417 ) const
418 {
419  updateLibrary(name, dynamicCodeContext(dict));
420 }
421 
422 
423 void Foam::codedBase::updateLibrary(const word& name) const
424 {
425  if (context_.valid())
426  {
427  updateLibrary(name, context_);
428  }
429  else
430  {
431  updateLibrary(name, dynamicCodeContext(this->codeDict()));
432  }
433 }
434 
435 
436 // ************************************************************************* //
void writeEntry(Ostream &os) const
Write sub-dictionary with its dictName as its header.
Definition: dictionaryIO.C:157
dictionary dict
A class for handling file names.
Definition: fileName.H:71
Inter-processor communication reduction functions.
std::string str(const bool prefixed=false) const
The digest (40-byte) text representation, optionally with &#39;_&#39; prefix.
Definition: SHA1I.H:114
off_t fileSize(const fileName &name, const bool followLink=true)
Return size of file or -1 on failure (normally follows symbolic links).
Definition: POSIX.C:858
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
static float fileModificationSkew
Time skew (seconds) for file modification checks.
Definition: IOobject.H:318
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:120
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:56
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:420
const word dictName("faMeshDefinition")
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
virtual const dictionary & dict() const =0
Return dictionary, if entry is a dictionary.
bool store()
Register object with its registry and transfer ownership to the registry.
Definition: regIOobjectI.H:36
static void reduceOr(bool &value, const label communicator=worldComm)
Logical (or) reduction (cf. MPI AllReduce)
Ostream & writeEntry(const keyType &key, const T &value)
Write a keyword/value entry.
Definition: Ostream.H:312
Ignore writing from objectRegistry::writeObject()
static int myProcNo(const label communicator=worldComm)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:688
void append(const std::string &str)
Add content to SHA1 hashing.
Definition: codedBase.C:344
const fileName & name() const noexcept
The dictionary name.
Definition: dictionaryI.H:41
static void broadcast(Type &value, const label comm=UPstream::worldComm)
Broadcast content (contiguous or non-contiguous) to all processes in communicator.
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:50
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
const dictionary & dict() const noexcept
Return the parent dictionary context.
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:52
static word basename(const fileName &libPath)
Library basename without leading &#39;lib&#39; or trailing &#39;.so&#39;.
A class for handling words, derived from Foam::string.
Definition: word.H:63
Type * getObjectPtr(const word &name, const bool recursive=false) const
Return non-const pointer to the object of the given Type, using a const-cast to have it behave like a...
const Time & time() const noexcept
Return time registry.
Reading required, file watched for runTime modification.
const word & system() const noexcept
Return system name.
Definition: TimePathsI.H:95
static int maxFileModificationPolls
Max number of times to poll for file modification changes.
Definition: IOobject.H:323
String literal.
Definition: keyType.H:82
#define DetailInfo
Definition: evalEntry.C:30
unsigned int sleep(const unsigned int sec)
Sleep for the specified number of seconds.
Definition: POSIX.C:1500
void setCodeContext(const dictionary &dict)
Set code context from a dictionary.
Definition: codedBase.C:338
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:55
OBJstream os(runTime.globalPath()/outputName)
defineTypeNameAndDebug(combustionModel, 0)
static void writeEntryIfPresent(Ostream &os, const dictionary &dict, const word &key)
Definition: codedBase.C:50
Tools for handling dynamic code compilation.
Definition: dynamicCode.H:55
Encapsulation of dynamic code dictionaries.
void updateLibrary(const word &name, const dynamicCodeContext &context) const
Update library as required, using the given context.
Definition: codedBase.C:351
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:607
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:103
static bool master(const label communicator=worldComm)
Am I the master rank.
Definition: UPstream.H:672
const entry * findEntry(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find an entry (const access) with the given keyword.
Definition: dictionaryI.H:80
#define DebugPout
Report an information message using Foam::Pout.
virtual bool isDict() const noexcept
Return true if this entry is a dictionary.
Definition: entry.H:287
#define IOWarningInFunction(ios)
Report an IO warning using Foam::Warning.
Registry of regIOobjects.
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:204
virtual ITstream & stream() const =0
Return token stream, if entry is a primitive entry.
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:58
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:166
static void writeCodeDict(Ostream &os, const dictionary &dict)
Write code-dictionary contents.
Definition: codedBase.C:81
label startLineNumber() const
Return line number of first token in dictionary.
Definition: dictionary.C:198
virtual const dictionary & codeDict() const =0
Namespace for OpenFOAM.
A keyword and a list of tokens is an &#39;entry&#39;.
Definition: entry.H:63
const SHA1 & sha1() const noexcept
The SHA1 calculated from options, libs, include, code, etc.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...