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$ 00027 00028 #ifndef CASA_LOCKFILE_H 00029 #define CASA_LOCKFILE_H 00030 00031 00032 //# Includes 00033 #include <casacore/casa/aips.h> 00034 #include <casacore/casa/IO/FileLocker.h> 00035 #include <casacore/casa/OS/Time.h> 00036 #include <casacore/casa/Containers/Block.h> 00037 #include <casacore/casa/BasicSL/String.h> 00038 #include <sys/types.h> 00039 00040 namespace casacore { //# NAMESPACE CASACORE - 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 Casacore 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 // <br> The <src>noLocking</src> argument is used to indicate that 00196 // no locking is needed. It means that acquiring a lock always succeeds. 00197 explicit LockFile (const String& fileName, double inspectInterval = 0, 00198 Bool create = False, Bool addToRequestList = True, 00199 Bool mustExist = True, uInt seqnr = 0, 00200 Bool permLocking = False, Bool noLocking = False); 00201 00202 // The destructor does not delete the file, because it is not known 00203 // when the last process using the lock file will stop. 00204 // For the table system this is no problem, because the lock file 00205 // is contained in the directory of the table, thus deleted when 00206 // the table gets deleted. 00207 ~LockFile(); 00208 00209 // Is the file associated with the LockFile object in use in 00210 // another process? 00211 Bool isMultiUsed(); 00212 00213 // Acquire a read or write lock. 00214 // It reads the information (if the <src>info</src> argument is given) 00215 // from the lock file. The user is responsible for interpreting the 00216 // information (e.g. converting from canonical to local format). 00217 // The seek pointer in the <src>MemoryIO</src> object is set to 0, 00218 // so the user can simply start reading the pointer. 00219 // <br>The argument <src>nattempts</src> tells how often it is 00220 // attempted (with 1 second intervals) to acquire the lock if 00221 // it does not succeed. 00222 // 0 means forever, while 1 means do not retry. 00223 // <group> 00224 Bool acquire (FileLocker::LockType = FileLocker::Write, uInt nattempts = 0); 00225 Bool acquire (MemoryIO& info, FileLocker::LockType = FileLocker::Write, 00226 uInt nattempts = 0); 00227 Bool acquire (MemoryIO* info, FileLocker::LockType type, uInt nattempts); 00228 // </group> 00229 00230 // Release a lock and write the information (if given) into the lock file. 00231 // The user is responsible for making the information machine-independent 00232 // (e.g. converting from local to canonical format). 00233 // <group> 00234 Bool release(); 00235 Bool release (const MemoryIO& info); 00236 Bool release (const MemoryIO* info); 00237 // </group> 00238 00239 // Inspect if another process wants to access the file (i.e. if the 00240 // request list is not empty). 00241 // It only inspects if the time passed since the last inspection 00242 // exceeds the inspection interval as given in the constructor. 00243 // If the time passed is too short, False is returned (indicating 00244 // that no access is needed). 00245 // If <src>always==True</src>, no test on inspection interval is done, 00246 // so the inspect is always done. 00247 Bool inspect (Bool always=False); 00248 00249 // Test if the file can be locked for read or write. 00250 Bool canLock (FileLocker::LockType = FileLocker::Write); 00251 00252 // Test if the process has a lock for read or write on the file. 00253 Bool hasLock (FileLocker::LockType = FileLocker::Write) const; 00254 00255 // Get the last error. 00256 int lastError() const; 00257 00258 // Get the message belonging to the last error. 00259 String lastMessage() const; 00260 00261 // Get the name of the lock file. 00262 const String& name() const; 00263 00264 // Get the block of request id's. 00265 const Block<Int>& reqIds() const; 00266 00267 // Get the request id's and the info from the lock file. 00268 void getInfo (MemoryIO& info); 00269 00270 // Put the info into the file (after the request id's). 00271 void putInfo (const MemoryIO& info) const; 00272 00273 // Tell if another process holds a read or write lock on the given file 00274 // or has the file opened. It returns: 00275 // <br> 3 if write-locked elsewhere. 00276 // <br> 2 if read-locked elsewhere. 00277 // <br> 1 if opened elsewhere. 00278 // <br> 0 if locked nor opened. 00279 // <br>It fills in the PID of the process having the file locked or opened. 00280 // <br>If locked, it also tells if it is permanently locked. 00281 // <br>An exception is thrown if the file does not exist or cannot 00282 // be opened. 00283 static uInt showLock (uInt& pid, Bool& permLocked, const String& fileName); 00284 00285 private: 00286 // The copy constructor cannot be used (its semantics are too difficult). 00287 LockFile (const LockFile&); 00288 00289 // Assignment cannot be used (its semantics are too difficult). 00290 LockFile& operator= (const LockFile&); 00291 00292 // Get an Int from the buffer at the given offset and convert 00293 // it from canonical to local format. 00294 // If the buffer is too short (i.e. does not contain the value), 00295 // a zero value is returned. 00296 Int getInt (const uChar* buffer, uInt leng, uInt offset) const; 00297 00298 // Add the request id of this process to the list. 00299 void addReqId(); 00300 00301 // Remove the request id of this process from the list 00302 // (and all the ones before it). 00303 void removeReqId(); 00304 00305 // Get the request list from the file. 00306 void getReqId(); 00307 00308 // Put the request list into the file. 00309 void putReqId (int fd) const; 00310 00311 // Convert the request id from canonical to local format. 00312 void convReqId (const uChar* buffer, uInt leng); 00313 00314 // Get the number of request id's. 00315 Int getNrReqId() const; 00316 00317 00318 //# The member variables. 00319 FileLocker itsLocker; 00320 FileLocker itsUseLocker; 00321 FiledesIO* itsFileIO; 00322 CanonicalIO* itsCanIO; 00323 Bool itsWritable; //# lock file is writable? 00324 Bool itsAddToList; //# Should acquire add to request list? 00325 double itsInterval; //# interval between inspections 00326 Time itsLastTime; //# time of last inspection 00327 String itsName; //# Name of lock file 00328 uInt itsPid; 00329 uInt itsHostId; 00330 Block<Int> itsReqId; //# Id's of processes requesting lock 00331 //# First value contains #req id's 00332 //# Thereafter pid, hostid 00333 Int itsInspectCount; //# The number of times inspect() has 00334 //# been called since the last elapsed 00335 //# time check. 00336 }; 00337 00338 00339 inline Bool LockFile::acquire (FileLocker::LockType type, uInt nattempts) 00340 { 00341 return acquire (0, type, nattempts); 00342 } 00343 inline Bool LockFile::acquire (MemoryIO& info, FileLocker::LockType type, 00344 uInt nattempts) 00345 { 00346 return acquire (&info, type, nattempts); 00347 } 00348 inline Bool LockFile::release() 00349 { 00350 return release (0); 00351 } 00352 inline Bool LockFile::release (const MemoryIO& info) 00353 { 00354 return release (&info); 00355 } 00356 inline Bool LockFile::canLock (FileLocker::LockType type) 00357 { 00358 return (itsFileIO == 0 ? True : itsLocker.canLock (type)); 00359 } 00360 inline Bool LockFile::hasLock (FileLocker::LockType type) const 00361 { 00362 return (itsFileIO == 0 ? True : itsLocker.hasLock (type)); 00363 } 00364 inline int LockFile::lastError() const 00365 { 00366 return itsLocker.lastError(); 00367 } 00368 inline String LockFile::lastMessage() const 00369 { 00370 return itsLocker.lastMessage(); 00371 } 00372 inline const String& LockFile::name() const 00373 { 00374 return itsName; 00375 } 00376 inline const Block<Int>& LockFile::reqIds() const 00377 { 00378 return itsReqId; 00379 } 00380 00381 00382 00383 } //# NAMESPACE CASACORE - END 00384 00385 #endif