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 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
49const Foam::Enum
50<
52>
54({
55 { fileState::UNMODIFIED, "unmodified" },
56 { fileState::MODIFIED, "modified" },
57 { fileState::DELETED, "deleted" },
58});
59
60
61namespace Foam
62{
63 defineTypeNameAndDebug(fileMonitor, 0);
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
97 class combineReduceFileStates
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:
112
113 const bool useInotify_;
114
115 // For inotify
116
117 //- File descriptor for the inotify instance
118 int inotifyFd_;
119
120 //- Current watchIDs and corresponding directory id
121 DynamicList<label> dirWatches_;
122 DynamicList<fileName> dirFiles_;
123
124 // For stat
125
126 //- From watch descriptor to modified time
127 DynamicList<double> lastMod_;
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 {
177 }
178 }
179
180 //- Remove all watches
182 {
183 #ifdef FOAM_USE_INOTIFY
184 if (useInotify_ && inotifyFd_ >= 0)
185 {
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 (
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
292void 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
427Foam::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)
448Foam::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;
456
457 if (freeWatchFds_.size())
458 {
459 watchFd = freeWatchFds_.remove();
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
489bool Foam::fileMonitor::removeWatch(const label watchFd)
490{
491 if (debug)
492 {
493 Pout<< "fileMonitor : removing watch " << watchFd << " on file "
494 << watchFile_[watchFd] << endl;
495 }
496
497 freeWatchFds_.appendUniq(watchFd);
498
499 return watcher_->removeWatch(watchFd);
500}
501
502
503const Foam::fileName& Foam::fileMonitor::getFile(const label watchFd) const
504{
505 return watchFile_[watchFd];
506}
507
508
510const
511{
512 return state_[watchFd];
513}
514
515
517(
518 const bool masterOnly,
519 const bool syncPar
520) const
521{
522 if (Pstream::master() || !masterOnly)
523 {
524 // Update the localState_
525 checkFiles();
526 }
527
528 if (syncPar)
529 {
530 // Pack local state (might be on master only)
531 PackedList<2> stats(state_.size(), MODIFIED);
532 if (Pstream::master() || !masterOnly)
533 {
534 forAll(state_, watchFd)
535 {
536 stats.set
537 (
538 watchFd,
539 static_cast<unsigned int>(localState_[watchFd])
540 );
541 }
542 }
543
544
545 // Scatter or reduce to synchronise state
546 if (masterOnly)
547 {
548 // Broadcast
549 if (stats.storage().size() == 1)
550 {
551 Pstream::broadcast(stats.storage()[0]);
552 }
553 else
554 {
555 Pstream::broadcast(stats.storage());
556 }
557 }
558 else
559 {
560 // Reduce
561 if (stats.storage().size() == 1)
562 {
563 // Optimisation valid for most cases.
564 reduce(stats.storage()[0], reduceFileStates());
565 }
566 else
567 {
569 (
570 stats.storage(),
571 combineReduceFileStates()
572 );
573 }
574 }
575
576
577 // Update synchronised state
578 forAll(state_, watchFd)
579 {
580 // Assign synchronised state
581 unsigned int stat = stats[watchFd];
582 state_[watchFd] = fileState(stat);
583
584 if (!masterOnly)
585 {
586 // Give warning for inconsistent state
587 if (state_[watchFd] != localState_[watchFd])
588 {
589 if (debug)
590 {
591 Pout<< "fileMonitor : Delaying reading "
592 << watchFile_[watchFd]
593 << " due to inconsistent "
594 "file time-stamps between processors"
595 << endl;
596 }
597
599 << "Delaying reading " << watchFile_[watchFd]
600 << " due to inconsistent "
601 "file time-stamps between processors" << endl;
602 }
603 }
604 }
605 }
606 else
607 {
608 state_ = localState_;
609 }
610}
611
612
613void Foam::fileMonitor::setUnmodified(const label watchFd)
614{
615 state_[watchFd] = UNMODIFIED;
616 localState_[watchFd] = UNMODIFIED;
617
618 if (!useInotify_)
619 {
620 watcher_->lastMod_[watchFd] = highResLastModified(watchFile_[watchFd]);
621 }
622}
623
624
625// ************************************************************************* //
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.
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
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
void operator()(unsigned int &x, const unsigned int y) const
Definition: fileMonitor.C:100
fileMonitorWatcher(const bool useInotify, const label sz=20)
Initialise inotify.
Definition: fileMonitor.C:132
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:202
~fileMonitorWatcher()
Remove all watches.
Definition: fileMonitor.C:181
bool removeWatch(const label watchFd)
Definition: fileMonitor.C:268
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:69
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