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 _WIN32
38 #undef FOAM_USE_INOTIFY
39 #endif
40 
41 #ifdef FOAM_USE_INOTIFY
42  #include <unistd.h>
43  #include <sys/inotify.h>
44  #include <sys/ioctl.h>
45  #include <errno.h>
46  #define EVENT_SIZE ( sizeof (struct inotify_event) )
47  #define EVENT_LEN (EVENT_SIZE + 16)
48  #define EVENT_BUF_LEN ( 1024 * EVENT_LEN )
49 #endif
50 
51 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
52 
53 const Foam::Enum
54 <
56 >
58 ({
59  { fileState::UNMODIFIED, "unmodified" },
60  { fileState::MODIFIED, "modified" },
61  { fileState::DELETED, "deleted" },
62 });
63 
64 
65 namespace Foam
66 {
67  defineTypeNameAndDebug(fileMonitor, 0);
68 
69  //- Reduction operator for PackedList of fileState
71  {
72  public:
73  unsigned int operator()(const unsigned int x, const unsigned int y)
74  const
75  {
76  // x,y are sets of 2bits representing fileState
77 
78  unsigned int mask = 3u;
79  unsigned int shift = 0;
80  unsigned int result = 0;
81 
82  while (mask)
83  {
84  // Combine state
85  unsigned int xState = (x & mask) >> shift;
86  unsigned int yState = (y & mask) >> shift;
87 
88  // Combine and add to result. Combine is such that UNMODIFIED
89  // wins.
90  unsigned int state = min(xState, yState);
91  result |= (state << shift);
92 
93  shift += 2;
94  mask <<= 2;
95  }
96  return result;
97  }
98  };
99 
100  //- Combine operator for PackedList of fileState
102  {
103  public:
104  void operator()(unsigned int& x, const unsigned int y) const
105  {
106  x = reduceFileStates()(x, y);
107  }
108  };
109 
110 
111 
112  //- Internal tracking via stat(3p) or inotify(7)
114  {
115  public:
116 
117  const bool useInotify_;
118 
119  // For inotify
120 
121  //- File descriptor for the inotify instance
123 
124  //- Current watchIDs and corresponding directory id
127 
128  // For stat
129 
130  //- From watch descriptor to modified time
132 
133 
134 
135  //- Initialise inotify
136  inline fileMonitorWatcher(const bool useInotify, const label sz = 20)
137  :
138  useInotify_(useInotify),
139  inotifyFd_(-1)
140  {
141  if (useInotify_)
142  {
143  #ifdef FOAM_USE_INOTIFY
144  inotifyFd_ = inotify_init();
146  dirFiles_.setCapacity(sz);
147 
148  if (inotifyFd_ < 0)
149  {
150  static bool hasWarned = false;
151  if (!hasWarned)
152  {
153  hasWarned = true;
155  << "Failed allocating an inotify descriptor : "
156  << string(strerror(errno)) << endl
157  << " Please increase the number of allowable "
158  << "inotify instances" << endl
159  << " (/proc/sys/fs/inotify/max_user_instances"
160  << " on Linux)" << endl
161  << " , switch off runTimeModifiable." << endl
162  << " or compile this file without "
163  << "FOAM_USE_INOTIFY"
164  << " to use time stamps instead of inotify." << endl
165  << " Continuing without additional file"
166  << " monitoring."
167  << endl;
168  }
169  }
170  #else
172  << "You selected inotify but this file was compiled"
173  << " without FOAM_USE_INOTIFY"
174  << " Please select another fileModification test method"
175  << exit(FatalError);
176  #endif
177  }
178  else
179  {
180  lastMod_.setCapacity(sz);
181  }
182  }
183 
184  //- Remove all watches
186  {
187  #ifdef FOAM_USE_INOTIFY
188  if (useInotify_ && inotifyFd_ >= 0)
189  {
190  forAll(dirWatches_, i)
191  {
192  if (dirWatches_[i] >= 0)
193  {
194  if (inotify_rm_watch(inotifyFd_, int(dirWatches_[i])))
195  {
197  << "Failed deleting directory watch "
198  << dirWatches_[i] << endl;
199  }
200  }
201  }
202  }
203  #endif
204  }
205 
206  inline bool addWatch(const label watchFd, const fileName& fName)
207  {
208  if (useInotify_)
209  {
210  if (inotifyFd_ < 0)
211  {
212  return false;
213  }
214 
215  #ifdef FOAM_USE_INOTIFY
216  // Add/retrieve watch on directory containing file.
217  // Note that fName might be non-existing in special situations
218  // (master-only reading for IODictionaries)
219 
220  const fileName dir = fName.path();
221 
222  label dirWatchID = -1;
223  if (isDir(dir))
224  {
225  dirWatchID = inotify_add_watch
226  (
227  inotifyFd_,
228  dir.c_str(),
229  IN_CLOSE_WRITE
230  );
231 
232  if (dirWatchID < 0)
233  {
235  << "Failed adding watch " << watchFd
236  << " to directory " << fName << " due to "
237  << string(strerror(errno))
238  << exit(FatalError);
239  }
240  }
241 
242  if (watchFd < dirWatches_.size() && dirWatches_[watchFd] != -1)
243  {
244  // Reuse of watchFd : should have dir watchID set to -1.
246  << "Problem adding watch " << watchFd
247  << " to file " << fName
248  << abort(FatalError);
249  }
250 
251  dirWatches_(watchFd) = dirWatchID;
252  dirFiles_(watchFd) = fName.name();
253  #endif
254  }
255  else
256  {
257  if (watchFd < lastMod_.size() && lastMod_[watchFd] != 0)
258  {
259  // Reuse of watchFd : should have lastMod set to 0.
261  << "Problem adding watch " << watchFd
262  << " to file " << fName
263  << abort(FatalError);
264  }
265 
266  lastMod_(watchFd) = highResLastModified(fName);
267  }
268 
269  return true;
270  }
271 
272  inline bool removeWatch(const label watchFd)
273  {
274  if (useInotify_)
275  {
276  if (inotifyFd_ < 0)
277  {
278  return false;
279  }
280 
281  dirWatches_[watchFd] = -1;
282  }
283  else
284  {
285  lastMod_[watchFd] = 0;
286  }
287  return true;
288  }
289 
290  };
291 }
292 
293 
294 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
295 
296 void Foam::fileMonitor::checkFiles() const
297 {
298  if (useInotify_)
299  {
300  #ifdef FOAM_USE_INOTIFY
301  // Large buffer for lots of events
302  char buffer[EVENT_BUF_LEN];
303 
304  while (true)
305  {
306  struct timeval zeroTimeout = {0, 0};
307 
308  //- Pre-allocated structure containing file descriptors
309  fd_set fdSet;
310  // Add notify descriptor to select fd_set
311  FD_ZERO(&fdSet);
312  FD_SET(watcher_->inotifyFd_, &fdSet);
313 
314  int ready = select
315  (
316  watcher_->inotifyFd_+1, // num filedescriptors in fdSet
317  &fdSet, // fdSet with only inotifyFd
318  nullptr, // No writefds
319  nullptr, // No errorfds
320  &zeroTimeout // eNo timeout
321  );
322 
323  if (ready < 0)
324  {
326  << "Problem in issuing select."
327  << abort(FatalError);
328  }
329  else if (FD_ISSET(watcher_->inotifyFd_, &fdSet))
330  {
331  // Read events
332  ssize_t nBytes = ::read
333  (
334  watcher_->inotifyFd_,
335  buffer,
336  EVENT_BUF_LEN
337  );
338 
339  if (nBytes < 0)
340  {
342  << "read of " << watcher_->inotifyFd_
343  << " failed with " << label(nBytes)
344  << abort(FatalError);
345  }
346 
347  // Go through buffer, consuming events
348  int i = 0;
349  while (i < nBytes)
350  {
351  const struct inotify_event* inotifyEvent =
352  reinterpret_cast<const struct inotify_event*>
353  (
354  &buffer[i]
355  );
356 
357  //Pout<< "watchFd:" << inotifyEvent->wd << nl
358  // << "mask:" << inotifyEvent->mask << nl
359  // << endl;
360  //Pout<< "file:" << fileName(inotifyEvent->name) << endl;
361  //Pout<< "len:" << inotifyEvent->len << endl;
362 
363  if
364  (
365  (inotifyEvent->mask & IN_CLOSE_WRITE)
366  && inotifyEvent->len
367  )
368  {
369  // Search for file
370  forAll(watcher_->dirWatches_, i)
371  {
372  label id = watcher_->dirWatches_[i];
373  if
374  (
375  id == inotifyEvent->wd
376  && inotifyEvent->name == watcher_->dirFiles_[i]
377  )
378  {
379  // Correct directory and name
380  localState_[i] = MODIFIED;
381  }
382  }
383  }
384 
385  i += EVENT_SIZE + inotifyEvent->len;
386  }
387  }
388  else
389  {
390  // No data
391  return;
392  }
393  }
394  #endif
395  }
396  else
397  {
398  forAll(watcher_->lastMod_, watchFd)
399  {
400  double oldTime = watcher_->lastMod_[watchFd];
401 
402  if (oldTime != 0)
403  {
404  const fileName& fName = watchFile_[watchFd];
405  double newTime = highResLastModified(fName);
406 
407  if (newTime == 0)
408  {
409  localState_[watchFd] = DELETED;
410  }
411  else
412  {
413  if (newTime > (oldTime + IOobject::fileModificationSkew))
414  {
415  localState_[watchFd] = MODIFIED;
416  }
417  else
418  {
419  localState_[watchFd] = UNMODIFIED;
420  }
421  }
422  }
423  }
424  }
425 }
426 
427 
428 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
429 
430 
431 Foam::fileMonitor::fileMonitor(const bool useInotify)
432 :
433  useInotify_(useInotify),
434  localState_(20),
435  state_(20),
436  watchFile_(20),
437  freeWatchFds_(2),
438  watcher_(new fileMonitorWatcher(useInotify_, 20))
439 {}
440 
441 
442 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
443 
445 {}
446 
447 
448 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
449 
450 // Note: fName might not exist (on slaves if in master-only mode for
451 // regIOobject)
452 Foam::label Foam::fileMonitor::addWatch(const fileName& fName)
453 {
454  if (debug)
455  {
456  Pout<< "fileMonitor : adding watch on file " << fName << endl;
457  }
458 
459  label watchFd;
460 
461  if (freeWatchFds_.size())
462  {
463  watchFd = freeWatchFds_.remove();
464  }
465  else
466  {
467  watchFd = state_.size();
468  }
469 
470  watcher_->addWatch(watchFd, fName);
471 
472  if (debug)
473  {
474  Pout<< "fileMonitor : added watch " << watchFd << " on file "
475  << fName << endl;
476  }
477 
478  if (watchFd < 0)
479  {
481  << "could not add watch for file " << fName << endl;
482  }
483  else
484  {
485  localState_(watchFd) = UNMODIFIED;
486  state_(watchFd) = UNMODIFIED;
487  watchFile_(watchFd) = fName;
488  }
489  return watchFd;
490 }
491 
492 
493 bool Foam::fileMonitor::removeWatch(const label watchFd)
494 {
495  if (debug)
496  {
497  Pout<< "fileMonitor : removing watch " << watchFd << " on file "
498  << watchFile_[watchFd] << endl;
499  }
500 
501  freeWatchFds_.appendUniq(watchFd);
502 
503  return watcher_->removeWatch(watchFd);
504 }
505 
506 
507 const Foam::fileName& Foam::fileMonitor::getFile(const label watchFd) const
508 {
509  return watchFile_[watchFd];
510 }
511 
512 
514 const
515 {
516  return state_[watchFd];
517 }
518 
519 
521 (
522  const bool masterOnly,
523  const bool syncPar
524 ) const
525 {
526  if (Pstream::master() || !masterOnly)
527  {
528  // Update the localState_
529  checkFiles();
530  }
531 
532  if (syncPar)
533  {
534  // Pack local state (might be on master only)
535  PackedList<2> stats(state_.size(), MODIFIED);
536  if (Pstream::master() || !masterOnly)
537  {
538  forAll(state_, watchFd)
539  {
540  stats.set
541  (
542  watchFd,
543  static_cast<unsigned int>(localState_[watchFd])
544  );
545  }
546  }
547 
548 
549  // Scatter or reduce to synchronise state
550  if (masterOnly)
551  {
552  // Scatter
553  if (stats.storage().size() == 1)
554  {
555  Pstream::scatter(stats.storage()[0]);
556  }
557  else
558  {
559  Pstream::listCombineScatter(stats.storage());
560  }
561  }
562  else
563  {
564  // Reduce
565  if (stats.storage().size() == 1)
566  {
567  // Optimisation valid for most cases.
568  reduce(stats.storage()[0], reduceFileStates());
569  }
570  else
571  {
573  (
574  stats.storage(),
576  );
577  }
578  }
579 
580 
581  // Update synchronised state
582  forAll(state_, watchFd)
583  {
584  // Assign synchronised state
585  unsigned int stat = stats[watchFd];
586  state_[watchFd] = fileState(stat);
587 
588  if (!masterOnly)
589  {
590  // Give warning for inconsistent state
591  if (state_[watchFd] != localState_[watchFd])
592  {
593  if (debug)
594  {
595  Pout<< "fileMonitor : Delaying reading "
596  << watchFile_[watchFd]
597  << " due to inconsistent "
598  "file time-stamps between processors"
599  << endl;
600  }
601 
603  << "Delaying reading " << watchFile_[watchFd]
604  << " due to inconsistent "
605  "file time-stamps between processors" << endl;
606  }
607  }
608  }
609  }
610  else
611  {
612  state_ = localState_;
613  }
614 }
615 
616 
617 void Foam::fileMonitor::setUnmodified(const label watchFd)
618 {
619  state_[watchFd] = UNMODIFIED;
620  localState_[watchFd] = UNMODIFIED;
621 
622  if (!useInotify_)
623  {
624  watcher_->lastMod_[watchFd] = highResLastModified(watchFile_[watchFd]);
625  }
626 }
627 
628 
629 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::combineReduceFileStates
Combine operator for PackedList of fileState.
Definition: fileMonitor.C:101
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::DynamicList< label >
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:136
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:104
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
fileMonitor.H
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::PackedList< 2 >
Foam::fileMonitorWatcher::addWatch
bool addWatch(const label watchFd, const fileName &fName)
Definition: fileMonitor.C:206
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:272
Foam::fileMonitorWatcher
Internal tracking via stat(3p) or inotify(7)
Definition: fileMonitor.C:113
Foam::reduceFileStates::operator()
unsigned int operator()(const unsigned int x, const unsigned int y) const
Definition: fileMonitor.C:73
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:185
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