ENH: added fileMonitor class (uses inotify)

This commit is contained in:
mattijs
2010-05-28 15:20:55 +01:00
parent 09143ab90e
commit 18925e6ee0
5 changed files with 584 additions and 0 deletions

44
doc/changes/inotify.txt Normal file
View File

@ -0,0 +1,44 @@
2010-05-28
Using asynchronous file modification (using inotify) instead of timestamp
checking.
- all files (usually only IOdictionary) that need to be monitored
should be registered using MUST_READ_IF_MODIFIED. The MUST_READ should
be used for objects that do not need to be re-read (e.g. fields).
In the old system it would actually monitor e.g. 0/U and constant/polyMesh
files.
I've temporarily added a warning in IOdictionary if constructed with MUST_READ.
Same for IOList,IOField,IOMap if constructed with MUST_READ_IF_MODIFIED
(or is rereading supported?). Please let me know if something does not work or
you see this warning.
- all file monitoring is done by an instance of 'fileMonitor' in the Time
class. The fileMonitor class can be found in OSspecific. Default is
to use the (linux-specific) 'inotify' system calls.
If compiled with -DFOAM_USE_STAT it will revert to the current 'stat' system
calls.
- inotify does not need timestamps. There is no need for fileModificationSkew
to allow for time differences. (there can still temporarily be a difference
in modified status between different processors due to nfs lagging)
- all reductions to synchronise status on different processors are done with
a single reduction instead of one reduction per registered object. This could
be quite a gain on large numbers of processors.
- fileMonitor stores two hashtables per file so there is a small overhead
adding and removing files from monitoring.
- if runTimeModifiable is false at start of run no files will get monitored,
however if runTimeModified gets set to false during the run and monitored files
will still get monitored (though never reloaded). This is only a hypothetical
problem in that the kernel still stores events for the monitored files. However
inotify is very efficient - e.g. it gets used to track changes on file systems
for desktop search engines.
- any monitored and modified file will get reloaded from the exact path
that was monitored. In the old system it would/could do a re-search through all
times.

View File

@ -9,6 +9,12 @@ POSIX.C
cpuTime/cpuTime.C
clockTime/clockTime.C
/*
* Note: fileMonitor assumes inotify by default. Compile with -DFOAM_USE_STAT
* to use stat (=timestamps) instead of inotify
*/
fileMonitor.C
#ifdef SunOS64
dummyPrintStack.C
#else

View File

@ -0,0 +1,3 @@
#ifdef SunOS64
EXE_INC = -DFOAM_USE_STAT
#endif

View File

