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-2021 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 
45 void* Foam::codedBase::loadLibrary
46 (
47  const fileName& libPath,
48  const string& globalFuncName,
49  const dictionary& contextDict
50 ) const
51 {
52  void* lib = 0;
53 
54  // avoid compilation by loading an existing library
55  if (!libPath.empty())
56  {
57  if (libs.open(libPath, false))
58  {
59  lib = libs.findLibrary(libPath);
60 
61  // verify the loaded version and unload if needed
62  if (lib)
63  {
64  // provision for manual execution of code after loading
65  if (dlSymFound(lib, globalFuncName))
66  {
67  loaderFunctionType function =
68  reinterpret_cast<loaderFunctionType>
69  (
70  dlSym(lib, globalFuncName)
71  );
72 
73  if (function)
74  {
75  (*function)(true); // force load
76  }
77  else
78  {
80  (
81  contextDict
82  ) << "Failed looking up symbol " << globalFuncName
83  << nl << "from " << libPath << exit(FatalIOError);
84  }
85  }
86  else
87  {
89  (
90  contextDict
91  ) << "Failed looking up symbol " << globalFuncName << nl
92  << "from " << libPath << exit(FatalIOError);
93 
94  lib = 0;
95  if (!libs.close(libPath, false))
96  {
98  (
99  contextDict
100  ) << "Failed unloading library "
101  << libPath
102  << exit(FatalIOError);
103  }
104  }
105  }
106  }
107  }
108 
109  return lib;
110 }
111 
112 
113 void Foam::codedBase::unloadLibrary
114 (
115  const fileName& libPath,
116  const string& globalFuncName,
117  const dictionary& contextDict
118 ) const
119 {
120  void* lib = 0;
121 
122  if (libPath.empty())
123  {
124  return;
125  }
126 
127  lib = libs.findLibrary(libPath);
128 
129  if (!lib)
130  {
131  return;
132  }
133 
134  // provision for manual execution of code before unloading
135  if (dlSymFound(lib, globalFuncName))
136  {
137  loaderFunctionType function =
138  reinterpret_cast<loaderFunctionType>
139  (
140  dlSym(lib, globalFuncName)
141  );
142 
143  if (function)
144  {
145  (*function)(false); // force unload
146  }
147  else
148  {
150  (
151  contextDict
152  ) << "Failed looking up symbol " << globalFuncName << nl
153  << "from " << libPath << exit(FatalIOError);
154  }
155  }
156 
157  if (!libs.close(libPath, false))
158  {
160  (
161  contextDict
162  ) << "Failed unloading library " << libPath
163  << exit(FatalIOError);
164  }
165 }
166 
167 
168 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
169 
171 {
172  return this->type() + " " + codeName();
173 }
174 
175 
176 void Foam::codedBase::createLibrary
177 (
178  dynamicCode& dynCode,
179  const dynamicCodeContext& context
180 ) const
181 {
182  bool create =
184  || (regIOobject::fileModificationSkew <= 0); // not NFS
185 
186  if (create)
187  {
188  // Write files for new library
189  if (!dynCode.upToDate(context))
190  {
191  // filter with this context
192  dynCode.reset(context);
193 
194  this->prepare(dynCode, context);
195 
196  if (!dynCode.copyOrCreateFiles(true))
197  {
199  (
200  context.dict()
201  ) << "Failed writing files for" << nl
202  << dynCode.libRelPath() << nl
203  << exit(FatalIOError);
204  }
205  }
206 
207  if (!dynCode.wmakeLibso())
208  {
210  (
211  context.dict()
212  ) << "Failed wmake " << dynCode.libRelPath() << nl
213  << exit(FatalIOError);
214  }
215  }
216 
217 
218  // all processes must wait for compile to finish
220  {
221  //- Since the library has only been compiled on the master the
222  // other nodes need to pick this library up through NFS
223  // We do this by just polling a few times using the
224  // fileModificationSkew.
225 
226  const fileName libPath = dynCode.libPath();
227 
228  off_t mySize = Foam::fileSize(libPath);
229  off_t masterSize = mySize;
230  Pstream::scatter(masterSize);
231 
232  if (debug)
233  {
234  Pout<< endl<< "on processor " << Pstream::myProcNo()
235  << " have masterSize:" << masterSize
236  << " and localSize:" << mySize
237  << endl;
238  }
239 
240 
241  if (mySize < masterSize)
242  {
243  if (debug)
244  {
245  Pout<< "Local file " << libPath
246  << " not of same size (" << mySize
247  << ") as master ("
248  << masterSize << "). Waiting for "
250  << " seconds." << endl;
251  }
253 
254  // Recheck local size
255  mySize = Foam::fileSize(libPath);
256 
257  if (mySize < masterSize)
258  {
260  (
261  context.dict()
262  ) << "Cannot read (NFS mounted) library " << nl
263  << libPath << nl
264  << "on processor " << Pstream::myProcNo()
265  << " detected size " << mySize
266  << " whereas master size is " << masterSize
267  << " bytes." << nl
268  << "If your case is not NFS mounted"
269  << " (so distributed) set fileModificationSkew"
270  << " to 0"
271  << exit(FatalIOError);
272  }
273  }
274 
275  if (debug)
276  {
277  Pout<< endl<< "on processor " << Pstream::myProcNo()
278  << " after waiting: have masterSize:" << masterSize
279  << " and localSize:" << mySize
280  << endl;
281  }
282  }
283 }
284 
285 
286 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
287 
289 {
290  const word& name = codeName();
291  const dictionary& dict = codeDict();
292 
294  (
295  "codedBase::updateLibrary()",
296  dict
297  );
298 
299  dynamicCodeContext context(dict, codeKeys());
300 
301  // codeName: name + _<sha1>
302  // codeDir : name
303  dynamicCode dynCode
304  (
305  name + context.sha1().str(true),
306  name
307  );
308  const fileName libPath = dynCode.libPath();
309 
310 
311  // the correct library was already loaded => we are done
312  if (libs.findLibrary(libPath))
313  {
314  return;
315  }
316 
317  Info<< "Using dynamicCode for " << this->description().c_str()
318  << " at line " << dict.startLineNumber()
319  << " in " << dict.name() << endl;
320 
321 
322  // remove instantiation of fvPatchField provided by library
323  this->clearRedirect();
324 
325  // may need to unload old library
326  unloadLibrary
327  (
328  oldLibPath_,
329  dynamicCode::libraryBaseName(oldLibPath_),
330  context.dict()
331  );
332 
333  // try loading an existing library (avoid compilation when possible)
334  if (!loadLibrary(libPath, dynCode.codeName(), context.dict()))
335  {
336  createLibrary(dynCode, context);
337 
338  if (!loadLibrary(libPath, dynCode.codeName(), context.dict()))
339  {
340  FatalIOErrorInFunction(context.dict())
341  << "Failed to load " << libPath << exit(FatalIOError);
342  }
343  }
344 
345  // retain for future reference
346  oldLibPath_ = libPath;
347 }
348 
349 
351 {
352  return codeName_;
353 }
354 
355 
357 {
358  return dict_;
359 }
360 
361 
362 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
363 
365 {}
366 
367 
369 :
370  codeName_(name.replaceAll("-", "_")),
371  dict_(dict)
372 {}
373 
374 
376 :
377  codeName_(dict.lookup("name")),
378  dict_(dict)
379 {}
380 
381 
383 :
384  codeName_(cb.codeName_),
385  dict_(cb.dict_)
386 {}
387 
388 
389 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
390 
392 {}
393 
394 
395 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
396 
398 {
399  return baseTypeName + "Template.C";
400 }
401 
402 
404 {
405  return baseTypeName + "Template.H";
406 }
407 
408 
410 {
411  if (codeName().size())
412  {
413  writeEntry(os, "name", codeName());
414  }
415 
416  wordList codeAndBuildKeys(codeKeys());
417  codeAndBuildKeys.append("codeOptions");
418  codeAndBuildKeys.append("codeLibs");
419 
420  forAll(codeAndBuildKeys, i)
421  {
422  if (codeDict().found(codeAndBuildKeys[i]))
423  {
424  writeKeyword(os, codeAndBuildKeys[i]);
425  os.write(verbatimString(codeDict()[codeAndBuildKeys[i]]))
426  << token::END_STATEMENT << nl;
427  }
428  }
429 }
430 
431 
432 // ************************************************************************* //
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Inter-processor communication reduction functions.
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:397
virtual void writeCode(Ostream &os) const
Definition: codedBase.C:409
virtual const word & codeName() const
Name of the dynamically generated CodedType.
Definition: codedBase.C:350
word codeTemplateH(const word &baseTypeName) const
Definition: codedBase.C:403
virtual ~codedBase()
Destructor.
Definition: codedBase.C:391
codedBase()
Construct null.
Definition: codedBase.C:364
virtual const dictionary & codeDict() const
Get the dictionary to initialise the codeContext.
Definition: codedBase.C:356
void updateLibrary() const
Update library as required.
Definition: codedBase.C:288
virtual string description() const
Return a description (type + name) for the output.
Definition: codedBase.C:170
const fileName & name() const
Return the dictionary name.
Definition: dictionary.H:109
A list of keyword definitions, which are a keyword followed by any number of values (e....
Definition: dictionary.H:160
label startLineNumber() const
Return line number of first token in dictionary.
Definition: dictionary.C:599
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:101
A class for handling character strings derived from std::string.
Definition: string.H:79
@ END_STATEMENT
Definition: token.H:105
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:318
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:251
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
Ostream & writeKeyword(Foam::Ostream &os, const keyType &kw)
Write the keyword to the Ostream with the current level of indentation.
Definition: keyType.C:155
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
unsigned int sleep(const unsigned int)
Sleep for the specified number of seconds.
Definition: POSIX.C:1130
static const char nl
Definition: Ostream.H:260
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