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-------------------------------------------------------------------------------
11License
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
53const Foam::Enum
54<
56>
58({
59 { fileState::UNMODIFIED, "unmodified" },
60 { fileState::MODIFIED, "modified" },
61 { fileState::DELETED, "deleted" },
62});
63
64
65namespace Foam
66{
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 {
181 }
182 }
183
184 //- Remove all watches
186 {
187 #ifdef FOAM_USE_INOTIFY
188 if (useInotify_ && inotifyFd_ >= 0)
189 {
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 (
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
296void 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
431Foam::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)
452Foam::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
493bool 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
507const Foam::fileName& Foam::fileMonitor::getFile(const label watchFd) const
508{
509 return watchFile_[watchFd];
510}
511
512
514const
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 // Broadcast
553 if (stats.storage().size() == 1)
554 {
555 Pstream::broadcast(stats.storage()[0]);
556 }
557 else
558 {
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
617void 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// ************************************************************************* //
Useful combination of include files which define Sin, Sout and Serr and the use of IO streams general...
scalar y
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Inter-processor communication reduction functions.
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:72
void setCapacity(const label len)
Alter the size of the underlying storage.
Definition: DynamicListI.H:303
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: Enum.H:61
static float fileModificationSkew
Time skew (seconds) for file modification checks.
Definition: IOobject.H:306
A dynamic list of packed unsigned integers, with the number of bits per item specified by the <Width>...
Definition: PackedList.H:129
bool set(const label i, unsigned int val=~0u)
Set value at index i, default value set is the max_value.
Definition: PackedListI.H:653
const List< unsigned int > & storage() const
Return the underlying storage blocks.
Definition: PackedListI.H:547
static void broadcast(Type &value, const label comm=UPstream::worldComm)
static void listCombineGather(const List< commsStruct > &comms, List< T > &values, const CombineOp &cop, const int tag, const label comm)
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
Combine operator for PackedList of fileState.
Definition: fileMonitor.C:102
void operator()(unsigned int &x, const unsigned int y) const
Definition: fileMonitor.C:104
Internal tracking via stat(3p) or inotify(7)
Definition: fileMonitor.C:114
fileMonitorWatcher(const bool useInotify, const label sz=20)
Initialise inotify.
Definition: fileMonitor.C:136
DynamicList< double > lastMod_
From watch descriptor to modified time.
Definition: fileMonitor.C:131
int inotifyFd_
File descriptor for the inotify instance.
Definition: fileMonitor.C:122
DynamicList< label > dirWatches_
Current watchIDs and corresponding directory id.
Definition: fileMonitor.C:125
bool addWatch(const label watchFd, const fileName &fName)
Definition: fileMonitor.C:206
~fileMonitorWatcher()
Remove all watches.
Definition: fileMonitor.C:185
bool removeWatch(const label watchFd)
Definition: fileMonitor.C:272
DynamicList< fileName > dirFiles_
Definition: fileMonitor.C:126
Checking for changes to files.
Definition: fileMonitor.H:67
fileState getState(const label watchFd) const
Check state using handle.
Definition: fileMonitor.C:513
static const Enum< fileState > fileStateNames_
Definition: fileMonitor.H:80
void setUnmodified(const label watchFd)
Reset state (e.g. after having read it) using handle.
Definition: fileMonitor.C:617
~fileMonitor()
Destructor.
Definition: fileMonitor.C:444
const fileName & getFile(const label watchFd) const
Get name of file being watched.
Definition: fileMonitor.C:507
void updateStates(const bool masterOnly, const bool syncPar) const
Check state of all files. Updates state_.
Definition: fileMonitor.C:521
fileState
Enumeration defining the file state.
Definition: fileMonitor.H:74
bool removeWatch(const label watchFd)
Remove file to watch. Return true if successful.
Definition: fileMonitor.C:493
A class for handling file names.
Definition: fileName.H:76
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:176
static std::string name(const std::string &str)
Return basename (part beyond last /), including its extension.
Definition: fileNameI.H:199
Reduction operator for PackedList of fileState.
Definition: fileMonitor.C:71
unsigned int operator()(const unsigned int x, const unsigned int y) const
Definition: fileMonitor.C:73
splitCell * master() const
Definition: splitCell.H:113
A class for handling character strings derived from std::string.
Definition: string.H:79
virtual void addWatch()
Add file watch on object (READ_IF_MODIFIED)
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
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()
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
#define WarningInFunction
Report a warning using Foam::Warning.
List< bool > select(const label n, const labelUList &locations)
Definition: BitOps.C:142
Namespace for OpenFOAM.
bool read(const char *buf, int32_t &val)
Same as readInt32.
Definition: int32.H:108
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
errorManip< error > abort(error &err)
Definition: errorManip.H:144
double highResLastModified(const fileName &, const bool followLink=true)
Return time of last file modification.
Definition: MSwindows.C:707
error FatalError
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: MSwindows.C:651
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333