ENH: parallel runTimeModifiable - master only

This commit is contained in:
mattijs
2010-11-16 12:41:44 +00:00
parent 57a443a183
commit b5dddd8980
13 changed files with 700 additions and 275 deletions

View File

@ -24,21 +24,63 @@ be quite a gain on large numbers of processors.
- all file monitoring is done by an instance of 'fileMonitor' in the Time - all file monitoring is done by an instance of 'fileMonitor' in the Time
class. The fileMonitor class can be found in OSspecific. Default is class. The fileMonitor class can be found in OSspecific. It uses either
to use the (linux-specific) 'inotify' system calls. timestamps as before or the (linux-specific) 'inotify' system framework
If compiled with -DFOAM_USE_STAT it will revert to the current 'stat' system (available only if compiled with -DFOAM_USE_INOTIFY).
calls.
- inotify does not need timestamps. There is no need for fileModificationSkew - the monitoring can be done in one of four modes as set by
OptimisationSwitches::fileModificationChecking
- timeStamp : old behaviour : all nodes check the timestamp
- inotify : using inotify instead of timestamps
- timeStampMaster,inotifyMaster : only the master node checks the file
and only the master node reads it and distribute it to the
slaves. This makes runTimeModifiable possible on distributed
running (see below).
- distributed running:
- set fileModificationChecking to e.g. timeStampMaster
- decompose a case, e.g. cavity
- copy system and constant to processor0/
- put the all the processor* directories on the wanted nodes inside
the case directory. E.g.
- on master have /tmp/cavity/processor0
- on slaveN have /tmp/cavity/processorN
- so to reiterate:
- there is no need for cavity/constant or cavity/system, all the
dictionaries are only in processor0/constant or processor0/system
- the slave processor directories have no system directory and the
constant directory only contains the mesh.
- start the job in distributed mode by specifying the slave roots
(so one less than the number of processors) with
the -roots command line option:
mpirun -np 2 icoFoam -roots '("/tmp")' -parallel
- the alternative to the -roots option is to have a
cavity/system/decomposeParDict on the master with
distributed yes;
roots ("/tmp");
Details:
- timeStampMaster, inotifyMaster : this works only for IOdictionaries that
are READ_IF_MODIFIED. It means that slaves read exactly the same dictionary
as the master so cannot be used for dictionaries that contain e.g. mesh
specific information.
- inotify is a monitoring framework used to monitor changes in
lots of files (e.g. used in desktop searched like beagle). You specify
files to monitor and then get warned for any changes to these files.
It does not need timestamps. There is no need for fileModificationSkew
to allow for time differences. (there can still temporarily be a difference to allow for time differences. (there can still temporarily be a difference
in modified status between different processors due to nfs lagging) in modified status between different processors due to nfs lagging). The big
problem is that it does not work over nfs3 (not sure about nfs4).
- fileMonitor stores two hashtables per file so there is a small overhead - fileMonitor stores two hashtables per file so there is a small overhead
adding and removing files from monitoring. adding and removing files from monitoring.
- if runTimeModifiable is false at start of run no files will get monitored, - if runTimeModifiable is false at start of run no files will get monitored,
however if runTimeModified gets set to false during the run the files however if runTimeModified gets set to false during the run the files
will still get monitored (though never reloaded). This is only a hypothetical will still get monitored (though never reloaded). This is only a hypothetical
@ -46,7 +88,6 @@ 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 inotify is very efficient - e.g. it gets used to track changes on file systems
for desktop search engines. for desktop search engines.
- in the old system one could call modified() on any object and get - in the old system one could call modified() on any object and get
and uptodate state. In the new system it will return the state from and uptodate state. In the new system it will return the state from
the last runTime++ (which if it triggered any re-reads will have reset the the last runTime++ (which if it triggered any re-reads will have reset the

View File

@ -872,6 +872,14 @@ InfoSwitches
OptimisationSwitches OptimisationSwitches
{ {
fileModificationSkew 10; fileModificationSkew 10;
//- Modification checking:
// - timeStamp : use modification time on file
// - inotify : use inotify framework
// - timeStampMaster : do time stamp (and file reading) only on master.
// - inotifyMaster : do inotify (and file reading) only on master.
fileModificationChecking timeStampMaster;//inotify;timeStamp;inotifyMaster;
commsType nonBlocking; //scheduled; //blocking; commsType nonBlocking; //scheduled; //blocking;
floatTransfer 0; floatTransfer 0;
nProcsSimpleSum 0; nProcsSimpleSum 0;

View File

@ -12,9 +12,9 @@ unset COMP_FLAGS LINK_FLAGS
if [ -f /usr/include/sys/inotify.h -a "${1%USE_STAT}" = "$1" ] if [ -f /usr/include/sys/inotify.h -a "${1%USE_STAT}" = "$1" ]
then then
echo "Found <sys/inotify.h> -- using inotify for file monitoring." echo "Found <sys/inotify.h> -- using inotify for file monitoring."
unset COMP_FLAGS export COMP_FLAGS="-DFOAM_USE_INOTIFY"
else else
export COMP_FLAGS="-DFOAM_USE_STAT" unset COMP_FLAGS
fi fi

View File

@ -32,17 +32,17 @@ Class
#include "PackedList.H" #include "PackedList.H"
#include "PstreamReduceOps.H" #include "PstreamReduceOps.H"
#include "OSspecific.H" #include "OSspecific.H"
#include "regIOobject.H" // for fileModificationSkew symbol
#ifdef FOAM_USE_STAT #ifdef FOAM_USE_INOTIFY
# include "OSspecific.H"
# include "regIOobject.H" // for fileModificationSkew symbol
#else
# include <sys/inotify.h> # include <sys/inotify.h>
# include <sys/ioctl.h> # include <sys/ioctl.h>
# include <errno.h> # include <errno.h>
# define EVENT_SIZE ( sizeof (struct inotify_event) ) # define EVENT_SIZE ( sizeof (struct inotify_event) )
# define EVENT_LEN (EVENT_SIZE + 16) # define EVENT_LEN (EVENT_SIZE + 16)
# define EVENT_BUF_LEN ( 1024 * EVENT_LEN ) # define EVENT_BUF_LEN ( 1024 * EVENT_LEN )
#else
# include "OSspecific.H"
#endif #endif
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -111,78 +111,77 @@ namespace Foam
{ {
public: public:
#ifdef FOAM_USE_STAT const bool useInotify_;
//- From watch descriptor to modified time
DynamicList<time_t> lastMod_;
//- initialize HashTable size // For inotify
inline fileMonitorWatcher(const label sz = 20)
:
lastMod_(sz)
{}
inline bool addWatch(const label watchFd, const fileName& fName) //- File descriptor for the inotify instance
{ int inotifyFd_;
if (watchFd < lastMod_.size() && lastMod_[watchFd] != 0)
{
// Reuse of watchFd : should have lastMod set to 0.
FatalErrorIn("addWatch(const label, const fileName&)")
<< "Problem adding watch " << watchFd
<< " to file " << fName
<< abort(FatalError);
}
lastMod_(watchFd) = lastModified(fName); //- Current watchIDs and corresponding directory id
return true; DynamicList<label> dirWatches_;
} DynamicList<fileName> dirFiles_;
inline bool removeWatch(const label watchFd) // For stat
{
lastMod_[watchFd] = 0; //- From watch descriptor to modified time
return true; DynamicList<time_t> lastMod_;
}
#else
//- File descriptor for the inotify instance
int inotifyFd_;
//- Current watchIDs and corresponding directory id
DynamicList<label> dirWatches_;
DynamicList<fileName> dirFiles_;
//- initialise inotify //- initialise inotify
inline fileMonitorWatcher(const label sz = 20) inline fileMonitorWatcher(const bool useInotify, const label sz = 20)
: :
inotifyFd_(inotify_init()), useInotify_(useInotify)
dirWatches_(sz),
dirFiles_(sz)
{ {
if (inotifyFd_ < 0) if (useInotify_)
{ {
static bool hasWarned = false; #ifdef FOAM_USE_INOTIFY
if (!hasWarned) inotifyFd_ = inotify_init();
dirWatches_.setCapacity(sz);
dirFiles_.setCapacity(sz);
if (inotifyFd_ < 0)
{ {
hasWarned = true; static bool hasWarned = false;
WarningIn("fileMonitorWatcher(const label)") if (!hasWarned)
<< "Failed allocating an inotify descriptor : " {
<< string(strerror(errno)) << endl hasWarned = true;
<< " Please increase the number of allowable " WarningIn("fileMonitorWatcher(const bool, const label)")
<< "inotify instances" << endl << "Failed allocating an inotify descriptor : "
<< " (/proc/sys/fs/inotify/max_user_instances" << string(strerror(errno)) << endl
<< " on Linux)" << endl << " Please increase the number of allowable "
<< " , switch off runTimeModifiable." << endl << "inotify instances" << endl
<< " or compile this file with FOAM_USE_STAT to use" << " (/proc/sys/fs/inotify/max_user_instances"
<< " time stamps instead of inotify." << endl << " on Linux)" << endl
<< " Continuing without additional file monitoring." << " , switch off runTimeModifiable." << endl
<< endl; << " or compile this file without "
<< "FOAM_USE_INOTIFY"
<< " to use time stamps instead of inotify." << endl
<< " Continuing without additional file"
<< " monitoring."
<< endl;
}
} }
#else
FatalErrorIn("fileMonitorWatcher(const bool, const label)")
<< "You selected inotify but this file was compiled"
<< " without FOAM_USE_INOTIFY"
<< "Please select another fileModification test method"
<< exit(FatalError);
#endif
}
else
{
lastMod_.setCapacity(sz);
} }
} }
//- remove all watches //- remove all watches
inline ~fileMonitorWatcher() inline ~fileMonitorWatcher()
{ {
if (inotifyFd_ >= 0) #ifdef FOAM_USE_INOTIFY
if (useInotify_ && inotifyFd_ >= 0)
{ {
forAll(dirWatches_, i) forAll(dirWatches_, i)
{ {
@ -197,57 +196,92 @@ namespace Foam
} }
} }
} }
#endif
} }
inline bool addWatch(const label watchFd, const fileName& fName) inline bool addWatch(const label watchFd, const fileName& fName)
{ {
if (inotifyFd_ < 0) if (useInotify_)
{ {
return false; if (inotifyFd_ < 0)
{
return false;
}
#ifdef FOAM_USE_INOTIFY
// Add/retrieve watch on directory containing file.
// Note that fName might be non-existing in special situations
// (master-only reading for IODictionaries)
const fileName dir = fName.path();
label dirWatchID = -1;
if (isDir(dir))
{
dirWatchID = inotify_add_watch
(
inotifyFd_,
dir.c_str(),
IN_CLOSE_WRITE
);
if (dirWatchID < 0)
{
FatalErrorIn("addWatch(const label, const fileName&)")
<< "Failed adding watch " << watchFd
<< " to directory " << fName << " due to "
<< string(strerror(errno))
<< exit(FatalError);
}
}
if (watchFd < dirWatches_.size() && dirWatches_[watchFd] != -1)
{
// Reuse of watchFd : should have dir watchID set to -1.
FatalErrorIn("addWatch(const label, const fileName&)")
<< "Problem adding watch " << watchFd
<< " to file " << fName
<< abort(FatalError);
}
dirWatches_(watchFd) = dirWatchID;
dirFiles_(watchFd) = fName.name();
#endif
}
else
{
if (watchFd < lastMod_.size() && lastMod_[watchFd] != 0)
{
// Reuse of watchFd : should have lastMod set to 0.
FatalErrorIn("addWatch(const label, const fileName&)")
<< "Problem adding watch " << watchFd
<< " to file " << fName
<< abort(FatalError);
}
lastMod_(watchFd) = lastModified(fName);
} }
// Add/retrieve watch on directory containing file
label dirWatchID = inotify_add_watch
(
inotifyFd_,
fName.path().c_str(),
IN_CLOSE_WRITE
);
if (dirWatchID < 0)
{
FatalErrorIn("addWatch(const label, const fileName&)")
<< "Failed adding watch " << watchFd
<< " to directory " << fName << " due to "
<< string(strerror(errno))
<< exit(FatalError);
}
if (watchFd < dirWatches_.size() && dirWatches_[watchFd] != -1)
{
// Reuse of watchFd : should have dir watchID set to -1.
FatalErrorIn("addWatch(const label, const fileName&)")
<< "Problem adding watch " << watchFd
<< " to file " << fName
<< abort(FatalError);
}
dirWatches_(watchFd) = dirWatchID;
dirFiles_(watchFd) = fName.name();
return true; return true;
} }
inline bool removeWatch(const label watchFd) inline bool removeWatch(const label watchFd)
{ {
if (inotifyFd_ < 0) if (useInotify_)
{ {
return false; if (inotifyFd_ < 0)
} {
return false;
}
dirWatches_[watchFd] = -1; dirWatches_[watchFd] = -1;
}
else
{
lastMod_[watchFd] = 0;
}
return true; return true;
} }
#endif
}; };
//! @endcond //! @endcond
@ -258,131 +292,146 @@ namespace Foam
void Foam::fileMonitor::checkFiles() const void Foam::fileMonitor::checkFiles() const
{ {
#ifdef FOAM_USE_STAT if (useInotify_)
forAll(watcher_->lastMod_, watchFd)
{ {
time_t oldTime = watcher_->lastMod_[watchFd]; #ifdef FOAM_USE_INOTIFY
// Large buffer for lots of events
char buffer[EVENT_BUF_LEN];
if (oldTime != 0) while (true)
{ {
const fileName& fName = watchFile_[watchFd]; struct timeval zeroTimeout = {0, 0};
time_t newTime = lastModified(fName);
if (newTime == 0) //- Pre-allocated structure containing file descriptors
fd_set fdSet;
// Add notify descriptor to select fd_set
FD_ZERO(&fdSet);
FD_SET(watcher_->inotifyFd_, &fdSet);
int ready = select
(
watcher_->inotifyFd_+1, // num filedescriptors in fdSet
&fdSet, // fdSet with only inotifyFd
NULL, // No writefds
NULL, // No errorfds
&zeroTimeout // eNo timeout
);
if (ready < 0)
{ {
state_[watchFd] = DELETED; FatalErrorIn("fileMonitor::updateStates()")
<< "Problem in issuing select."
<< abort(FatalError);
}
else if (FD_ISSET(watcher_->inotifyFd_, &fdSet))
{
// Read events
ssize_t nBytes = read
(
watcher_->inotifyFd_,
buffer,
EVENT_BUF_LEN
);
if (nBytes < 0)
{
FatalErrorIn("fileMonitor::updateStates(const fileName&)")
<< "read of " << watcher_->inotifyFd_
<< " failed with " << label(nBytes)
<< abort(FatalError);
}
// Go through buffer, consuming events
int i = 0;
while (i < nBytes)
{
const struct inotify_event* inotifyEvent =
reinterpret_cast<const struct inotify_event*>
(
&buffer[i]
);
//Pout<< "watchFd:" << inotifyEvent->wd << nl
// << "mask:" << inotifyEvent->mask << nl
// << endl;
//Pout<< "file:" << fileName(inotifyEvent->name) << endl;
//Pout<< "len:" << inotifyEvent->len << endl;
if
(
(inotifyEvent->mask & IN_CLOSE_WRITE)
&& inotifyEvent->len
)
{
// Search for file
forAll(watcher_->dirWatches_, i)
{
label id = watcher_->dirWatches_[i];
if
(
id == inotifyEvent->wd
&& inotifyEvent->name == watcher_->dirFiles_[i]
)
{
// Correct directory and name
state_[i] = MODIFIED;
}
}
}
i += EVENT_SIZE + inotifyEvent->len;
}
} }
else else
{ {
if (newTime > (oldTime + regIOobject::fileModificationSkew)) // No data
return;
}
}
#endif
}
else
{
forAll(watcher_->lastMod_, watchFd)
{
time_t oldTime = watcher_->lastMod_[watchFd];
if (oldTime != 0)
{
const fileName& fName = watchFile_[watchFd];
time_t newTime = lastModified(fName);
if (newTime == 0)
{ {
watcher_->lastMod_[watchFd] = newTime; state_[watchFd] = DELETED;
state_[watchFd] = MODIFIED;
} }
else else
{ {
state_[watchFd] = UNMODIFIED; if (newTime > (oldTime + regIOobject::fileModificationSkew))
}
}
}
}
#else
// Large buffer for lots of events
char buffer[EVENT_BUF_LEN];
while (true)
{
struct timeval zeroTimeout = {0, 0};
//- Pre-allocated structure containing file descriptors
fd_set fdSet;
// Add notify descriptor to select fd_set
FD_ZERO(&fdSet);
FD_SET(watcher_->inotifyFd_, &fdSet);
int ready = select
(
watcher_->inotifyFd_+1, // num filedescriptors in fdSet
&fdSet, // fdSet with only inotifyFd
NULL, // No writefds
NULL, // No errorfds
&zeroTimeout // eNo timeout
);
if (ready < 0)
{
FatalErrorIn("fileMonitor::updateStates()")
<< "Problem in issuing select."
<< abort(FatalError);
}
else if (FD_ISSET(watcher_->inotifyFd_, &fdSet))
{
// Read events
ssize_t nBytes = read(watcher_->inotifyFd_, buffer, EVENT_BUF_LEN);
if (nBytes < 0)
{
FatalErrorIn("fileMonitor::updateStates(const fileName&)")
<< "read of " << watcher_->inotifyFd_
<< " failed with " << label(nBytes)
<< abort(FatalError);
}
// Go through buffer, consuming events
int i = 0;
while (i < nBytes)
{
const struct inotify_event* inotifyEvent =
reinterpret_cast<const struct inotify_event*>
(
&buffer[i]
);
//Pout<< "watchFd:" << inotifyEvent->wd << nl
// << "mask:" << inotifyEvent->mask << nl
// << endl;
//Pout<< "file:" << fileName(inotifyEvent->name) << endl;
//Pout<< "len:" << inotifyEvent->len << endl;
if ((inotifyEvent->mask & IN_CLOSE_WRITE) && inotifyEvent->len)
{
// Search for file
forAll(watcher_->dirWatches_, i)
{ {
label id = watcher_->dirWatches_[i]; watcher_->lastMod_[watchFd] = newTime;
if state_[watchFd] = MODIFIED;
( }
id == inotifyEvent->wd else
&& inotifyEvent->name == watcher_->dirFiles_[i] {
) state_[watchFd] = UNMODIFIED;
{
// Correct directory and name
state_[i] = MODIFIED;
}
} }
} }
i += EVENT_SIZE + inotifyEvent->len;
} }
} }
else
{
// No data
return;
}
} }
#endif
} }
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fileMonitor::fileMonitor() Foam::fileMonitor::fileMonitor(const bool useInotify)
: :
useInotify_(useInotify),
state_(20), state_(20),
watchFile_(20), watchFile_(20),
freeWatchFds_(2), freeWatchFds_(2),
watcher_(new fileMonitorWatcher(20)) watcher_(new fileMonitorWatcher(useInotify_, 20))
{} {}
@ -394,6 +443,8 @@ Foam::fileMonitor::~fileMonitor()
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
// Note: fName might not exist (on slaves if in master-only mode for
// regIOobject)
Foam::label Foam::fileMonitor::addWatch(const fileName& fName) Foam::label Foam::fileMonitor::addWatch(const fileName& fName)
{ {
label watchFd; label watchFd;
@ -458,50 +509,97 @@ const
} }
void Foam::fileMonitor::updateStates(const bool syncPar) const void Foam::fileMonitor::updateStates
(
const bool masterOnly,
const bool syncPar
) const
{ {
checkFiles(); if (Pstream::master() || !masterOnly)
{
checkFiles();
}
if (syncPar) if (syncPar)
{ {
PackedList<2> stats(state_.size()); // Pack current state (might be on master only)
forAll(state_, watchFd) PackedList<2> stats(state_.size(), MODIFIED);
if (Pstream::master() || !masterOnly)
{ {
stats[watchFd] = static_cast<unsigned int>(state_[watchFd]); forAll(state_, watchFd)
{
stats[watchFd] = static_cast<unsigned int>(state_[watchFd]);
}
} }
// Save local state for warning message below
PackedList<2> thisProcStats(stats);
if (stats.storage().size() == 1)
// Save local state for warning message below
PackedList<2> thisProcStats;
if (!masterOnly)
{ {
// Optimisation valid for most cases. thisProcStats = stats;
reduce(stats.storage()[0], reduceFileStates()); }
// Scatter or reduce to synchronise state
if (masterOnly)
{
// Scatter
if (stats.storage().size() == 1)
{
Pstream::scatter(stats.storage()[0]);
}
else
{
Pstream::listCombineScatter(stats.storage());
}
} }
else else
{ {
Pstream::listCombineGather // Reduce
( if (stats.storage().size() == 1)
stats.storage(), {
combineReduceFileStates() // Optimisation valid for most cases.
); reduce(stats.storage()[0], reduceFileStates());
}
else
{
Pstream::listCombineGather
(
stats.storage(),
combineReduceFileStates()
);
}
} }
// Update local state
forAll(state_, watchFd) forAll(state_, watchFd)
{ {
if (thisProcStats[watchFd] != UNMODIFIED) if (masterOnly)
{ {
if (stats[watchFd] == UNMODIFIED) // No need to check for inconsistent state. Just assign.
unsigned int stat = stats[watchFd];
state_[watchFd] = fileState(stat);
}
else
{
// Check for inconsistent state before assigning.
if (thisProcStats[watchFd] != UNMODIFIED)
{ {
WarningIn("fileMonitor::updateStates(const bool) const") if (stats[watchFd] == UNMODIFIED)
<< "Delaying reading " << watchFile_[watchFd] {
<< " due to inconsistent " WarningIn("fileMonitor::updateStates(const bool) const")
"file time-stamps between processors" << "Delaying reading " << watchFile_[watchFd]
<< endl; << " due to inconsistent "
} "file time-stamps between processors"
else << endl;
{ }
unsigned int stat = stats[watchFd]; else
state_[watchFd] = fileState(stat); {
unsigned int stat = stats[watchFd];
state_[watchFd] = fileState(stat);
}
} }
} }
} }
@ -511,10 +609,12 @@ void Foam::fileMonitor::updateStates(const bool syncPar) const
void Foam::fileMonitor::setUnmodified(const label watchFd) void Foam::fileMonitor::setUnmodified(const label watchFd)
{ {
#ifdef FOAM_USE_STAT
watcher_->lastMod_[watchFd] = lastModified(watchFile_[watchFd]);
#endif
state_[watchFd] = UNMODIFIED; state_[watchFd] = UNMODIFIED;
if (!useInotify_)
{
watcher_->lastMod_[watchFd] = lastModified(watchFile_[watchFd]);
}
} }

View File

@ -28,10 +28,11 @@ Description
Checking for changes to files. Checking for changes to files.
Note Note
The default is to use inotify (Linux specific, since 2.6.13) The default is to use stat to get the timestamp.
Compiling with FOAM_USE_STAT (or if /usr/include/sys/inotify.h Compile with FOAM_USE_INOTIFY to use the inotify
does not exist) uses the stat function call. (Linux specific, since 2.6.13) framework. The problem is that inotify does
not work on nfs3 mounted directories!!
SourceFiles SourceFiles
fileMonitor.C fileMonitor.C
@ -78,6 +79,9 @@ public:
private: private:
// Private data // Private data
//- Whether to use inotify (requires -DFOAM_USE_INOTIFY, see above)
const bool useInotify_;
//- State for all watchFds //- State for all watchFds
mutable DynamicList<fileState> state_; mutable DynamicList<fileState> state_;
@ -111,7 +115,7 @@ public:
// Constructors // Constructors
//- Construct null //- Construct null
fileMonitor(); fileMonitor(const bool useInotify);
//- Destructor //- Destructor
@ -133,7 +137,11 @@ public:
fileState getState(const label watchFd) const; fileState getState(const label watchFd) const;
//- Check state of all files. Updates state_. //- Check state of all files. Updates state_.
void updateStates(const bool syncPar) const; void updateStates
(
const bool masterOnly,
const bool syncPar
) const;
//- Reset state (e.g. after having read it) using handle //- Reset state (e.g. after having read it) using handle
void setUnmodified(const label watchFd); void setUnmodified(const label watchFd);

View File

@ -31,12 +31,61 @@ Description
#include "IOdictionary.H" #include "IOdictionary.H"
#include "objectRegistry.H" #include "objectRegistry.H"
#include "Pstream.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
defineTypeNameAndDebug(Foam::IOdictionary, 0); defineTypeNameAndDebug(Foam::IOdictionary, 0);
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
// Parallel aware reading, using non-virtual type information (typeName instead
// of type()) because of use in constructor.
void Foam::IOdictionary::readFile(const bool masterOnly)
{
if (Pstream::master() || !masterOnly)
{
if (debug)
{
Pout<< "IOdictionary : Reading " << objectPath()
<< " from file " << endl;
}
readStream(typeName) >> *this;
close();
}
if (masterOnly)
{
// Scatter master data
if (Pstream::master())
{
for
(
int slave=Pstream::firstSlave();
slave<=Pstream::lastSlave();
slave++
)
{
OPstream toSlave(Pstream::scheduled, slave);
IOdictionary::writeData(toSlave);
}
}
else
{
if (debug)
{
Pout<< "IOdictionary : Reading " << objectPath()
<< " from master processor " << Pstream::masterNo() << endl;
}
IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
IOdictionary::readData(fromMaster);
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::IOdictionary::IOdictionary(const IOobject& io) Foam::IOdictionary::IOdictionary(const IOobject& io)
@ -56,17 +105,41 @@ Foam::IOdictionary::IOdictionary(const IOobject& io)
//<< abort(FatalError); //<< abort(FatalError);
} }
// Everyone check or just master
bool masterOnly =
regIOobject::fileModificationChecking == timeStampMaster
|| regIOobject::fileModificationChecking == inotifyMaster;
// Check if header is ok for READ_IF_PRESENT
bool isHeaderOk = false;
if (io.readOpt() == IOobject::READ_IF_PRESENT)
{
if (masterOnly)
{
if (Pstream::master())
{
isHeaderOk = headerOk();
}
Pstream::scatter(isHeaderOk);
}
else
{
isHeaderOk = headerOk();
}
}
if if
( (
( (
io.readOpt() == IOobject::MUST_READ io.readOpt() == IOobject::MUST_READ
|| io.readOpt() == IOobject::MUST_READ_IF_MODIFIED || io.readOpt() == IOobject::MUST_READ_IF_MODIFIED
) )
|| (io.readOpt() == IOobject::READ_IF_PRESENT && headerOk()) || isHeaderOk
) )
{ {
readStream(typeName) >> *this; readFile(masterOnly);
close();
} }
dictionary::name() = IOobject::objectPath(); dictionary::name() = IOobject::objectPath();
@ -90,17 +163,41 @@ Foam::IOdictionary::IOdictionary(const IOobject& io, const dictionary& dict)
<< endl; << endl;
} }
// Everyone check or just master
bool masterOnly =
regIOobject::fileModificationChecking == timeStampMaster
|| regIOobject::fileModificationChecking == inotifyMaster;
// Check if header is ok for READ_IF_PRESENT
bool isHeaderOk = false;
if (io.readOpt() == IOobject::READ_IF_PRESENT)
{
if (masterOnly)
{
if (Pstream::master())
{
isHeaderOk = headerOk();
}
Pstream::scatter(isHeaderOk);
}
else
{
isHeaderOk = headerOk();
}
}
if if
( (
( (
io.readOpt() == IOobject::MUST_READ io.readOpt() == IOobject::MUST_READ
|| io.readOpt() == IOobject::MUST_READ_IF_MODIFIED || io.readOpt() == IOobject::MUST_READ_IF_MODIFIED
) )
|| (io.readOpt() == IOobject::READ_IF_PRESENT && headerOk()) || isHeaderOk
) )
{ {
readStream(typeName) >> *this; readFile(masterOnly);
close();
} }
else else
{ {

View File

@ -57,6 +57,11 @@ class IOdictionary
public dictionary public dictionary
{ {
// Private Member Functions
//- read dictionary from file
void readFile(const bool);
public: public:
TypeName("dictionary"); TypeName("dictionary");

View File

@ -250,8 +250,27 @@ Foam::Time::Time
// Time objects not registered so do like objectRegistry::checkIn ourselves. // Time objects not registered so do like objectRegistry::checkIn ourselves.
if (runTimeModifiable_) if (runTimeModifiable_)
{ {
monitorPtr_.reset(new fileMonitor()); monitorPtr_.reset
controlDict_.watchIndex() = addWatch(controlDict_.filePath()); (
new fileMonitor
(
regIOobject::fileModificationChecking == inotify
|| regIOobject::fileModificationChecking == inotifyMaster
)
);
// File might not exist yet.
fileName f(controlDict_.filePath());
if (!f.size())
{
// We don't have this file but would like to re-read it.
// Possibly if in master-only reading mode. Use a non-existing
// file to keep fileMonitor synced.
f = controlDict_.objectPath();
}
controlDict_.watchIndex() = addWatch(f);
} }
} }
@ -308,19 +327,36 @@ Foam::Time::Time
readLibs_(controlDict_, "libs"), readLibs_(controlDict_, "libs"),
functionObjects_(*this) functionObjects_(*this)
{ {
// Since could not construct regIOobject with setting:
controlDict_.readOpt() = IOobject::MUST_READ_IF_MODIFIED;
setControls(); setControls();
// Time objects not registered so do like objectRegistry::checkIn ourselves. // Time objects not registered so do like objectRegistry::checkIn ourselves.
if (runTimeModifiable_) if (runTimeModifiable_)
{ {
monitorPtr_.reset(new fileMonitor()); monitorPtr_.reset
(
new fileMonitor
(
regIOobject::fileModificationChecking == inotify
|| regIOobject::fileModificationChecking == inotifyMaster
)
);
// File might not exist yet. // File might not exist yet.
fileName f(controlDict_.filePath()); fileName f(controlDict_.filePath());
if (f != fileName::null)
if (!f.size())
{ {
controlDict_.watchIndex() = addWatch(f); // We don't have this file but would like to re-read it.
// Possibly if in master-only reading mode. Use a non-existing
// file to keep fileMonitor synced.
f = controlDict_.objectPath();
} }
controlDict_.watchIndex() = addWatch(f);
} }
} }

View File

@ -211,7 +211,14 @@ void Foam::Time::readModifiedObjects()
// valid filePath). // valid filePath).
// Note: requires same ordering in objectRegistries on different // Note: requires same ordering in objectRegistries on different
// processors! // processors!
monitorPtr_().updateStates(Pstream::parRun()); monitorPtr_().updateStates
(
(
regIOobject::fileModificationChecking == inotifyMaster
|| regIOobject::fileModificationChecking == timeStampMaster
),
Pstream::parRun()
);
// Time handling is special since controlDict_ is the one dictionary // Time handling is special since controlDict_ is the one dictionary
// that is not registered to any database. // that is not registered to any database.

View File

@ -36,6 +36,35 @@ int Foam::regIOobject::fileModificationSkew
Foam::debug::optimisationSwitch("fileModificationSkew", 30) Foam::debug::optimisationSwitch("fileModificationSkew", 30)
); );
namespace Foam
{
template<>
const char* Foam::NamedEnum<Foam::regIOobject::fileCheckTypes, 4>::names[] =
{
"timeStamp",
"timeStampMaster",
"inotify",
"inotifyMaster"
};
}
const Foam::NamedEnum<Foam::regIOobject::fileCheckTypes, 4>
Foam::regIOobject::fileCheckTypesNames;
// Default fileCheck type
Foam::regIOobject::fileCheckTypes Foam::regIOobject::fileModificationChecking
(
fileCheckTypesNames.read
(
debug::optimisationSwitches().lookup
(
"fileModificationChecking"
//Foam::regIOobject::timeStamp
)
)
);
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
@ -149,10 +178,13 @@ bool Foam::regIOobject::checkIn()
} }
fileName f = filePath(); fileName f = filePath();
if (f != fileName::null) if (!f.size())
{ {
watchIndex_ = time().addWatch(f); // We don't have this file but would like to re-read it.
// Possibly if master-only reading mode.
f = objectPath();
} }
watchIndex_ = time().addWatch(f);
} }
// check-in on defaultRegion is allowed to fail, since subsetted meshes // check-in on defaultRegion is allowed to fail, since subsetted meshes

View File

@ -41,6 +41,7 @@ SourceFiles
#include "IOobject.H" #include "IOobject.H"
#include "typeInfo.H" #include "typeInfo.H"
#include "OSspecific.H" #include "OSspecific.H"
#include "NamedEnum.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -57,6 +58,20 @@ class regIOobject
public IOobject public IOobject
{ {
public:
//- Types of communications
enum fileCheckTypes
{
timeStamp,
timeStampMaster,
inotify,
inotifyMaster
};
static const NamedEnum<fileCheckTypes, 4> fileCheckTypesNames;
private: private:
// Private data // Private data
@ -95,6 +110,8 @@ public:
static int fileModificationSkew; static int fileModificationSkew;
static fileCheckTypes fileModificationChecking;
// Constructors // Constructors

View File

@ -26,7 +26,7 @@ License
#include "regIOobject.H" #include "regIOobject.H"
#include "IFstream.H" #include "IFstream.H"
#include "Time.H" #include "Time.H"
//#include "PstreamReduceOps.H" #include "Pstream.H"
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
@ -170,8 +170,50 @@ bool Foam::regIOobject::readData(Istream&)
bool Foam::regIOobject::read() bool Foam::regIOobject::read()
{ {
bool ok = readData(readStream(type())); // Note: cannot do anything in readStream itself since this is used by
close(); // e.g. GeometricField.
bool masterOnly =
regIOobject::fileModificationChecking == timeStampMaster
|| regIOobject::fileModificationChecking == inotifyMaster;
bool ok;
if (Pstream::master() || !masterOnly)
{
ok = readData(readStream(type()));
close();
}
if (masterOnly)
{
// Scatter master data
if (Pstream::master())
{
for
(
int slave=Pstream::firstSlave();
slave<=Pstream::lastSlave();
slave++
)
{
OPstream toSlave(Pstream::scheduled, slave);
writeData(toSlave);
}
}
else
{
if (IFstream::debug)
{
Pout<< "regIOobject::read() : "
<< "reading object " << name()
<< " from master processor " << Pstream::masterNo()
<< endl;
}
IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
ok = readData(fromMaster);
}
}
return ok; return ok;
} }

View File

@ -55,6 +55,12 @@ Foam::argList::initValidTables::initValidTables()
); );
argList::addBoolOption("parallel", "run in parallel"); argList::addBoolOption("parallel", "run in parallel");
validParOptions.set("parallel", ""); validParOptions.set("parallel", "");
argList::addOption
(
"roots", "(dir1 .. dirn)",
"slave root directories for distributed running"
);
validParOptions.set("roots", "(dir1 .. dirn)");
Pstream::addValidParOptions(validParOptions); Pstream::addValidParOptions(validParOptions);
} }
@ -511,6 +517,10 @@ Foam::argList::argList
// Case is a single processor run unless it is running parallel // Case is a single processor run unless it is running parallel
int nProcs = 1; int nProcs = 1;
// Roots if running distributed
fileNameList roots;
// If this actually is a parallel run // If this actually is a parallel run
if (parRunControl_.parRun()) if (parRunControl_.parRun())
{ {
@ -520,28 +530,42 @@ Foam::argList::argList
// establish rootPath_/globalCase_/case_ for master // establish rootPath_/globalCase_/case_ for master
getRootCase(); getRootCase();
IFstream decompDictStream // See if running distributed (different roots for different procs)
( label dictNProcs = -1;
rootPath_/globalCase_/"system/decomposeParDict" fileName source;
);
if (!decompDictStream.good()) if (options_.found("roots"))
{ {
FatalError IStringStream str(options_["roots"]);
<< "Cannot read " str >> roots;
<< decompDictStream.name() dictNProcs = roots.size()+1;
<< exit(FatalError); source = "roots-command-line";
} }
else
{
source = rootPath_/globalCase_/"system/decomposeParDict";
IFstream decompDictStream(source);
dictionary decompDict(decompDictStream); if (!decompDictStream.good())
{
FatalError
<< "Cannot read "
<< decompDictStream.name()
<< exit(FatalError);
}
label dictNProcs dictionary decompDict(decompDictStream);
(
readLabel dictNProcs = readLabel
( (
decompDict.lookup("numberOfSubdomains") decompDict.lookup("numberOfSubdomains")
) );
);
if (decompDict.lookupOrDefault("distributed", false))
{
decompDict.lookup("roots") >> roots;
}
}
// Check number of processors. // Check number of processors.
// nProcs => number of actual procs // nProcs => number of actual procs
@ -555,18 +579,17 @@ Foam::argList::argList
if (dictNProcs > Pstream::nProcs()) if (dictNProcs > Pstream::nProcs())
{ {
FatalError FatalError
<< decompDictStream.name() << source
<< " specifies " << dictNProcs << " specifies " << dictNProcs
<< " processors but job was started with " << " processors but job was started with "
<< Pstream::nProcs() << " processors." << Pstream::nProcs() << " processors."
<< exit(FatalError); << exit(FatalError);
} }
// distributed data // distributed data
if (decompDict.lookupOrDefault("distributed", false)) if (roots.size())
{ {
fileNameList roots;
decompDict.lookup("roots") >> roots;
forAll(roots, i) forAll(roots, i)
{ {
roots[i] = roots[i].expand(); roots[i] = roots[i].expand();
@ -575,7 +598,8 @@ Foam::argList::argList
if (roots.size() != Pstream::nProcs()-1) if (roots.size() != Pstream::nProcs()-1)
{ {
FatalError FatalError
<< "number of entries in decompositionDict::roots" << "number of entries in roots "
<< roots.size()
<< " is not equal to the number of slaves " << " is not equal to the number of slaves "
<< Pstream::nProcs()-1 << Pstream::nProcs()-1
<< exit(FatalError); << exit(FatalError);
@ -709,8 +733,12 @@ Foam::argList::argList
if (parRunControl_.parRun()) if (parRunControl_.parRun())
{ {
Info<< "Slaves : " << slaveProcs << nl Info<< "Slaves : " << slaveProcs << nl;
<< "Pstream initialized with:" << nl if (roots.size())
{
Info<< "Roots : " << roots << nl;
}
Info<< "Pstream initialized with:" << nl
<< " floatTransfer : " << Pstream::floatTransfer << nl << " floatTransfer : " << Pstream::floatTransfer << nl
<< " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl << " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl
<< " commsType : " << " commsType : "
@ -726,6 +754,10 @@ Foam::argList::argList
{ {
jobInfo.add("slaves", slaveProcs); jobInfo.add("slaves", slaveProcs);
} }
if (roots.size())
{
jobInfo.add("roots", roots);
}
jobInfo.write(); jobInfo.write();
// Switch on signal trapping. We have to wait until after Pstream::init // Switch on signal trapping. We have to wait until after Pstream::init