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