codeStream.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-2017 OpenFOAM Foundation
9  Copyright (C) 2018-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 "codeStream.H"
31 #include "StringStream.H"
32 #include "dynamicCode.H"
33 #include "dynamicCodeContext.H"
34 #include "Time.H"
35 
36 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
37 
38 namespace Foam
39 {
40 namespace functionEntries
41 {
42  defineTypeNameAndDebug(codeStream, 0);
43 
45  (
46  functionEntry,
47  codeStream,
48  execute,
49  dictionaryIstream,
50  codeStream
51  );
52 
54  (
55  functionEntry,
56  codeStream,
57  execute,
58  primitiveEntryIstream,
59  codeStream
60  );
61 } // End namespace functionEntries
62 } // End namespace Foam
63 
64 
65 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
66 
68 (
69  const dictionary& dict
70 )
71 {
72  const baseIOdictionary& d = static_cast<const baseIOdictionary&>
73  (
74  dict.topDict()
75  );
76  return const_cast<Time&>(d.time()).libs();
77 }
78 
79 
81 (
82  const dictionary& dict
83 )
84 {
85  const dictionary& topDict = dict.topDict();
86 
87  if (isA<baseIOdictionary>(topDict))
88  {
89  const baseIOdictionary& d = static_cast<const baseIOdictionary&>
90  (
91  topDict
92  );
93 
94  DebugPout
95  << "codeStream : baseIOdictionary:" << dict.name()
96  << " master-only-reading:" << d.globalObject() << endl;
97 
98  return d.globalObject();
99  }
100 
101  DebugPout
102  << "codeStream : not a baseIOdictionary:" << dict.name()
103  << " master-only-reading:" << regIOobject::masterOnlyReading << endl;
104 
105  // Fall back to regIOobject::masterOnlyReading
106  return regIOobject::masterOnlyReading;
107 }
108 
109 
112 (
113  const dictionary& parentDict,
114  const dictionary& codeDict
115 )
116 {
117  // get code, codeInclude, codeOptions
118  dynamicCodeContext context(codeDict);
119 
120  // codeName: codeStream + _<sha1>
121  // codeDir : _<sha1>
122  std::string sha1Str(context.sha1().str(true));
123  dynamicCode dynCode("codeStream" + sha1Str, sha1Str);
124 
125  // Load library if not already loaded
126  // Version information is encoded in the libPath (encoded with the SHA1)
127  const fileName libPath = dynCode.libPath();
128 
129  // see if library is loaded
130  void* lib = nullptr;
131 
132  const dictionary& topDict = parentDict.topDict();
133 
134  if (isA<baseIOdictionary>(topDict))
135  {
136  lib = libs(parentDict).findLibrary(libPath);
137  }
138 
139  // nothing loaded
140  // avoid compilation if possible by loading an existing library
141  if (!lib)
142  {
143  DetailInfo
144  << "Using #codeStream with " << libPath << endl;
145 
146  if (isA<baseIOdictionary>(topDict))
147  {
148  // Cached access to dl libs. Guarantees clean up upon destruction
149  // of Time.
150  dlLibraryTable& dlLibs = libs(parentDict);
151  if (dlLibs.open(libPath, false))
152  {
153  lib = dlLibs.findLibrary(libPath);
154  }
155  }
156  else
157  {
158  // Uncached opening of libPath. Do not complain if cannot be loaded
159  lib = dlOpen(libPath, false);
160  }
161  }
162 
163 
164  // create library if required
165  if (!lib)
166  {
167  const bool create =
168  Pstream::master()
169  || (regIOobject::fileModificationSkew <= 0); // not NFS
170 
171  if (create)
172  {
173  if (!dynCode.upToDate(context))
174  {
175  // filter with this context
176  dynCode.reset(context);
177 
178  // compile filtered C template
179  dynCode.addCompileFile(codeTemplateC);
180 
181  // define Make/options
182  dynCode.setMakeOptions
183  (
184  "EXE_INC = -g \\\n"
185  + context.options()
186  + "\n\nLIB_LIBS = \\\n"
187  " -lOpenFOAM \\\n"
188  + context.libs()
189  );
190 
191  if (!dynCode.copyOrCreateFiles(true))
192  {
193  FatalIOErrorInFunction(parentDict)
194  << "Failed writing files for" << nl
195  << dynCode.libRelPath() << nl
196  << exit(FatalIOError);
197  }
198  }
199 
200  if (!dynCode.wmakeLibso())
201  {
202  FatalIOErrorInFunction(parentDict)
203  << "Failed wmake " << dynCode.libRelPath() << nl
204  << exit(FatalIOError);
205  }
206  }
207 
208  //- Only block if we're not doing master-only reading. (flag set by
209  // regIOobject::read, baseIOdictionary constructor)
210  if
211  (
212  !doingMasterOnlyReading(topDict)
213  && regIOobject::fileModificationSkew > 0
214  )
215  {
216  //- Since the library has only been compiled on the master the
217  // other nodes need to pick this library up through NFS
218  // We do this by just polling a few times using the
219  // fileModificationSkew.
220 
221  off_t mySize = Foam::fileSize(libPath);
222  off_t masterSize = mySize;
223  Pstream::scatter(masterSize);
224 
225  for
226  (
227  label iter = 0;
228  iter < regIOobject::maxFileModificationPolls;
229  iter++
230  )
231  {
232  DebugPout
233  << "on processor " << Pstream::myProcNo()
234  << "masterSize:" << masterSize
235  << " and localSize:" << mySize
236  << endl;
237 
238  if (mySize == masterSize)
239  {
240  break;
241  }
242  else if (mySize > masterSize)
243  {
244  FatalIOErrorInFunction(context.dict())
245  << "Excessive size when reading (NFS mounted) library "
246  << nl << libPath << nl
247  << "on processor " << Pstream::myProcNo()
248  << " detected size " << mySize
249  << " whereas master size is " << masterSize
250  << " bytes." << nl
251  << "If your case is NFS mounted increase"
252  << " fileModificationSkew or maxFileModificationPolls;"
253  << nl << "If your case is not NFS mounted"
254  << " (so distributed) set fileModificationSkew"
255  << " to 0"
256  << exit(FatalIOError);
257  }
258  else
259  {
260  DebugPout
261  << "Local file " << libPath
262  << " not of same size (" << mySize
263  << ") as master ("
264  << masterSize << "). Waiting for "
265  << regIOobject::fileModificationSkew
266  << " seconds." << endl;
267 
268  Foam::sleep(regIOobject::fileModificationSkew);
269 
270  // Recheck local size
271  mySize = Foam::fileSize(libPath);
272  }
273  }
274 
275 
276  // Finished doing iterations. Do final check
277  if (mySize != masterSize)
278  {
279  FatalIOErrorInFunction(context.dict())
280  << "Cannot read (NFS mounted) library " << nl
281  << libPath << nl
282  << "on processor " << Pstream::myProcNo()
283  << " detected size " << mySize
284  << " whereas master size is " << masterSize
285  << " bytes." << nl
286  << "If your case is NFS mounted increase"
287  << " fileModificationSkew or maxFileModificationPolls;"
288  << nl << "If your case is not NFS mounted"
289  << " (so distributed) set fileModificationSkew"
290  << " to 0"
291  << exit(FatalIOError);
292  }
293 
294  DebugPout
295  << "on processor " << Pstream::myProcNo()
296  << " after waiting: have masterSize:" << masterSize
297  << " and localSize:" << mySize << endl;
298  }
299 
300  if (isA<baseIOdictionary>(topDict))
301  {
302  // Cached access to dl libs. Guarantees clean up upon destruction
303  // of Time.
304  dlLibraryTable& dlLibs = libs(parentDict);
305 
306  DebugPout
307  << "Opening cached dictionary:" << libPath << endl;
308 
309  if (!dlLibs.open(libPath, false))
310  {
311  FatalIOErrorInFunction(parentDict)
312  << "Failed loading library " << libPath << nl
313  << "Did you add all libraries to the 'libs' entry"
314  << " in system/controlDict?"
315  << exit(FatalIOError);
316  }
317 
318  lib = dlLibs.findLibrary(libPath);
319  }
320  else
321  {
322  // Uncached opening of libPath
323  DebugPout
324  << "Opening uncached dictionary:" << libPath << endl;
325 
326  lib = dlOpen(libPath, true);
327  }
328  }
329 
330  bool haveLib = lib;
331  if (!doingMasterOnlyReading(topDict))
332  {
333  reduce(haveLib, andOp<bool>());
334  }
335 
336  if (!haveLib)
337  {
338  FatalIOErrorInFunction(parentDict)
339  << "Failed loading library " << libPath
340  << " on some processors."
341  << exit(FatalIOError);
342  }
343 
344 
345  // Find the function handle in the library
346  streamingFunctionType function =
347  reinterpret_cast<streamingFunctionType>
348  (
349  dlSym(lib, dynCode.codeName())
350  );
351 
352 
353  if (!function)
354  {
355  FatalIOErrorInFunction(parentDict)
356  << "Failed looking up symbol " << dynCode.codeName()
357  << " in library " << lib << exit(FatalIOError);
358  }
359 
360  return function;
361 }
362 
363 
365 (
366  const dictionary& parentDict,
367  Istream& is
368 )
369 {
370  DetailInfo
371  << "Using #codeStream at line " << is.lineNumber()
372  << " in file " << parentDict.name() << endl;
373 
374  dynamicCode::checkSecurity
375  (
376  "functionEntries::codeStream::evaluate(..)",
377  parentDict
378  );
379 
380  // Get code dictionary
381  dictionary codeDict("#codeStream", parentDict, is);
382 
383  // Use function to write stream
384  OStringStream os(is.format());
385 
386  streamingFunctionType function = getFunction(parentDict, codeDict);
387  (*function)(os, parentDict);
388 
389  // Return evaluated content as string
390  return os.str();
391 }
392 
393 
394 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
395 
397 (
398  const dictionary& parentDict,
400  Istream& is
401 )
402 {
403  IStringStream result(evaluate(parentDict, is));
404  entry.read(parentDict, result);
405 
406  return true;
407 }
408 
409 
411 (
412  dictionary& parentDict,
413  Istream& is
414 )
415 {
416  IStringStream result(evaluate(parentDict, is));
417  parentDict.read(result);
418 
419  return true;
420 }
421 
422 
423 // ************************************************************************* //
Foam::dynamicCodeContext::libs
const string & libs() const
Return the code-libs.
Definition: dynamicCodeContext.H:146
Foam::entry
A keyword and a list of tokens is an 'entry'.
Definition: entry.H:67
Foam::PackedList::reset
void reset()
Clear all bits but do not adjust the addressable size.
Definition: PackedListI.H:495
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
Foam::dlLibraryTable
A table of dynamically loaded libraries.
Definition: dlLibraryTable.H:51
Foam::dlLibraryTable::findLibrary
void * findLibrary(const fileName &libName)
Find the handle of the named library.
Definition: dlLibraryTable.C:334
Foam::Time
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:73
Foam::primitiveEntry
A keyword and a list of tokens comprise a primitiveEntry. A primitiveEntry can be read,...
Definition: primitiveEntry.H:62
Foam::fileName
A class for handling file names.
Definition: fileName.H:69
Foam::SHA1::str
std::string str(const bool prefixed=false) const
The digest (40-byte) text representation, optionally with '_' prefix.
Definition: SHA1I.H:123
codeStream.H
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::functionEntries::codeStream::evaluate
static string evaluate(const dictionary &parentDict, Istream &is)
Evaluate dynamically compiled code, returning result as string.
Definition: codeStream.C:365
Foam::dynamicCodeContext
Encapsulation of dynamic code dictionaries.
Definition: dynamicCodeContext.H:53
Foam::functionEntries::defineTypeNameAndDebug
defineTypeNameAndDebug(codeStream, 0)
Foam::IOstreamOption::format
streamFormat format() const noexcept
Get the current stream format.
Definition: IOstreamOption.H:273
StringStream.H
Input/output from string buffers.
Foam::FatalIOError
IOerror FatalIOError
Foam::functionEntries::codeStream::execute
static bool execute(const dictionary &parentDict, primitiveEntry &entry, Istream &is)
Execute in a primitiveEntry context.
Definition: codeStream.C:397
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:337
Foam::string
A class for handling character strings derived from std::string.
Definition: string.H:73
dynamicCodeContext.H
Foam::IOobject::time
const Time & time() const
Return time.
Definition: IOobject.C:438
Foam::functionEntries::codeStream::getFunction
static streamingFunctionType getFunction(const dictionary &parentDict, const dictionary &codeDict)
Construct, compile, load and return streaming function.
Definition: codeStream.C:112
Foam::functionEntries::codeStream::streamingFunctionType
void(* streamingFunctionType)(Ostream &, const dictionary &)
Interpreter function type.
Definition: codeStream.H:123
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::Istream
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition: Istream.H:61
Foam::dlSym
void * dlSym(void *handle, const std::string &symbol)
Lookup a symbol in a dlopened library using handle to library.
Definition: OSspecific.H:284
Foam::functionEntries::codeStream::libs
static dlLibraryTable & libs(const dictionary &dict)
Helper function: access to dlLibraryTable of Time.
Definition: codeStream.C:68
Foam::IOobject::globalObject
bool globalObject() const
Is object same for all processors?
Definition: IOobjectI.H:100
Foam::baseIOdictionary
baseIOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO function...
Definition: baseIOdictionary.H:56
addToMemberFunctionSelectionTable.H
Macros for easy insertion into member function selection tables.
Foam::andOp
Definition: ops.H:233
dynamicCode.H
DetailInfo
#define DetailInfo
Definition: evalEntry.C:36
Foam::dictionary::topDict
const dictionary & topDict() const
Return the top of the tree.
Definition: dictionary.C:192
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
Foam::dynamicCodeContext::options
const string & options() const
Return the code-options.
Definition: dynamicCodeContext.H:140
Foam::functionEntries::addNamedToMemberFunctionSelectionTable
addNamedToMemberFunctionSelectionTable(functionEntry, calcEntry, execute, dictionaryIstream, calc)
Foam::IStringStream
Input from string buffer, using a ISstream.
Definition: StringStream.H:112
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::dlLibraryTable::open
bool open(bool verbose=true)
Definition: dlLibraryTable.C:227
DebugPout
#define DebugPout
Report an information message using Foam::Pout.
Definition: messageStream.H:361
Foam::dictionary::read
bool read(Istream &is)
Read dictionary from Istream.
Definition: dictionaryIO.C:141
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::functionEntries::codeStream::doingMasterOnlyReading
static bool doingMasterOnlyReading(const dictionary &dict)
Helper: access IOobject for master-only-reading functionality.
Definition: codeStream.C:81
Time.H
Foam::nl
constexpr char nl
Definition: Ostream.H:372
Foam::OStringStream
Output to string buffer, using a OSstream.
Definition: StringStream.H:189
Foam::IOstream::lineNumber
label lineNumber() const
Const access to the current stream line number.
Definition: IOstream.H:301
FatalIOErrorInFunction
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:375
Foam::dynamicCodeContext::sha1
const SHA1 & sha1() const
Return SHA1 calculated from options, libs, include, code.
Definition: dynamicCodeContext.H:164
Foam::sleep
unsigned int sleep(const unsigned int sec)
Sleep for the specified number of seconds.
Definition: MSwindows.C:1096
Foam::dlOpen
void * dlOpen(const fileName &libName, const bool check=true)
Open a shared library and return handle to library.
Definition: MSwindows.C:1206
Foam::stringOps::evaluate
string evaluate(const std::string &s, size_t pos=0, size_t len=std::string::npos)
Definition: stringOpsEvaluate.C:37