1 package org.apache.lucene.store;
2
3 /**
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 import java.io.IOException;
21
22 /** An interprocess mutex lock.
23 * <p>Typical use might look like:<pre>
24 * new Lock.With(directory.makeLock("my.lock")) {
25 * public Object doBody() {
26 * <i>... code to execute while locked ...</i>
27 * }
28 * }.run();
29 * </pre>
30 *
31 *
32 * @version $Id: Lock.java 595448 2007-11-15 20:42:54Z mikemccand $
33 * @see Directory#makeLock(String)
34 */
35 public abstract class Lock {
36
37 /** How long {@link #obtain(long)} waits, in milliseconds,
38 * in between attempts to acquire the lock. */
39 public static long LOCK_POLL_INTERVAL = 1000;
40
41 /** Pass this value to {@link #obtain(long)} to try
42 * forever to obtain the lock. */
43 public static final long LOCK_OBTAIN_WAIT_FOREVER = -1;
44
45 /** Attempts to obtain exclusive access and immediately return
46 * upon success or failure.
47 * @return true iff exclusive access is obtained
48 */
49 public abstract boolean obtain() throws IOException;
50
51 /**
52 * If a lock obtain called, this failureReason may be set
53 * with the "root cause" Exception as to why the lock was
54 * not obtained.
55 */
56 protected Throwable failureReason;
57
58 /** Attempts to obtain an exclusive lock within amount of
59 * time given. Polls once per {@link #LOCK_POLL_INTERVAL}
60 * (currently 1000) milliseconds until lockWaitTimeout is
61 * passed.
62 * @param lockWaitTimeout length of time to wait in
63 * milliseconds or {@link
64 * #LOCK_OBTAIN_WAIT_FOREVER} to retry forever
65 * @return true if lock was obtained
66 * @throws LockObtainFailedException if lock wait times out
67 * @throws IllegalArgumentException if lockWaitTimeout is
68 * out of bounds
69 * @throws IOException if obtain() throws IOException
70 */
71 public boolean obtain(long lockWaitTimeout) throws LockObtainFailedException, IOException {
72 failureReason = null;
73 boolean locked = obtain();
74 if (lockWaitTimeout < 0 && lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER)
75 throw new IllegalArgumentException("lockWaitTimeout should be LOCK_OBTAIN_WAIT_FOREVER or a non-negative number (got " + lockWaitTimeout + ")");
76
77 long maxSleepCount = lockWaitTimeout / LOCK_POLL_INTERVAL;
78 long sleepCount = 0;
79 while (!locked) {
80 if (lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER && sleepCount++ >= maxSleepCount) {
81 String reason = "Lock obtain timed out: " + this.toString();
82 if (failureReason != null) {
83 reason += ": " + failureReason;
84 }
85 LockObtainFailedException e = new LockObtainFailedException(reason);
86 if (failureReason != null) {
87 e.initCause(failureReason);
88 }
89 throw e;
90 }
91 try {
92 Thread.sleep(LOCK_POLL_INTERVAL);
93 } catch (InterruptedException e) {
94 throw new IOException(e.toString());
95 }
96 locked = obtain();
97 }
98 return locked;
99 }
100
101 /** Releases exclusive access. */
102 public abstract void release() throws IOException;
103
104 /** Returns true if the resource is currently locked. Note that one must
105 * still call {@link #obtain()} before using the resource. */
106 public abstract boolean isLocked();
107
108
109 /** Utility class for executing code with exclusive access. */
110 public abstract static class With {
111 private Lock lock;
112 private long lockWaitTimeout;
113
114
115 /** Constructs an executor that will grab the named lock. */
116 public With(Lock lock, long lockWaitTimeout) {
117 this.lock = lock;
118 this.lockWaitTimeout = lockWaitTimeout;
119 }
120
121 /** Code to execute with exclusive access. */
122 protected abstract Object doBody() throws IOException;
123
124 /** Calls {@link #doBody} while <i>lock</i> is obtained. Blocks if lock
125 * cannot be obtained immediately. Retries to obtain lock once per second
126 * until it is obtained, or until it has tried ten times. Lock is released when
127 * {@link #doBody} exits.
128 * @throws LockObtainFailedException if lock could not
129 * be obtained
130 * @throws IOException if {@link Lock#obtain} throws IOException
131 */
132 public Object run() throws LockObtainFailedException, IOException {
133 boolean locked = false;
134 try {
135 locked = lock.obtain(lockWaitTimeout);
136 return doBody();
137 } finally {
138 if (locked)
139 lock.release();
140 }
141 }
142 }
143
144 }