codedBase.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2024 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "codedBase.H"
27 #include "SHA1Digest.H"
28 #include "dynamicCode.H"
29 #include "dynamicCodeContext.H"
30 #include "dlLibraryTable.H"
31 #include "regIOobject.H"
32 #include "PstreamReduceOps.H"
33 #include "OSspecific.H"
34 
35 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
36 
37 namespace Foam
38 {
40 }
41 
42 
43 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
44 
46 {
47  word result(name);
48 
49  if (!isalpha(result[0]))
50  {
52  << "Cannot construct code name from function name \"" << name
53  << "\" as the first character is not alphabetic"
54  << exit(FatalError);
55  }
56 
57  for (word::size_type i = 1; i < name.size(); ++ i)
58  {
59  const bool valid = isalnum(result[i]) || result[i] == '_';
60 
61  if (!valid)
62  {
63  result[i] = '_';
64  }
65  }
66 
67  return result;
68 }
69 
70 
71 void* Foam::codedBase::loadLibrary
72 (
73  const fileName& libPath,
74  const string& globalFuncName,
75  const dictionary& contextDict
76 ) const
77 {
78  void* lib = 0;
79 
80  // avoid compilation by loading an existing library
81  if (!libPath.empty())
82  {
83  if (libs.open(libPath, false))
84  {
85  lib = libs.findLibrary(libPath);
86 
87  // verify the loaded version and unload if needed
88  if (lib)
89  {
90  // provision for manual execution of code after loading
91  if (dlSymFound(lib, globalFuncName))
92  {
93  loaderFunctionType function =
94  reinterpret_cast<loaderFunctionType>
95  (
96  dlSym(lib, globalFuncName)
97  );
98 
99  if (function)
100  {
101  (*function)(true); // force load
102  }
103  else
104  {
106  (
107  contextDict
108  ) << "Failed looking up symbol " << globalFuncName
109  << nl << "from " << libPath << exit(FatalIOError);
110  }
111  }
112  else
113  {
115  (
116  contextDict
117  ) << "Failed looking up symbol " << globalFuncName << nl
118  << "from " << libPath << exit(FatalIOError);
119 
120  lib = 0;
121  if (!libs.close(libPath, false))
122  {
124  (
125  contextDict
126  ) << "Failed unloading library "
127  << libPath
128  << exit(FatalIOError);
129  }
130  }
131  }
132  }
133  }
134 
135  return lib;
136 }
137 
138 
139 void Foam::codedBase::unloadLibrary
140 (
141  const fileName& libPath,
142  const string& globalFuncName,
143  const dictionary& contextDict
144 ) const
145 {
146  void* lib = 0;
147 
148  if (libPath.empty())
149  {
150  return;
151  }
152 
153  lib = libs.findLibrary(libPath);
154 
155  if (!lib)
156  {
157  return;
158  }
159 
160  // provision for manual execution of code before unloading
161  if (dlSymFound(lib, globalFuncName))
162  {
163  loaderFunctionType function =
164  reinterpret_cast<loaderFunctionType>
165  (
166  dlSym(lib, globalFuncName)
167  );
168 
169  if (function)
170  {
171  (*function)(false); // force unload
172  }
173  else
174  {
176  (
177  contextDict
178  ) << "Failed looking up symbol " << globalFuncName << nl
179  << "from " << libPath << exit(FatalIOError);
180  }
181  }
182 
183  if (!libs.close(libPath, false))
184  {
186  (
187  contextDict
188  ) << "Failed unloading library " << libPath
189  << exit(FatalIOError);
190  }
191 }
192 
193 
194 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
195 
197 {
198  return this->type() + " " + codeName();
199 }
200 
201 
202 void Foam::codedBase::createLibrary
203 (
204  dynamicCode& dynCode,
205  const dynamicCodeContext& context
206 ) const
207 {
208  bool create =
210  || (regIOobject::fileModificationSkew <= 0); // not NFS
211 
212  if (create)
213  {
214  // Write files for new library
215  if (!dynCode.upToDate(context))
216  {
217  // filter with this context
218  dynCode.reset(context);
219 
220  this->prepare(dynCode, context);
221 
222  if (!dynCode.copyOrCreateFiles(true))
223  {
225  (
226  context.dict()
227  ) << "Failed writing files for" << nl
228  << dynCode.libRelPath() << nl
229  << exit(FatalIOError);
230  }
231  }
232 
233  if (!dynCode.wmakeLibso())
234  {
236  (
237  context.dict()
238  ) << "Failed wmake " << dynCode.libRelPath() << nl
239  << exit(FatalIOError);
240  }
241  }
242 
243 
244  // all processes must wait for compile to finish
246  {
247  //- Since the library has only been compiled on the master the
248  // other nodes need to pick this library up through NFS
249  // We do this by just polling a few times using the
250  // fileModificationSkew.
251 
252  const fileName libPath = dynCode.libPath();
253 
254  off_t mySize = Foam::fileSize(libPath);
255  off_t masterSize = mySize;
256  Pstream::scatter(masterSize);
257 
258  if (debug)
259  {
260  Pout<< endl<< "on processor " << Pstream::myProcNo()
261  << " have masterSize:" << masterSize
262  << " and localSize:" << mySize
263  << endl;
264  }
265 
266 
267  if (mySize < masterSize)
268  {
269  if (debug)
270  {
271  Pout<< "Local file " << libPath
272  << " not of same size (" << mySize
273  << ") as master ("
274  << masterSize << "). Waiting for "
276  << " seconds." << endl;
277  }
279 
280  // Recheck local size
281  mySize = Foam::fileSize(libPath);
282 
283  if (mySize < masterSize)
284  {
286  (
287  context.dict()
288  ) << "Cannot read (NFS mounted) library " << nl
289  << libPath << nl
290  << "on processor " << Pstream::myProcNo()
291  << " detected size " << mySize
292  << " whereas master size is " << masterSize
293  << " bytes." << nl
294  << "If your case is not NFS mounted"
295  << " (so distributed) set fileModificationSkew"
296  << " to 0"
297  << exit(FatalIOError);
298  }
299  }
300 
301  if (debug)
302  {
303  Pout<< endl<< "on processor " << Pstream::myProcNo()
304  << " after waiting: have masterSize:" << masterSize
305  << " and localSize:" << mySize
306  << endl;
307  }
308  }
309 }
310 
311 
312 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
313 
315 {
316  const word& name = codeName();
317  const dictionary& dict = codeDict();
318 
320  (
321  "codedBase::updateLibrary()",
322  dict
323  );
324 
325  dynamicCodeContext context(dict, codeKeys(), codeDictVars());
326 
327  // codeName: name + _<sha1>
328  // codeDir : name
329  dynamicCode dynCode
330  (
331  name + context.sha1().str(true),
332  name
333  );
334  const fileName libPath = dynCode.libPath();
335 
336 
337  // the correct library was already loaded => we are done
338  if (libs.findLibrary(libPath))
339  {
340  return;
341  }
342 
343  Info<< "Using dynamicCode for " << this->description().c_str()
344  << " at line " << dict.startLineNumber()
345  << " in " << dict.name() << endl;
346 
347 
348  // remove instantiation of fvPatchField provided by library
349  this->clearRedirect();
350 
351  // may need to unload old library
352  unloadLibrary
353  (
354  oldLibPath_,
355  dynamicCode::libraryBaseName(oldLibPath_),
356  context.dict()
357  );
358 
359  // try loading an existing library (avoid compilation when possible)
360  if (!loadLibrary(libPath, dynCode.codeName(), context.dict()))
361  {
362  createLibrary(dynCode, context);
363 
364  if (!loadLibrary(libPath, dynCode.codeName(), context.dict()))
365  {
366  FatalIOErrorInFunction(context.dict())
367  << "Failed to load " << libPath << exit(FatalIOError);
368  }
369  }
370 
371  // retain for future reference
372  oldLibPath_ = libPath;
373 }
374 
375 
377 {
378  return codeName_;
379 }
380 
381 
383 {
384  return dict_;
385 }
386 
387 
388 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
389 
391 {}
392 
393 
395 :
396  codeName_(codeName(name)),
397  dict_(dict)
398 {}
399 
400 
402 :
403  codeName_(codeName(dict.lookup("name"))),
404  dict_(dict)
405 {}
406 
407 
409 :
410  codeName_(cb.codeName_),
411  dict_(cb.dict_)
412 {}
413 
414 
415 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
416 
418 {}
419 
420 
421 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
422 
424 {
425  return baseTypeName + "Template.C";
426 }
427 
428 
430 {
431  return baseTypeName + "Template.H";
432 }
433 
434 
436 {
437  if (codeName().size())
438  {
439  writeEntry(os, "name", codeName());
440  }
441 
442  wordList codeAndBuildKeys(codeKeys());
443  codeAndBuildKeys.append("codeOptions");
444  codeAndBuildKeys.append("codeLibs");
445 
446  forAll(codeAndBuildKeys, i)
447  {
448  if (codeDict().found(codeAndBuildKeys[i]))
449  {
450  writeKeyword(os, codeAndBuildKeys[i]);
451  os.write(verbatimString(codeDict()[codeAndBuildKeys[i]]))
452  << token::END_STATEMENT << nl;
453  }
454  }
455 }
456 
457 
458 // ************************************************************************* //
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Inter-processor communication reduction functions.
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:73
bool found
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:178
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
virtual Ostream & write(const char)=0
Write character.
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
std::string str(const bool prefixed=false) const
Return (40-byte) text representation, optionally with '_' prefix.
Definition: SHA1Digest.C:112
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
Base class for function objects and boundary conditions using dynamic code.
Definition: codedBase.H:54
word codeTemplateC(const word &baseTypeName) const
Definition: codedBase.C:423
virtual void writeCode(Ostream &os) const
Definition: codedBase.C:435
virtual const word & codeName() const
Name of the dynamically generated CodedType.
Definition: codedBase.C:376
word codeTemplateH(const word &baseTypeName) const
Definition: codedBase.C:429
virtual ~codedBase()
Destructor.
Definition: codedBase.C:417
codedBase()
Construct null.
Definition: codedBase.C:390
virtual const dictionary & codeDict() const
Get the dictionary to initialise the codeContext.
Definition: codedBase.C:382
void updateLibrary() const
Update library as required.
Definition: codedBase.C:314
virtual string description() const
Return a description (type + name) for the output.
Definition: codedBase.C:196
const fileName & name() const
Return the dictionary name.
Definition: dictionary.H:111
A list of keyword definitions, which are a keyword followed by any number of values (e....
Definition: dictionary.H:162
label startLineNumber() const
Return line number of first token in dictionary.
Definition: dictionary.C:449
bool close(const fileName &name, const bool verbose=true)
Close the named library, optionally with warnings if problems occur.
void * findLibrary(const fileName &libName)
Find the handle of the named library.
bool open(const fileName &libName, const bool verbose=true)
Open the named library, optionally with warnings if problems occur.
Encapsulation of dynamic code dictionaries.
const dictionary & dict() const
Return the parent dictionary context.
const SHA1Digest & sha1() const
Return SHA1 digest calculated from include, options, code.
Tools for handling dynamic code compilation.
Definition: dynamicCode.H:57
const word & codeName() const
Return the code-name.
Definition: dynamicCode.H:185
bool copyOrCreateFiles(const bool verbose=false) const
Copy/create files prior to compilation.
Definition: dynamicCode.C:423
fileName libPath() const
Library path for specified code name.
Definition: dynamicCode.H:219
void reset(const dynamicCodeContext &)
Clear files and reset variables to specified context.
Definition: dynamicCode.C:370
fileName libRelPath() const
Library path for specified code name relative to $FOAM_CASE.
Definition: dynamicCode.C:347
static word libraryBaseName(const fileName &libPath)
Return the library basename without leading 'lib' or trailing '.so'.
Definition: dynamicCode.C:93
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:58
bool upToDate(const dynamicCodeContext &context) const
Verify if the copied code is up-to-date, based on Make/SHA1Digest.
Definition: dynamicCode.C:549
bool wmakeLibso() const
Compile a libso.
Definition: dynamicCode.C:520
A class for handling file names.
Definition: fileName.H:82
static float fileModificationSkew
Definition: regIOobject.H:100
A class for handling character strings derived from std::string.
Definition: string.H:79
@ END_STATEMENT
Definition: token.H:108
A class for handling verbatimStrings, derived from string.
A class for handling words, derived from string.
Definition: word.H:62
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:346
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
bool valid(const PtrList< ModelType > &l)
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
dlLibraryTable libs
Table of loaded dynamic libraries.
off_t fileSize(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return size of file.
Definition: POSIX.C:576
bool dlSymFound(void *handle, const std::string &symbol)
Report if symbol in a dlopened library could be found.
Definition: POSIX.C:1305
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:257
word name(const bool)
Return a word representation of a bool.
Definition: boolIO.C:39
messageStream Info
void writeEntry(Ostream &os, const HashTable< T, Key, Hash > &ht)
Definition: HashTableIO.C:96
defineTypeNameAndDebug(combustionModel, 0)
IOerror FatalIOError
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
void * dlSym(void *handle, const std::string &symbol)
Lookup a symbol in a dlopened library using handle to library.
Definition: POSIX.C:1276
error FatalError
Ostream & writeKeyword(Foam::Ostream &os, const keyType &kw)
Write the keyword to the Ostream with the current level of indentation.
Definition: keyType.C:155
unsigned int sleep(const unsigned int)
Sleep for the specified number of seconds.
Definition: POSIX.C:1130
static const char nl
Definition: Ostream.H:266
fileType type(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return the file type: directory or file.
Definition: POSIX.C:488
dictionary dict