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-2023 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 "objectRegistry.H"
35 #include "IOdictionary.H"
36 #include "Pstream.H"
37 #include "PstreamReduceOps.H"
38 #include "OSspecific.H"
39 #include "Ostream.H"
40 #include "Time.H"
41 #include "regIOobject.H"
42 
43 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
44 
45 namespace Foam
46 {
47  defineTypeNameAndDebug(codedBase, 0);
48 }
49 
50 
51 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
52 
53 namespace Foam
54 {
55 
56 static inline void writeEntryIfPresent
57 (
58  Ostream& os,
59  const dictionary& dict,
60  const word& key
61 )
62 {
63  const entry* eptr = dict.findEntry(key, keyType::LITERAL);
64  if (!eptr)
65  {
66  // Nothing to do
67  }
68  else if (eptr->isDict())
69  {
70  eptr->dict().writeEntry(os);
71  }
72  else
73  {
74  const tokenList& toks = eptr->stream();
75 
76  if (!toks.empty()) // Could also check that it is a string-type
77  {
78  os.writeEntry(key, toks[0]);
79  }
80  }
81 }
82 
83 } // End namespace Foam
84 
85 
86 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
87 
88 void Foam::codedBase::writeCodeDict(Ostream& os, const dictionary& dict)
89 {
90  writeEntryIfPresent(os, dict, "codeContext");
91  writeEntryIfPresent(os, dict, "codeInclude");
92  writeEntryIfPresent(os, dict, "localCode");
93  writeEntryIfPresent(os, dict, "code");
94  writeEntryIfPresent(os, dict, "codeOptions");
95  writeEntryIfPresent(os, dict, "codeLibs");
96 }
97 
98 
99 const Foam::dictionary&
101 (
102  const objectRegistry& obr,
103  const word& dictName
104 )
105 {
106  auto* dictptr = obr.getObjectPtr<IOdictionary>(dictName);
107 
108  if (!dictptr)
109  {
110  dictptr = new IOdictionary
111  (
112  IOobject
113  (
114  dictName,
115  obr.time().system(),
116  obr,
120  )
121  );
122 
123  obr.store(dictptr);
124  }
125 
126  return *dictptr;
127 }
128 
129 
130 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
131 
132 void* Foam::codedBase::loadLibrary
133 (
134  const fileName& libPath,
135  const std::string& funcName,
136  const dynamicCodeContext& context
137 ) const
138 {
139  // Avoid compilation by loading an existing library
140 
141  void* handle = libs().open(libPath, false);
142 
143  if (!handle)
144  {
145  return handle;
146  }
147 
148  // Verify the loaded version and unload if needed
149 
150  // Manual execution of code after loading.
151  // This is mandatory for codedBase.
152 
153  const bool ok = libs().loadHook(handle, funcName, false);
154 
155  if (!ok)
156  {
157  FatalIOErrorInFunction(context.dict())
158  << "Failed symbol lookup " << funcName.c_str() << nl
159  << "from " << libPath << nl
160  << exit(FatalIOError);
161 
162  handle = nullptr;
163  if (!libs().close(libPath, false))
164  {
165  FatalIOErrorInFunction(context.dict())
166  << "Failed unloading library " << libPath << nl
167  << exit(FatalIOError);
168  }
169  }
170 
171  return handle;
172 }
173 
174 
175 void Foam::codedBase::unloadLibrary
176 (
177  const fileName& libPath,
178  const std::string& funcName,
179  const dynamicCodeContext& context
180 ) const
181 {
182  void* handle = libs().open(libPath, false);
183 
184  if (!handle)
185  {
186  return;
187  }
188 
189  // Manual execution of code before unloading.
190  // This is mandatory for codedBase.
191 
192  const bool ok = libs().unloadHook(handle, funcName, false);
193 
194  if (!ok)
195  {
196  IOWarningInFunction(context.dict())
197  << "Failed looking up symbol " << funcName << nl
198  << "from " << libPath << nl;
199  }
200 
201  if (!libs().close(libPath, false))
202  {
203  FatalIOErrorInFunction(context.dict())
204  << "Failed unloading library " << libPath << nl
205  << exit(FatalIOError);
206  }
207 }
208 
209 
210 void Foam::codedBase::createLibrary
211 (
212  dynamicCode& dynCode,
213  const dynamicCodeContext& context
214 ) const
215 {
216  // No library?
217  // The overall master should compile it (so only it needs a compiler) and
218  // - see if it appears at the other processors
219  // - or copy it across if it does not
220 
221  // Indicates NFS filesystem
222  const bool isNFS = (IOobject::fileModificationSkew > 0);
223 
224  if
225  (
226  (UPstream::master() || !isNFS)
227  && !dynCode.upToDate(context)
228  )
229  {
230  // Filter with this context
231  dynCode.reset(context);
232 
233  this->prepare(dynCode, context);
234 
235  if (!dynCode.copyOrCreateFiles(true))
236  {
237  FatalIOErrorInFunction(context.dict())
238  << "Failed writing files for" << nl
239  << dynCode.libRelPath() << nl
240  << exit(FatalIOError);
241  }
242 
243  if (!dynCode.wmakeLibso())
244  {
245  FatalIOErrorInFunction(context.dict())
246  << "Failed wmake " << dynCode.libRelPath() << nl
247  << exit(FatalIOError);
248  }
249  }
250 
251  if (isNFS)
252  {
253  // Wait for compile to finish before attempting filesystem polling
255  }
256 
257  const fileName libPath = dynCode.libPath();
258 
259  // Broadcast to distributed masters
260  if (fileHandler().distributed())
261  {
262  fileHandler().broadcastCopy
263  (
265  UPstream::master(fileHandler().comm()),
266  libPath,
267  libPath
268  );
269  }
271  dynamicCode::waitForFile(libPath, context.dict());
272 }
273 
274 
275 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
278 {
279  context_.setCodeContext(dict);
280 }
281 
282 
283 void Foam::codedBase::append(const std::string& str)
284 {
285  context_.append(str);
286 }
287 
288 
290 (
291  const word& name,
292  const dynamicCodeContext& context
293 ) const
294 {
296  (
297  "codedBase::updateLibrary()",
298  context.dict()
299  );
300 
301  // codeName: name + _<sha1>
302  // codeDir : name
303  dynamicCode dynCode
304  (
305  name + context.sha1().str(true),
306  name
307  );
308 
309  const fileName libPath = dynCode.libPath();
310 
311 
312  // The correct library was already loaded => we are done
313  if (libs().findLibrary(libPath))
314  {
315  return;
316  }
317 
318  DetailInfo
319  << "Using dynamicCode for " << this->description().c_str()
320  << " at line " << context.dict().startLineNumber()
321  << " in " << context.dict().name() << endl;
322 
323 
324  // Remove instantiation of fvPatchField provided by library
325  this->clearRedirect();
326 
327  // May need to unload old library
328  unloadLibrary(oldLibPath_, dlLibraryTable::basename(oldLibPath_), context);
329 
330  // Try loading an existing library (avoid compilation when possible)
331  void* lib = loadLibrary(libPath, dynCode.codeName(), context);
332 
333  if (returnReduceOr(lib == nullptr))
334  {
335  if (lib)
336  {
337  // Ensure consistency
338  unloadLibrary(libPath, dlLibraryTable::basename(libPath), context);
339  }
340 
341  createLibrary(dynCode, context);
342 
343  loadLibrary(libPath, dynCode.codeName(), context);
344  }
346  // Retain for future reference
347  oldLibPath_ = libPath;
348 }
349 
350 
352 (
353  const word& name,
355 ) const
356 {
357  updateLibrary(name, dynamicCodeContext(dict));
358 }
359 
360 
361 void Foam::codedBase::updateLibrary(const word& name) const
362 {
363  if (context_.good())
364  {
365  updateLibrary(name, context_);
366  }
367  else
368  {
369  updateLibrary(name, dynamicCodeContext(this->codeDict()));
370  }
371 }
372 
373 
374 // ************************************************************************* //
void writeEntry(Ostream &os) const
Write sub-dictionary with its dictName as its header.
Definition: dictionaryIO.C:157
dictionary dict
A class for handling file names.
Definition: fileName.H:72
Inter-processor communication reduction functions.
std::string str(const bool prefixed=false) const
The digest (40-byte) text representation, optionally with &#39;_&#39; prefix.
Definition: SHA1I.H:114
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
static float fileModificationSkew
Time skew (seconds) for file modification checks.
Definition: IOobject.H:348
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:56
static void waitForFile(const fileName &file, const dictionary &contextDict)
Wait for libPath() file to appear on sub-ranks.
Definition: dynamicCode.C:532
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
bool empty() const noexcept
True if List is empty (ie, size() is zero)
Definition: UList.H:666
const word dictName("faMeshDefinition")
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
virtual const dictionary & dict() const =0
Return dictionary, if entry is a dictionary.
bool store()
Register object with its registry and transfer ownership to the registry.
Definition: regIOobjectI.H:36
refPtr< fileOperation > fileHandler(std::nullptr_t)
Delete current file handler - forwards to fileOperation::handler()
Ostream & writeEntry(const keyType &key, const T &value)
Write a keyword/value entry.
Definition: Ostream.H:321
Ignore writing from objectRegistry::writeObject()
static label worldComm
Communicator for all ranks. May differ from commGlobal() if local worlds are in use.
Definition: UPstream.H:409
void append(const std::string &str)
Add content to SHA1 hashing.
Definition: codedBase.C:276
const fileName & name() const noexcept
The dictionary name.
Definition: dictionaryI.H:41
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:50
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
const dictionary & dict() const noexcept
Return the parent dictionary context.
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
static word basename(const fileName &libPath)
Library basename without leading &#39;lib&#39; or trailing &#39;.so&#39;.
A class for handling words, derived from Foam::string.
Definition: word.H:63
Type * getObjectPtr(const word &name, const bool recursive=false) const
Return non-const pointer to the object of the given Type, using a const-cast to have it behave like a...
static void * loadLibrary(const Foam::fileName &libName)
Definition: POSIX.C:111
const Time & time() const noexcept
Return time registry.
const word & system() const noexcept
Return system name.
Definition: TimePathsI.H:118
String literal.
Definition: keyType.H:82
#define DetailInfo
Definition: evalEntry.C:30
void setCodeContext(const dictionary &dict)
Set code context from a dictionary.
Definition: codedBase.C:270
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
OBJstream os(runTime.globalPath()/outputName)
defineTypeNameAndDebug(combustionModel, 0)
static void writeEntryIfPresent(Ostream &os, const dictionary &dict, const word &key)
Definition: codedBase.C:50
Tools for handling dynamic code compilation.
Definition: dynamicCode.H:56
Encapsulation of dynamic code dictionaries.
void updateLibrary(const word &name, const dynamicCodeContext &context) const
Update library as required, using the given context.
Definition: codedBase.C:283
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:627
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:103
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
const entry * findEntry(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find an entry (const access) with the given keyword.
Definition: dictionaryI.H:84
virtual bool isDict() const noexcept
Return true if this entry is a dictionary.
Definition: entry.H:287
#define IOWarningInFunction(ios)
Report an IO warning using Foam::Warning.
Registry of regIOobjects.
virtual ITstream & stream() const =0
Return token stream, if entry is a primitive entry.
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:59
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:172
Request registration (bool: true)
static void writeCodeDict(Ostream &os, const dictionary &dict)
Write code-dictionary contents.
Definition: codedBase.C:81
label startLineNumber() const
Return line number of first token in dictionary.
Definition: dictionary.C:198
virtual const dictionary & codeDict() const =0
Namespace for OpenFOAM.
A keyword and a list of tokens is an &#39;entry&#39;.
Definition: entry.H:63
static void barrier(const label communicator, UPstream::Request *req=nullptr)
Impose a synchronisation barrier (optionally non-blocking)
Definition: UPstream.C:83
const SHA1 & sha1() const noexcept
The SHA1 calculated from options, libs, include, code, etc.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...