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 "dynamicCode.H"
28 #include "dlLibraryTable.H"
29 #include "regIOobject.H"
30 #include "OSspecific.H"
31 #include "stringOps.H"
32 
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 
35 namespace Foam
36 {
38 }
39 
40 
41 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
42 
44 {
45  word result(name);
46 
47  if (!isalpha(result[0]))
48  {
50  << "Cannot construct code name from function name \"" << name
51  << "\" as the first character is not alphabetic"
52  << exit(FatalError);
53  }
54 
55  for (word::size_type i = 1; i < name.size(); ++ i)
56  {
57  const bool valid = isalnum(result[i]) || result[i] == '_';
58 
59  if (!valid)
60  {
61  result[i] = '_';
62  }
63  }
64 
65  return result;
66 }
67 
68 
69 void* Foam::codedBase::loadLibrary
70 (
71  const fileName& libPath,
72  const string& globalFuncName,
73  const dictionary& contextDict
74 ) const
75 {
76  void* lib = 0;
77 
78  // Avoid compilation by loading an existing library
79  if (!libPath.empty())
80  {
81  if (libs.open(libPath, false))
82  {
83  lib = libs.findLibrary(libPath);
84 
85  // Verify the loaded version and unload if needed
86  if (lib)
87  {
88  // Provision for manual execution of code after loading
89  if (dlSymFound(lib, globalFuncName))
90  {
91  const loaderFunctionType function =
92  reinterpret_cast<loaderFunctionType>
93  (
94  dlSym(lib, globalFuncName)
95  );
96 
97  if (function)
98  {
99  (*function)(true); // Force load
100  }
101  else
102  {
104  (
105  contextDict
106  ) << "Failed looking up symbol " << globalFuncName
107  << nl << "from " << libPath << exit(FatalIOError);
108  }
109  }
110  else
111  {
113  (
114  contextDict
115  ) << "Failed looking up symbol " << globalFuncName << nl
116  << "from " << libPath << exit(FatalIOError);
117 
118  lib = 0;
119  if (!libs.close(libPath, false))
120  {
122  (
123  contextDict
124  ) << "Failed unloading library "
125  << libPath
126  << exit(FatalIOError);
127  }
128  }
129  }
130  }
131  }
132 
133  return lib;
134 }
135 
136 
137 void Foam::codedBase::unloadLibrary
138 (
139  const fileName& libPath,
140  const string& globalFuncName,
141  const dictionary& contextDict
142 ) const
143 {
144  void* lib = 0;
145 
146  if (libPath.empty())
147  {
148  return;
149  }
150 
151  lib = libs.findLibrary(libPath);
152 
153  if (!lib)
154  {
155  return;
156  }
157 
158  // Provision for manual execution of code before unloading
159  if (dlSymFound(lib, globalFuncName))
160  {
161  const loaderFunctionType function =
162  reinterpret_cast<loaderFunctionType>
163  (
164  dlSym(lib, globalFuncName)
165  );
166 
167  if (function)
168  {
169  (*function)(false); // Force unload
170  }
171  else
172  {
174  (
175  contextDict
176  ) << "Failed looking up symbol " << globalFuncName << nl
177  << "from " << libPath << exit(FatalIOError);
178  }
179  }
180 
181  if (!libs.close(libPath, false))
182  {
184  (
185  contextDict
186  ) << "Failed unloading library " << libPath
187  << exit(FatalIOError);
188  }
189 }
190 
191 
192 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
193 
194 Foam::verbatimString Foam::codedBase::expandCodeString
195 (
196  const word& codeKey,
197  const word& codeDictVar,
198  const dictionary& dict
199 ) const
200 {
201  verbatimString codeString;
202 
203  if (dict.found(codeKey))
204  {
205  codeString = dict.lookupOrDefault<verbatimString>
206  (
207  codeKey,
209  );
210 
212  (
213  codeString,
214  dict,
215  codeDictVar
216  );
217  }
218 
219  return codeString;
220 }
221 
222 
223 void Foam::codedBase::createLibrary
224 (
225  const dictionary& dict,
226  dynamicCode& dynCode,
227  const dynamicCodeContext& context
228 ) const
229 {
230  const bool create =
232  || (regIOobject::fileModificationSkew <= 0); // Not NFS
233 
234  if (create)
235  {
236  // Write files for new library
237  if (!dynCode.upToDate(context))
238  {
239  // Filter with this context
240  dynCode.reset(context);
241 
242  this->prepare(dynCode, context);
243 
244  if (!dynCode.copyOrCreateFiles(true))
245  {
247  (
248  dict
249  ) << "Failed writing files for" << nl
250  << dynCode.libRelPath() << nl
251  << exit(FatalIOError);
252  }
253  }
254 
255  if (!dynCode.wmakeLibso())
256  {
258  (
259  dict
260  ) << "Failed wmake " << dynCode.libRelPath() << nl
261  << exit(FatalIOError);
262  }
263  }
264 
265 
266  // All processes must wait for compile to finish
268  {
269  //- Since the library has only been compiled on the master the
270  // other nodes need to pick this library up through NFS
271  // We do this by just polling a few times using the
272  // fileModificationSkew.
273 
274  const fileName libPath = dynCode.libPath();
275 
276  off_t mySize = fileSize(libPath);
277  off_t masterSize = mySize;
278  Pstream::scatter(masterSize);
279 
280  if (debug)
281  {
282  Pout<< endl<< "on processor " << Pstream::myProcNo()
283  << " have masterSize:" << masterSize
284  << " and localSize:" << mySize
285  << endl;
286  }
287 
288  if (mySize < masterSize)
289  {
290  if (debug)
291  {
292  Pout<< "Local file " << libPath
293  << " not of same size (" << mySize
294  << ") as master ("
295  << masterSize << "). Waiting for "
297  << " seconds." << endl;
298  }
300 
301  // Recheck local size
302  mySize = Foam::fileSize(libPath);
303 
304  if (mySize < masterSize)
305  {
307  (
308  dict
309  ) << "Cannot read (NFS mounted) library " << nl
310  << libPath << nl
311  << "on processor " << Pstream::myProcNo()
312  << " detected size " << mySize
313  << " whereas master size is " << masterSize
314  << " bytes." << nl
315  << "If your case is not NFS mounted"
316  << " (so distributed) set fileModificationSkew"
317  << " to 0"
318  << exit(FatalIOError);
319  }
320  }
321 
322  if (debug)
323  {
324  Pout<< endl<< "on processor " << Pstream::myProcNo()
325  << " after waiting: have masterSize:" << masterSize
326  << " and localSize:" << mySize
327  << endl;
328  }
329  }
330 }
331 
332 
333 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
334 
336 (
337  const word& name,
338  const dictionary& dict,
339  const wordList& codeKeys,
340  const wordList& codeDictVars
341 )
342 :
343  codeName_(codeName(name)),
344  codeContext_(dict, codeKeys, codeDictVars)
345 {}
346 
347 
349 (
350  const dictionary& dict,
351  const wordList& codeKeys,
352  const wordList& codeDictVars
353 )
354 :
355  codedBase(codeName(dict.lookup("name")), dict, codeKeys, codeDictVars)
356 {}
357 
358 
360 :
361  codeName_(cb.codeName_),
362  codeContext_(cb.codeContext_)
363 {}
364 
365 
366 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
367 
369 {}
370 
371 
372 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
373 
375 {
376  return codeName_;
377 }
378 
379 
381 {
382  return this->type() + " " + codeName();
383 }
384 
385 
387 {
388  return baseTypeName + "Template.C";
389 }
390 
391 
393 {
394  return baseTypeName + "Template.H";
395 }
396 
397 
399 {
400  const word& name = codeName();
401 
403  (
404  "codedBase::updateLibrary()",
405  dict
406  );
407 
408  // codeName: name + _<sha1>
409  // codeDir : name
410  dynamicCode dynCode
411  (
412  name + codeContext_.sha1().str(true),
413  name
414  );
415  const fileName libPath = dynCode.libPath();
416 
417 
418  // The correct library was already loaded => we are done
419  if (libs.findLibrary(libPath))
420  {
421  return false;
422  }
423 
424  Info<< "Using dynamicCode for " << this->description().c_str()
425  << " at line " << dict.startLineNumber()
426  << " in " << dict.name() << endl;
427 
428  // May need to unload old library
429  unloadLibrary
430  (
431  oldLibPath_,
432  dynamicCode::libraryBaseName(oldLibPath_),
433  dict
434  );
435 
436  // Try loading an existing library (avoid compilation when possible)
437  if (!loadLibrary(libPath, dynCode.codeName(), dict))
438  {
439  createLibrary(dict, dynCode, codeContext_);
440 
441  if (!loadLibrary(libPath, dynCode.codeName(), dict))
442  {
444  << "Failed to load " << libPath << exit(FatalIOError);
445  }
446  }
447 
448  // Retain for future reference
449  oldLibPath_ = libPath;
450 
451  return true;
452 }
453 
454 
456 {
457  codeContext_.read(dict);
458 }
459 
460 
462 {
463  if (codeName().size())
464  {
465  writeEntry(os, "name", codeName());
466  }
467 
468  codeContext_.write(os);
469 }
470 
471 
472 // ************************************************************************* //
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:73
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
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:52
word codeTemplateC(const word &baseTypeName) const
Definition: codedBase.C:386
void read(const dictionary &dict)
Read the dictionary and update the code.
Definition: codedBase.C:455
const word & codeName() const
Name of the dynamically generated CodedType.
Definition: codedBase.C:374
void write(Ostream &os) const
Write the code for restart.
Definition: codedBase.C:461
codedBase(const word &name, const dictionary &dict, const wordList &codeKeys, const wordList &codeDictVars)
Construct from name and dictionary.
Definition: codedBase.C:336
word codeTemplateH(const word &baseTypeName) const
Definition: codedBase.C:392
virtual ~codedBase()
Destructor.
Definition: codedBase.C:368
bool updateLibrary(const dictionary &dict) const
Update library from given updated dictionary as required.
Definition: codedBase.C:398
string description() const
Return a description (type + name) for the output.
Definition: codedBase.C:380
const fileName & name() const
Return the dictionary name.
Definition: dictionary.H:111
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
T lookupOrDefault(const word &, const T &, const bool writeDefault=writeOptionalEntries > 0) const
Find and return a T, if not found return the given default.
bool found(const word &, bool recursive=false, bool patternMatch=true) const
Search dictionary for given keyword.
Definition: dictionary.C:539
virtual label startLineNumber() const
Return line number of first token in dictionary.
Definition: dictionary.C:472
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.
Tools for handling dynamic code compilation.
Definition: dynamicCode.H:57
const word & codeName() const
Return the code-name.
Definition: dynamicCode.H:185
fileName libPath() const
Library path for specified code name.
Definition: dynamicCode.H:219
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
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
A class for handling verbatimStrings, derived from string.
static const verbatimString null
An empty verbatimString.
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)
string & inplaceExpandCodeString(string &, const dictionary &dict, const word &dictVar="dict", const char sigil='$')
Inplace expand occurrences of variables according to the dictionary.
Definition: stringOps.C:410
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:258
messageStream Info
void writeEntry(Ostream &os, const HashTable< T, Key, Hash > &ht)
Definition: HashTableIO.C:96
defineTypeNameAndDebug(combustionModel, 0)
IOerror FatalIOError
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
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
unsigned int sleep(const unsigned int)
Sleep for the specified number of seconds.
Definition: POSIX.C:1130
static const char nl
Definition: Ostream.H:267
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