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-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 "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 {
39  defineTypeNameAndDebug(codeStream, 1);
40 
42  (
43  functionEntry,
44  codeStream,
45  execute,
46  dictionaryIstream
47  );
48 
50  (
51  functionEntry,
52  codeStream,
53  execute,
54  primitiveEntryIstream
55  );
56 }
57 }
58 
59 
60 const Foam::word Foam::functionEntries::codeStream::codeTemplateC =
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 (!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  Info<< "Using #codeStream at line " << is.lineNumber()
325  << " in file " << parentDict.name() << endl;
326 
328  (
329  "functionEntries::codeStream::execute(..)",
330  parentDict
331  );
332 
333  // Get code dictionary
334  // must reference parent for stringOps::expand to work nicely
335  const dictionary codeDict("#codeStream", parentDict, is);
336 
337  const streamingFunctionType function = getFunction(parentDict, codeDict);
338 
339  // Use function to write stream
340  OStringStream os(is.format());
341  (*function)(os, parentDict);
342 
343  // Return the string containing the results of the code execution
344  return os.str();
345 }
346 
347 
348 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
349 
351 (
352  dictionary& parentDict,
353  Istream& is
354 )
355 {
356  return insert(parentDict, run(parentDict, is));
357 }
358 
359 
361 (
362  const dictionary& parentDict,
363  primitiveEntry& thisEntry,
364  Istream& is
365 )
366 {
367  return insert(parentDict, thisEntry, run(parentDict, is));
368 }
369 
370 
371 // ************************************************************************* //
virtual const fileName & name() const
Return the name of the stream.
Definition: OSstream.H:82
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
An Istream is an abstract base class for all input systems (streams, files, token lists etc)...
Definition: Istream.H:57
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
addToMemberFunctionSelectionTable(functionEntry, calcEntry, execute, dictionaryIstream)
dlLibraryTable libs
Table of loaded dynamic libraries.
void insert(const scalar, DynamicList< floatScalar > &)
Append scalar to given DynamicList.
A keyword and a list of tokens is a &#39;primitiveEntry&#39;. An primitiveEntry can be read, written and printed, and the types and values of its tokens analysed.
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
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 open(const fileName &libName, const bool verbose=true)
Open the named library, optionally with warnings if problems occur.
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
static bool execute(dictionary &parentDict, Istream &)
Execute the functionEntry in a sub-dict context.
Definition: codeStream.C:351
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:335
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
void * findLibrary(const fileName &libName)
Find the handle of the named library.
Macros for easy insertion into member function selection tables.
static float fileModificationSkew
Definition: regIOobject.H:101
messageStream Info
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
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
void * dlOpen(const fileName &lib, const bool check=true)
Open a shared library. Return handle to library. Print error message.
Definition: POSIX.C:1236
void * dlSym(void *handle, const std::string &symbol)
Lookup a symbol in a dlopened library using handle to library.
Definition: POSIX.C:1276
defineTypeNameAndDebug(calcEntry, 0)
Namespace for OpenFOAM.
IOerror FatalIOError