diff --git a/src/rewrite/Context.cpp b/src/rewrite/Context.cpp index 8c16ccfe4..35c64a68c 100644 --- a/src/rewrite/Context.cpp +++ b/src/rewrite/Context.cpp @@ -103,8 +103,7 @@ void Context::initialize (int argc, char** argv) tdb.location (expandPath (*path)); // Allow user override of file locking. Solaris/NFS machines may want this. - if (! config.get ("locking", true)) - tdb.noLock (); + tdb.lock (config.get ("locking", true)); // TODO Load pending.data. // TODO Load completed.data. @@ -114,11 +113,12 @@ void Context::initialize (int argc, char** argv) //////////////////////////////////////////////////////////////////////////////// int Context::run () { - throw std::string ("unimplemented Context::run"); // TODO Dispatch to command handlers. // TODO Auto shadow update. // TODO Auto gc. + // TODO tdb.load (Filter); + throw std::string ("unimplemented Context::run"); return 0; } diff --git a/src/rewrite/TDB.cpp b/src/rewrite/TDB.cpp index 3f2d658be..6a4ccb018 100644 --- a/src/rewrite/TDB.cpp +++ b/src/rewrite/TDB.cpp @@ -26,14 +26,45 @@ //////////////////////////////////////////////////////////////////////////////// #include +#include #include "text.h" #include "util.h" #include "TDB.h" #include "task.h" //////////////////////////////////////////////////////////////////////////////// +// The ctor/dtor do nothing. +// The lock/unlock methods hold the file open. +// There should be only one commit. +// +// +- TDB::TDB +// | +// | +- TDB::lock +// | | open +// | | [lock] +// | | +// | | +- TDB::load (Filter) +// | | | read all +// | | | apply filter +// | | | return subset +// | | | +// | | +- TDB::add (T) +// | | | +// | | +- TDB::update (T, T') +// | | | +// | | +- TDB::commit +// | | write all +// | | +// | +- TDB::unlock +// | [unlock] +// | close +// | +// +- TDB::~TDB +// [TDB::unlock] +// TDB::TDB () : mLock (true) +, mAllOpenAndLocked (false) { } @@ -41,7 +72,13 @@ TDB::TDB () TDB::TDB (const TDB& other) { throw std::string ("unimplemented TDB::TDB"); - mLocations = other.mLocations; + mLocations = other.mLocations; + mLock = other.mLock; + mAllOpenAndLocked = false; // Deliberately so. + + // Set all to NULL. + foreach (location, mLocations) + mLocations[location->first] = NULL; } //////////////////////////////////////////////////////////////////////////////// @@ -50,7 +87,13 @@ TDB& TDB::operator= (const TDB& other) throw std::string ("unimplemented TDB::operator="); if (this != &other) { - mLocations = other.mLocations; + mLocations = other.mLocations; + mLock = other.mLock; + mAllOpenAndLocked = false; // Deliberately so. + + // Set all to NULL. + foreach (location, mLocations) + mLocations[location->first] = NULL; } return *this; @@ -59,6 +102,8 @@ TDB& TDB::operator= (const TDB& other) //////////////////////////////////////////////////////////////////////////////// TDB::~TDB () { + if (mAllOpenAndLocked) + unlock (); } //////////////////////////////////////////////////////////////////////////////// @@ -69,7 +114,33 @@ void TDB::location (const std::string& path) path + "' does not exist, or is not readable and writable."; - mLocations.push_back (path); + mLocations[path] = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +void TDB::lock (bool lockFile /* = true */) +{ + mLock = lockFile; + + foreach (location, mLocations) + mLocations[location->first] = openAndLock (location->first); + + mAllOpenAndLocked = true; +} + +//////////////////////////////////////////////////////////////////////////////// +void TDB::unlock () +{ + foreach (location, mLocations) + { + if (mLocations[location->first] != NULL) + { + fclose (mLocations[location->first]); + mLocations[location->first] = NULL; + } + } + + mAllOpenAndLocked = false; } //////////////////////////////////////////////////////////////////////////////// @@ -80,6 +151,13 @@ int TDB::load (std::vector & tasks, Filter& filter) return 0; } +//////////////////////////////////////////////////////////////////////////////// +// TODO Write to transaction log. +void TDB::add (T& after) +{ + throw std::string ("unimplemented TDB::add"); +} + //////////////////////////////////////////////////////////////////////////////// // TODO Write to transaction log. void TDB::update (T& before, T& after) @@ -101,19 +179,13 @@ void TDB::upgrade () throw std::string ("unimplemented TDB::upgrade"); } -//////////////////////////////////////////////////////////////////////////////// -void TDB::noLock () -{ - mLock = false; -} - //////////////////////////////////////////////////////////////////////////////// void TDB::getPendingFiles (std::vector files) { files.clear (); foreach (location, mLocations) - files.push_back (*location + "/pending.data"); + files.push_back (location->first + "/pending.data"); } //////////////////////////////////////////////////////////////////////////////// @@ -122,7 +194,32 @@ void TDB::getCompletedFiles (std::vector files) files.clear (); foreach (location, mLocations) - files.push_back (*location + "/completed.data"); + files.push_back (location->first + "/completed.data"); +} + +//////////////////////////////////////////////////////////////////////////////// +FILE* TDB::openAndLock (const std::string& file) +{ + // Check for access. + if (access (file.c_str (), F_OK | R_OK | W_OK)) + throw std::string ("Task does not have the correct permissions for '") + + file + "'."; + + // Open the file. + FILE* in = fopen (file.c_str (), "rw"); + if (!in) + throw std::string ("Could not open '") + file + "'."; + + // Lock if desired. Try three times before failing. + int retry = 0; + if (mLock) + while (flock (fileno (in), LOCK_EX) && ++retry <= 3) + delay (0.1); + + if (!in) + throw std::string ("Could not lock '") + file + "'."; + + return in; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/rewrite/TDB.h b/src/rewrite/TDB.h index 0e6721fb1..c2f94f697 100644 --- a/src/rewrite/TDB.h +++ b/src/rewrite/TDB.h @@ -27,6 +27,7 @@ #ifndef INCLUDED_TDB #define INCLUDED_TDB +#include #include #include #include "Filter.h" @@ -40,21 +41,26 @@ public: TDB& operator= (const TDB&); // Assignment operator ~TDB (); // Destructor - void location (const std::string&); - int load (std::vector &, Filter&); - void update (T&, T&); - int commit (); - void upgrade (); + void location (const std::string&); - void noLock (); + void lock (bool lockFile = true); + void unlock (); + + int load (std::vector &, Filter&); + void add (T&); + void update (T&, T&); + int commit (); + void upgrade (); private: - void getPendingFiles (std::vector ); - void getCompletedFiles (std::vector ); + void getPendingFiles (std::vector ); + void getCompletedFiles (std::vector ); + FILE* openAndLock (const std::string&); private: - std::vector mLocations; + std::map mLocations; bool mLock; + bool mAllOpenAndLocked; // TODO Need cache of raw file contents. };