fileMonitor.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) 2018-2021 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 "fileMonitor.H"
30 #include "IOstreams.H"
31 #include "Pstream.H"
32 #include "PackedList.H"
33 #include "PstreamReduceOps.H"
34 #include "OSspecific.H"
35 #include "IOobject.H" // for fileModificationSkew symbol
36 
37 #ifdef FOAM_USE_INOTIFY
38  #include <unistd.h>
39  #include <sys/inotify.h>
40  #include <sys/ioctl.h>
41  #include <errno.h>
42  #define EVENT_SIZE ( sizeof (struct inotify_event) )
43  #define EVENT_LEN (EVENT_SIZE + 16)
44  #define EVENT_BUF_LEN ( 1024 * EVENT_LEN )
45 #endif
46 
47 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
48 
49 const Foam::Enum
50 <
52 >
54 ({
55  { fileState::UNMODIFIED, "unmodified" },
56  { fileState::MODIFIED, "modified" },
57  { fileState::DELETED, "deleted" },
58 });
59 
60 
61 namespace Foam
62 {
63  defineTypeNameAndDebug(fileMonitor, 0);
64 
65  //- Reduction operator for PackedList of fileState
66  class reduceFileStates
67  {
68  public:
69  unsigned int operator()(const unsigned int x, const unsigned int y)
70  const
71  {
72  // x,y are sets of 2bits representing fileState
73 
74  unsigned int mask = 3u;
75  unsigned int shift = 0;
76  unsigned int result = 0;
77 
78  while (mask)
79  {
80  // Combine state
81  unsigned int xState = (x & mask) >> shift;
82  unsigned int yState = (y & mask) >> shift;
83 
84  // Combine and add to result. Combine is such that UNMODIFIED
85  // wins.
86  unsigned int state = min(xState, yState);
87  result |= (state << shift);
88 
89  shift += 2;
90  mask <<= 2;
91  }
92  return result;
93  }
94  };
95 
96  //- Combine operator for PackedList of fileState
97  class combineReduceFileStates
98  {
99  public:
100  void operator()(unsigned int& x, const unsigned int y) const
101  {
102  x = reduceFileStates()(x, y);
103  }
104  };
105 
106 
107 
108  //- Internal tracking via stat(3p) or inotify(7)
109  class fileMonitorWatcher
110  {
111  public:
112 
113  const bool useInotify_;
114 
115  // For inotify
116 
117  //- File descriptor for the inotify instance
118  int inotifyFd_;
119 
120  //- Current watchIDs and corresponding directory id
121  DynamicList<label> dirWatches_;
122  DynamicList<fileName> dirFiles_;
123 
124  // For stat
125 
126  //- From watch descriptor to modified time
127  DynamicList<double> lastMod_;
128 
129 
130 
131  //- Initialise inotify
132  inline fileMonitorWatcher(const bool useInotify, const label sz = 20)
133  :
134  useInotify_(useInotify),
135  inotifyFd_(-1)
136  {
137  if (useInotify_)
138  {
139  #ifdef FOAM_USE_INOTIFY
140  inotifyFd_ = inotify_init();
142  dirFiles_.setCapacity(sz);
143 
144  if (inotifyFd_ < 0)
145  {
146  static bool hasWarned = false;
147  if (!hasWarned)
148  {
149  hasWarned = true;
151  << "Failed allocating an inotify descriptor : "
152  << string(strerror(errno)) << endl
153  << " Please increase the number of allowable "
154  << "inotify instances" << endl
155  << " (/proc/sys/fs/inotify/max_user_instances"
156  << " on Linux)" << endl
157  << " , switch off runTimeModifiable." << endl
158  << " or compile this file without "
159  << "FOAM_USE_INOTIFY"
160  << " to use time stamps instead of inotify." << endl
161  << " Continuing without additional file"
162  << " monitoring."
163  << endl;
164  }
165  }
166  #else
168  << "You selected inotify but this file was compiled"
169  << " without FOAM_USE_INOTIFY"
170  << " Please select another fileModification test method"
171  << exit(FatalError);
172  #endif
173  }
174  else
175  {
176  lastMod_.setCapacity(sz);
177  }
178  }
179 
180  //- Remove all watches
182  {
183  #ifdef FOAM_USE_INOTIFY
184  if (useInotify_ && inotifyFd_ >= 0)
185  {
186  forAll(dirWatches_, i)
187  {
188  if (dirWatches_[i] >= 0)
189  {
190  if (inotify_rm_watch(inotifyFd_, int(dirWatches_[i])))
191  {
193  << "Failed deleting directory watch "
194  << dirWatches_[i] << endl;
195  }
196  }
197  }
198  }
199  #endif
200  }
201 
202  inline bool addWatch(const label watchFd, const fileName& fName)
203  {
204  if (useInotify_)
205  {
206  if (inotifyFd_ < 0)
207  {
208  return false;
209  }
210 
211  #ifdef FOAM_USE_INOTIFY
212  // Add/retrieve watch on directory containing file.
213  // Note that fName might be non-existing in special situations
214  // (master-only reading for IODictionaries)
215 
216  const fileName dir = fName.path();
217 
218  label dirWatchID = -1;
219  if (isDir(dir))
220  {
221  dirWatchID = inotify_add_watch
222  (
223  inotifyFd_,
224  dir.c_str(),
225  IN_CLOSE_WRITE
226  );
227 
228  if (dirWatchID < 0)
229  {
231  << "Failed adding watch " << watchFd
232  << " to directory " << fName << " due to "
233  << string(strerror(errno))
234  << exit(FatalError);
235  }
236  }
237 
238  if (watchFd < dirWatches_.size() && dirWatches_[watchFd] != -1)
239  {
240  // Reuse of watchFd : should have dir watchID set to -1.
242  << "Problem adding watch " << watchFd
243  << " to file " << fName
244  << abort(FatalError);
245  }
246 
247  dirWatches_(watchFd) = dirWatchID;
248  dirFiles_(watchFd) = fName.name();
249  #endif
250  }
251  else
252  {
253  if (watchFd < lastMod_.size() && lastMod_[watchFd] != 0)
254  {
255  // Reuse of watchFd : should have lastMod set to 0.
257  << "Problem adding watch " << watchFd
258  << " to file " << fName
259  << abort(FatalError);
260  }
261 
262  lastMod_(watchFd) = highResLastModified(fName);
263  }
264 
265  return true;
266  }
267 
268  inline bool removeWatch(const label watchFd)
269  {
270  if (useInotify_)
271  {
272  if (inotifyFd_ < 0)
273  {
274  return false;
275  }
276 
277  dirWatches_[watchFd] = -1;
278  }
279  else
280  {
281  lastMod_[watchFd] = 0;
282  }
283  return true;
284  }
285 
286  };
287 }
288 
289 
290 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
291 
292 void Foam::fileMonitor::checkFiles() const
293 {
294  if (useInotify_)
295  {
296  #ifdef FOAM_USE_INOTIFY
297  // Large buffer for lots of events
298  char buffer[EVENT_BUF_LEN];
299 
300  while (true)
301  {
302  struct timeval zeroTimeout = {0, 0};
303 
304  //- Pre-allocated structure containing file descriptors
305  fd_set fdSet;
306  // Add notify descriptor to select fd_set
307  FD_ZERO(&fdSet);
308  FD_SET(watcher_->inotifyFd_, &fdSet);
309 
310  int ready = select
311  (
312  watcher_->inotifyFd_+1, // num filedescriptors in fdSet
313  &fdSet, // fdSet with only inotifyFd
314  nullptr, // No writefds
315  nullptr, // No errorfds
316  &zeroTimeout // eNo timeout
317  );
318 
319  if (ready < 0)
320  {
322  << "Problem in issuing select."
323  << abort(FatalError);
324  }
325  else if (FD_ISSET(watcher_->inotifyFd_, &fdSet))
326  {
327  // Read events
328  ssize_t nBytes = ::read
329  (
330  watcher_->inotifyFd_,
331  buffer,
332  EVENT_BUF_LEN
333  );
334 
335  if (nBytes < 0)
336  {
338  << "read of " << watcher_->inotifyFd_
339  << " failed with " << label(nBytes)
340  << abort(FatalError);
341  }
342 
343  // Go through buffer, consuming events
344  int i = 0;
345  while (i < nBytes)
346  {
347  const struct inotify_event* inotifyEvent =
348  reinterpret_cast<const struct inotify_event*>
349  (
350  &buffer[i]
351  );
352 
353  //Pout<< "watchFd:" << inotifyEvent->wd << nl
354  // << "mask:" << inotifyEvent->mask << nl
355  // << endl;
356  //Pout<< "file:" << fileName(inotifyEvent->name) << endl;
357  //Pout<< "len:" << inotifyEvent->len << endl;
358 
359  if
360  (
361  (inotifyEvent->mask & IN_CLOSE_WRITE)
362  && inotifyEvent->len
363  )
364  {
365  // Search for file
366  forAll(watcher_->dirWatches_, i)
367  {
368  label id = watcher_->dirWatches_[i];
369  if
370  (
371  id == inotifyEvent->wd
372  && inotifyEvent->name == watcher_->dirFiles_[i]
373  )
374  {
375  // Correct directory and name
376  localState_[i] = MODIFIED;
377  }
378  }
379  }
380 
381  i += EVENT_SIZE + inotifyEvent->len;
382  }
383  }
384  else
385  {
386  // No data
387  return;
388  }
389  }
390  #endif
391  }
392  else
393  {
394  forAll(watcher_->lastMod_, watchFd)
395  {
396  double oldTime = watcher_->lastMod_[watchFd];
397 
398  if (oldTime != 0)
399  {
400  const fileName& fName = watchFile_[watchFd];
401  double newTime = highResLastModified(fName);
402 
403  if (newTime == 0)
404  {
405  localState_[watchFd] = DELETED;
406  }
407  else
408  {
409  if (newTime > (oldTime + IOobject::fileModificationSkew))
410  {
411  localState_[watchFd] = MODIFIED;
412  }
413  else
414  {
415  localState_[watchFd] = UNMODIFIED;
416  }
417  }
418  }
419  }
420  }
421 }
422 
423 
424 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
425 
426 
427 Foam::fileMonitor::fileMonitor(const bool useInotify)
428 :
429  useInotify_(useInotify),
430  localState_(20),
431  state_(20),
432  watchFile_(20),
433  freeWatchFds_(2),
434  watcher_(new fileMonitorWatcher(useInotify_, 20))
435 {}
436 
437 
438 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
439 
441 {}
442 
443 
444 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
445 
446 // Note: fName might not exist (on slaves if in master-only mode for
447 // regIOobject)
448 Foam::label Foam::fileMonitor::addWatch(const fileName& fName)
449 {
450  if (debug)
451  {
452  Pout<< "fileMonitor : adding watch on file " << fName << endl;
453  }
454 
455  label watchFd;
456 
457  if (freeWatchFds_.size())
458  {
459  watchFd = freeWatchFds_.remove();
460  }
461  else
462  {
463  watchFd = state_.size();
464  }
465 
466  watcher_->addWatch(watchFd, fName);
467 
468  if (debug)
469  {
470  Pout<< "fileMonitor : added watch " << watchFd << " on file "
471  << fName << endl;
472  }
473 
474  if (watchFd < 0)
475  {
477  << "could not add watch for file " << fName << endl;
478  }
479  else
480  {
481  localState_(watchFd) = UNMODIFIED;
482  state_(watchFd) = UNMODIFIED;
483  watchFile_(watchFd) = fName;
484  }
485  return watchFd;
486 }
487 
488 
489 bool Foam::fileMonitor::removeWatch(const label watchFd)
490 {
491  if (debug)
492  {
493  Pout<< "fileMonitor : removing watch " << watchFd << " on file "
494  << watchFile_[watchFd] << endl;
495  }
496 
497  freeWatchFds_.appendUniq(watchFd);
498 
499  return watcher_->removeWatch(watchFd);
500 }
501 
502 
503 const Foam::fileName& Foam::fileMonitor::getFile(const label watchFd) const
504 {
505  return watchFile_[watchFd];
506 }
507 
508 
510 const
511 {
512  return state_[watchFd];
513 }
514 
515 
517 (
518  const bool masterOnly,
519  const bool syncPar
520 ) const
521 {
522  if (Pstream::master() || !masterOnly)
523  {
524  // Update the localState_
525  checkFiles();
526  }
527 
528  if (syncPar)
529  {
530  // Pack local state (might be on master only)
531  PackedList<2> stats(state_.size(), MODIFIED);
532  if (Pstream::master() || !masterOnly)
533  {
534  forAll(state_, watchFd)
535  {
536  stats.set
537  (
538  watchFd,
539  static_cast<unsigned int>(localState_[watchFd])
540  );
541  }
542  }
543 
544 
545  // Scatter or reduce to synchronise state
546  if (masterOnly)
547  {
548  // Scatter
549  if (stats.storage().size() == 1)
550  {
551  Pstream::scatter(stats.storage()[0]);
552  }
553  else
554  {
555  Pstream::listCombineScatter(stats.storage());
556  }
557  }
558  else
559  {
560  // Reduce
561  if (stats.storage().size() == 1)
562  {
563  // Optimisation valid for most cases.
564  reduce(stats.storage()[0], reduceFileStates());
565  }
566  else
567  {
569  (
570  stats.storage(),
571  combineReduceFileStates()
572  );
573  }
574  }
575 
576 
577  // Update synchronised state
578  forAll(state_, watchFd)
579  {
580  // Assign synchronised state
581  unsigned int stat = stats[watchFd];
582  state_[watchFd] = fileState(stat);
583 
584  if (!masterOnly)
585  {
586  // Give warning for inconsistent state
587  if (state_[watchFd] != localState_[watchFd])
588  {
589  if (debug)
590  {
591  Pout<< "fileMonitor : Delaying reading "
592  << watchFile_[watchFd]
593  << " due to inconsistent "
594  "file time-stamps between processors"
595  << endl;
596  }
597 
599  << "Delaying reading " << watchFile_[watchFd]
600  << " due to inconsistent "
601  "file time-stamps between processors" << endl;
602  }
603  }
604  }
605  }
606  else
607  {
608  state_ = localState_;
609  }
610 }
611 
612 
613 void Foam::fileMonitor::setUnmodified(const label watchFd)
614 {
615  state_[watchFd] = UNMODIFIED;
616  localState_[watchFd] = UNMODIFIED;
617 
618  if (!useInotify_)
619  {
620  watcher_->lastMod_[watchFd] = highResLastModified(watchFile_[watchFd]);
621  }
622 }
623 
624 
625 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
OSspecific.H
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Foam::Enum
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: IOstreamOption.H:57
IOstreams.H
Useful combination of include files which define Sin, Sout and Serr and the use of IO streams general...
PackedList.H
Foam::fileName
A class for handling file names.
Definition: fileName.H:73
Foam::fileMonitor::UNMODIFIED
Definition: fileMonitor.H:75
Foam::fileName::path
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:176
Foam::fileMonitor::getFile
const fileName & getFile(const label watchFd) const
Get name of file being watched.
Definition: fileMonitor.C:507
Foam::fileMonitor::removeWatch
bool removeWatch(const label watchFd)
Remove file to watch. Return true if successful.
Definition: fileMonitor.C:493
Foam::fileMonitorWatcher::dirFiles_
DynamicList< fileName > dirFiles_
Definition: fileMonitor.C:126
Foam::fileName::name
static std::string name(const std::string &str)
Return basename (part beyond last /), including its extension.
Definition: fileNameI.H:199
Foam::read
bool read(const char *buf, int32_t &val)
Same as readInt32.
Definition: int32.H:108
Foam::fileMonitorWatcher::fileMonitorWatcher
fileMonitorWatcher(const bool useInotify, const label sz=20)
Initialise inotify.
Definition: fileMonitor.C:132
fileMonitor.H
oldTime
Info<< "Creating field kinetic energy K\n"<< endl;volScalarField K("K", 0.5 *magSqr(U));if(U.nOldTimes()){ volVectorField *Uold=&U.oldTime();volScalarField *Kold=&K.oldTime();*Kold==0.5 *magSqr(*Uold);while(Uold->nOldTimes()) { Uold=&Uold-> oldTime()
Definition: createK.H:12
Foam::fileMonitor::addWatch
label addWatch(const fileName &)
Add file to watch. Return watch descriptor.
Definition: fileMonitor.C:452
Foam::UPstream::master
static bool master(const label communicator=worldComm)
Am I the master process.
Definition: UPstream.H:457
Foam::Pstream::scatter
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
Definition: gatherScatter.C:150
Foam::fileMonitor::fileState
fileState
Enumeration defining the file state.
Definition: fileMonitor.H:73
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::string
A class for handling character strings derived from std::string.
Definition: string.H:76
Foam::Pout
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Foam::min
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
Foam::DynamicList::setCapacity
void setCapacity(const label len)
Alter the size of the underlying storage.
Definition: DynamicListI.H:303
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::fileMonitor::~fileMonitor
~fileMonitor()
Destructor.
Definition: fileMonitor.C:444
Foam::fileMonitorWatcher::inotifyFd_
int inotifyFd_
File descriptor for the inotify instance.
Definition: fileMonitor.C:122
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::fileMonitor::fileStateNames_
static const Enum< fileState > fileStateNames_
Definition: fileMonitor.H:80
Foam::combineReduceFileStates::operator()
void operator()(unsigned int &x, const unsigned int y) const
Definition: fileMonitor.C:100
Foam::fileMonitor::MODIFIED
Definition: fileMonitor.H:76
Foam::highResLastModified
double highResLastModified(const fileName &, const bool followLink=true)
Return time of last file modification.
Definition: MSwindows.C:699
Foam::IOobject::fileModificationSkew
static float fileModificationSkew
Time skew (seconds) for file modification checks.
Definition: IOobject.H:306
IOobject.H
Foam::FatalError
error FatalError
Pstream.H
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::abort
errorManip< error > abort(error &err)
Definition: errorManip.H:144
PstreamReduceOps.H
Inter-processor communication reduction functions.
Foam::reduceFileStates
Reduction operator for PackedList of fileState.
Definition: fileMonitor.C:70
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::fileMonitorWatcher::lastMod_
DynamicList< double > lastMod_
From watch descriptor to modified time.
Definition: fileMonitor.C:131
Foam::Pstream::listCombineGather
static void listCombineGather(const List< commsStruct > &comms, List< T > &Value, const CombineOp &cop, const int tag, const label comm)
Definition: combineGatherScatter.C:290
Foam::fileMonitorWatcher::dirWatches_
DynamicList< label > dirWatches_
Current watchIDs and corresponding directory id.
Definition: fileMonitor.C:125
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
Foam::fileMonitor::getState
fileState getState(const label watchFd) const
Check state using handle.
Definition: fileMonitor.C:513
Foam::fileMonitor::setUnmodified
void setUnmodified(const label watchFd)
Reset state (e.g. after having read it) using handle.
Definition: fileMonitor.C:617
Foam::fileMonitorWatcher::addWatch
bool addWatch(const label watchFd, const fileName &fName)
Definition: fileMonitor.C:202
x
x
Definition: LISASMDCalcMethod2.H:52
Foam::fileMonitor::updateStates
void updateStates(const bool masterOnly, const bool syncPar) const
Check state of all files. Updates state_.
Definition: fileMonitor.C:521
Foam::fileMonitorWatcher::removeWatch
bool removeWatch(const label watchFd)
Definition: fileMonitor.C:268
Foam::reduceFileStates::operator()
unsigned int operator()(const unsigned int x, const unsigned int y) const
Definition: fileMonitor.C:69
Foam::fileMonitor::DELETED
Definition: fileMonitor.H:77
Foam::Pstream::listCombineScatter
static void listCombineScatter(const List< commsStruct > &comms, List< T > &Value, const int tag, const label comm)
Scatter data. Reverse of combineGather.
Definition: combineGatherScatter.C:432
Foam::fileMonitorWatcher::useInotify_
const bool useInotify_
Definition: fileMonitor.C:117
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:328
Foam::fileMonitorWatcher::~fileMonitorWatcher
~fileMonitorWatcher()
Remove all watches.
Definition: fileMonitor.C:181
Foam::isDir
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: MSwindows.C:643
y
scalar y
Definition: LISASMDCalcMethod1.H:14