comparison src/org/tmatesoft/hg/internal/Lock.java @ 486:d740edfff563

Provisional support for Mercurial lock mechanism
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 10 Aug 2012 21:03:03 +0200
parents
children
comparison
equal deleted inserted replaced
485:cdd53e5884ae 486:d740edfff563
1 /*
2 * Copyright (c) 2012 TMate Software Ltd
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * For information on how to redistribute this software under
14 * the terms of a license other than GNU General Public License
15 * contact TMate Software at support@hg4j.com
16 */
17 package org.tmatesoft.hg.internal;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.lang.management.ManagementFactory;
24 import java.net.InetAddress;
25 import java.nio.ByteBuffer;
26 import java.nio.channels.FileChannel;
27
28 import org.tmatesoft.hg.repo.HgInvalidStateException;
29
30 /**
31 * NOT SAFE FOR MULTITHREAD USE!
32 *
33 * Unlike original mechanism, we don't use symlinks, rather files, as it's easier to implement
34 *
35 * @author Artem Tikhomirov
36 * @author TMate Software Ltd.
37 */
38 public class Lock {
39 /*
40 * Lock .hg/ except .hg/store/ .hg/wlock (HgRepository.repoPathHelper("wlock"))
41 * Lock .hg/store/ .hg/store/lock (HgRepository.repoPathHelper("store/lock") ???)
42 */
43
44 private final File lockFile;
45 private int use = 0;
46 private final int timeoutSeconds;
47
48 public Lock(File lock) {
49 this(lock, 10);
50 }
51
52 public Lock(File lock, int timeoutInSeconds) {
53 lockFile = lock;
54 timeoutSeconds = timeoutInSeconds;
55 }
56
57 /**
58 * Tries to read lock file and supplies hostname:pid (or just pid) information from there
59 * @return <code>null</code> if no lock file available at the moment
60 */
61 public String readLockInfo() {
62 if (lockFile.exists()) {
63 try {
64 byte[] bytes = read(lockFile);
65 if (bytes != null && bytes.length > 0) {
66 return new String(bytes);
67 }
68 } catch (Exception ex) {
69 // deliberately ignored
70 }
71 }
72 return null;
73 }
74
75 public boolean isLocked() {
76 return use > 0;
77 }
78
79 public void acquire() {
80 if (use > 0) {
81 use++;
82 return;
83 }
84 StringBuilder lockDescription = new StringBuilder();
85 lockDescription.append(getHostname());
86 lockDescription.append(':');
87 lockDescription.append(getPid());
88 byte[] bytes = lockDescription.toString().getBytes();
89 long stopTime = System.currentTimeMillis() + timeoutSeconds*1000;
90 do {
91 synchronized(this) {
92 try {
93 if (!lockFile.exists()) {
94 write(lockFile, bytes);
95 use++;
96 return;
97 }
98 } catch (IOException ex) {
99 // deliberately ignored
100 }
101 try {
102 wait(1000);
103 } catch (InterruptedException ex) {
104 // deliberately ignored
105 }
106 }
107
108 } while (System.currentTimeMillis() <= stopTime);
109 String msg = String.format("Failed to aquire lock, waited for %d seconds, present owner: '%s'", timeoutSeconds, readLockInfo());
110 throw new HgInvalidStateException(msg);
111 }
112
113 public void release() {
114 if (use == 0) {
115 throw new HgInvalidStateException("");
116 }
117 use--;
118 if (use > 0) {
119 return;
120 }
121 // do release
122 lockFile.delete();
123 }
124
125 protected String getHostname() {
126 try {
127 return InetAddress.getLocalHost().getHostName();
128 } catch (Exception ex) {
129 return "localhost";
130 }
131 }
132
133 protected int getPid() {
134 try {
135 // @see http://blog.igorminar.com/2007/03/how-java-application-can-discover-its.html
136 if (!Internals.runningOnWindows()) {
137 File f = new File("/proc/self");
138 if (f.exists()) {
139 // /proc/self is a symlink to /proc/pid/ directory
140 return Integer.parseInt(f.getCanonicalFile().getName());
141 }
142 }
143 String rtBean = ManagementFactory.getRuntimeMXBean().getName();
144 int x;
145 if ((x = rtBean.indexOf('@')) != -1) {
146 return Integer.parseInt(rtBean.substring(0, x));
147 }
148 return -1;
149 } catch (Exception ex) {
150 return -1;
151 }
152 }
153
154 private static void write(File f, byte[] content) throws IOException {
155 FileOutputStream fos = new FileOutputStream(f);
156 fos.write(content);
157 fos.close();
158 }
159
160 private static byte[] read(File f) throws IOException {
161 FileChannel fc = new FileInputStream(f).getChannel();
162 ByteBuffer bb = ByteBuffer.allocate(Internals.ltoi(fc.size()));
163 fc.read(bb);
164 fc.close();
165 return bb.array();
166 }
167 }