casa
$Rev:20696$
|
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>0 means that it is only inspected if the last inspect 00179 // was at least <src>inspectInterval</src> seconds ago. 00180 // An interval<=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