📄 lockfile.java
字号:
public static final LockFile newLockFileLock(final String path) throws HsqlException { LockFile lockFile = null; try { lockFile = LockFile.newLockFile(path + ".lck"); } catch (LockFile.BaseException e) { throw Trace.error(Trace.LOCK_FILE_ACQUISITION_FAILURE, e.getMessage()); } boolean locked = false; try { locked = lockFile.tryLock(); } catch (LockFile.BaseException e) { throw Trace.error(Trace.LOCK_FILE_ACQUISITION_FAILURE, e.getMessage()); } // Paranoia mode: In theory, this case can't happen, given the way // tryLock now works; by all current understanding of the involved API // contracts, an exception will always be thrown instead by the code // above. if (!locked) { throw Trace.error(Trace.LOCK_FILE_ACQUISITION_FAILURE, lockFile.toString()); } return lockFile; } /** * Checks whether the underlying file is an HSQLDB lock file and, if so, * whether its heartbeat timestamp is live (is, as far as can be known, * presumably in use by another <tt>LockFile</tt> instance) or stale. <p> * * The check conforms to the following rules: <p> * * <ol> * <li>If the parameter <tt>withCreateNewFile</tt> is true, {@link * java.io.File#createNewFile()} is available and its invocation * upon this object's <tt>file</tt> object indicates the underlying * file was atomically created if and only if it did not yet exist, * then return immediately (we have won the <em>race</em> to establish * a lock file). <p> * * <li>Test again if the file exists, returning immediately if it does not * (there's no file and hence no heartbeat to check). <p> * * An immediate return can occur here only under pre-JDK 1.2 runtimes; * or when the underlying platform does not correctly support * <tt>File.createNewFile()</tt>; 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 yeilds false 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 by some uncooperative * process.<p> * * <li>If a Java security exception is thrown while testing for existence, * it is rethrown as a <tt>FileSecurityException</tt>. * * <li>Read the file's length. * * <li>If a Java security exception is thrown reading length, it is rethrown * as a <tt>FileSecurityException</tt> (it <em>is</em> possible somebody * concurrently refreshed the system Policy in the interim). * * <li>If the file does not have the expected length, a * <tt>WrongLengthException</tt> is thrown (we're trying to check * something that is not an HSQLDB lock file). * * <li>Open an input steam to read the file's <tt>MAGIC</tt> and heartbeat * timestamp values. * * <li>If a file not found exception is thrown above, it is rethrown as an * <tt>UnexpectedFileNotFoundException</tt> (we've already tested for * existence). * * <li>If a Java security exception is thrown above, it is rethrown as a * <tt>FileSecurityException</tt> (it <em>is</em> possible somebody * concurrently refreshed the system Policy in the interim). * * <li>Read the <tt>MAGIC</tt> value. * * <li>If an end of file exepction is thrown above, it is rethrown as an * <tt>UnexpectedEndOfFileException</tt> (we've already tested the * length... did someone truncate the file in the interim?). * * <li>If an I/O exception is thrown, it is rethrown as an * <tt>UnexpectedFileIOException</tt> (we've already tested for * existence, length and successfully opened a stream...did someone, * for example, force unmount or physically remove the underlying device * in the interim?) * * <li>If the value read in does not match the expected <tt>MAGIC</tt> value, * a <tt>WrongMagicException</tt> is thrown (we're trying to check * something that is not an HSQLDB lock file). * * <li>Read the heartbeat timestamp. * * <li>If a Java security exception is thrown above, it is rethrown as a * <tt>FileSecurityException</tt> (it <em>is</em> possible somebody * concurrently refreshed the system Policy in the interim). * * <li>If an end of file exection is thrown above, it is rethrown as an * <tt>UnexpectedEndOfFileException</tt> (we've already tested the * length... did someone truncate the file in the interim?). * * <li>If an I/O exception is thrown, it is rethrown as an * <tt>UnexpectedFileIOException</tt> (we've already tested for * existence, length and successfully opened a stream...did someone, * for example, force unmount or physically remove the underlying device * in the interim?) * * <li>If the timestamp read in is less than or equal to * {@link #HEARTBEAT_INTERVAL_PADDED} milliseconds into the past or * future, then a <tt>LockHeldExternallyException</tt> is thrown. * * <li>Otherwise, this method simply returns. * </ol> * * @param withCreateNewFile if <tt>true</tt>, attempt to employ * <tt>File.createNewFile()</tt> as part of the check so as to * eliminate potential race conditions when establising a new * lock file * @throws FileSecurityException if the check fails due to a Java * security permission check failure * @throws LockHeldExternallyException if it is determined that the * file's heartbeat timestamp is less than * <tt>HEARTBEAT_INTERVAL_PADDED</tt> into the past (or future) * @throws UnexpectedEndOfFileException if an <tt>EOFExceoption</tt> is * thrown while reading either the magic or heartbeat timestamp values * @throws UnexpectedFileIOException if an <tt>IOException</tt> other than * <tt>EOFException</tt> is thrown while reading either the magic or * heartbeat timestamp values * @throws UnexpectedFileNotFoundException if a * <tt>FileNotFoundException</tt> is thrown while attempting to open a * stream to read the underlying file's magic and heartbeat timestamp * values * @throws WrongLengthException if it is determined that the length * of the file does not equal {@link #USED_REGION} * @throws WrongMagicException if it is determined that the file's * content does not start with {@link #MAGIC}. */ private final void checkHeartbeat(boolean withCreateNewFile) throws LockFile.FileSecurityException, LockFile.LockHeldExternallyException, LockFile.UnexpectedEndOfFileException, LockFile.UnexpectedFileIOException, LockFile.UnexpectedFileNotFoundException, LockFile.WrongLengthException, LockFile.WrongMagicException { long now; long lastHeartbeat; long length = 0;//#ifdef JAVA2FULL try { if (withCreateNewFile) { try { file.createNewFile(); return; } catch (IOException ioe) {} } if (!file.exists()) { return; } length = file.length(); } catch (SecurityException se) { throw new FileSecurityException(this, "checkHeartbeat", se); }//#else/* if (!file.exists()) { return; } length = file.length();*///#endif JAVA2 if (length != USED_REGION) { throw new WrongLengthException(this, "checkHeartbeat", length); } // Compute the current wall clock time *first* to reduce possibility // of unwanted time dilation effects introduced, for example, // by intervening thread or process context switches under CPU // bursts. // // Example: // // Say currentTimeMillis is actually somewhere in (-0.5 and 0.5] // and another LockFile concurrently writes a 0-valued heartbeat // timestamp. // // Then, if readHeartbeat comes first here, happens to 'win the race // condition' (reads the previous heartbeat: -10,000) and an intervening // switch causes greater than ~0.5 millisecond elapsed time to // be experienced between readHeartbeat and currentTimeMillis, then // currentTimeMillis will be computed as n (n > 0), and (now - // lastHearbeat) will be HEARTBEAT_INTERVAL + n, instead of // HEARTBEAT_INTERVAL. // // Now, let n be greater than (HEARTBEAT_INTERVAL_PADDED - // HEARTBEAT_INTERVAL). // // Then the check will succeed, although it should fail. // // On the other hand, if currentTimeMillis is computed first, the // worst than can happen is a false positive indication that // the read heartbeat timestamp value was written by a live LockFile // instance. // now = System.currentTimeMillis(); lastHeartbeat = readHeartbeat(); // Using padded interval to further reduce corner case effects, // now that heartbeat polling is in effect. // // Basically, it is absolutely essential to fail when a lock really is // still held elsewhere, so it is OK to fail on corner cases where // the last written heartbeat is very close to HEARTBEAT_INTERVAL // in the past and it is possible that timing jitters make it uncertain // whether the lock really is still held. if (Math.abs(now - lastHeartbeat) <= (HEARTBEAT_INTERVAL_PADDED)) { throw new LockHeldExternallyException(this, "checkHeartbeat", now, lastHeartbeat); } } /** * Closes this object's {@link #raf RandomAccessFile}. <p> * * As a side-effect, the associated <tt>FileChannel</tt> object, if any, * is closed as well. * * @throws UnexpectedFileIOException if an <tt>IOException</tt> is thrown */ private final void closeRAF() throws LockFile.UnexpectedFileIOException { if (raf != null) { try { raf.close(); } catch (IOException ex) { throw new UnexpectedFileIOException(this, "closeRAF", ex); } finally { raf = null; } } } /** * Provides any optional locking actions for the {@link #tryLock() * tryLock()} template method. <p> * * Descendents are free to provide additional functionality here, * using the following rules: <p> * * <b>PRE:</b><p> * * This method is called only from <tt>tryLock()</tt> and it is called if * and only if <tt>tryLock()</tt> successfully invokes * <tt>pollHeartbeat()</tt> and <tt>openRAF()</tt> first. <p> * * From this, it can be inferred that upon entry: <p> * * <ol> * <li><tt>locked == false</tt>. * <li><tt>raf</tt> is a non-null instance that can be used to get a * <tt>FileChannel</tt> instance, if desired. * <li>the underlying file either did not exist before invoking * <tt>openRAF()</tt> or it was a valid but stale HSQLDB lock file * because it: * * <ol style="list-style-type: lower-roman"> * <li>did exist, * <li>was readable on <tt>USED_REGION</tt>, * <li>had the expected length and <tt>MAGIC</tt> value and * <li>had a stale heartbeat timestamp value. * </ol> * </ol> <p> * * Further, it can be assumed that this object's heatbeat task is definitely * cancelled and/or has never been scheduled at this point, so whatever * timestamp is recorded in the lock file, if it did pre-exist, was written * by a different <tt>LockFile</tt> instance or as the result of a previous, * successful <tt>tryLock()</tt> invocation upon this <tt>LockFile</tt> * instance. <p> * * Finally, it is important that this method does not rethrow any exceptions * it encounters as unchecked exceptions to the calling context. <p> * * <b>POST:</b><p> *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -