# HG changeset patch # User Artem Tikhomirov # Date 1344874307 -7200 # Node ID db48a77ec8ff4ef7962ad1e1999e394740d41b95 # Parent d740edfff563d05c7b811e7ffc5bda5078101efb Access to reposiotry lock mechanism via HgRepositoryLock diff -r d740edfff563 -r db48a77ec8ff src/org/tmatesoft/hg/internal/ConfigFile.java --- a/src/org/tmatesoft/hg/internal/ConfigFile.java Fri Aug 10 21:03:03 2012 +0200 +++ b/src/org/tmatesoft/hg/internal/ConfigFile.java Mon Aug 13 18:11:47 2012 +0200 @@ -91,6 +91,11 @@ return value == null ? defaultValue : value; } + public int getInteger(String sectionName, String key, int defaultValue) { + String value = getSection(sectionName).get(key); + return value == null ? defaultValue : Integer.parseInt(value); + } + public void putString(String sectionName, String key, String newValue) { Map section = null; if (sections == null) { diff -r d740edfff563 -r db48a77ec8ff src/org/tmatesoft/hg/internal/Lock.java --- a/src/org/tmatesoft/hg/internal/Lock.java Fri Aug 10 21:03:03 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2012 TMate Software Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For information on how to redistribute this software under - * the terms of a license other than GNU General Public License - * contact TMate Software at support@hg4j.com - */ -package org.tmatesoft.hg.internal; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.net.InetAddress; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; - -import org.tmatesoft.hg.repo.HgInvalidStateException; - -/** - * NOT SAFE FOR MULTITHREAD USE! - * - * Unlike original mechanism, we don't use symlinks, rather files, as it's easier to implement - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ -public class Lock { - /* - * Lock .hg/ except .hg/store/ .hg/wlock (HgRepository.repoPathHelper("wlock")) - * Lock .hg/store/ .hg/store/lock (HgRepository.repoPathHelper("store/lock") ???) - */ - - private final File lockFile; - private int use = 0; - private final int timeoutSeconds; - - public Lock(File lock) { - this(lock, 10); - } - - public Lock(File lock, int timeoutInSeconds) { - lockFile = lock; - timeoutSeconds = timeoutInSeconds; - } - - /** - * Tries to read lock file and supplies hostname:pid (or just pid) information from there - * @return null if no lock file available at the moment - */ - public String readLockInfo() { - if (lockFile.exists()) { - try { - byte[] bytes = read(lockFile); - if (bytes != null && bytes.length > 0) { - return new String(bytes); - } - } catch (Exception ex) { - // deliberately ignored - } - } - return null; - } - - public boolean isLocked() { - return use > 0; - } - - public void acquire() { - if (use > 0) { - use++; - return; - } - StringBuilder lockDescription = new StringBuilder(); - lockDescription.append(getHostname()); - lockDescription.append(':'); - lockDescription.append(getPid()); - byte[] bytes = lockDescription.toString().getBytes(); - long stopTime = System.currentTimeMillis() + timeoutSeconds*1000; - do { - synchronized(this) { - try { - if (!lockFile.exists()) { - write(lockFile, bytes); - use++; - return; - } - } catch (IOException ex) { - // deliberately ignored - } - try { - wait(1000); - } catch (InterruptedException ex) { - // deliberately ignored - } - } - - } while (System.currentTimeMillis() <= stopTime); - String msg = String.format("Failed to aquire lock, waited for %d seconds, present owner: '%s'", timeoutSeconds, readLockInfo()); - throw new HgInvalidStateException(msg); - } - - public void release() { - if (use == 0) { - throw new HgInvalidStateException(""); - } - use--; - if (use > 0) { - return; - } - // do release - lockFile.delete(); - } - - protected String getHostname() { - try { - return InetAddress.getLocalHost().getHostName(); - } catch (Exception ex) { - return "localhost"; - } - } - - protected int getPid() { - try { - // @see http://blog.igorminar.com/2007/03/how-java-application-can-discover-its.html - if (!Internals.runningOnWindows()) { - File f = new File("/proc/self"); - if (f.exists()) { - // /proc/self is a symlink to /proc/pid/ directory - return Integer.parseInt(f.getCanonicalFile().getName()); - } - } - String rtBean = ManagementFactory.getRuntimeMXBean().getName(); - int x; - if ((x = rtBean.indexOf('@')) != -1) { - return Integer.parseInt(rtBean.substring(0, x)); - } - return -1; - } catch (Exception ex) { - return -1; - } - } - - private static void write(File f, byte[] content) throws IOException { - FileOutputStream fos = new FileOutputStream(f); - fos.write(content); - fos.close(); - } - - private static byte[] read(File f) throws IOException { - FileChannel fc = new FileInputStream(f).getChannel(); - ByteBuffer bb = ByteBuffer.allocate(Internals.ltoi(fc.size())); - fc.read(bb); - fc.close(); - return bb.array(); - } -} diff -r d740edfff563 -r db48a77ec8ff src/org/tmatesoft/hg/repo/HgRepoConfig.java --- a/src/org/tmatesoft/hg/repo/HgRepoConfig.java Fri Aug 10 21:03:03 2012 +0200 +++ b/src/org/tmatesoft/hg/repo/HgRepoConfig.java Mon Aug 13 18:11:47 2012 +0200 @@ -62,6 +62,10 @@ public String getStringValue(String section, String key, String defaultValue) { return config.getString(section, key, defaultValue); } + + public int getIntegerValue(String section, String key, int defaultValue) { + return config.getInteger(section, key, defaultValue); + } // // diff -r d740edfff563 -r db48a77ec8ff src/org/tmatesoft/hg/repo/HgRepository.java --- a/src/org/tmatesoft/hg/repo/HgRepository.java Fri Aug 10 21:03:03 2012 +0200 +++ b/src/org/tmatesoft/hg/repo/HgRepository.java Mon Aug 13 18:11:47 2012 +0200 @@ -38,7 +38,6 @@ import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.internal.Filter; import org.tmatesoft.hg.internal.Internals; -import org.tmatesoft.hg.internal.Lock; import org.tmatesoft.hg.internal.RevlogStream; import org.tmatesoft.hg.internal.SubrepoManager; import org.tmatesoft.hg.util.CancelledException; @@ -423,7 +422,7 @@ } } - private Lock wdLock, storeLock; + private HgRepositoryLock wdLock, storeLock; /** * PROVISIONAL CODE, DO NOT USE @@ -432,16 +431,17 @@ * everything that has to do with working directory state). * * Note, the lock object returned merely gives access to lock mechanism. NO ACTUAL LOCKING IS DONE. - * Use {@link Lock#acquire()} to actually lock the repository. + * Use {@link HgRepositoryLock#acquire()} to actually lock the repository. * * @return lock object, never null */ @Experimental(reason="WORK IN PROGRESS") - public Lock getWorkingDirLock() { + public HgRepositoryLock getWorkingDirLock() { if (wdLock == null) { + int timeout = getLockTimeout(); synchronized (this) { if (wdLock == null) { - wdLock = new Lock(new File(repoPathHelper.rewrite("wlock").toString())); + wdLock = new HgRepositoryLock(new File(repoPathHelper.rewrite("wlock").toString()), timeout); } } } @@ -449,11 +449,12 @@ } @Experimental(reason="WORK IN PROGRESS") - public Lock getStoreLock() { + public HgRepositoryLock getStoreLock() { if (storeLock == null) { + int timeout = getLockTimeout(); synchronized (this) { if (storeLock == null) { - storeLock = new Lock(new File(repoPathHelper.rewrite("store/lock").toString())); + storeLock = new HgRepositoryLock(new File(repoPathHelper.rewrite("store/lock").toString()), timeout); } } } @@ -542,4 +543,8 @@ } return rv; } + + private int getLockTimeout() { + return getConfiguration().getIntegerValue("ui", "timeout", 600); + } } diff -r d740edfff563 -r db48a77ec8ff src/org/tmatesoft/hg/repo/HgRepositoryLock.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/repo/HgRepositoryLock.java Mon Aug 13 18:11:47 2012 +0200 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2012 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.repo; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +import org.tmatesoft.hg.internal.Internals; + +/** + * NOT SAFE FOR MULTITHREAD USE! + * + * Unlike original mechanism, we don't use symlinks, rather files, as it's easier to implement + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class HgRepositoryLock { + /* + * Lock .hg/ except .hg/store/ .hg/wlock (HgRepository.repoPathHelper("wlock")) + * Lock .hg/store/ .hg/store/lock (HgRepository.repoPathHelper("store/lock") ???) + */ + + private final File lockFile; + private int use = 0; + private final int timeoutSeconds; + + HgRepositoryLock(File lock, int timeoutInSeconds) { + lockFile = lock; + timeoutSeconds = timeoutInSeconds; + } + + /** + * Tries to read lock file and supplies hostname:pid (or just pid) information from there + * @return null if no lock file available at the moment + */ + public String readLockInfo() { + if (lockFile.exists()) { + try { + byte[] bytes = read(lockFile); + if (bytes != null && bytes.length > 0) { + return new String(bytes); + } + } catch (Exception ex) { + // deliberately ignored + } + } + return null; + } + + public boolean isLocked() { + return use > 0; + } + + public void acquire() { + if (use > 0) { + use++; + return; + } + StringBuilder lockDescription = new StringBuilder(); + lockDescription.append(getHostname()); + lockDescription.append(':'); + lockDescription.append(getPid()); + byte[] bytes = lockDescription.toString().getBytes(); + long stopTime = timeoutSeconds < 0 ? -1 : (System.currentTimeMillis() + timeoutSeconds*1000); + do { + synchronized(this) { + try { + if (!lockFile.exists()) { + write(lockFile, bytes); + use++; + return; + } + } catch (IOException ex) { + // deliberately ignored + } + try { + wait(1000); + } catch (InterruptedException ex) { + // deliberately ignored + } + } + + } while (stopTime == -1/*no timeout*/ || System.currentTimeMillis() <= stopTime); + String msg = String.format("Failed to aquire lock, waited for %d seconds, present owner: '%s'", timeoutSeconds, readLockInfo()); + throw new HgInvalidStateException(msg); + } + + public void release() { + if (use == 0) { + throw new HgInvalidStateException(""); + } + use--; + if (use > 0) { + return; + } + // do release + lockFile.delete(); + } + + protected String getHostname() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (Exception ex) { + return "localhost"; + } + } + + protected int getPid() { + try { + // @see http://blog.igorminar.com/2007/03/how-java-application-can-discover-its.html + if (!Internals.runningOnWindows()) { + File f = new File("/proc/self"); + if (f.exists()) { + // /proc/self is a symlink to /proc/pid/ directory + return Integer.parseInt(f.getCanonicalFile().getName()); + } + } + String rtBean = ManagementFactory.getRuntimeMXBean().getName(); + int x; + if ((x = rtBean.indexOf('@')) != -1) { + return Integer.parseInt(rtBean.substring(0, x)); + } + return -1; + } catch (Exception ex) { + return -1; + } + } + + private static void write(File f, byte[] content) throws IOException { + FileOutputStream fos = new FileOutputStream(f); + fos.write(content); + fos.close(); + } + + private static byte[] read(File f) throws IOException { + FileChannel fc = new FileInputStream(f).getChannel(); + ByteBuffer bb = ByteBuffer.allocate(Internals.ltoi(fc.size())); + fc.read(bb); + fc.close(); + return bb.array(); + } +}