@ -0,0 +1,372 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2010-2010 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
fileMonitor
\*----------------------------------------------------------------------------*/
#include "fileMonitor.H"
#include "IOstreams.H"
#include "Pstream.H"
#include "PackedList.H"
#ifdef FOAM_USE_STAT
# include "OSspecific.H"
# include "regIOobject.H" // for fileModificationSkew symbol
#else
# include <sys/inotify.h>
# include <stropts.h>
# include <sys/ioctl.h>
#endif
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
defineTypeNameAndDebug(Foam::fileMonitor, 0);
template<>
const char* Foam::NamedEnum<Foam::fileMonitor::fileState, 3>::names[] =
{
"unmodified",
"deleted",
"modified"
};
const Foam::NamedEnum<Foam::fileMonitor::fileState, 3>
Foam::fileMonitor::fileStateNames_;
namespace Foam
{
class fileStateEqOp
{
public:
void operator()(unsigned int& x, const unsigned int& y) const
{
// x,y are list of 2bits representing fileState
unsigned int mask = 3u;
unsigned int shift = 0;
unsigned int result = 0;
while (mask)
{
// Combine state
unsigned int xState = (x & mask) >> shift;
unsigned int yState = (y & mask) >> shift;
// Combine and add to result. Combine is such that UNMODIFIED
// wins.
unsigned int state = min(xState, yState);
result |= (state << shift);
shift += 2;
mask <<= 2;
}
x = result;
}
};
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
#ifdef FOAM_USE_STAT
void Foam::fileMonitor::checkFiles() const
{
for
(
HashTable<label, time_t>::iterator iter = lastModified_.begin();
iter != lastModified_.end();
++iter
)
{
label watchFd = iter.key();
const fileName& fName = watchFile_[watchFd];
time_t newTime = lastModified(fName);
if (newTime == 0)
{
state_.set(watchFd, DELETED);
}
else
{
time_t oldTime = iter();
if (newTime > (oldTime + regIOobject::fileModificationSkew))
{
iter() = newTime;
state_.set(watchFd, MODIFIED);
}
else
{
state_.set(watchFd, UNMODIFIED);
}
}
}
}
#else
void Foam::fileMonitor::checkFiles() const
{
while (true)
{
struct timeval zeroTimeout = {0, 0};
int ready = select
(
inotifyFd_+1, // num filedescriptors in watchSet_
&watchSet_, // watchSet_ with only inotifyFd
NULL,
NULL,
&zeroTimeout
);
if (ready < 0)
{
FatalErrorIn("fileMonitor::updateStates()")
<< "Problem in issuing select."
<< abort(FatalError);
}
else if (FD_ISSET(inotifyFd_, &watchSet_))
{
struct inotify_event inotifyEvent;
// Read first event
ssize_t nBytes = read
(
inotifyFd_,
&inotifyEvent,
sizeof(inotifyEvent)
);
if (nBytes != sizeof(inotifyEvent))
{
FatalErrorIn("fileMonitor::updateStates(const fileName&)")
<< "Read " << label(nBytes) << " ; expected "
<< label(sizeof(inotifyEvent))
<< abort(FatalError);
}
//Pout<< "mask:" << inotifyEvent.mask << endl;
//Pout<< "watchFd:" << inotifyEvent.wd << endl;
//Pout<< "watchName:" << watchFile_[inotifyEvent.wd] << endl;
switch (inotifyEvent.mask)
{
case IN_DELETE_SELF:
{
Map<fileState>::iterator iter =
state_.find(label(inotifyEvent.wd));
iter() = DELETED;
}
break;
case IN_MODIFY:
case IN_CLOSE_WRITE:
{
Map<fileState>::iterator iter =
state_.find(label(inotifyEvent.wd));
iter() = MODIFIED;
}
break;
}
}
else
{
// No data. Reset watchSet_
FD_SET(inotifyFd_, &watchSet_);
return;
}
}
}
#endif
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
// Null constructor
#ifdef FOAM_USE_STAT
Foam::fileMonitor::fileMonitor()
:
state_(20),
watchFile_(20),
lastModified_(20)
{}
#else
Foam::fileMonitor::fileMonitor()
:
state_(20),
watchFile_(20),
inotifyFd_(inotify_init())
{
//- Add notify descriptor to select set
FD_ZERO(&watchSet_);
FD_SET(inotifyFd_, &watchSet_);
}
#endif
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::fileMonitor::~fileMonitor()
{
// Remove any remaining files
List<label> watchFds(state_.toc());
forAll(watchFds, i)
{
removeWatch(watchFds[i]);
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::label Foam::fileMonitor::addWatch(const fileName& fName)
{
#ifdef FOAM_USE_STAT
label watchFd = lastModified_.size();
lastModified_.insert(watchFd, lastModified(fName));
#else
label watchFd = inotify_add_watch
(
inotifyFd_,
fName.c_str(),
//IN_ALL_EVENTS
IN_CLOSE_WRITE | IN_DELETE_SELF | IN_MODIFY
);
#endif
if (debug)
{
Pout<< "fileMonitor : added watch " << watchFd << " on file "
<< fName << endl;
}
if (watchFd < 0)
{
WarningIn("fileMonitor::addWatch(const fileName&)")
<< "could not add watch for file " << fName << endl;
}
else
{
state_.insert(watchFd, UNMODIFIED);
watchFile_.insert(watchFd, fName);
}
return watchFd;
}
bool Foam::fileMonitor::removeWatch(const label watchFd)
{
if (debug)
{
Pout<< "fileMonitor : removing watch " << watchFd << " on file "
<< watchFile_[watchFd] << endl;
}
state_.erase(watchFd);
watchFile_.erase(watchFd);
#ifdef FOAM_USE_STAT
return lastModified_.erase(watchFd);
#else
return inotify_rm_watch(inotifyFd_, int(watchFd)) == 0;
#endif
}
const Foam::fileName& Foam::fileMonitor::getFile(const label watchFd) const
{
return watchFile_[watchFd];
}
Foam::fileMonitor::fileState Foam::fileMonitor::getState(const label watchFd)
const
{
return state_[watchFd];
}
void Foam::fileMonitor::updateStates(const bool syncPar) const
{
checkFiles();
if (syncPar)
{
PackedList<2> stats(state_.size());
label i = 0;
forAllConstIter(Map<fileState>, state_, iter)
{
stats[i++] = (unsigned int)(iter());
}
// Save local state for warning message below
PackedList<2> thisProcStats(stats);
Pstream::listCombineGather(stats.storage(), fileStateEqOp());
i = 0;
forAllIter(Map<fileState>, state_, iter)
{
if (thisProcStats[i] != UNMODIFIED)
{
if (stats[i] == UNMODIFIED)
{
WarningIn("fileMonitor::updateStates(const bool) const")
<< "Delaying reading " << watchFile_[iter.key()]
<< " due to inconsistent "
"file time-stamps between processors"
<< endl;
}
else
{
unsigned int stat = stats[i];
iter() = fileState(stat);
}
}
i++;
}
}
}
void Foam::fileMonitor::setUnmodified(const label watchFd)
{
#ifdef FOAM_USE_STAT
lastModified_[watchFd] = lastModified(watchFile_[watchFd]);
#endif
Map<fileState>::iterator iter = state_.find(watchFd);
if (iter == state_.end())
{
FatalErrorIn("fileMonitor::setUnmodified(const label)")
<< "Illegal watchFd " << watchFd
<< abort(FatalError);
}
iter() = UNMODIFIED;
}
// ************************************************************************* //

View File

@ -0,0 +1,159 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2010-2010 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
fileMonitor
Description
Checking for changes to files.
!!!!!!!NOTE:
Default is to use inotify (Linux specific, since 2.6.13)
Compile with FOAM_USE_STAT to use the stat function call.
- works fine except for if file gets deleted and recreated
it stops monitoring the file!
(does work though if the file gets moved)
SourceFiles
fileMonitor.C
\*---------------------------------------------------------------------------*/
#ifndef fileMonitor_H
#define fileMonitor_H
#include <sys/types.h>
#include "Map.H"
#include "NamedEnum.H"
#include "labelList.H"
#include "className.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
class fileMonitor;
/*---------------------------------------------------------------------------*\
Class fileMonitor Declaration
\*---------------------------------------------------------------------------*/
class fileMonitor
{
public:
// Public data types
//- Enumeration defining the file state.
enum fileState
{
UNMODIFIED = 0,
DELETED = 1,
MODIFIED = 2
};
static const NamedEnum<fileState, 3> fileStateNames_;
private:
// Private data
//- State for all watchFds
mutable Map<fileState> state_;
//- From watch descriptor to filename
HashTable<fileName, label> watchFile_;
#ifdef FOAM_USE_STAT
//- From watch descriptor to modified time
mutable HashTable<label, time_t> lastModified_;
#else
//- File descriptor for the inotify instance
int inotifyFd_;
//- Pre-allocated structure containing file descriptors
mutable fd_set watchSet_;
#endif
//- Update state_ from any events.
void checkFiles() const;
//- Disallow default bitwise copy construct
fileMonitor(const fileMonitor&);
//- Disallow default bitwise assignment
void operator=(const fileMonitor&);
public:
// Declare name of the class and its debug switch
ClassName("fileMonitor");
// Constructors
//- Construct null
fileMonitor();
// Destructor
~fileMonitor();
// Member Functions
//- Add file to watch. Returns watch descriptor
label addWatch(const fileName&);
//- Remove file to watch. Return true if successful
bool removeWatch(const label watchFd);
//- Get name of file being watched
const fileName& getFile(const label watchFd) const;
//- Check state using handle
fileState getState(const label watchFd) const;
//- Check state of all files. Updates state_.
void updateStates(const bool syncPar) const;
//- Reset state (e.g. after having read it) using handle
void setUnmodified(const label watchFd);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //