codedBase.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2016-2019 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "codedBase.H"
30 #include "SHA1Digest.H"
31 #include "dynamicCode.H"
32 #include "dynamicCodeContext.H"
33 #include "dlLibraryTable.H"
34 #include "Pstream.H"
35 #include "PstreamReduceOps.H"
36 #include "OSspecific.H"
37 #include "Ostream.H"
38 #include "regIOobject.H"
39 
40 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
41 
42 namespace Foam
43 {
44  defineTypeNameAndDebug(codedBase, 0);
45 }
46 
47 
48 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
49 
50 namespace Foam
51 {
52 
54 static inline void writeEntryIfPresent
55 (
56  Ostream& os,
57  const dictionary& dict,
58  const word& key
59 )
60 {
61  const entry* eptr = dict.findEntry(key, keyType::LITERAL);
62 
63  if (eptr)
64  {
65  os.writeKeyword(key)
67 
68  os.writeQuoted(string(eptr->stream()), false)
71  }
72 }
74 
75 } // End namespace Foam
76 
77 
78 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
79 
81 {
82  writeEntryIfPresent(os, dict, "codeInclude");
83  writeEntryIfPresent(os, dict, "localCode");
84  writeEntryIfPresent(os, dict, "code");
85  writeEntryIfPresent(os, dict, "codeOptions");
86  writeEntryIfPresent(os, dict, "codeLibs");
87 }
88 
89 
90 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
91 
92 void* Foam::codedBase::loadLibrary
93 (
94  const fileName& libPath,
95  const std::string& funcName,
96  const dynamicCodeContext& context
97 ) const
98 {
99  // Avoid compilation by loading an existing library
100 
101  void* handle =
102  (
103  !libPath.empty() && libs().open(libPath, false)
104  ? libs().findLibrary(libPath)
105  : nullptr
106  );
107 
108  if (!handle)
109  {
110  return handle;
111  }
112 
113  // Verify the loaded version and unload if needed
114 
115  // Manual execution of code after loading.
116  // This is mandatory for codedBase.
117 
118  void* rawSymbol = dlSymFind(handle, funcName);
119 
120  if (rawSymbol)
121  {
122  loaderType fun = reinterpret_cast<loaderType>(rawSymbol);
123 
124  if (fun)
125  {
126  (*fun)(true); // force load
127  }
128  else
129  {
130  FatalIOErrorInFunction(context.dict())
131  << "Failed symbol lookup " << funcName.c_str() << nl
132  << "from " << libPath << nl
133  << exit(FatalIOError);
134  }
135  }
136  else
137  {
138  FatalIOErrorInFunction(context.dict())
139  << "Failed symbol lookup " << funcName.c_str() << nl
140  << "from " << libPath << nl
141  << exit(FatalIOError);
142 
143  handle = nullptr;
144  if (!libs().close(libPath, false))
145  {
146  FatalIOErrorInFunction(context.dict())
147  << "Failed unloading library " << libPath << nl
148  << exit(FatalIOError);
149  }
150  }
151 
152  return handle;
153 }
154 
155 
156 void Foam::codedBase::unloadLibrary
157 (
158  const fileName& libPath,
159  const std::string& funcName,
160  const dynamicCodeContext& context
161 ) const
162 {
163  void* handle =
164  (
165  !libPath.empty() && libs().open(libPath, false)
166  ? libs().findLibrary(libPath)
167  : nullptr
168  );
169 
170  if (!handle)
171  {
172  return;
173  }
174 
175  // Manual execution of code before unloading.
176  // This is mandatory for codedBase.
177 
178  void* rawSymbol = dlSymFind(handle, funcName);
179 
180  if (rawSymbol)
181  {
182  loaderType fun = reinterpret_cast<loaderType>(rawSymbol);
183 
184  if (fun)
185  {
186  (*fun)(false); // force unload
187  }
188  else
189  {
190  FatalIOErrorInFunction(context.dict())
191  << "Failed symbol lookup " << funcName.c_str() << nl
192  << "from " << libPath << nl
193  << exit(FatalIOError);
194  }
195  }
196 
197  if (!libs().close(libPath, false))
198  {
199  FatalIOErrorInFunction(context.dict())
200  << "Failed unloading library " << libPath << nl
201  << exit(FatalIOError);
202  }
203 }
204 
205 
206 void Foam::codedBase::createLibrary
207 (
208  dynamicCode& dynCode,
209  const dynamicCodeContext& context
210 ) const
211 {
212  bool create =
214  || (regIOobject::fileModificationSkew <= 0); // not NFS
215 
216  if (create)
217  {
218  // Write files for new library
219  if (!dynCode.upToDate(context))
220  {
221  // filter with this context
222  dynCode.reset(context);
223 
224  this->prepare(dynCode, context);
225 
226  if (!dynCode.copyOrCreateFiles(true))
227  {
228  FatalIOErrorInFunction(context.dict())
229  << "Failed writing files for" << nl
230  << dynCode.libRelPath() << nl
231  << exit(FatalIOError);
232  }
233  }
234 
235  if (!dynCode.wmakeLibso())
236  {
237  FatalIOErrorInFunction(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  for
259  (
260  label iter = 0;
262  iter++
263  )
264  {
265  DebugPout
266  << "on processor " << Pstream::myProcNo()
267  << " have masterSize:" << masterSize
268  << " and localSize:" << mySize
269  << endl;
270 
271  if (mySize == masterSize)
272  {
273  break;
274  }
275  else if (mySize > masterSize)
276  {
277  FatalIOErrorInFunction(context.dict())
278  << "Excessive size when reading (NFS mounted) library "
279  << nl << libPath << nl
280  << "on processor " << Pstream::myProcNo()
281  << " detected size " << mySize
282  << " whereas master size is " << masterSize
283  << " bytes." << nl
284  << "If your case is NFS mounted increase"
285  << " fileModificationSkew or maxFileModificationPolls;"
286  << nl << "If your case is not NFS mounted"
287  << " (so distributed) set fileModificationSkew"
288  << " to 0"
289  << exit(FatalIOError);
290  }
291  else
292  {
293  DebugPout
294  << "Local file " << libPath
295  << " not of same size (" << mySize
296  << ") as master ("
297  << masterSize << "). Waiting for "
299  << " seconds." << endl;
300 
302 
303  // Recheck local size
304  mySize = Foam::fileSize(libPath);
305  }
306  }
307 
308 
309  // Finished doing iterations. Do final check
310  if (mySize != masterSize)
311  {
312  FatalIOErrorInFunction(context.dict())
313  << "Cannot read (NFS mounted) library " << nl
314  << libPath << nl
315  << "on processor " << Pstream::myProcNo()
316  << " detected size " << mySize
317  << " whereas master size is " << masterSize
318  << " bytes." << nl
319  << "If your case is NFS mounted increase"
320  << " fileModificationSkew or maxFileModificationPolls;"
321  << nl << "If your case is not NFS mounted"
322  << " (so distributed) set fileModificationSkew"
323  << " to 0"
324  << exit(FatalIOError);
325  }
326 
327  DebugPout
328  << "on processor " << Pstream::myProcNo()
329  << " after waiting: have masterSize:" << masterSize
330  << " and localSize:" << mySize << endl;
331  }
332  reduce(create, orOp<bool>());
333 }
334 
335 
336 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
337 
339 {
340  context_.setCodeContext(dict);
341 }
342 
343 
344 void Foam::codedBase::append(const std::string& str)
345 {
346  context_.append(str);
347 }
348 
349 
351 (
352  const word& name,
353  const dynamicCodeContext& context
354 ) const
355 {
357  (
358  "codedBase::updateLibrary()",
359  context.dict()
360  );
361 
362  // codeName: name + _<sha1>
363  // codeDir : name
364  dynamicCode dynCode
365  (
366  name + context.sha1().str(true),
367  name
368  );
369 
370  const fileName libPath = dynCode.libPath();
371 
372 
373  // The correct library was already loaded => we are done
374  if (libs().findLibrary(libPath))
375  {
376  return;
377  }
378 
379  DetailInfo
380  << "Using dynamicCode for " << this->description().c_str()
381  << " at line " << context.dict().startLineNumber()
382  << " in " << context.dict().name() << endl;
383 
384 
385  // Remove instantiation of fvPatchField provided by library
386  this->clearRedirect();
387 
388  // May need to unload old library
389  unloadLibrary
390  (
391  oldLibPath_,
392  dynamicCode::libraryBaseName(oldLibPath_),
393  context
394  );
395 
396  // Try loading an existing library (avoid compilation when possible)
397  if (!loadLibrary(libPath, dynCode.codeName(), context))
398  {
399  createLibrary(dynCode, context);
400 
401  loadLibrary(libPath, dynCode.codeName(), context);
402  }
403 
404  // Retain for future reference
405  oldLibPath_ = libPath;
406 }
407 
408 
410 (
411  const word& name,
412  const dictionary& dict
413 ) const
414 {
415  updateLibrary(name, dynamicCodeContext(dict));
416 }
417 
418 
420 {
421  if (context_.valid())
422  {
423  updateLibrary(name, context_);
424  }
425  else
426  {
427  updateLibrary(name, dynamicCodeContext(this->codeDict()));
428  }
429 }
430 
431 
432 // ************************************************************************* //
regIOobject.H
Foam::codedBase::append
void append(const std::string &str)
Add content to SHA1 hashing.
Definition: codedBase.C:344
Foam::BitSetOps::create
bitSet create(const label n, const labelHashSet &locations, const bool on=true)
Create a bitSet with length n with the specifed on locations.
Definition: BitOps.C:35
OSspecific.H
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:62
Foam::fileName
A class for handling file names.
Definition: fileName.H:69
Foam::codedBase::writeCodeDict
static void writeCodeDict(Ostream &os, const dictionary &dict)
Write code-dictionary contents.
Definition: codedBase.C:80
Foam::SHA1::str
std::string str(const bool prefixed=false) const
The digest (40-byte) text representation, optionally with '_' prefix.
Definition: SHA1I.H:123
Foam::dynamicCode
Tools for handling dynamic code compilation.
Definition: dynamicCode.H:59
Foam::dynamicCodeContext::dict
const dictionary & dict() const
Return the parent dictionary context.
Definition: dynamicCodeContext.H:128
Foam::regIOobject::fileModificationSkew
static float fileModificationSkew
Definition: regIOobject.H:131
Foam::dynamicCodeContext
Encapsulation of dynamic code dictionaries.
Definition: dynamicCodeContext.H:53
Foam::regIOobject::maxFileModificationPolls
static int maxFileModificationPolls
Definition: regIOobject.H:133
Foam::Pstream::scatter
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
Definition: gatherScatter.C:150
Foam::FatalIOError
IOerror FatalIOError
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:337
dynamicCodeContext.H
Foam::codedBase::setCodeContext
void setCodeContext(const dictionary &dict)
Set code context from a dictionary.
Definition: codedBase.C:338
SHA1Digest.H
Foam::reduce
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
Definition: PstreamReduceOps.H:51
Foam::dictionary::name
const fileName & name() const
The dictionary name.
Definition: dictionary.H:446
Foam::fileSize
off_t fileSize(const fileName &name, const bool followLink=true)
Return size of file or -1 on failure (normally follows symbolic links).
Definition: MSwindows.C:676
Foam::label
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:62
Foam::dlSymFind
void * dlSymFind(void *handle, const std::string &symbol, bool required=false)
Look for symbol in a dlopened library.
Definition: MSwindows.C:1321
Foam::name
word name(const complex &c)
Return string representation of complex.
Definition: complex.C:76
Foam::dynamicCode::checkSecurity
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:72
Foam::dictionary::startLineNumber
label startLineNumber() const
Return line number of first token in dictionary.
Definition: dictionary.C:205
dynamicCode.H
Foam::token::END_STATEMENT
End entry [isseparator].
Definition: token.H:116
DetailInfo
#define DetailInfo
Definition: evalEntry.C:36
Foam::token::END_BLOCK
End block [isseparator].
Definition: token.H:122
dict
dictionary dict
Definition: searchingEngine.H:14
Foam::dictionary
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:121
Pstream.H
Foam::token::HASH
Hash - directive or verbatim string.
Definition: token.H:125
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
PstreamReduceOps.H
Inter-processor communication reduction functions.
Foam::token::BEGIN_BLOCK
Begin block [isseparator].
Definition: token.H:121
DebugPout
#define DebugPout
Report an information message using Foam::Pout.
Definition: messageStream.H:361
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::UPstream::myProcNo
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:444
Foam::UPstream::master
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:438
Foam::dynamicCode::libraryBaseName
static word libraryBaseName(const fileName &libPath)
Return the library basename without leading 'lib' or trailing '.so'.
Definition: dynamicCode.C:106
Ostream.H
Foam::nl
constexpr char nl
Definition: Ostream.H:372
dlLibraryTable.H
Foam::dictionary::findEntry
entry * findEntry(const word &keyword, enum keyType::option matchOpt=keyType::REGEX)
Find for an entry (non-const access) with the given keyword.
Definition: dictionary.C:369
codedBase.H
FatalIOErrorInFunction
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:375
Foam::keyType::LITERAL
String literal.
Definition: keyType.H:73
Foam::Ostream
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:56
Foam::dynamicCodeContext::sha1
const SHA1 & sha1() const
Return SHA1 calculated from options, libs, include, code.
Definition: dynamicCodeContext.H:164
Foam::codedBase::updateLibrary
void updateLibrary(const word &name, const dynamicCodeContext &context) const
Update library as required, using the given context.
Definition: codedBase.C:351
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
Foam::sleep
unsigned int sleep(const unsigned int sec)
Sleep for the specified number of seconds.
Definition: MSwindows.C:1096