1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
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.
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
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/>.
24 \*----------------------------------------------------------------------------*/
26 #include "fileMonitor.H"
27 #include "IOstreams.H"
29 #include "PackedList.H"
30 #include "PstreamReduceOps.H"
31 #include "OSspecific.H"
32 #include "regIOobject.H" // for fileModificationSkew symbol
34 #ifdef FOAM_USE_INOTIFY
35 # include <sys/inotify.h>
36 # include <sys/ioctl.h>
38 # define EVENT_SIZE ( sizeof (struct inotify_event) )
39 # define EVENT_LEN (EVENT_SIZE + 16)
40 # define EVENT_BUF_LEN ( 1024 * EVENT_LEN )
42 # include "OSspecific.H"
45 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
47 defineTypeNameAndDebug(Foam::fileMonitor, 0);
49 const Foam::NamedEnum<Foam::fileMonitor::fileState, 3>
50 Foam::fileMonitor::fileStateNames_;
55 const char* Foam::NamedEnum
57 Foam::fileMonitor::fileState,
66 //- Reduction operator for PackedList of fileState
67 class reduceFileStates
70 unsigned int operator()(const unsigned int x, const unsigned int y)
73 // x,y are sets of 2bits representing fileState
75 unsigned int mask = 3u;
76 unsigned int shift = 0;
77 unsigned int result = 0;
82 unsigned int xState = (x & mask) >> shift;
83 unsigned int yState = (y & mask) >> shift;
85 // Combine and add to result. Combine is such that UNMODIFIED
87 unsigned int state = min(xState, yState);
88 result |= (state << shift);
97 //- Combine operator for PackedList of fileState
98 class combineReduceFileStates
101 void operator()(unsigned int& x, const unsigned int y) const
103 x = reduceFileStates()(x, y);
109 //- Internal tracking via stat(3p) or inotify(7)
110 class fileMonitorWatcher
114 const bool useInotify_;
118 //- File descriptor for the inotify instance
121 //- Current watchIDs and corresponding directory id
122 DynamicList<label> dirWatches_;
123 DynamicList<fileName> dirFiles_;
127 //- From watch descriptor to modified time
128 DynamicList<time_t> lastMod_;
132 //- initialise inotify
133 inline fileMonitorWatcher(const bool useInotify, const label sz = 20)
135 useInotify_(useInotify)
139 #ifdef FOAM_USE_INOTIFY
140 inotifyFd_ = inotify_init();
141 dirWatches_.setCapacity(sz);
142 dirFiles_.setCapacity(sz);
146 static bool hasWarned = false;
150 WarningIn("fileMonitorWatcher(const bool, const label)")
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"
167 FatalErrorIn("fileMonitorWatcher(const bool, const label)")
168 << "You selected inotify but this file was compiled"
169 << " without FOAM_USE_INOTIFY"
170 << "Please select another fileModification test method"
176 lastMod_.setCapacity(sz);
180 //- remove all watches
181 inline ~fileMonitorWatcher()
183 #ifdef FOAM_USE_INOTIFY
184 if (useInotify_ && inotifyFd_ >= 0)
186 forAll(dirWatches_, i)
188 if (dirWatches_[i] >= 0)
190 if (inotify_rm_watch(inotifyFd_, int(dirWatches_[i])))
192 WarningIn("fileMonitor::~fileMonitor()")
193 << "Failed deleting directory watch "
194 << dirWatches_[i] << endl;
202 inline bool addWatch(const label watchFd, const fileName& fName)
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)
216 const fileName dir = fName.path();
218 label dirWatchID = -1;
221 dirWatchID = inotify_add_watch
230 FatalErrorIn("addWatch(const label, const fileName&)")
231 << "Failed adding watch " << watchFd
232 << " to directory " << fName << " due to "
233 << string(strerror(errno))
238 if (watchFd < dirWatches_.size() && dirWatches_[watchFd] != -1)
240 // Reuse of watchFd : should have dir watchID set to -1.
241 FatalErrorIn("addWatch(const label, const fileName&)")
242 << "Problem adding watch " << watchFd
243 << " to file " << fName
244 << abort(FatalError);
247 dirWatches_(watchFd) = dirWatchID;
248 dirFiles_(watchFd) = fName.name();
253 if (watchFd < lastMod_.size() && lastMod_[watchFd] != 0)
255 // Reuse of watchFd : should have lastMod set to 0.
256 FatalErrorIn("addWatch(const label, const fileName&)")
257 << "Problem adding watch " << watchFd
258 << " to file " << fName
259 << abort(FatalError);
262 lastMod_(watchFd) = lastModified(fName);
268 inline bool removeWatch(const label watchFd)
277 dirWatches_[watchFd] = -1;
281 lastMod_[watchFd] = 0;
290 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
292 void Foam::fileMonitor::checkFiles() const
296 #ifdef FOAM_USE_INOTIFY
297 // Large buffer for lots of events
298 char buffer[EVENT_BUF_LEN];
302 struct timeval zeroTimeout = {0, 0};
304 //- Pre-allocated structure containing file descriptors
306 // Add notify descriptor to select fd_set
308 FD_SET(watcher_->inotifyFd_, &fdSet);
312 watcher_->inotifyFd_+1, // num filedescriptors in fdSet
313 &fdSet, // fdSet with only inotifyFd
316 &zeroTimeout // eNo timeout
321 FatalErrorIn("fileMonitor::checkFiles()")
322 << "Problem in issuing select."
323 << abort(FatalError);
325 else if (FD_ISSET(watcher_->inotifyFd_, &fdSet))
328 ssize_t nBytes = read
330 watcher_->inotifyFd_,
337 FatalErrorIn("fileMonitor::checkFiles()")
338 << "read of " << watcher_->inotifyFd_
339 << " failed with " << label(nBytes)
340 << abort(FatalError);
343 // Go through buffer, consuming events
347 const struct inotify_event* inotifyEvent =
348 reinterpret_cast<const struct inotify_event*>
353 //Pout<< "watchFd:" << inotifyEvent->wd << nl
354 // << "mask:" << inotifyEvent->mask << nl
356 //Pout<< "file:" << fileName(inotifyEvent->name) << endl;
357 //Pout<< "len:" << inotifyEvent->len << endl;
361 (inotifyEvent->mask & IN_CLOSE_WRITE)
366 forAll(watcher_->dirWatches_, i)
368 label id = watcher_->dirWatches_[i];
371 id == inotifyEvent->wd
372 && inotifyEvent->name == watcher_->dirFiles_[i]
375 // Correct directory and name
376 localState_[i] = MODIFIED;
381 i += EVENT_SIZE + inotifyEvent->len;
394 forAll(watcher_->lastMod_, watchFd)
396 time_t oldTime = watcher_->lastMod_[watchFd];
400 const fileName& fName = watchFile_[watchFd];
401 time_t newTime = lastModified(fName);
405 localState_[watchFd] = DELETED;
409 if (newTime > (oldTime + regIOobject::fileModificationSkew))
411 localState_[watchFd] = MODIFIED;
415 localState_[watchFd] = UNMODIFIED;
424 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
427 Foam::fileMonitor::fileMonitor(const bool useInotify)
429 useInotify_(useInotify),
434 watcher_(new fileMonitorWatcher(useInotify_, 20))
438 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
440 Foam::fileMonitor::~fileMonitor()
444 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
446 // Note: fName might not exist (on slaves if in master-only mode for
448 Foam::label Foam::fileMonitor::addWatch(const fileName& fName)
452 label sz = freeWatchFds_.size();
456 watchFd = freeWatchFds_[sz-1];
457 freeWatchFds_.setSize(sz-1);
461 watchFd = state_.size();
464 watcher_->addWatch(watchFd, fName);
468 Pout<< "fileMonitor : added watch " << watchFd << " on file "
474 WarningIn("fileMonitor::addWatch(const fileName&)")
475 << "could not add watch for file " << fName << endl;
479 localState_(watchFd) = UNMODIFIED;
480 state_(watchFd) = UNMODIFIED;
481 watchFile_(watchFd) = fName;
487 bool Foam::fileMonitor::removeWatch(const label watchFd)
491 Pout<< "fileMonitor : removing watch " << watchFd << " on file "
492 << watchFile_[watchFd] << endl;
495 freeWatchFds_.append(watchFd);
496 return watcher_->removeWatch(watchFd);
500 const Foam::fileName& Foam::fileMonitor::getFile(const label watchFd) const
502 return watchFile_[watchFd];
506 Foam::fileMonitor::fileState Foam::fileMonitor::getState(const label watchFd)
509 return state_[watchFd];
513 void Foam::fileMonitor::updateStates
515 const bool masterOnly,
519 if (Pstream::master() || !masterOnly)
521 // Update the localState_
527 // Pack local state (might be on master only)
528 PackedList<2> stats(state_.size(), MODIFIED);
529 if (Pstream::master() || !masterOnly)
531 forAll(state_, watchFd)
533 stats[watchFd] = static_cast<unsigned int>
541 // Scatter or reduce to synchronise state
545 if (stats.storage().size() == 1)
547 Pstream::scatter(stats.storage()[0]);
551 Pstream::listCombineScatter(stats.storage());
557 if (stats.storage().size() == 1)
559 // Optimisation valid for most cases.
560 reduce(stats.storage()[0], reduceFileStates());
564 Pstream::listCombineGather
567 combineReduceFileStates()
573 // Update synchronised state
574 forAll(state_, watchFd)
576 // Assign synchronised state
577 unsigned int stat = stats[watchFd];
578 state_[watchFd] = fileState(stat);
582 // Give warning for inconsistent state
583 if (state_[watchFd] != localState_[watchFd])
587 Pout<< "fileMonitor : Delaying reading "
588 << watchFile_[watchFd]
589 << " due to inconsistent "
590 "file time-stamps between processors"
596 "fileMonitor::updateStates"
597 "(const bool, const bool) const"
598 ) << "Delaying reading " << watchFile_[watchFd]
599 << " due to inconsistent "
600 "file time-stamps between processors" << endl;
607 state_ = localState_;
612 void Foam::fileMonitor::setUnmodified(const label watchFd)
614 state_[watchFd] = UNMODIFIED;
615 localState_[watchFd] = UNMODIFIED;
619 watcher_->lastMod_[watchFd] = lastModified(watchFile_[watchFd]);
624 // ************************************************************************* //