codeStream.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-2022 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 "codeStream.H"
27 #include "dynamicCode.H"
28 #include "dynamicCodeContext.H"
29 #include "Time.H"
30 #include "OSspecific.H"
32 
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 
35 namespace Foam
36 {
37 namespace functionEntries
38 {
40 
42  (
44  codeStream,
45  execute,
46  dictionaryIstream
47  );
48 
50  (
52  codeStream,
53  execute,
54  primitiveEntryIstream
55  );
56 }
57 }
58 
59 
61  "codeStreamTemplate.C";
62 
63 
64 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
65 
66 bool Foam::functionEntries::codeStream::masterOnlyRead
67 (
68  const dictionary& dict
69 )
70 {
71  const dictionary& topDict = dict.topDict();
72 
73  if (debug)
74  {
75  Pout<< "codeStream : dictionary:" << dict.name()
76  << " master-only-reading:" << topDict.global()
77  << endl;
78  }
79 
80  return topDict.global();
81 }
82 
83 
84 Foam::functionEntries::codeStream::streamingFunctionType
85 Foam::functionEntries::codeStream::getFunction
86 (
87  const dictionary& parentDict,
88  const dictionary& codeDict
89 )
90 {
91  // Get code, codeInclude, ...
92  const dynamicCodeContext context
93  (
94  codeDict,
95  {"code", "codeInclude", "localCode"}
96  );
97 
98  // codeName: codeStream + _<sha1>
99  // codeDir : _<sha1>
100  const std::string sha1Str(context.sha1().str(true));
101  dynamicCode dynCode("codeStream" + sha1Str, sha1Str);
102 
103  // Load library if not already loaded
104  // Version information is encoded in the libPath (encoded with the SHA1)
105  const fileName libPath = dynCode.libPath();
106 
107  // See if library is loaded
108  void* lib = libs.findLibrary(libPath);
109 
110  if (debug && !lib)
111  {
112  Info<< "Using #codeStream with " << libPath << endl;
113  }
114 
115  // Nothing loaded
116  // avoid compilation if possible by loading an existing library
117  if (!lib)
118  {
119  // Cached access to dl libs.
120  // Guarantees clean up upon destruction of Time.
121  if (libs.open(libPath, false))
122  {
123  lib = libs.findLibrary(libPath);
124  }
125  else
126  {
127  // Uncached opening of libPath. Do not complain if cannot be loaded
128  lib = dlOpen(libPath, false);
129  }
130  }
131 
132  // Create library if required
133  if (!lib)
134  {
135  const bool create =
137  || (regIOobject::fileModificationSkew <= 0); // not NFS
138 
139  if (create)
140  {
141  if (!dynCode.upToDate(context))
142  {
143  // Filter with this context
144  dynCode.reset(context);
145 
146  // Compile filtered C template
147  dynCode.addCompileFile(codeTemplateC);
148 
149  // Define Make/options
150  dynCode.setMakeOptions
151  (
152  "EXE_INC = -g \\\n"
153  + context.options()
154  + "\n\nLIB_LIBS = \\\n"
155  + " -lOpenFOAM \\\n"
156  + context.libs()
157  );
158 
159  if (!dynCode.copyOrCreateFiles(true))
160  {
162  (
163  parentDict
164  ) << "Failed writing files for" << nl
165  << dynCode.libRelPath() << nl
166  << exit(FatalIOError);
167  }
168  }
169 
170  if (!dynCode.wmakeLibso())
171  {
173  (
174  parentDict
175  ) << "Failed wmake " << dynCode.libRelPath() << nl
176  << exit(FatalIOError);
177  }
178  }
179 
180  // Only block if not master only reading of a global dictionary
181  if
182  (
183  !masterOnlyRead(parentDict)
185  )
186  {
187  // Since the library has only been compiled on the master the
188  // other nodes need to pick this library up through NFS
189  // We do this by just polling a few times using the
190  // fileModificationSkew.
191 
192  off_t mySize = Foam::fileSize(libPath);
193  off_t masterSize = mySize;
194  Pstream::scatter(masterSize);
195 
196  if (debug)
197  {
198  Pout<< endl<< "on processor " << Pstream::myProcNo()
199  << " have masterSize:" << masterSize
200  << " and localSize:" << mySize
201  << endl;
202  }
203 
204 
205  if (mySize < masterSize)
206  {
207  if (debug)
208  {
209  Pout<< "Local file " << libPath
210  << " not of same size (" << mySize
211  << ") as master ("
212  << masterSize << "). Waiting for "
214  << " seconds." << endl;
215  }
217 
218  // Recheck local size
219  mySize = Foam::fileSize(libPath);
220 
221  if (mySize < masterSize)
222  {
224  (
225  parentDict
226  ) << "Cannot read (NFS mounted) library " << nl
227  << libPath << nl
228  << "on processor " << Pstream::myProcNo()
229  << " detected size " << mySize
230  << " whereas master size is " << masterSize
231  << " bytes." << nl
232  << "If your case is not NFS mounted"
233  << " (so distributed) set fileModificationSkew"
234  << " to 0"
235  << exit(FatalIOError);
236  }
237  }
238 
239  if (debug)
240  {
241  Pout<< endl<< "on processor " << Pstream::myProcNo()
242  << " after waiting: have masterSize:" << masterSize
243  << " and localSize:" << mySize
244  << endl;
245  }
246  }
247 
248  if (libs.open(libPath, false))
249  {
250  if (debug)
251  {
252  Pout<< "Opening cached dictionary:" << libPath << endl;
253  }
254 
255  lib = libs.findLibrary(libPath);
256  }
257  else
258  {
259  // Uncached opening of libPath
260  if (debug)
261  {
262  Pout<< "Opening uncached dictionary:" << libPath << endl;
263  }
264 
265  lib = dlOpen(libPath, true);
266  }
267  }
268 
269  if (!lib)
270  {
272  (
273  parentDict
274  ) << "Failed loading library " << libPath << nl
275  << "Did you add all libraries to the 'libs' entry"
276  << " in system/controlDict?"
277  << exit(FatalIOError);
278  }
279 
280  bool haveLib = lib;
281  if (!masterOnlyRead(parentDict))
282  {
283  reduce(haveLib, andOp<bool>());
284  }
285 
286  if (!haveLib)
287  {
289  (
290  parentDict
291  ) << "Failed loading library " << libPath
292  << " on some processors."
293  << exit(FatalIOError);
294  }
295 
296 
297  // Find the function handle in the library
298  const streamingFunctionType function =
299  reinterpret_cast<streamingFunctionType>
300  (
301  dlSym(lib, dynCode.codeName())
302  );
303 
304 
305  if (!function)
306  {
308  (
309  parentDict
310  ) << "Failed looking up symbol " << dynCode.codeName()
311  << " in library " << lib << exit(FatalIOError);
312  }
313 
314  return function;
315 }
316 
317 
318 Foam::string Foam::functionEntries::codeStream::run
319 (
320  const dictionary& parentDict,
321  Istream& is
322 )
323 {
324  if (debug)
325  {
326  Info<< "Using #codeStream at line " << is.lineNumber()
327  << " in file " << parentDict.name() << endl;
328  }
329 
331  (
332  "functionEntries::codeStream::execute(..)",
333  parentDict
334  );
335 
336  // Get code dictionary
337  // must reference parent for stringOps::expand to work nicely
338  const dictionary codeDict("#codeStream", parentDict, is);
339 
340  const streamingFunctionType function = getFunction(parentDict, codeDict);
341 
342  // Use function to write stream
343  OStringStream os(is.format());
344  (*function)(os, parentDict);
345 
346  // Return the string containing the results of the code execution
347  return os.str();
348 }
349 
350 
351 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
352 
354 (
355  dictionary& parentDict,
356  Istream& is
357 )
358 {
359  return insert(parentDict, run(parentDict, is));
360 }
361 
362 
364 (
365  const dictionary& parentDict,
366  primitiveEntry& thisEntry,
367  Istream& is
368 )
369 {
370  return insert(parentDict, thisEntry, run(parentDict, is));
371 }
372 
373 
374 // ************************************************************************* //
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Macros for easy insertion into member function selection tables.
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition: Istream.H:60
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
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
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
const dictionary & topDict() const
Return the top of the tree.
Definition: dictionary.C:564
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.
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:58
Dictionary entry that contains C++ OpenFOAM code that is compiled to generate the entry itself....
Definition: codeStream.H:116
static bool execute(dictionary &parentDict, Istream &)
Execute the functionEntry in a sub-dict context.
Definition: codeStream.C:354
static const word codeTemplateC
Name of the C code template to be used.
Definition: codeStream.H:149
A functionEntry causes entries to be added/manipulated on the specified dictionary given an input str...
Definition: functionEntry.H:66
A keyword and a list of tokens is a 'primitiveEntry'. An primitiveEntry can be read,...
const dictionary & dict() const
This entry is not a dictionary,.
static float fileModificationSkew
Definition: regIOobject.H:101
A class for handling character strings derived from std::string.
Definition: string.H:79
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
defineTypeNameAndDebug(includeFvConstraintEntry, 0)
addToMemberFunctionSelectionTable(functionEntry, includeFvConstraintEntry, execute, dictionaryIstream)
void insert(const scalar, DynamicList< floatScalar > &)
Append scalar to given DynamicList.
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
void * dlOpen(const fileName &lib, const bool check=true)
Open a shared library. Return handle to library. Print error message.
Definition: POSIX.C:1236
messageStream Info
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
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
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
dictionary dict