casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
LockFile.h
Go to the documentation of this file.
00001 //# LockFile.h: Class to handle file locking and synchronization
00002 //# Copyright (C) 1997,1998,1999,2000,2001,2002
00003 //# Associated Universities, Inc. Washington DC, USA.
00004 //#
00005 //# This library is free software; you can redistribute it and/or modify it
00006 //# under the terms of the GNU Library General Public License as published by
00007 //# the Free Software Foundation; either version 2 of the License, or (at your
00008 //# option) any later version.
00009 //#
00010 //# This library is distributed in the hope that it will be useful, but WITHOUT
00011 //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00012 //# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00013 //# License for more details.
00014 //#
00015 //# You should have received a copy of the GNU Library General Public License
00016 //# along with this library; if not, write to the Free Software Foundation,
00017 //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
00018 //#
00019 //# Correspondence concerning AIPS++ should be addressed as follows:
00020 //#        Internet email: aips2-request@nrao.edu.
00021 //#        Postal address: AIPS++ Project Office
00022 //#                        National Radio Astronomy Observatory
00023 //#                        520 Edgemont Road
00024 //#                        Charlottesville, VA 22903-2475 USA
00025 //#
00026 //# $Id: LockFile.h 20551 2009-03-25 00:11:33Z Malte.Marquarding $
00027 
00028 #ifndef CASA_LOCKFILE_H
00029 #define CASA_LOCKFILE_H
00030 
00031 
00032 //# Includes
00033 #include <casa/aips.h>
00034 #include <casa/IO/FileLocker.h>
00035 #include <casa/OS/Time.h>
00036 #include <casa/Containers/Block.h>
00037 #include <casa/BasicSL/String.h>
00038 #include <sys/types.h>
00039 
00040 namespace casa { //# NAMESPACE CASA - BEGIN
00041 
00042 //# Forward declarations
00043 class FiledesIO;
00044 class MemoryIO;
00045 class CanonicalIO;
00046 
00047 
00048 // <summary> 
00049 // Class to handle file locking and synchronization.
00050 // </summary>
00051 
00052 // <use visibility=export>
00053 
00054 // <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="tLockFile" demos="">
00055 // </reviewed>
00056 
00057 // <prerequisite> 
00058 //    <li> class <linkto class=FileLocker>FileLocker</linkto>
00059 //    <li> class <linkto class=MemoryIO>MemoryIO</linkto>
00060 // </prerequisite>
00061 
00062 // <synopsis> 
00063 // This class handles file locking by means of a special lock file
00064 // which serves as the locking mechanism for another file or
00065 // group of files. It is for instance used to lock a table in
00066 // the AIPS++ table system.
00067 // <p>
00068 // The lock file has in principle world read/write access, so every
00069 // process accessing the main file can write information in it.
00070 // The lock file contains the following information (in canonical format):
00071 // <ul>
00072 // <li> A request list indicating which processes want to acquire a lock.
00073 //      The process holding the lock can inspect this list to decide if it
00074 //      should release its lock. An interval can be defined to be sure
00075 //      that the list is not inspected too often.
00076 //      A user can choose not to add to this list, because it incurs some
00077 //      overhead to write the list. However, that should only be done when
00078 //      one is sure that another process cannot keep a lock forever.
00079 // <li> Some information telling if the state of the main file has changed.
00080 //      The information can be used by a process to synchronize its
00081 //      internal buffers with the new contents of the file(s).
00082 //      E.g. a table could store one or more counters in it, which can be
00083 //      used to determine if the table has to refresh its caches.
00084 //      This information is passed as a MemoryIO object and is opaque
00085 //      for the <src>LockFile</src> class. It is simply handled as a
00086 //      stream of bytes.
00087 // </ul>
00088 // <p>
00089 // Acquiring a lock works as follows:
00090 // <ul>
00091 //  <li> Class <linkto class=FileLocker>FileLocker</linkto> is used
00092 //   to do one attempt to acquire a read or write lock.
00093 //  <li> If it fails and multiple attempts have to be done, the
00094 //   request is added to the request list in the lock file to tell
00095 //   the process holding the lock that another process needs a lock.
00096 //  <li> Other attempts (with 1 second intervals) will be done until the
00097 //   lock is acquired or until the maximum number of attempts is reached.
00098 //  <li> The lock request is removed from the request list.
00099 //  <li> When the lock was acquired, the synchronization info is read
00100 //   from the lock file.
00101 // </ul>
00102 // Releasing a lock writes the synchronization info into the lock file
00103 // and tells <src>FileLocker</src> to release the lock.
00104 // <p>
00105 // When the lock file cannot be opened as read/write, it is opened as
00106 // readonly. It means that the request list cannot be stored in it,
00107 // so the process has no way to tell the other processes it wants
00108 // access to the file. It has to wait until the lock is released.
00109 // <br> In principle a lock file should always be there. However, it
00110 // is possible (with a constructor option) that there is no lock file.
00111 // In that case each lock request succeeds without doing actual locking.
00112 // This mode is needed to be able to handle readonly tables containing
00113 // no lock file.
00114 // <p>
00115 // After each write the <src>fsync</src> function is called to make
00116 // sure that the contents of the file are written to disk. This is
00117 // necessary for correct file synchronization in NFS.
00118 // However, at the moment this feature is switched off, because it
00119 // degraded performance severely.
00120 // <p>
00121 // Apart from the read/write lock handling, the <src>LockFile</src>
00122 // also contains a mechanism to detect if a file is opened by another
00123 // process. This can be used to test if a process can safely delete the file.
00124 // For this purpose it sets another read lock when the file gets opened.
00125 // The function <src>isMultiUsed</src> tests this lock to see if the file is
00126 // used in other processes.
00127 // <br> This lock is also used to tell if the file is permanently locked.
00128 // If that is the case, the locked block is 2 bytes instead of 1.
00129 // <p>
00130 // When in the same process multiple LockFile objects are created for the same
00131 // file, deleting one object releases all locks on the file, thus also the
00132 // locks held by the other LockFile objects. This behaviour is due to the way
00133 // file locking is working on UNIX machines (certainly on Solaris 2.6).
00134 // One can use the test program tLockFile to test for this behaviour.
00135 // </synopsis>
00136 
00137 // <example>
00138 // <srcblock>
00139 // // Create/open the lock file (with 1 sec inspection interval).
00140 // // Acquire the lock and get the synchronization info.
00141 // LockFile lock ("file.name", 1);
00142 // MemoryIO syncInfo;
00143 // if (! lock.acquire (syncInfo)) {
00144 //     throw (AipsError ("Locking failed: " + lock.message()));
00145 // }
00146 // while (...) {
00147 //      ... do something with the table files ...
00148 //      // Test if another process needs the files.
00149 //      // If so, synchronize files and release lock.
00150 //      if (lock.inspect()) {
00151 //         do fsync for all other files
00152 //         syncInfo.seek (0);
00153 //         syncInfo.write (...);
00154 //         lock.release (syncInfo);
00155 //         // At this point another process can grab the lock.
00156 //         // Reacquire the lock
00157 //         lock.acquire (syncInfo);
00158 //             throw (AipsError ("Locking failed: " + lock.message()));
00159 //         }
00160 //     }
00161 // }
00162 // </srcblock>
00163 // </example>
00164 
00165 // <motivation> 
00166 // Make it possible to lock and synchronize tables in an easy and
00167 // efficient way.
00168 // </motivation>
00169 
00170 
00171 class LockFile
00172 {
00173 public: 
00174     // Create or open the lock file with the given name.
00175     // It is created if create=True or if the file does not exist yet.
00176     // The interval (in seconds) defines how often function <src>inspect</src>
00177     // inspects the request list in the lock file.
00178     // An interval&gt;0 means that it is only inspected if the last inspect
00179     // was at least <src>inspectInterval</src> seconds ago.
00180     // An interval&lt;=0 means that <src>inspect</src> always inspects
00181     // the request list.
00182     // <br>When addToRequestList=False, function <src>acquire</src> does not
00183     // add the request to the lock file when a lock cannot be acquired.
00184     // This may result in better performance, but should be used with care.
00185     // <br> If <src>create==True</src>, a new lock file will always be created.
00186     // Otherwise it will be created if it does not exist yet.
00187     // <br> If <src>mustExist==False</src>, it is allowed that the LockFile
00188     // does not exist and cannot be created either.
00189     // <br> The seqnr is used to set the offset where LockFile will use 2 bytes
00190     // to set the locks on. Only in special cases it should be other than 0.
00191     // At the moment the offset is 2*seqnr.
00192     // <br> The <src>permLocking</src> argument is used to indicate if
00193     // permanent locking will be used. If so, it'll indicate so. In that
00194     // way showLock() can find out if if table is permanently locked.
00195     explicit LockFile (const String& fileName, double inspectInterval = 0,
00196                        Bool create = False, Bool addToRequestList = True,
00197                        Bool mustExist = True, uInt seqnr = 0,
00198                        Bool permLocking = False);
00199 
00200     // The destructor does not delete the file, because it is not known
00201     // when the last process using the lock file will stop.
00202     // For the table system this is no problem, because the lock file
00203     // is contained in the directory of the table, thus deleted when
00204     // the table gets deleted.
00205     ~LockFile();
00206 
00207     // Is the file associated with the LockFile object in use in
00208     // another process?
00209     Bool isMultiUsed();
00210 
00211     // Acquire a read or write lock.
00212     // It reads the information (if the <src>info</src> argument is given)
00213     // from the lock file. The user is responsible for interpreting the
00214     // information (e.g. converting from canonical to local format).
00215     // The seek pointer in the <src>MemoryIO</src> object is set to 0,
00216     // so the user can simply start reading the pointer.
00217     // <br>The argument <src>nattempts</src> tells how often it is
00218     // attempted (with 1 second intervals) to acquire the lock if
00219     // it does not succeed.
00220     // 0 means forever, while 1 means do not retry.
00221     // <group>
00222     Bool acquire (FileLocker::LockType = FileLocker::Write, uInt nattempts = 0);
00223     Bool acquire (MemoryIO& info, FileLocker::LockType = FileLocker::Write,
00224                   uInt nattempts = 0);
00225     Bool acquire (MemoryIO* info, FileLocker::LockType type, uInt nattempts);
00226     // </group>
00227 
00228     // Release a lock and write the information (if given) into the lock file.
00229     // The user is responsible for making the information machine-independent
00230     // (e.g. converting from local to canonical format).
00231     // <group>
00232     Bool release();
00233     Bool release (const MemoryIO& info);
00234     Bool release (const MemoryIO* info);
00235     // </group>
00236 
00237     // Inspect if another process wants to access the file (i.e. if the
00238     // request list is not empty).
00239     // It only inspects if the time passed since the last inspection
00240     // exceeds the inspection interval as given in the constructor.
00241     // If the time passed is too short, False is returned (indicating
00242     // that no access is needed).
00243     // If <src>always==True</src>, no test on inspection interval is done,
00244     // so the inspect is always done.
00245     Bool inspect (Bool always=False);
00246 
00247     // Test if the file can be locked for read or write.
00248     Bool canLock (FileLocker::LockType = FileLocker::Write);
00249 
00250     // Test if the process has a lock for read or write on the file.
00251     Bool hasLock (FileLocker::LockType = FileLocker::Write) const;
00252 
00253     // Get the last error.
00254     int lastError() const;
00255 
00256     // Get the message belonging to the last error.
00257     String lastMessage() const;
00258 
00259     // Get the name of the lock file.
00260     const String& name() const;
00261 
00262     // Get the block of request id's.
00263     const Block<Int>& reqIds() const;
00264 
00265     // Get the request id's and the info from the lock file.
00266     void getInfo (MemoryIO& info);
00267 
00268     // Put the info into the file (after the request id's).
00269     void putInfo (const MemoryIO& info) const;
00270 
00271     // Tell if another process holds a read or write lock on the given file
00272     // or has the file opened. It returns:
00273     // <br> 3 if write-locked elsewhere.
00274     // <br> 2 if read-locked elsewhere.
00275     // <br> 1 if opened elsewhere.
00276     // <br> 0 if locked nor opened.
00277     // <br>It fills in the PID of the process having the file locked or opened.
00278     // <br>If locked, it also tells if it is permanently locked.
00279     // <br>An exception is thrown if the file does not exist or cannot
00280     // be opened.
00281     static uInt showLock (uInt& pid, Bool& permLocked, const String& fileName);
00282 
00283 private:
00284     // The copy constructor cannot be used (its semantics are too difficult).
00285     LockFile (const LockFile&);
00286 
00287     // Assignment cannot be used (its semantics are too difficult).
00288     LockFile& operator= (const LockFile&);
00289 
00290     // Get an Int from the buffer at the given offset and convert
00291     // it from canonical to local format.
00292     // If the buffer is too short (i.e. does not contain the value),
00293     // a zero value is returned.
00294     Int getInt (const uChar* buffer, uInt leng, uInt offset) const;
00295 
00296     // Add the request id of this process to the list.
00297     void addReqId();
00298 
00299     // Remove the request id of this process from the list
00300     // (and all the ones before it).
00301     void removeReqId();
00302 
00303     // Get the request list from the file.
00304     void getReqId();
00305 
00306     // Put the request list into the file.
00307     void putReqId (int fd) const;
00308 
00309     // Convert the request id from canonical to local format.
00310     void convReqId (const uChar* buffer, uInt leng);
00311 
00312     // Get the number of request id's.
00313     Int getNrReqId() const;
00314 
00315 
00316     //# The member variables.
00317     FileLocker   itsLocker;
00318     FileLocker   itsUseLocker;
00319     FiledesIO*   itsFileIO;
00320     CanonicalIO* itsCanIO;
00321     Bool         itsWritable;         //# lock file is writable?
00322     Bool         itsAddToList;        //# Should acquire add to request list?
00323     double       itsInterval;         //# interval between inspections
00324     Time         itsLastTime;         //# time of last inspection
00325     String       itsName;             //# Name of lock file
00326     uInt         itsPid;
00327     uInt         itsHostId;
00328     Block<Int>   itsReqId;            //# Id's of processes requesting lock
00329                                       //# First value contains #req id's
00330                                       //# Thereafter pid, hostid
00331     Int          itsInspectCount;     //# The number of times inspect() has
00332                                       //# been called since the last elapsed
00333                                       //# time check.
00334 };
00335 
00336 
00337 inline Bool LockFile::acquire (FileLocker::LockType type, uInt nattempts)
00338 {
00339     return acquire (0, type, nattempts);
00340 }
00341 inline Bool LockFile::acquire (MemoryIO& info, FileLocker::LockType type,
00342                                uInt nattempts)
00343 {
00344     return acquire (&info, type, nattempts);
00345 }
00346 inline Bool LockFile::release()
00347 {
00348     return release (0);
00349 }
00350 inline Bool LockFile::release (const MemoryIO& info)
00351 {
00352     return release (&info);
00353 }
00354 inline Bool LockFile::canLock (FileLocker::LockType type)
00355 {
00356     return (itsFileIO == 0  ?  True : itsLocker.canLock (type));
00357 }
00358 inline Bool LockFile::hasLock (FileLocker::LockType type) const
00359 {
00360     return (itsFileIO == 0  ?  False : itsLocker.hasLock (type));
00361 }
00362 inline int LockFile::lastError() const
00363 {
00364     return itsLocker.lastError();
00365 }
00366 inline String LockFile::lastMessage() const
00367 {
00368     return itsLocker.lastMessage();
00369 }
00370 inline const String& LockFile::name() const
00371 {
00372     return itsName;
00373 }
00374 inline const Block<Int>& LockFile::reqIds() const
00375 {
00376     return itsReqId;
00377 }
00378 
00379 
00380 
00381 } //# NAMESPACE CASA - END
00382 
00383 #endif