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 {
39  defineTypeNameAndDebug(codedBase, 0);
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 // ************************************************************************* //
A class for handling verbatimStrings, derived from string.
dictionary dict
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
word codeTemplateH(const word &baseTypeName) const
Definition: codedBase.C:403
virtual Ostream & write(const char)=0
Write character.
A class for handling file names.
Definition: fileName.H:79
Inter-processor communication reduction functions.
const SHA1Digest & sha1() const
Return SHA1 digest calculated from include, options, code.
Ostream & writeKeyword(Foam::Ostream &os, const keyType &kw)
Write the keyword to the Ostream with the current level of indentation.
Definition: keyType.C:155
void reset(const dynamicCodeContext &)
Clear files and reset variables to specified context.
Definition: dynamicCode.C:370
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:156
const word & codeName() const
Return the code-name.
Definition: dynamicCode.H:185
word codeTemplateC(const word &baseTypeName) const
Definition: codedBase.C:397
virtual const dictionary & codeDict() const
Get the dictionary to initialise the codeContext.
Definition: codedBase.C:356
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
virtual const word & codeName() const
Name of the dynamically generated CodedType.
Definition: codedBase.C:350
const dictionary & dict() const
Return the parent dictionary context.
dlLibraryTable libs
Table of loaded dynamic libraries.
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
virtual wordList codeKeys() const =0
Get the keywords associated with source code.
stressControl lookup("compactNormalStress") >> compactNormalStress
const fileName & name() const
Return the dictionary name.
Definition: dictionary.H:109
fileName libRelPath() const
Library path for specified code name relative to $FOAM_CASE.
Definition: dynamicCode.C:347
A class for handling words, derived from string.
Definition: word.H:59
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:178
Base class for function objects and boundary conditions using dynamic code.
Definition: codedBase.H:53
virtual void writeCode(Ostream &os) const
Definition: codedBase.C:409
bool wmakeLibso() const
Compile a libso.
Definition: dynamicCode.C:520
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
fileName libPath() const
Library path for specified code name.
Definition: dynamicCode.H:219
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:54
static const char nl
Definition: Ostream.H:260
bool close(const fileName &name, const bool verbose=true)
Close the named library, optionally with warnings if problems occur.
bool upToDate(const dynamicCodeContext &context) const
Verify if the copied code is up-to-date, based on Make/SHA1Digest.
Definition: dynamicCode.C:549
codedBase()
Construct null.
Definition: codedBase.C:364
bool copyOrCreateFiles(const bool verbose=false) const
Copy/create files prior to compilation.
Definition: dynamicCode.C:423
defineTypeNameAndDebug(combustionModel, 0)
void writeEntry(Ostream &os, const HashTable< T, Key, Hash > &ht)
Definition: HashTableIO.C:96
bool open(const fileName &libName, const bool verbose=true)
Open the named library, optionally with warnings if problems occur.
static word libraryBaseName(const fileName &libPath)
Return the library basename without leading &#39;lib&#39; or trailing &#39;.so&#39;.
Definition: dynamicCode.C:93
void updateLibrary() const
Update library as required.
Definition: codedBase.C:288
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
Tools for handling dynamic code compilation.
Definition: dynamicCode.H:56
Encapsulation of dynamic code dictionaries.
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:318
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
void * findLibrary(const fileName &libName)
Find the handle of the named library.
fileType type(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return the file type: directory or file.
Definition: POSIX.C:488
static float fileModificationSkew
Definition: regIOobject.H:101
messageStream Info
std::string str(const bool prefixed=false) const
Return (40-byte) text representation, optionally with &#39;_&#39; prefix.
Definition: SHA1Digest.C:112
off_t fileSize(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return size of file.
Definition: POSIX.C:576
virtual string description() const
Return a description (type + name) for the output.
Definition: codedBase.C:170
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:58
A class for handling character strings derived from std::string.
Definition: string.H:76
unsigned int sleep(const unsigned int)
Sleep for the specified number of seconds.
Definition: POSIX.C:1130
bool found
virtual ~codedBase()
Destructor.
Definition: codedBase.C:391
void * dlSym(void *handle, const std::string &symbol)
Lookup a symbol in a dlopened library using handle to library.
Definition: POSIX.C:1276
bool dlSymFound(void *handle, const std::string &symbol)
Report if symbol in a dlopened library could be found.
Definition: POSIX.C:1305
label startLineNumber() const
Return line number of first token in dictionary.
Definition: dictionary.C:603
Namespace for OpenFOAM.
IOerror FatalIOError