You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@labs.apache.org by ka...@apache.org on 2009/03/13 08:48:36 UTC
svn commit: r753152 - in /labs/bananadb/trunk/src:
main/java/org/apache/labs/bananadb/hashtable/
main/java/org/apache/labs/bananadb/hashtable/lock/
test/java/org/apache/labs/bananadb/hashtable/
Author: kalle
Date: Fri Mar 13 07:48:35 2009
New Revision: 753152
URL: http://svn.apache.org/viewvc?rev=753152&view=rev
Log:
BananaDB
Write locks (clone of Lucene store locks)
Added:
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/Lock.java
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockFactory.java (with props)
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockObtainFailedException.java
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockReleaseFailedException.java
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockStressTest.java
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockVerifyServer.java
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NativeFSLockFactory.java (with props)
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoLockFactory.java (with props)
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoSuchDirectoryException.java
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/README.txt
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SimpleFSLockFactory.java (with props)
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SingleInstanceLockFactory.java (with props)
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/VerifyingLockFactory.java
Modified:
labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/Hashtable.java
labs/bananadb/trunk/src/test/java/org/apache/labs/bananadb/hashtable/TestHashtable.java
Modified: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/Hashtable.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/Hashtable.java?rev=753152&r1=753151&r2=753152&view=diff
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/Hashtable.java (original)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/Hashtable.java Fri Mar 13 07:48:35 2009
@@ -20,6 +20,9 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.labs.bananadb.hashtable.lock.Lock;
+import org.apache.labs.bananadb.hashtable.lock.NativeFSLockFactory;
+import org.apache.labs.bananadb.hashtable.lock.LockFactory;
import java.io.*;
import java.util.*;
@@ -71,6 +74,9 @@
private int entityCount;
private int nextKeyPostingOffset;
+ private long lockWaitTimeoutMilliseconds = 10000;
+ private Lock writeLock;
+ private LockFactory lockFactory;
private void setNextKeyPostingOffset(int nextKeyPostingOffset) {
this.nextKeyPostingOffset = nextKeyPostingOffset;
@@ -98,6 +104,12 @@
return accessor;
}
+ public Hashtable(File path, int capacity, KeyClassHandler<K> keyClassHandler, ValueClassHandler<V> valueClassHandler) throws IOException {
+ this(path, capacity, keyClassHandler, valueClassHandler, new NativeFSLockFactory(path));
+ }
+
+
+
/**
* Open or create a hashtable.
*
@@ -114,7 +126,9 @@
* @param valueClassHandler
* @throws IOException
*/
- public Hashtable(File path, int capacity, KeyClassHandler<K> keyClassHandler, ValueClassHandler<V> valueClassHandler) throws IOException {
+ public Hashtable(File path, int capacity, KeyClassHandler<K> keyClassHandler, ValueClassHandler<V> valueClassHandler, LockFactory lockFactory) throws IOException {
+
+ writeLock = lockFactory.makeLock("lock");
this.keyClassHandler = keyClassHandler;
this.valueClassHandler = valueClassHandler;
@@ -203,7 +217,24 @@
* @return Previous value of the parameter key, or null if a new key.
* @throws IOException
*/
- public synchronized V put(HashtableAccessor accessor, K key, V value) throws IOException {
+ public V put(final HashtableAccessor accessor, final K key, final V value) throws IOException {
+ Lock.With<V> with = new Lock.With<V>(writeLock, lockWaitTimeoutMilliseconds) {
+ protected V doBody() throws IOException {
+ return doPut(accessor, key, value);
+ }
+ };
+ return with.run();
+ }
+
+ /**
+ *
+ * @param accessor
+ * @param key
+ * @param value
+ * @return Previous value of the parameter key, or null if a new key.
+ * @throws IOException
+ */
+ private V doPut(HashtableAccessor accessor, K key, V value) throws IOException {
// write key to buf
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
@@ -220,6 +251,7 @@
baos.close();
}
+
V oldValue = null;
int keyHash = key.hashCode();
@@ -312,7 +344,7 @@
}
- private synchronized void writePostings(HashtableAccessor accessor, int nextKeyPostingOffset, byte[] keyBuf, byte[] valueBuf, int keyHashCode) throws IOException {
+ private void writePostings(HashtableAccessor accessor, int nextKeyPostingOffset, byte[] keyBuf, byte[] valueBuf, int keyHashCode) throws IOException {
int partitionNumber = -1;
long offset = -1;
@@ -566,7 +598,7 @@
return append(accessor, bytes, bytes.length);
}
- public synchronized long append(HashtableAccessor accessor, byte[] bytes, int length) throws IOException {
+ public long append(HashtableAccessor accessor, byte[] bytes, int length) throws IOException {
bytesLeft -= length;
@@ -846,6 +878,13 @@
return partitions;
}
+ public long getLockWaitTimeoutMilliseconds() {
+ return lockWaitTimeoutMilliseconds;
+ }
+
+ public void setLockWaitTimeoutMilliseconds(long lockWaitTimeoutMilliseconds) {
+ this.lockWaitTimeoutMilliseconds = lockWaitTimeoutMilliseconds;
+ }
}
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/Lock.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/Lock.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/Lock.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/Lock.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,140 @@
+package org.apache.labs.bananadb.hashtable.lock;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+/** An interprocess mutex lock.
+ * <p>Typical use might look like:<pre>
+ * new Lock.With(directory.makeLock("my.lock")) {
+ * public Object doBody() {
+ * <i>... code to execute while locked ...</i>
+ * }
+ * }.run();
+ * </pre>
+ */
+public abstract class Lock {
+
+ /** How long {@link #obtain(long)} waits, in milliseconds,
+ * in between attempts to acquire the lock. */
+ public static long LOCK_POLL_INTERVAL = 1000;
+
+ /** Pass this value to {@link #obtain(long)} to try
+ * forever to obtain the lock. */
+ public static final long LOCK_OBTAIN_WAIT_FOREVER = -1;
+
+ /** Attempts to obtain exclusive access and immediately return
+ * upon success or failure.
+ * @return true iff exclusive access is obtained
+ */
+ public abstract boolean obtain() throws IOException;
+
+ /**
+ * If a lock obtain called, this failureReason may be set
+ * with the "root cause" Exception as to why the lock was
+ * not obtained.
+ */
+ protected Throwable failureReason;
+
+ /** Attempts to obtain an exclusive lock within amount of
+ * time given. Polls once per {@link #LOCK_POLL_INTERVAL}
+ * (currently 1000) milliseconds until lockWaitTimeout is
+ * passed.
+ * @param lockWaitTimeout length of time to wait in
+ * milliseconds or {@link
+ * #LOCK_OBTAIN_WAIT_FOREVER} to retry forever
+ * @return true if lock was obtained
+ * @throws LockObtainFailedException if lock wait times out
+ * @throws IllegalArgumentException if lockWaitTimeout is
+ * out of bounds
+ * @throws IOException if obtain() throws IOException
+ */
+ public boolean obtain(long lockWaitTimeout) throws LockObtainFailedException, IOException {
+ failureReason = null;
+ boolean locked = obtain();
+ if (lockWaitTimeout < 0 && lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER)
+ throw new IllegalArgumentException("lockWaitTimeout should be LOCK_OBTAIN_WAIT_FOREVER or a non-negative number (got " + lockWaitTimeout + ")");
+
+ long maxSleepCount = lockWaitTimeout / LOCK_POLL_INTERVAL;
+ long sleepCount = 0;
+ while (!locked) {
+ if (lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER && sleepCount++ >= maxSleepCount) {
+ String reason = "Lock obtain timed out: " + this.toString();
+ if (failureReason != null) {
+ reason += ": " + failureReason;
+ }
+ LockObtainFailedException e = new LockObtainFailedException(reason);
+ if (failureReason != null) {
+ e.initCause(failureReason);
+ }
+ throw e;
+ }
+ try {
+ Thread.sleep(LOCK_POLL_INTERVAL);
+ } catch (InterruptedException e) {
+ throw new IOException(e.toString());
+ }
+ locked = obtain();
+ }
+ return locked;
+ }
+
+ /** Releases exclusive access. */
+ public abstract void release() throws IOException;
+
+ /** Returns true if the resource is currently locked. Note that one must
+ * still call {@link #obtain()} before using the resource. */
+ public abstract boolean isLocked();
+
+
+ /** Utility class for executing code with exclusive access. */
+ public abstract static class With<T> {
+ private Lock lock;
+ private long lockWaitTimeout;
+
+
+ /** Constructs an executor that will grab the named lock. */
+ public With(Lock lock, long lockWaitTimeout) {
+ this.lock = lock;
+ this.lockWaitTimeout = lockWaitTimeout;
+ }
+
+ /** Code to execute with exclusive access. */
+ protected abstract T doBody() throws IOException;
+
+ /** Calls {@link #doBody} while <i>lock</i> is obtained. Blocks if lock
+ * cannot be obtained immediately. Retries to obtain lock once per second
+ * until it is obtained, or until it has tried ten times. Lock is released when
+ * {@link #doBody} exits.
+ * @throws LockObtainFailedException if lock could not
+ * be obtained
+ * @throws IOException if {@link Lock#obtain} throws IOException
+ */
+ public T run() throws LockObtainFailedException, IOException {
+ boolean locked = false;
+ try {
+ locked = lock.obtain(lockWaitTimeout);
+ return doBody();
+ } finally {
+ if (locked)
+ lock.release();
+ }
+ }
+ }
+
+}
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockFactory.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockFactory.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockFactory.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockFactory.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,74 @@
+package org.apache.labs.bananadb.hashtable.lock;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+/**
+ * <p>Base class for Locking implementation. {@link Directory} uses
+ * instances of this class to implement locking.</p>
+ *
+ * <p>Note that there are some useful tools to verify that
+ * your LockFactory is working correctly: {@link
+ * VerifyingLockFactory}, {@link LockStressTest}, {@link
+ * LockVerifyServer}.</p>
+ *
+ * @see LockVerifyServer
+ * @see LockStressTest
+ * @see VerifyingLockFactory
+ */
+
+public abstract class LockFactory {
+
+ protected String lockPrefix = "";
+
+ /**
+ * Set the prefix in use for all locks created in this
+ * LockFactory. This is normally called once, when a
+ * Directory gets this LockFactory instance. However, you
+ * can also call this (after this instance is assigned to
+ * a Directory) to override the prefix in use. This
+ * is helpful if you're running Lucene on machines that
+ * have different mount points for the same shared
+ * directory.
+ */
+ public void setLockPrefix(String lockPrefix) {
+ this.lockPrefix = lockPrefix;
+ }
+
+ /**
+ * Get the prefix in use for all locks created in this LockFactory.
+ */
+ public String getLockPrefix() {
+ return this.lockPrefix;
+ }
+
+ /**
+ * Return a new Lock instance identified by lockName.
+ * @param lockName name of the lock to be created.
+ */
+ public abstract Lock makeLock(String lockName);
+
+ /**
+ * Attempt to clear (forcefully unlock and remove) the
+ * specified lock. Only call this at a time when you are
+ * certain this lock is no longer in use.
+ * @param lockName name of the lock to be cleared.
+ */
+ abstract public void clearLock(String lockName) throws IOException;
+}
Propchange: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockFactory.java
------------------------------------------------------------------------------
svn:executable = *
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockObtainFailedException.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockObtainFailedException.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockObtainFailedException.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockObtainFailedException.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.labs.bananadb.hashtable.lock;
+
+import java.io.IOException;
+
+/**
+ * This exception is thrown when the <code>write.lock</code>
+ * could not be acquired. This
+ * happens when a writer tries to open an index
+ * that another writer already has open.
+ * @see Lock#obtain(long).
+ */
+public class LockObtainFailedException extends IOException {
+ public LockObtainFailedException(String message) {
+ super(message);
+ }
+}
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockReleaseFailedException.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockReleaseFailedException.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockReleaseFailedException.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockReleaseFailedException.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.labs.bananadb.hashtable.lock;
+
+import java.io.IOException;
+
+/**
+ * This exception is thrown when the <code>write.lock</code>
+ * could not be released.
+ * @see Lock#release().
+ */
+public class LockReleaseFailedException extends IOException {
+ public LockReleaseFailedException(String message) {
+ super(message);
+ }
+}
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockStressTest.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockStressTest.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockStressTest.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockStressTest.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,117 @@
+package org.apache.labs.bananadb.hashtable.lock;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.io.File;
+
+/**
+ * Simple standalone tool that forever acquires & releases a
+ * lock using a specific LockFactory. Run without any args
+ * to see usage.
+ *
+ * @see VerifyingLockFactory
+ * @see LockVerifyServer
+ */
+
+public class LockStressTest {
+
+ public static void main(String[] args) throws Exception {
+
+ if (args.length != 6) {
+ System.out.println("\nUsage: java org.apache.labs.bananadb.hashtable.lock.LockStressTest myID verifierHostOrIP verifierPort lockFactoryClassName lockDirName sleepTime\n" +
+ "\n" +
+ " myID = int from 0 .. 255 (should be unique for test process)\n" +
+ " verifierHostOrIP = host name or IP address where LockVerifyServer is running\n" +
+ " verifierPort = port that LockVerifyServer is listening on\n" +
+ " lockFactoryClassName = primary LockFactory class that we will use\n" +
+ " lockDirName = path to the lock directory (only set for Simple/NativeFSLockFactory\n" +
+ " sleepTimeMS = milliseconds to pause betweeen each lock obtain/release\n" +
+ "\n" +
+ "You should run multiple instances of this process, each with its own\n" +
+ "unique ID, and each pointing to the same lock directory, to verify\n" +
+ "that locking is working correctly.\n" +
+ "\n" +
+ "Make sure you are first running LockVerifyServer.\n" +
+ "\n");
+ System.exit(1);
+ }
+
+ final int myID = Integer.parseInt(args[0]);
+
+ if (myID < 0 || myID > 255) {
+ System.out.println("myID must be a unique int 0..255");
+ System.exit(1);
+ }
+
+ final String verifierHost = args[1];
+ final int verifierPort = Integer.parseInt(args[2]);
+ final String lockFactoryClassName = args[3];
+ final String lockDirName = args[4];
+ final int sleepTimeMS = Integer.parseInt(args[5]);
+
+ Class c;
+ try {
+ c = Class.forName(lockFactoryClassName);
+ } catch (ClassNotFoundException e) {
+ throw new IOException("unable to find LockClass " + lockFactoryClassName);
+ }
+
+ LockFactory lockFactory;
+ try {
+ lockFactory = (LockFactory) c.newInstance();
+ } catch (IllegalAccessException e) {
+ throw new IOException("IllegalAccessException when instantiating LockClass " + lockFactoryClassName);
+ } catch (InstantiationException e) {
+ throw new IOException("InstantiationException when instantiating LockClass " + lockFactoryClassName);
+ } catch (ClassCastException e) {
+ throw new IOException("unable to cast LockClass " + lockFactoryClassName + " instance to a LockFactory");
+ }
+
+ File lockDir = new File(lockDirName);
+
+ if (lockFactory instanceof NativeFSLockFactory) {
+ ((NativeFSLockFactory) lockFactory).setLockDir(lockDir);
+ } else if (lockFactory instanceof SimpleFSLockFactory) {
+ ((SimpleFSLockFactory) lockFactory).setLockDir(lockDir);
+ }
+
+ lockFactory.setLockPrefix("test");
+
+ LockFactory verifyLF = new VerifyingLockFactory((byte) myID, lockFactory, verifierHost, verifierPort);
+
+ Lock l = verifyLF.makeLock("test.lock");
+
+ while(true) {
+
+ boolean obtained = false;
+
+ try {
+ obtained = l.obtain(10);
+ } catch (LockObtainFailedException e) {
+ System.out.print("x");
+ }
+
+ if (obtained) {
+ System.out.print("l");
+ l.release();
+ }
+ Thread.sleep(sleepTimeMS);
+ }
+ }
+}
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockVerifyServer.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockVerifyServer.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockVerifyServer.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/LockVerifyServer.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,96 @@
+package org.apache.labs.bananadb.hashtable.lock;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Simple standalone server that must be running when you
+ * use {@link VerifyingLockFactory}. This server simply
+ * verifies at most one process holds the lock at a time.
+ * Run without any args to see usage.
+ *
+ * @see VerifyingLockFactory
+ * @see LockStressTest
+ */
+
+public class LockVerifyServer {
+
+ private static String getTime(long startTime) {
+ return "[" + ((System.currentTimeMillis()-startTime)/1000) + "s] ";
+ }
+
+ public static void main(String[] args) throws IOException {
+
+ if (args.length != 1) {
+ System.out.println("\nUsage: java org.apache.labs.bananadb.hashtable.lock.LockVerifyServer port\n");
+ System.exit(1);
+ }
+
+ final int port = Integer.parseInt(args[0]);
+
+ ServerSocket s = new ServerSocket(port);
+ s.setReuseAddress(true);
+ System.out.println("\nReady on port " + port + "...");
+
+ int lockedID = 0;
+ long startTime = System.currentTimeMillis();
+
+ while(true) {
+ Socket cs = s.accept();
+ OutputStream out = cs.getOutputStream();
+ InputStream in = cs.getInputStream();
+
+ int id = in.read();
+ int command = in.read();
+
+ boolean err = false;
+
+ if (command == 1) {
+ // Locked
+ if (lockedID != 0) {
+ err = true;
+ System.out.println(getTime(startTime) + " ERROR: id " + id + " got lock, but " + lockedID + " already holds the lock");
+ }
+ lockedID = id;
+ } else if (command == 0) {
+ if (lockedID != id) {
+ err = true;
+ System.out.println(getTime(startTime) + " ERROR: id " + id + " released the lock, but " + lockedID + " is the one holding the lock");
+ }
+ lockedID = 0;
+ } else
+ throw new RuntimeException("unrecognized command " + command);
+
+ System.out.print(".");
+
+ if (err)
+ out.write(1);
+ else
+ out.write(0);
+
+ out.close();
+ in.close();
+ cs.close();
+ }
+ }
+}
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NativeFSLockFactory.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NativeFSLockFactory.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NativeFSLockFactory.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NativeFSLockFactory.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,332 @@
+package org.apache.labs.bananadb.hashtable.lock;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Random;
+
+/**
+ * <p>Implements {@link LockFactory} using native OS file
+ * locks. Note that because this LockFactory relies on
+ * java.nio.* APIs for locking, any problems with those APIs
+ * will cause locking to fail. Specifically, on certain NFS
+ * environments the java.nio.* locks will fail (the lock can
+ * incorrectly be double acquired) whereas {@link
+ * SimpleFSLockFactory} worked perfectly in those same
+ * environments. For NFS based access to an index, it's
+ * recommended that you try {@link SimpleFSLockFactory}
+ * first and work around the one limitation that a lock file
+ * could be left when the JVM exits abnormally.</p>
+ *
+ * <p>The primary benefit of {@link NativeFSLockFactory} is
+ * that lock files will be properly removed (by the OS) if
+ * the JVM has an abnormal exit.</p>
+ *
+ * <p>Note that, unlike {@link SimpleFSLockFactory}, the existence of
+ * leftover lock files in the filesystem on exiting the JVM
+ * is fine because the OS will free the locks held against
+ * these files even though the files still remain.</p>
+ *
+ * <p>If you suspect that this or any other LockFactory is
+ * not working properly in your environment, you can easily
+ * test it by using {@link VerifyingLockFactory}, {@link
+ * LockVerifyServer} and {@link LockStressTest}.</p>
+ *
+ * @see LockFactory
+ */
+
+public class NativeFSLockFactory extends LockFactory {
+
+ /**
+ * Directory specified by <code>org.apache.lucene.lockDir</code>
+ * system property. If that is not set, then <code>java.io.tmpdir</code>
+ * system property is used.
+ */
+
+ private File lockDir;
+
+ // Simple test to verify locking system is "working". On
+ // NFS, if it's misconfigured, you can hit long (35
+ // second) timeouts which cause Lock.obtain to take far
+ // too long (it assumes the obtain() call takes zero
+ // time). Since it's a configuration problem, we test up
+ // front once on creating the LockFactory:
+ private void acquireTestLock() throws IOException {
+ String randomLockName = "lucene-" + Long.toString(new Random().nextInt(), Character.MAX_RADIX) + "-test.lock";
+
+ Lock l = makeLock(randomLockName);
+ try {
+ l.obtain();
+ } catch (IOException e) {
+ IOException e2 = new IOException("Failed to acquire random test lock; please verify filesystem for lock directory '" + lockDir + "' supports locking");
+ e2.initCause(e);
+ throw e2;
+ }
+
+ l.release();
+ }
+
+ /**
+ * Create a NativeFSLockFactory instance, with null (unset)
+ * lock directory. This is package-private and is only
+ * used by FSDirectory when creating this LockFactory via
+ * the System property
+ * org.apache.lucene.store.FSDirectoryLockFactoryClass.
+ */
+ NativeFSLockFactory() throws IOException {
+ this((File) null);
+ }
+
+ /**
+ * Create a NativeFSLockFactory instance, storing lock
+ * files into the specified lockDirName:
+ *
+ * @param lockDirName where lock files are created.
+ */
+ public NativeFSLockFactory(String lockDirName) throws IOException {
+ this(new File(lockDirName));
+ }
+
+ /**
+ * Create a NativeFSLockFactory instance, storing lock
+ * files into the specified lockDir:
+ *
+ * @param lockDir where lock files are created.
+ */
+ public NativeFSLockFactory(File lockDir) throws IOException {
+ setLockDir(lockDir);
+ }
+
+ /**
+ * Set the lock directory. This is package-private and is
+ * only used externally by FSDirectory when creating this
+ * LockFactory via the System property
+ * org.apache.lucene.store.FSDirectoryLockFactoryClass.
+ */
+ void setLockDir(File lockDir) throws IOException {
+ this.lockDir = lockDir;
+ if (lockDir != null) {
+ // Ensure that lockDir exists and is a directory.
+ if (!lockDir.exists()) {
+ if (!lockDir.mkdirs())
+ throw new IOException("Cannot create directory: " +
+ lockDir.getAbsolutePath());
+ } else if (!lockDir.isDirectory()) {
+ throw new IOException("Found regular file where directory expected: " +
+ lockDir.getAbsolutePath());
+ }
+
+ acquireTestLock();
+ }
+ }
+
+ public synchronized Lock makeLock(String lockName) {
+ if (lockPrefix != null)
+ lockName = lockPrefix + "-n-" + lockName;
+ return new NativeFSLock(lockDir, lockName);
+ }
+
+ public void clearLock(String lockName) throws IOException {
+ // Note that this isn't strictly required anymore
+ // because the existence of these files does not mean
+ // they are locked, but, still do this in case people
+ // really want to see the files go away:
+ if (lockDir.exists()) {
+ if (lockPrefix != null) {
+ lockName = lockPrefix + "-n-" + lockName;
+ }
+ File lockFile = new File(lockDir, lockName);
+ if (lockFile.exists() && !lockFile.delete()) {
+ throw new IOException("Cannot delete " + lockFile);
+ }
+ }
+ }
+};
+
+class NativeFSLock extends Lock {
+
+ private RandomAccessFile f;
+ private FileChannel channel;
+ private FileLock lock;
+ private File path;
+ private File lockDir;
+
+ /*
+ * The javadocs for FileChannel state that you should have
+ * a single instance of a FileChannel (per JVM) for all
+ * locking against a given file. To ensure this, we have
+ * a single (static) HashSet that contains the file paths
+ * of all currently locked locks. This protects against
+ * possible cases where different Directory instances in
+ * one JVM (each with their own NativeFSLockFactory
+ * instance) have set the same lock dir and lock prefix.
+ */
+ private static HashSet LOCK_HELD = new HashSet();
+
+ public NativeFSLock(File lockDir, String lockFileName) {
+ this.lockDir = lockDir;
+ path = new File(lockDir, lockFileName);
+ }
+
+ public synchronized boolean obtain() throws IOException {
+
+ if (isLocked()) {
+ // Our instance is already locked:
+ return false;
+ }
+
+ // Ensure that lockDir exists and is a directory.
+ if (!lockDir.exists()) {
+ if (!lockDir.mkdirs())
+ throw new IOException("Cannot create directory: " +
+ lockDir.getAbsolutePath());
+ } else if (!lockDir.isDirectory()) {
+ throw new IOException("Found regular file where directory expected: " +
+ lockDir.getAbsolutePath());
+ }
+
+ String canonicalPath = path.getCanonicalPath();
+
+ boolean markedHeld = false;
+
+ try {
+
+ // Make sure nobody else in-process has this lock held
+ // already, and, mark it held if not:
+
+ synchronized(LOCK_HELD) {
+ if (LOCK_HELD.contains(canonicalPath)) {
+ // Someone else in this JVM already has the lock:
+ return false;
+ } else {
+ // This "reserves" the fact that we are the one
+ // thread trying to obtain this lock, so we own
+ // the only instance of a channel against this
+ // file:
+ LOCK_HELD.add(canonicalPath);
+ markedHeld = true;
+ }
+ }
+
+ try {
+ f = new RandomAccessFile(path, "rw");
+ } catch (IOException e) {
+ // On Windows, we can get intermittant "Access
+ // Denied" here. So, we treat this as failure to
+ // acquire the lock, but, lock the reason in case
+ // there is in fact a real error case.
+ failureReason = e;
+ f = null;
+ }
+
+ if (f != null) {
+ try {
+ channel = f.getChannel();
+ try {
+ lock = channel.tryLock();
+ } catch (IOException e) {
+ // At least on OS X, we will sometimes get an
+ // intermittant "Permission Denied" IOException,
+ // which seems to simply mean "you failed to get
+ // the lock". But other IOExceptions could be
+ // "permanent" (eg, locking is not supported via
+ // the filesystem). So, we record the failure
+ // reason here; the timeout obtain (usually the
+ // one calling us) will use this as "root cause"
+ // if it fails to get the lock.
+ failureReason = e;
+ } finally {
+ if (lock == null) {
+ try {
+ channel.close();
+ } finally {
+ channel = null;
+ }
+ }
+ }
+ } finally {
+ if (channel == null) {
+ try {
+ f.close();
+ } finally {
+ f = null;
+ }
+ }
+ }
+ }
+
+ } finally {
+ if (markedHeld && !isLocked()) {
+ synchronized(LOCK_HELD) {
+ if (LOCK_HELD.contains(canonicalPath)) {
+ LOCK_HELD.remove(canonicalPath);
+ }
+ }
+ }
+ }
+ return isLocked();
+ }
+
+ public synchronized void release() throws IOException {
+ if (isLocked()) {
+ try {
+ lock.release();
+ } finally {
+ lock = null;
+ try {
+ channel.close();
+ } finally {
+ channel = null;
+ try {
+ f.close();
+ } finally {
+ f = null;
+ synchronized(LOCK_HELD) {
+ LOCK_HELD.remove(path.getCanonicalPath());
+ }
+ }
+ }
+ }
+ if (!path.delete())
+ throw new LockReleaseFailedException("failed to delete " + path);
+ }
+ }
+
+ public synchronized boolean isLocked() {
+ return lock != null;
+ }
+
+ public String toString() {
+ return "NativeFSLock@" + path;
+ }
+
+ public void finalize() throws Throwable {
+ try {
+ if (isLocked()) {
+ release();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+}
Propchange: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NativeFSLockFactory.java
------------------------------------------------------------------------------
svn:executable = *
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoLockFactory.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoLockFactory.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoLockFactory.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoLockFactory.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,63 @@
+package org.apache.labs.bananadb.hashtable.lock;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+/**
+ * Use this {@link LockFactory} to disable locking entirely.
+ * This LockFactory is used when you call {@link FSDirectory#setDisableLocks}.
+ * Only one instance of this lock is created. You should call {@link
+ * #getNoLockFactory()} to get the instance.
+ *
+ * @see LockFactory
+ */
+
+public class NoLockFactory extends LockFactory {
+
+ // Single instance returned whenever makeLock is called.
+ private static NoLock singletonLock = new NoLock();
+ private static NoLockFactory singleton = new NoLockFactory();
+
+ public static NoLockFactory getNoLockFactory() {
+ return singleton;
+ }
+
+ public Lock makeLock(String lockName) {
+ return singletonLock;
+ }
+
+ public void clearLock(String lockName) {};
+};
+
+class NoLock extends Lock {
+ public boolean obtain() throws IOException {
+ return true;
+ }
+
+ public void release() {
+ }
+
+ public boolean isLocked() {
+ return false;
+ }
+
+ public String toString() {
+ return "NoLock";
+ }
+}
Propchange: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoLockFactory.java
------------------------------------------------------------------------------
svn:executable = *
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoSuchDirectoryException.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoSuchDirectoryException.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoSuchDirectoryException.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/NoSuchDirectoryException.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,31 @@
+package org.apache.labs.bananadb.hashtable.lock;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.FileNotFoundException;
+
+/**
+ * This exception is thrown when you try to list a
+ * non-existent directory.
+ */
+
+public class NoSuchDirectoryException extends FileNotFoundException {
+ public NoSuchDirectoryException(String message) {
+ super(message);
+ }
+}
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/README.txt
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/README.txt?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/README.txt (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/README.txt Fri Mar 13 07:48:35 2009
@@ -0,0 +1,3 @@
+This package is a copy of the Lucene lock mechanism as of SVN revision 745917.
+
+https://svn.apache.org/repos/asf/lucene/java/trunk/src/java/org/apache/lucene/store
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SimpleFSLockFactory.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SimpleFSLockFactory.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SimpleFSLockFactory.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SimpleFSLockFactory.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,159 @@
+package org.apache.labs.bananadb.hashtable.lock;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * <p>Implements {@link LockFactory} using {@link
+ * File#createNewFile()}. This is the default LockFactory
+ * for {@link FSDirectory}.</p>
+ *
+ * <p><b>NOTE:</b> the <a target="_top"
+ * href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/File.html#createNewFile()">javadocs
+ * for <code>File.createNewFile</code></a> contain a vague
+ * yet spooky warning about not using the API for file
+ * locking. This warning was added due to <a target="_top"
+ * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4676183">this
+ * bug</a>, and in fact the only known problem with using
+ * this API for locking is that the Lucene write lock may
+ * not be released when the JVM exits abnormally.</p>
+
+ * <p>When this happens, a {@link LockObtainFailedException}
+ * is hit when trying to create a writer, in which case you
+ * need to explicitly clear the lock file first. You can
+ * either manually remove the file, or use the {@link
+ * org.apache.lucene.index.IndexReader#unlock(Directory)}
+ * API. But, first be certain that no writer is in fact
+ * writing to the index otherwise you can easily corrupt
+ * your index.</p>
+ *
+ * <p>If you suspect that this or any other LockFactory is
+ * not working properly in your environment, you can easily
+ * test it by using {@link VerifyingLockFactory}, {@link
+ * LockVerifyServer} and {@link LockStressTest}.</p>
+ *
+ * @see LockFactory
+ */
+
+public class SimpleFSLockFactory extends LockFactory {
+
+ /**
+ * Directory specified by <code>org.apache.lucene.lockDir</code>
+ * system property. If that is not set, then <code>java.io.tmpdir</code>
+ * system property is used.
+ */
+
+ private File lockDir;
+
+ /**
+ * Create a SimpleFSLockFactory instance, with null (unset)
+ * lock directory. This is package-private and is only
+ * used by FSDirectory when creating this LockFactory via
+ * the System property
+ * org.apache.lucene.store.FSDirectoryLockFactoryClass.
+ */
+ SimpleFSLockFactory() throws IOException {
+ this((File) null);
+ }
+
+ /**
+ * Instantiate using the provided directory (as a File instance).
+ * @param lockDir where lock files should be created.
+ */
+ public SimpleFSLockFactory(File lockDir) throws IOException {
+ setLockDir(lockDir);
+ }
+
+ /**
+ * Instantiate using the provided directory name (String).
+ * @param lockDirName where lock files should be created.
+ */
+ public SimpleFSLockFactory(String lockDirName) throws IOException {
+ lockDir = new File(lockDirName);
+ setLockDir(lockDir);
+ }
+
+ /**
+ * Set the lock directory. This is package-private and is
+ * only used externally by FSDirectory when creating this
+ * LockFactory via the System property
+ * org.apache.lucene.store.FSDirectoryLockFactoryClass.
+ */
+ void setLockDir(File lockDir) throws IOException {
+ this.lockDir = lockDir;
+ }
+
+ public Lock makeLock(String lockName) {
+ if (lockPrefix != null) {
+ lockName = lockPrefix + "-" + lockName;
+ }
+ return new SimpleFSLock(lockDir, lockName);
+ }
+
+ public void clearLock(String lockName) throws IOException {
+ if (lockDir.exists()) {
+ if (lockPrefix != null) {
+ lockName = lockPrefix + "-" + lockName;
+ }
+ File lockFile = new File(lockDir, lockName);
+ if (lockFile.exists() && !lockFile.delete()) {
+ throw new IOException("Cannot delete " + lockFile);
+ }
+ }
+ }
+};
+
+class SimpleFSLock extends Lock {
+
+ File lockFile;
+ File lockDir;
+
+ public SimpleFSLock(File lockDir, String lockFileName) {
+ this.lockDir = lockDir;
+ lockFile = new File(lockDir, lockFileName);
+ }
+
+ public boolean obtain() throws IOException {
+
+ // Ensure that lockDir exists and is a directory:
+ if (!lockDir.exists()) {
+ if (!lockDir.mkdirs())
+ throw new IOException("Cannot create directory: " +
+ lockDir.getAbsolutePath());
+ } else if (!lockDir.isDirectory()) {
+ throw new IOException("Found regular file where directory expected: " +
+ lockDir.getAbsolutePath());
+ }
+ return lockFile.createNewFile();
+ }
+
+ public void release() throws LockReleaseFailedException {
+ if (lockFile.exists() && !lockFile.delete())
+ throw new LockReleaseFailedException("failed to delete " + lockFile);
+ }
+
+ public boolean isLocked() {
+ return lockFile.exists();
+ }
+
+ public String toString() {
+ return "SimpleFSLock@" + lockFile;
+ }
+}
Propchange: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SimpleFSLockFactory.java
------------------------------------------------------------------------------
svn:executable = *
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SingleInstanceLockFactory.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SingleInstanceLockFactory.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SingleInstanceLockFactory.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SingleInstanceLockFactory.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,85 @@
+package org.apache.labs.bananadb.hashtable.lock;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.HashSet;
+
+/**
+ * Implements {@link LockFactory} for a single in-process instance,
+ * meaning all locking will take place through this one instance.
+ * Only use this {@link LockFactory} when you are certain all
+ * IndexReaders and IndexWriters for a given index are running
+ * against a single shared in-process Directory instance. This is
+ * currently the default locking for RAMDirectory.
+ *
+ * @see LockFactory
+ */
+
+public class SingleInstanceLockFactory extends LockFactory {
+
+ private HashSet locks = new HashSet();
+
+ public Lock makeLock(String lockName) {
+ // We do not use the LockPrefix at all, because the private
+ // HashSet instance effectively scopes the locking to this
+ // single Directory instance.
+ return new SingleInstanceLock(locks, lockName);
+ }
+
+ public void clearLock(String lockName) throws IOException {
+ synchronized(locks) {
+ if (locks.contains(lockName)) {
+ locks.remove(lockName);
+ }
+ }
+ }
+};
+
+class SingleInstanceLock extends Lock {
+
+ String lockName;
+ private HashSet locks;
+
+ public SingleInstanceLock(HashSet locks, String lockName) {
+ this.locks = locks;
+ this.lockName = lockName;
+ }
+
+ public boolean obtain() throws IOException {
+ synchronized(locks) {
+ return locks.add(lockName);
+ }
+ }
+
+ public void release() {
+ synchronized(locks) {
+ locks.remove(lockName);
+ }
+ }
+
+ public boolean isLocked() {
+ synchronized(locks) {
+ return locks.contains(lockName);
+ }
+ }
+
+ public String toString() {
+ return super.toString() + ": " + lockName;
+ }
+}
Propchange: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/SingleInstanceLockFactory.java
------------------------------------------------------------------------------
svn:executable = *
Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/VerifyingLockFactory.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/VerifyingLockFactory.java?rev=753152&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/VerifyingLockFactory.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/hashtable/lock/VerifyingLockFactory.java Fri Mar 13 07:48:35 2009
@@ -0,0 +1,119 @@
+package org.apache.labs.bananadb.hashtable.lock;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.Socket;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A {@link LockFactory} that wraps another {@link
+ * LockFactory} and verifies that each lock obtain/release
+ * is "correct" (never results in two processes holding the
+ * lock at the same time). It does this by contacting an
+ * external server ({@link LockVerifyServer}) to assert that
+ * at most one process holds the lock at a time. To use
+ * this, you should also run {@link LockVerifyServer} on the
+ * host & port matching what you pass to the constructor.
+ *
+ * @see LockVerifyServer
+ * @see LockStressTest
+ */
+
+public class VerifyingLockFactory extends LockFactory {
+
+ LockFactory lf;
+ byte id;
+ String host;
+ int port;
+
+ private class CheckedLock extends Lock {
+ private Lock lock;
+
+ public CheckedLock(Lock lock) {
+ this.lock = lock;
+ }
+
+ private void verify(byte message) {
+ try {
+ Socket s = new Socket(host, port);
+ OutputStream out = s.getOutputStream();
+ out.write(id);
+ out.write(message);
+ InputStream in = s.getInputStream();
+ int result = in.read();
+ in.close();
+ out.close();
+ s.close();
+ if (result != 0)
+ throw new RuntimeException("lock was double acquired");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public synchronized boolean obtain(long lockWaitTimeout)
+ throws LockObtainFailedException, IOException {
+ boolean obtained = lock.obtain(lockWaitTimeout);
+ if (obtained)
+ verify((byte) 1);
+ return obtained;
+ }
+
+ public synchronized boolean obtain()
+ throws LockObtainFailedException, IOException {
+ return lock.obtain();
+ }
+
+ public synchronized boolean isLocked() {
+ return lock.isLocked();
+ }
+
+ public synchronized void release() throws IOException {
+ if (isLocked()) {
+ verify((byte) 0);
+ lock.release();
+ }
+ }
+ }
+
+ /**
+ * @param id should be a unique id across all clients
+ * @param lf the LockFactory that we are testing
+ * @param host host or IP where {@link LockVerifyServer}
+ is running
+ * @param port the port {@link LockVerifyServer} is
+ listening on
+ */
+ public VerifyingLockFactory(byte id, LockFactory lf, String host, int port) throws IOException {
+ this.id = id;
+ this.lf = lf;
+ this.host = host;
+ this.port = port;
+ }
+
+ public synchronized Lock makeLock(String lockName) {
+ return new CheckedLock(lf.makeLock(lockName));
+ }
+
+ public synchronized void clearLock(String lockName)
+ throws IOException {
+ lf.clearLock(lockName);
+ }
+}
Modified: labs/bananadb/trunk/src/test/java/org/apache/labs/bananadb/hashtable/TestHashtable.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/test/java/org/apache/labs/bananadb/hashtable/TestHashtable.java?rev=753152&r1=753151&r2=753152&view=diff
==============================================================================
--- labs/bananadb/trunk/src/test/java/org/apache/labs/bananadb/hashtable/TestHashtable.java (original)
+++ labs/bananadb/trunk/src/test/java/org/apache/labs/bananadb/hashtable/TestHashtable.java Fri Mar 13 07:48:35 2009
@@ -20,10 +20,15 @@
import junit.framework.TestCase;
import org.apache.labs.bananadb.hashtable.handlers.IntegerHandler;
import org.apache.labs.bananadb.hashtable.handlers.StringValueHandler;
+import org.apache.labs.bananadb.hashtable.lock.NoLockFactory;
+import org.apache.labs.bananadb.hashtable.lock.SingleInstanceLockFactory;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* User: kalle
@@ -41,6 +46,72 @@
}
}
+
+ private class TestThreadedRunnable implements Runnable {
+ private Hashtable<Integer, String> map;
+ private HashtableAccessor accessor;
+ private Exception exception;
+ private AtomicInteger pk;
+ private int id;
+
+ private TestThreadedRunnable(Hashtable<Integer, String> map, AtomicInteger pk) throws IOException {
+ this.map = map;
+ this.pk = pk;
+ accessor = map.createAccessor(false);
+ }
+
+ public void run() {
+ try {
+ for (int i = 0; i < 1000; i++) {
+ map.put(accessor, pk.getAndIncrement(), "foobar");
+ System.out.println(id + "\t" + i);
+ }
+ } catch (Exception exception) {
+ this.exception = exception;
+ throw new RuntimeException(exception);
+ }
+ }
+ }
+
+ @Test
+ public void testThreaded() throws Exception {
+ File path = new File(this.path, "testThreaded");
+
+ Hashtable<Integer, String> map = new Hashtable<Integer, String>(path, 40000, new IntegerHandler(), new StringValueHandler());
+ map.setLockWaitTimeoutMilliseconds(60000);
+ AtomicInteger pk = new AtomicInteger(0);
+
+ List<Thread> threads = new ArrayList<Thread>();
+ List<TestThreadedRunnable> runnables = new ArrayList<TestThreadedRunnable>();
+
+ for (int i = 0; i < 10; i++) {
+ TestThreadedRunnable runnable = new TestThreadedRunnable(map, pk);
+ runnable.id = i;
+ runnables.add(runnable);
+ Thread thread = new Thread(runnable);
+ thread.setName("TestThread " + String.valueOf(i));
+ threads.add(thread);
+ }
+ for (Thread thread : threads) {
+ thread.start();
+ }
+ for (Thread thread : threads) {
+ thread.join();
+ }
+
+ for (TestThreadedRunnable runnable : runnables) {
+ if (runnable.exception != null) {
+ throw new RuntimeException(runnable.exception);
+ }
+ runnable.accessor.close();
+ }
+
+ assertEquals(10000, map.size());
+
+
+
+ }
+
@Test
public void testNull() throws Exception {
@@ -147,8 +218,6 @@
accessor.close();
-
-
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@labs.apache.org
For additional commands, e-mail: commits-help@labs.apache.org