fileMonitor.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2018 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "fileMonitor.H"
27 #include "IOstreams.H"
28 #include "Pstream.H"
29 #include "PackedList.H"
30 #include "PstreamReduceOps.H"
31 #include "OSspecific.H"
32 #include "regIOobject.H" // for fileModificationSkew symbol
33 
34 #ifdef FOAM_USE_INOTIFY
35  #include <unistd.h>
36  #include <sys/inotify.h>
37  #include <sys/ioctl.h>
38  #include <errno.h>
39  #define EVENT_SIZE ( sizeof (struct inotify_event) )
40  #define EVENT_LEN (EVENT_SIZE + 16)
41  #define EVENT_BUF_LEN ( 1024 * EVENT_LEN )
42 #else
43  #include "OSspecific.H"
44 #endif
45 
46 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
47 
50 
51 namespace Foam
52 {
53  defineTypeNameAndDebug(fileMonitor, 0);
54 
55  template<>
56  const char* Foam::NamedEnum
57  <
59  3
60  >::names[] =
61  {
62  "unmodified",
63  "modified",
64  "deleted"
65  };
66 
67  //- Reduction operator for PackedList of fileState
69  {
70  public:
71  unsigned int operator()(const unsigned int x, const unsigned int y)
72  const
73  {
74  // x,y are sets of 2bits representing fileState
75 
76  unsigned int mask = 3u;
77  unsigned int shift = 0;
78  unsigned int result = 0;
79 
80  while (mask)
81  {
82  // Combine state
83  unsigned int xState = (x & mask) >> shift;
84  unsigned int yState = (y & mask) >> shift;
85 
86  // Combine and add to result. Combine is such that UNMODIFIED
87  // wins.
88  unsigned int state = min(xState, yState);
89  result |= (state << shift);
90 
91  shift += 2;
92  mask <<= 2;
93  }
94  return result;
95  }
96  };
97 
98  //- Combine operator for PackedList of fileState
100  {
101  public:
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)
112  {
113  public:
114 
115  const bool useInotify_;
116 
117  // For inotify
118 
119  //- File descriptor for the inotify instance
121 
122  //- Current watchIDs and corresponding directory id
125 
126  // For stat
127 
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();
143  dirWatches_.setCapacity(sz);
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
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 + regIOobject::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)
451 {
452  label watchFd;
453 
454  label sz = freeWatchFds_.size();
455 
456  if (sz)
457  {
458  watchFd = freeWatchFds_[sz-1];
459  freeWatchFds_.setSize(sz-1);
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 
490 {
491  if (debug)
492  {
493  Pout<< "fileMonitor : removing watch " << watchFd << " on file "
494  << watchFile_[watchFd] << endl;
495  }
496 
497  freeWatchFds_.append(watchFd);
498  return watcher_->removeWatch(watchFd);
499 }
500 
501 
503 {
504  return watchFile_[watchFd];
505 }
506 
507 
509 const
510 {
511  return state_[watchFd];
512 }
513 
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[watchFd] = static_cast<unsigned int>
536  (
537  localState_[watchFd]
538  );
539  }
540  }
541 
542 
543  // Scatter or reduce to synchronise state
544  if (masterOnly)
545  {
546  // Scatter
547  if (stats.storage().size() == 1)
548  {
549  Pstream::scatter(stats.storage()[0]);
550  }
551  else
552  {
553  Pstream::listCombineScatter(stats.storage());
554  }
555  }
556  else
557  {
558  // Reduce
559  if (stats.storage().size() == 1)
560  {
561  // Optimisation valid for most cases.
562  reduce(stats.storage()[0], reduceFileStates());
563  }
564  else
565  {
567  (
568  stats.storage(),
570  );
571  }
572  }
573 
574 
575  // Update synchronised state
576  forAll(state_, watchFd)
577  {
578  // Assign synchronised state
579  unsigned int stat = stats[watchFd];
580  state_[watchFd] = fileState(stat);
581 
582  if (!masterOnly)
583  {
584  // Give warning for inconsistent state
585  if (state_[watchFd] != localState_[watchFd])
586  {
587  if (debug)
588  {
589  Pout<< "fileMonitor : Delaying reading "
590  << watchFile_[watchFd]
591  << " due to inconsistent "
592  "file time-stamps between processors"
593  << endl;
594  }
595 
597  << "Delaying reading " << watchFile_[watchFd]
598  << " due to inconsistent "
599  "file time-stamps between processors" << endl;
600  }
601  }
602  }
603  }
604  else
605  {
606  state_ = localState_;
607  }
608 }
609 
610 
612 {
613  state_[watchFd] = UNMODIFIED;
614  localState_[watchFd] = UNMODIFIED;
615 
616  if (!useInotify_)
617  {
618  watcher_->lastMod_[watchFd] = highResLastModified(watchFile_[watchFd]);
619  }
620 }
621 
622 
623 // ************************************************************************* //
fileMonitor(const bool useInotify)
Construct null.
Definition: fileMonitor.C:429
DynamicList< double > lastMod_
From watch descriptor to modified time.
Definition: fileMonitor.C:129
static const NamedEnum< fileState, 3 > fileStateNames_
Definition: fileMonitor.H:76
static void listCombineScatter(const List< commsStruct > &comms, List< T > &Value, const int tag, const label comm)
Scatter data. Reverse of combineGather.
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
FvWallInfoData< WallInfo, label > label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
A class for handling file names.
Definition: fileName.H:79
Inter-processor communication reduction functions.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
Internal tracking via stat(3p) or inotify(7)
Definition: fileMonitor.C:111
error FatalError
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
fileState getState(const label watchFd) const
Check state using handle.
Definition: fileMonitor.C:508
fileState
Enumeration defining the file state.
Definition: fileMonitor.H:69
void setUnmodified(const label watchFd)
Reset state (e.g. after having read it) using handle.
Definition: fileMonitor.C:611
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
DynamicList< label > dirWatches_
Current watchIDs and corresponding directory id.
Definition: fileMonitor.C:123
Initialise the NamedEnum HashTable from the static list of names.
Definition: NamedEnum.H:51
static void listCombineGather(const List< commsStruct > &comms, List< T > &Value, const CombineOp &cop, const int tag, const label comm)
Reduction operator for PackedList of fileState.
Definition: fileMonitor.C:68
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:134
void operator()(unsigned int &x, const unsigned int y) const
Definition: fileMonitor.C:102
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
scalar y
bool read(const char *, int32_t &)
Definition: int32IO.C:85
bool removeWatch(const label watchFd)
Remove file to watch. Return true if successful.
Definition: fileMonitor.C:489
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:56
bool isDir(const fileName &, const bool followLink=true)
Does the name exist as a directory in the file system?
Definition: POSIX.C:539
void updateStates(const bool masterOnly, const bool syncPar) const
Check state of all files. Updates state_.
Definition: fileMonitor.C:516
const fileName & getFile(const label watchFd) const
Get name of file being watched.
Definition: fileMonitor.C:502
unsigned int operator()(const unsigned int x, const unsigned int y) const
Definition: fileMonitor.C:71
void setCapacity(const label)
Alter the size of the underlying storage.
Definition: DynamicListI.H:130
word name() const
Return file name (part beyond last /)
Definition: fileName.C:195
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
errorManip< error > abort(error &err)
Definition: errorManip.H:131
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
int inotifyFd_
File descriptor for the inotify instance.
Definition: fileMonitor.C:120
defineTypeNameAndDebug(combustionModel, 0)
~fileMonitor()
Destructor.
Definition: fileMonitor.C:442
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
rho oldTime()
double highResLastModified(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return time of last file modification.
Definition: POSIX.C:632
#define WarningInFunction
Report a warning using Foam::Warning.
label addWatch(const fileName &)
Add file to watch. Return watch descriptor.
Definition: fileMonitor.C:450
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
DynamicList< fileName > dirFiles_
Definition: fileMonitor.C:124
static float fileModificationSkew
Definition: regIOobject.H:101
fileName path() const
Return directory path name (part before last /)
Definition: fileName.C:265
bool removeWatch(const label watchFd)
Definition: fileMonitor.C:270
~fileMonitorWatcher()
Remove all watches.
Definition: fileMonitor.C:183
Combine operator for PackedList of fileState.
Definition: fileMonitor.C:99
bool addWatch(const label watchFd, const fileName &fName)
Definition: fileMonitor.C:204
A class for handling character strings derived from std::string.
Definition: string.H:76
Namespace for OpenFOAM.