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-2020 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 "PstreamReduceOps.H"
32 #include "OSspecific.H"
33 #include "regIOobject.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 
170 void Foam::codedBase::createLibrary
171 (
172  dynamicCode& dynCode,
173  const dynamicCodeContext& context
174 ) const
175 {
176  bool create =
178  || (regIOobject::fileModificationSkew <= 0); // not NFS
179 
180  if (create)
181  {
182  // Write files for new library
183  if (!dynCode.upToDate(context))
184  {
185  // filter with this context
186  dynCode.reset(context);
187 
188  this->prepare(dynCode, context);
189 
190  if (!dynCode.copyOrCreateFiles(true))
191  {
193  (
194  context.dict()
195  ) << "Failed writing files for" << nl
196  << dynCode.libRelPath() << nl
197  << exit(FatalIOError);
198  }
199  }
200 
201  if (!dynCode.wmakeLibso())
202  {
204  (
205  context.dict()
206  ) << "Failed wmake " << dynCode.libRelPath() << nl
207  << exit(FatalIOError);
208  }
209  }
210 
211 
212  // all processes must wait for compile to finish
214  {
215  //- Since the library has only been compiled on the master the
216  // other nodes need to pick this library up through NFS
217  // We do this by just polling a few times using the
218  // fileModificationSkew.
219 
220  const fileName libPath = dynCode.libPath();
221 
222  off_t mySize = Foam::fileSize(libPath);
223  off_t masterSize = mySize;
224  Pstream::scatter(masterSize);
225 
226  if (debug)
227  {
228  Pout<< endl<< "on processor " << Pstream::myProcNo()
229  << " have masterSize:" << masterSize
230  << " and localSize:" << mySize
231  << endl;
232  }
233 
234 
235  if (mySize < masterSize)
236  {
237  if (debug)
238  {
239  Pout<< "Local file " << libPath
240  << " not of same size (" << mySize
241  << ") as master ("
242  << masterSize << "). Waiting for "
244  << " seconds." << endl;
245  }
247 
248  // Recheck local size
249  mySize = Foam::fileSize(libPath);
250 
251  if (mySize < masterSize)
252  {
254  (
255  context.dict()
256  ) << "Cannot read (NFS mounted) library " << nl
257  << libPath << nl
258  << "on processor " << Pstream::myProcNo()
259  << " detected size " << mySize
260  << " whereas master size is " << masterSize
261  << " bytes." << nl
262  << "If your case is not NFS mounted"
263  << " (so distributed) set fileModificationSkew"
264  << " to 0"
265  << exit(FatalIOError);
266  }
267  }
268 
269  if (debug)
270  {
271  Pout<< endl<< "on processor " << Pstream::myProcNo()
272  << " after waiting: have masterSize:" << masterSize
273  << " and localSize:" << mySize
274  << endl;
275  }
276  }
277  reduce(create, orOp<bool>());
278 }
279 
280 
281 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
282 
284 {
285  const word& name = codeName();
286  const dictionary& dict = codeDict();
287 
289  (
290  "codedBase::updateLibrary()",
291  dict
292  );
293 
294  dynamicCodeContext context(dict, codeKeys());
295 
296  // codeName: name + _<sha1>
297  // codeDir : name
298  dynamicCode dynCode
299  (
300  name + context.sha1().str(true),
301  name
302  );
303  const fileName libPath = dynCode.libPath();
304 
305 
306  // the correct library was already loaded => we are done
307  if (libs.findLibrary(libPath))
308  {
309  return;
310  }
311 
312  Info<< "Using dynamicCode for " << this->description().c_str()
313  << " at line " << dict.startLineNumber()
314  << " in " << dict.name() << endl;
315 
316 
317  // remove instantiation of fvPatchField provided by library
318  this->clearRedirect();
319 
320  // may need to unload old library
321  unloadLibrary
322  (
323  oldLibPath_,
324  dynamicCode::libraryBaseName(oldLibPath_),
325  context.dict()
326  );
327 
328  // try loading an existing library (avoid compilation when possible)
329  if (!loadLibrary(libPath, dynCode.codeName(), context.dict()))
330  {
331  createLibrary(dynCode, context);
332 
333  if (!loadLibrary(libPath, dynCode.codeName(), context.dict()))
334  {
335  FatalIOErrorInFunction(context.dict())
336  << "Failed to load " << libPath << exit(FatalIOError);
337  }
338  }
339 
340  // retain for future reference
341  oldLibPath_ = libPath;
342 }
343 
344 
345 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
346 
348 {}
349 
350 
351 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
352 
354 {}
355 
356 
357 // ************************************************************************* //
dictionary dict
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.
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:158
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
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...
const fileName & name() const
Return the dictionary name.
Definition: dictionary.H:111
A class for handling words, derived from string.
Definition: word.H:59
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
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.
codedBase()
Construct null.
Definition: codedBase.C:347
defineTypeNameAndDebug(combustionModel, 0)
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 reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
void updateLibrary() const
Update library as required.
Definition: codedBase.C:283
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:331
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
void * findLibrary(const fileName &libName)
Find the handle of the named library.
static float fileModificationSkew
Definition: regIOobject.H:103
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
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:58
unsigned int sleep(const unsigned int)
Sleep for the specified number of seconds.
Definition: POSIX.C:1134
virtual ~codedBase()
Destructor.
Definition: codedBase.C:353
void * dlSym(void *handle, const std::string &symbol)
Lookup a symbol in a dlopened library using handle to library.
Definition: POSIX.C:1280
bool dlSymFound(void *handle, const std::string &symbol)
Report if symbol in a dlopened library could be found.
Definition: POSIX.C:1309
label startLineNumber() const
Return line number of first token in dictionary.
Definition: dictionary.C:607
Namespace for OpenFOAM.
IOerror FatalIOError