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 {
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
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:
113  const bool useInotify_;
114 
115  // For inotify
116 
117  //- File descriptor for the inotify instance
118  int inotifyFd_;
120  //- Current watchIDs and corresponding directory id
123 
124  // For stat
126  //- From watch descriptor to modified time
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
181  inline ~fileMonitorWatcher()
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 = state_.size();
456 
457  if (!freeWatchFds_.empty())
458  {
459  watchFd = freeWatchFds_.back();
460  freeWatchFds_.pop_back();
461  }
462 
463  watcher_->addWatch(watchFd, fName);
464 
465  if (debug)
466  {
467  Pout<< "fileMonitor : added watch " << watchFd << " on file "
468  << fName << endl;
469  }
470 
471  if (watchFd < 0)
472  {
474  << "could not add watch for file " << fName << endl;
475  }
476  else
477  {
478  localState_(watchFd) = UNMODIFIED;
479  state_(watchFd) = UNMODIFIED;
480  watchFile_(watchFd) = fName;
481  }
482  return watchFd;
483 }
484 
485 
486 bool Foam::fileMonitor::removeWatch(const label watchFd)
487 {
488  if (debug)
489  {
490  Pout<< "fileMonitor : removing watch " << watchFd << " on file "
491  << watchFile_[watchFd] << endl;
492  }
493 
494  freeWatchFds_.appendUniq(watchFd);
495 
496  return watcher_->removeWatch(watchFd);
497 }
498 
499 
500 const Foam::fileName& Foam::fileMonitor::getFile(const label watchFd) const
501 {
502  return watchFile_[watchFd];
503 }
504 
505 
507 const
508 {
509  return state_[watchFd];
510 }
512 
514 (
515  const bool masterOnly,
516  const bool syncPar
517 ) const
518 {
519  if (Pstream::master() || !masterOnly)
520  {
521  // Update the localState_
522  checkFiles();
523  }
524 
525  if (syncPar)
526  {
527  // Pack local state (might be on master only)
528  PackedList<2> stats(state_.size(), MODIFIED);
529  if (Pstream::master() || !masterOnly)
530  {
531  forAll(state_, watchFd)
532  {
533  stats.set
534  (
535  watchFd,
536  static_cast<unsigned int>(localState_[watchFd])
537  );
538  }
539  }
540 
541 
542  // Scatter or reduce to synchronise state
543  if (masterOnly)
544  {
545  // Broadcast
546  if (stats.storage().size() == 1)
547  {
548  Pstream::broadcast(stats.storage()[0]);
549  }
550  else
551  {
552  Pstream::broadcast(stats.storage());
553  }
554  }
555  else
556  {
557  // Reduce
558  if (stats.storage().size() == 1)
559  {
560  // Optimisation valid for most cases.
561  reduce(stats.storage()[0], reduceFileStates());
562  }
563  else
564  {
566  (
567  stats.storage(),
569  );
570  }
571  }
572 
573 
574  // Update synchronised state
575  forAll(state_, watchFd)
576  {
577  // Assign synchronised state
578  unsigned int stat = stats[watchFd];
579  state_[watchFd] = fileState(stat);
580 
581  if (!masterOnly)
582  {
583  // Give warning for inconsistent state
584  if (state_[watchFd] != localState_[watchFd])
585  {
586  if (debug)
587  {
588  Pout<< "fileMonitor : Delaying reading "
589  << watchFile_[watchFd]
590  << " due to inconsistent "
591  "file time-stamps between processors"
592  << endl;
593  }
594 
596  << "Delaying reading " << watchFile_[watchFd]
597  << " due to inconsistent "
598  "file time-stamps between processors" << endl;
599  }
600  }
601  }
602  }
603  else
604  {
605  state_ = localState_;
606  }
607 }
608 
609 
610 void Foam::fileMonitor::setUnmodified(const label watchFd)
611 {
612  state_[watchFd] = UNMODIFIED;
613  localState_[watchFd] = UNMODIFIED;
614 
615  if (!useInotify_)
616  {
617  watcher_->lastMod_[watchFd] = highResLastModified(watchFile_[watchFd]);
618  }
619 }
620 
622 // ************************************************************************* //
DynamicList< double > lastMod_
From watch descriptor to modified time.
Definition: fileMonitor.C:132
static const Enum< fileState > fileStateNames_
Definition: fileMonitor.H:78
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:71
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:108
static float fileModificationSkew
Time skew (seconds) for file modification checks.
Definition: IOobject.H:331
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:62
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:578
fileState getState(const label watchFd) const
Check state using handle.
Definition: fileMonitor.C:517
fileState
Enumeration defining the file state.
Definition: fileMonitor.H:71
void setUnmodified(const label watchFd)
Reset state (e.g. after having read it) using handle.
Definition: fileMonitor.C:621
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:487
void setCapacity(const label len)
Alter the size of the underlying storage.
Definition: DynamicListI.H:301
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:124
Reduction operator for PackedList of fileState.
Definition: fileMonitor.C:61
static void broadcast(Type &value, const label comm=UPstream::worldComm)
Broadcast content (contiguous or non-contiguous) to all processes in communicator.
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:414
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:139
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: POSIX.C:860
void operator()(unsigned int &x, const unsigned int y) const
Definition: fileMonitor.C:97
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:497
void updateStates(const bool masterOnly, const bool syncPar) const
Check state of all files. Updates state_.
Definition: fileMonitor.C:525
const fileName & getFile(const label watchFd) const
Get name of file being watched.
Definition: fileMonitor.C:511
unsigned int operator()(const unsigned int x, const unsigned int y) const
Definition: fileMonitor.C:64
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:119
int debug
Static debugging option.
defineTypeNameAndDebug(combustionModel, 0)
~fileMonitor()
Destructor.
Definition: fileMonitor.C:451
#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...
label addWatch(const fileName &)
Add file to watch. Return watch descriptor.
Definition: fileMonitor.C:459
DynamicList< fileName > dirFiles_
Definition: fileMonitor.C:125
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1037
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.
bool removeWatch(const label watchFd)
Definition: fileMonitor.C:277
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:190
Combine operator for PackedList of fileState.
Definition: fileMonitor.C:94
bool addWatch(const label watchFd, const fileName &fName)
Definition: fileMonitor.C:211
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