📄 lockfile.java
字号:
/* Copyright (c) 2001-2008, The HSQL Development Group * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the HSQL Development Group nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */package org.hsqldb.persist;import java.io.DataInputStream;import java.io.EOFException;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;import org.hsqldb.DatabaseManager;import org.hsqldb.HsqlDateTime;import org.hsqldb.HsqlException;import org.hsqldb.Trace;import org.hsqldb.lib.FileUtil;import org.hsqldb.lib.HsqlTimer;import org.hsqldb.lib.StringConverter;import org.hsqldb.lib.java.JavaSystem;/** * Base cooperative file locking implementation and <tt>LockFile</tt> * factory. <p> * * <hr> * * Provides a facility for cooperative file locking across process boundaries * and isolated in-process class loader contexts. <p> * * The need is obvious for inter-process cases, but it is no less essential for * in-process Java clients whose classes have been loaded by isolated class * loaders. This is because static fields--the conventional<a href="#note1"> * <sup>*</sup></a> means for supporting in-process global discovery--become * distinct and inaccessible across Java class loader context boundaries when * the contexts do not share a common parent class loader or do not implement * normal parent class loader delegation semantics. <p> * * <a name="note1"><sup>*</sup></a> * The only purely in-process global discovery alternative known to the author * is to reflect upon objects found while traversing up the Java runtime thread * hierarchy. However, this method is often subject to Java security * restrictions whose collective purpose is essentially dissimilar to that of * restrictions in effect relative to cooperative file locking requirements, * making it a generally unacceptable option in this context. <p> * * <hr> * * Here is the way this class presently operates: <p> * * <ol style="list-style-type: upper-latin"> * <li>A file with a commonly agreed-upon path is used to implement * cooperative locking semantics regarding another set of files with * commonly agreed-upon paths.<p> * * <li>In particular, a background thread periodically writes a timestamp * value, which acts as a heartbeat that indicates to others whether a * cooperative lock condition is currently held.<p> * * <li>In addition, a magic value is written so that it is possible to * distinguish with a reasonably high degree of accuracy between the * existence of a lock file and some other type of file.<p> * * <li>The generic rules used to aquire a cooperative lock condition are * as follows:<p> * * <ol> * <li>If a lock condition is already held by this object, do nothing and * signify that the lock attempt was successful, else...<p> * * <li>Poll the underlying file, using a configured maximum number of * retries and a configured interval between the end of a failed * poll and the beginning of the next.<p> * * <li>For each poll:<p> * * <ol style="list-style-type: lower-roman"> * * <li>Attempt to atomically create the underlying file if and only * if it does not yet exist, exit the polling loop immediately * indicating success if the attempt succeeds, else fast fail * the current poll if a security exeption is thrown in response * to the attempt, else...<p> * * <li>Test if the underlying file exists, fast failing the current * poll if it is impossible to determine (i.e. if a security * exeption is thrown).<p> * * <li>If the file does not exist, exit the polling loop immediately * indicating success.<p> * * This can occur only under pre-JDK 1.2 runtimes; or when the * underlying platform does not correctly support {@link * java.io.File#createNewFile()}; or when the underlying file is * deleted within a very short time after i.), above (typically * on the order of microseconds). <p> * * If the underlying platform employs a kernel-enforced mandatory * file locking blanket policy for open files (e.g. <em>Windows * </em><sup>tm</sup>), then this is likely a non-issue. And if * this case makes possible a race condition with another * <tt>LockFile</tt> object (because the test for existence and * subsequent file creation is not atomic relative to all other * file system actions), it is still <em>very</em> unlikely that * so unfortunate a timing will occur as to allow simultaneous * lock conditions to be established. Finally, if some * non-<tt>LockFile</tt> entity deleted the file, then there are * much worse things to worry about, in particular that the files * this object is supposed to protect are in reality subject to * arbitrary external modification and deletion.<p> * * <li>Test the file's length, fast failing the current poll if the * length cannot be determined or it is not the expected * value.<p> * * <li>Open a stream to read the file's <tt>MAGIC</tt> and heartbeat * timestamp values, fast failing the current poll if the stream * cannot be opened.<p> * * <li>Test the file's <tt>MAGIC</tt> value, failing the current poll * if the value cannot be read or it is not the expected * value.<p> * * <li>Test the file's heartbeat timestamp value, fast failing the * current poll if it cannot be read or it is less than a * commonly agreed-upon value into the past (or future, to * overcome a caveat observed by a patch contributor).<p> * </ol> * <li>If the polling phase exits with a failure indication, then one or * more of the following cases must have been true at every poll * iteration: <p> * * <ul> * <li>The file had the wrong length or <tt>MAGIC</tt> value (was * not an HSQLDB lock file). * * <li>The file was deleted externally after a poll's initial * test for existence and recreated at some point before * the next poll's initial test for existence. * * <li>An incompatible OS-enforced security restriction was in * effect. * * <li>An incompatible Java-enforced security restriction was * in effect. * * <li>The target file system media was effectively inaccessible. * <li>A cooperative lock condition was held by some other * <tt>LockFile</tt>. * * <li>A kernel-enforced manditory or advisory file lock was held. * </ul> <p> * * In this case, signify failure indicating the last encountered * reason, else...<p> * * <li>Open the file for reading and writing, write the magic value and * an initial heartbeat timestamp, schedule a periodic heartbeat * timestamp writer task and signify success.<p> * </ol> * <li>The generic rules used to release a cooperative lock condition are:<p> * <ol> * <li>If a lock condition is not currently held, do nothing and signify * success, else...<p> * * <li>A lock condition is currently held by this object, so try to * release it. <p> * * By default, releasing the lock condition consists of closing and * nullifying any objects that have a file descriptor open on the * lock file, cancelling the periodic heartbeat timestamp writer * task and deleting the lock file. If the release occurs without * raising an exception, signify success, else signify that the * release attempt <em>might</em> have failed. <p> * </ol> * </ol> <p> * * <hr> * * Additionally, {@link #doOptionalLockActions() doOptionalLockActions()} and * {@link #doOptionalReleaseActions() doOptionalReleaseActions()} are invoked * during lock and release attempts, respectively. This enables integration of * extended lock and release strategies based on subclassing. Subclass * availability is automatically detected and exposed by the factory method * {@link #newLockFile newLockFile()}.<p> * * In particular, if {@link #USE_NIO_FILELOCK_PROPERTY} is true and the required * classes are available at static initialization, then <tt>newLockFile()</tt> * produces {@link org.hsqldb.NIOLockFile NIOLockFile} instances.<p> * * When <tt>NIOLockFile</tt> instances are produced, then it is possible that * true kernel-enforced advisory or manditory file locking is used to protect * the underlying lock file from inadvertent modification (and possibly even * from deletion, including deletion by the system superuser). * * Otherwise, <tt>newLockFile()</tt> produces vanilla <tt>LockFile</tt> * instances, which exhibit just the elementary cooperative locking behavior on * platforms that do not, by default, implement kernel-enforced manditory * locking for open files. <p> * * At this point, it must be noted that not every target platform upon which * Java can run actually provides true kernel-enforced manditory (or even * advisory) file locking. Indeed, even when a target platform <em>does</em> * provide such locking guarantees for local file systems, it may not be able * to do so for network file systems, or it may only be able to do so safely * (or at all) with certain restrictions. Further, external system configuration * may be a prerequisite to enable manditory locking on systems that support it * but employ advisory locking by default. <p> * * In recognition of these facts, the official Java NIO package specification * explicitly states basically the same information. What is unfortunate, * however, is that no capabilities API is yet provided as part of the package. * What is even more unfortunate is that without something like a capabilities * API, it is impossible for an implementation to indicate or clients to * distiguish between simple lack of platform support and cases involving * immature Java runtimes that do not fully or correctly implement all NIO * features (and hence may throw exceptions at unexpected times or in places * where the API specification indicates none can be thrown).<p> * * It is for the preceding reasons that, as of HSQLDB 1.8.0.3, * <tt>FileLock</tt>'s use of Java NIO has been made a purely optional feature. * Previous to HSQLDB 1.8.0.3, if NIO was detected available, used to create a * <tt>FileLock</tt> and failed, then the enclosing cooperative lock attempt * failed also, despite the fact that a vanilla locking approach could * succeed. <p> * * <b>Polling Configuration</b>:<p> * * Although the {@link #HEARTBEAT_INTERVAL} and default polling values may * seem quite conservative, they are the result of ongoing research into * generally reasonable concerns regarding normal timing and resource * availability fluctuations experienced frequently under most, if not all * operating systems. <p> * * Regardless, flexibility is almost always a good thing, so this class is * designed to allow polling interval and retry count values to be configured * at run-time. <p> * * At present, this can be done at any time by setting the system properties * whose names are {@link #POLL_RETRIES_PROPERTY} and {@link * #POLL_INTERVAL_PROPERTY}. <p> * * Some consideration has also been given to modifying the polling scheme so * that run-time configuration of the HEARTBEAT_INTERVAL is possible. For now, * however, this option has been rejected due to the relative complexity of * guaranteeing acceptably safe, deterministic behaviour. On the other hand, * if it can be guaranteed that certain site invariants hold (in particular, * that only one version of the hsqldb jar will ever be used to open database * instances at the site) and it is desirable or required to experiment with * a lower interval, then it is recommended for now simply to recompile the * jar using a different value in the static field assignment. Note that great * care should be taken to avoid assigning too low a value, or else it may * become possible that even very short-lived timing and resource availability * fluctuations will cause incorrect operation of this class. <p> * * <b>NIO Configuration</b>:<p> * * Starting with 1.8.0.3, NIO-enhanced file lock attempts are turned off by * default. The general reasons for this are discussed above. Anyone interested * in the reading the detailed research notes should refer to the overview of * {@link NIOLockFile}. If, after reviewing the notes and the capabilities of * the intended target platform, one should still wish to enable NIO-enhanced * file lock attempts, it can be done by setting the system property {@link * #USE_NIO_FILELOCK_PROPERTY} true at JVM startup (for example, by using a * command-line <tt>-D<property-name>=true</tt> directive). Be aware that * the system property value is read only once, in the static initializer block * for this class. <p> * * <b>Design Notes</b>:<p> * * First, it should be noted that no thread synchronization occurs in * this class. Primarily, this is because the standard entry point, * {@link #newLockFileLock(String)}, is always called from within a block * synchronized upon an HSQLDB Database instance. If this class is to be used * elsewhere and it could be accessed concurrently, then access should be * synchronized on an appropriate monitor. That said, certain members of this * class have been declared volatile to minimize possibility of inconsistent * views under concurrent read-only access. <p> * * Second, to the limit of the author's present understanding, the * implementation details of this class represent a good comprimse under varying * and generally uncontrollable JVM, OS and hardware platform
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -