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-2019 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"
28 #include "IStringStream.H"
29 #include "OStringStream.H"
30 #include "dynamicCode.H"
31 #include "dynamicCodeContext.H"
32 #include "Time.H"
33 
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38 namespace functionEntries
39 {
40  defineTypeNameAndDebug(codeStream, 0);
41 
43  (
44  functionEntry,
45  codeStream,
46  execute,
47  dictionaryIstream
48  );
49 
51  (
52  functionEntry,
53  codeStream,
54  execute,
55  primitiveEntryIstream
56  );
57 
58 }
59 }
60 
61 
62 const Foam::word Foam::functionEntries::codeStream::codeTemplateC =
63  "codeStreamTemplate.C";
64 
65 
66 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
67 
68 Foam::dlLibraryTable& Foam::functionEntries::codeStream::libs
69 (
70  const dictionary& dict
71 )
72 {
73  const baseIOdictionary& d = static_cast<const baseIOdictionary&>
74  (
75  dict.topDict()
76  );
77  return const_cast<Time&>(d.time()).libs();
78 }
79 
80 
81 bool Foam::functionEntries::codeStream::doingMasterOnlyReading
82 (
83  const dictionary& dict
84 )
85 {
86  const dictionary& topDict = dict.topDict();
87 
88  if (isA<baseIOdictionary>(topDict))
89  {
90  const baseIOdictionary& d = static_cast<const baseIOdictionary&>
91  (
92  topDict
93  );
94 
95  if (debug)
96  {
97  Pout<< "codeStream : baseIOdictionary:" << dict.name()
98  << " master-only-reading:" << d.global()
99  << endl;
100  }
101 
102  return d.global();
103  }
104  else
105  {
106  if (debug)
107  {
108  Pout<< "codeStream : not a baseIOdictionary:" << dict.name()
109  << " master-only-reading:" << false
110  << endl;
111  }
112 
113  // Fall back to false
114  return false;
115  }
116 }
117 
118 
119 Foam::functionEntries::codeStream::streamingFunctionType
120 Foam::functionEntries::codeStream::getFunction
121 (
122  const dictionary& parentDict,
123  const dictionary& codeDict
124 )
125 {
126  // get code, codeInclude, ...
127  dynamicCodeContext context(codeDict, {"code", "codeInclude", "localCode"});
128 
129  // codeName: codeStream + _<sha1>
130  // codeDir : _<sha1>
131  std::string sha1Str(context.sha1().str(true));
132  dynamicCode dynCode("codeStream" + sha1Str, sha1Str);
133 
134  // Load library if not already loaded
135  // Version information is encoded in the libPath (encoded with the SHA1)
136  const fileName libPath = dynCode.libPath();
137 
138  // see if library is loaded
139  void* lib = nullptr;
140 
141  const dictionary& topDict = parentDict.topDict();
142  if (isA<baseIOdictionary>(topDict))
143  {
144  lib = libs(parentDict).findLibrary(libPath);
145  }
146 
147  if (!lib)
148  {
149  Info<< "Using #codeStream with " << libPath << endl;
150  }
151 
152 
153  // nothing loaded
154  // avoid compilation if possible by loading an existing library
155  if (!lib)
156  {
157  if (isA<baseIOdictionary>(topDict))
158  {
159  // Cached access to dl libs. Guarantees clean up upon destruction
160  // of Time.
161  dlLibraryTable& dlLibs = libs(parentDict);
162  if (dlLibs.open(libPath, false))
163  {
164  lib = dlLibs.findLibrary(libPath);
165  }
166  }
167  else
168  {
169  // Uncached opening of libPath. Do not complain if cannot be loaded
170  lib = dlOpen(libPath, false);
171  }
172  }
173 
174 
175  // create library if required
176  if (!lib)
177  {
178  bool create =
180  || (regIOobject::fileModificationSkew <= 0); // not NFS
181 
182  if (create)
183  {
184  if (!dynCode.upToDate(context))
185  {
186  // filter with this context
187  dynCode.reset(context);
188 
189  // compile filtered C template
190  dynCode.addCompileFile(codeTemplateC);
191 
192  // define Make/options
193  dynCode.setMakeOptions
194  (
195  "EXE_INC = -g \\\n"
196  + context.options()
197  + "\n\nLIB_LIBS = \\\n"
198  + " -lOpenFOAM \\\n"
199  + context.libs()
200  );
201 
202  if (!dynCode.copyOrCreateFiles(true))
203  {
205  (
206  parentDict
207  ) << "Failed writing files for" << nl
208  << dynCode.libRelPath() << nl
209  << exit(FatalIOError);
210  }
211  }
212 
213  if (!dynCode.wmakeLibso())
214  {
216  (
217  parentDict
218  ) << "Failed wmake " << dynCode.libRelPath() << nl
219  << exit(FatalIOError);
220  }
221  }
222 
223  //- Only block if we're not doing master-only reading. (flag set by
224  // regIOobject::read, baseIOdictionary constructor)
225  if
226  (
227  !doingMasterOnlyReading(topDict)
229  )
230  {
231  //- Since the library has only been compiled on the master the
232  // other nodes need to pick this library up through NFS
233  // We do this by just polling a few times using the
234  // fileModificationSkew.
235 
236  off_t mySize = Foam::fileSize(libPath);
237  off_t masterSize = mySize;
238  Pstream::scatter(masterSize);
239 
240  if (debug)
241  {
242  Pout<< endl<< "on processor " << Pstream::myProcNo()
243  << " have masterSize:" << masterSize
244  << " and localSize:" << mySize
245  << endl;
246  }
247 
248 
249  if (mySize < masterSize)
250  {
251  if (debug)
252  {
253  Pout<< "Local file " << libPath
254  << " not of same size (" << mySize
255  << ") as master ("
256  << masterSize << "). Waiting for "
258  << " seconds." << endl;
259  }
261 
262  // Recheck local size
263  mySize = Foam::fileSize(libPath);
264 
265  if (mySize < masterSize)
266  {
268  (
269  parentDict
270  ) << "Cannot read (NFS mounted) library " << nl
271  << libPath << nl
272  << "on processor " << Pstream::myProcNo()
273  << " detected size " << mySize
274  << " whereas master size is " << masterSize
275  << " bytes." << nl
276  << "If your case is not NFS mounted"
277  << " (so distributed) set fileModificationSkew"
278  << " to 0"
279  << exit(FatalIOError);
280  }
281  }
282 
283  if (debug)
284  {
285  Pout<< endl<< "on processor " << Pstream::myProcNo()
286  << " after waiting: have masterSize:" << masterSize
287  << " and localSize:" << mySize
288  << endl;
289  }
290  }
291 
292  if (isA<baseIOdictionary>(topDict))
293  {
294  // Cached access to dl libs. Guarantees clean up upon destruction
295  // of Time.
296  dlLibraryTable& dlLibs = libs(parentDict);
297 
298  if (debug)
299  {
300  Pout<< "Opening cached dictionary:" << libPath << endl;
301  }
302 
303  if (!dlLibs.open(libPath, false))
304  {
306  (
307  parentDict
308  ) << "Failed loading library " << libPath << nl
309  << "Did you add all libraries to the 'libs' entry"
310  << " in system/controlDict?"
311  << exit(FatalIOError);
312  }
313 
314  lib = dlLibs.findLibrary(libPath);
315  }
316  else
317  {
318  // Uncached opening of libPath
319  if (debug)
320  {
321  Pout<< "Opening uncached dictionary:" << libPath << endl;
322  }
323  lib = dlOpen(libPath, true);
324  }
325  }
326 
327  bool haveLib = lib;
328  if (!doingMasterOnlyReading(topDict))
329  {
330  reduce(haveLib, andOp<bool>());
331  }
332 
333  if (!haveLib)
334  {
336  (
337  parentDict
338  ) << "Failed loading library " << libPath
339  << " on some processors."
340  << exit(FatalIOError);
341  }
342 
343 
344  // Find the function handle in the library
345  streamingFunctionType function =
346  reinterpret_cast<streamingFunctionType>
347  (
348  dlSym(lib, dynCode.codeName())
349  );
350 
351 
352  if (!function)
353  {
355  (
356  parentDict
357  ) << "Failed looking up symbol " << dynCode.codeName()
358  << " in library " << lib << exit(FatalIOError);
359  }
360 
361  return function;
362 }
363 
364 
365 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
366 
368 (
369  const dictionary& parentDict,
370  primitiveEntry& entry,
371  Istream& is
372 )
373 {
374  Info<< "Using #codeStream at line " << is.lineNumber()
375  << " in file " << parentDict.name() << endl;
376 
378  (
379  "functionEntries::codeStream::execute(..)",
380  parentDict
381  );
382 
383  // get code dictionary
384  // must reference parent for stringOps::expand to work nicely
385  dictionary codeDict("#codeStream", parentDict, is);
386 
387  streamingFunctionType function = getFunction(parentDict, codeDict);
388 
389  // use function to write stream
390  OStringStream os(is.format());
391  (*function)(os, parentDict);
392 
393  // get the entry from this stream
394  IStringStream resultStream(os.str());
395  entry.read(parentDict, resultStream);
396 
397  return true;
398 }
399 
400 
402 (
403  dictionary& parentDict,
404  Istream& is
405 )
406 {
407  Info<< "Using #codeStream at line " << is.lineNumber()
408  << " in file " << parentDict.name() << endl;
409 
411  (
412  "functionEntries::codeStream::execute(..)",
413  parentDict
414  );
415 
416  // get code dictionary
417  // must reference parent for stringOps::expand to work nicely
418  dictionary codeDict("#codeStream", parentDict, is);
419 
420  streamingFunctionType function = getFunction(parentDict, codeDict);
421 
422  // use function to write stream
423  OStringStream os(is.format());
424  (*function)(os, parentDict);
425 
426  // get the entry from this stream
427  IStringStream resultStream(os.str());
428  parentDict.read(resultStream);
429 
430  return true;
431 }
432 
433 
434 // ************************************************************************* //
virtual const fileName & name() const
Return the name of the stream.
Definition: OSstream.H:82
bool read(Istream &)
Read dictionary from Istream.
Definition: dictionaryIO.C:125
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
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:256
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
label lineNumber() const
Return current stream line number.
Definition: IOstream.H:438
addToMemberFunctionSelectionTable(functionEntry, calcEntry, execute, dictionaryIstream)
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.
const fileName & name() const
Return the dictionary name.
Definition: dictionary.H:111
A class for handling words, derived from string.
Definition: word.H:59
streamFormat format() const
Return current stream format.
Definition: IOstream.H:377
A table of dynamically loaded libraries.
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:265
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:402
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:331
Input from memory buffer stream.
Definition: IStringStream.H:49
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
Macros for easy insertion into member function selection tables.
static float fileModificationSkew
Definition: regIOobject.H:106
messageStream Info
virtual bool read(const dictionary &, Istream &)
Read tokens from the given stream.
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
void * dlOpen(const fileName &lib, const bool check=true)
Open a shared library. Return handle to library. Print error message.
Definition: POSIX.C:1240
Output to memory buffer stream.
Definition: OStringStream.H:49
void * dlSym(void *handle, const std::string &symbol)
Lookup a symbol in a dlopened library using handle to library.
Definition: POSIX.C:1280
defineTypeNameAndDebug(calcEntry, 0)
Namespace for OpenFOAM.
IOerror FatalIOError