[JGIT PATCH] Fix a race condition when loading non-mmapd byte windows

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Back in 439d441b0800 ("Change ByteArrayWindow to read content
outside of WindowCache's lock") I introduced a race condition
to WindowCache.

It is possible for a ByteArrayWindow to be allocated and put
into the cache, and then for the calling thread to get put
to sleep before it can actually load the window from disk.
By the time the thread wakes up again, the window may have
already been evicted from the cache, and its underlying file
was closed.  This results in stack traces such as:

java.lang.RuntimeException: Cannot fault in window
        at org.spearce.jgit.lib.ByteArrayWindow.ensureLoaded(ByteArrayWindow.java:111)
...
Caused by: java.nio.channels.ClosedChannelException
        at sun.nio.ch.FileChannelImpl.ensureOpen(FileChannelImpl.java:91)

Now when we allocate a ByteArrayWindow for a specific window reference
we increment the file reference count one additional time, forcing the
file to stay open even if the window was evicted from the cache.  The
reference count is decremented once the window loads successfully, and
this puts us back into the state of 1 reference count value for each
window still in the window cache.

Signed-off-by: Shawn O. Pearce <spearce@xxxxxxxxxxx>
---

 I've decided I hate WindowedFile, ByteWindow, WindowCache.

 The API and implementation is crap.  Its all my own fault.  This
 code is mine, but its impossible to work with, not as concurrent
 as I'd like the cache to be, and there's problems getting it to
 be thread-safe, as this patch just pointed out.  *sigh*

 I think we should apply this, but really, someone needs to come
 back here and overhaul how we do our most low level IO ops.
 Its a crime that this code is in the state it is in.

 Or maybe I'm just pissed off because I'm hunting down a stupid
 concurrency issue on a Saturday.
 
 .../src/org/spearce/jgit/lib/ByteArrayWindow.java  |    7 +++----
 .../src/org/spearce/jgit/lib/ByteBufferWindow.java |    4 ++++
 .../src/org/spearce/jgit/lib/ByteWindow.java       |    2 ++
 .../src/org/spearce/jgit/lib/WindowCache.java      |   12 +++++++++++-
 .../src/org/spearce/jgit/lib/WindowedFile.java     |    1 +
 5 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteArrayWindow.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteArrayWindow.java
index 7b7c7a4..055a329 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteArrayWindow.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteArrayWindow.java
@@ -68,7 +68,6 @@ ByteArrayWindow(final WindowedFile o, final long p, final int d,
 	}
 
 	int copy(final byte[] array, final int p, final byte[] b, final int o, int n) {
-		ensureLoaded(array);
 		n = Math.min(array.length - p, n);
 		System.arraycopy(array, p, b, o, n);
 		return n;
@@ -76,7 +75,6 @@ int copy(final byte[] array, final int p, final byte[] b, final int o, int n) {
 
 	int inflate(final byte[] array, final int pos, final byte[] b, int o,
 			final Inflater inf) throws DataFormatException {
-		ensureLoaded(array);
 		while (!inf.finished()) {
 			if (inf.needsInput()) {
 				inf.setInput(array, pos, array.length - pos);
@@ -91,7 +89,6 @@ int inflate(final byte[] array, final int pos, final byte[] b, int o,
 
 	void inflateVerify(final byte[] array, final int pos, final Inflater inf)
 			throws DataFormatException {
-		ensureLoaded(array);
 		while (!inf.finished()) {
 			if (inf.needsInput()) {
 				inf.setInput(array, pos, array.length - pos);
@@ -103,12 +100,14 @@ void inflateVerify(final byte[] array, final int pos, final Inflater inf)
 			inf.inflate(verifyGarbageBuffer, 0, verifyGarbageBuffer.length);
 	}
 
-	private synchronized void ensureLoaded(final byte[] array) {
+	synchronized void ensureLoaded(final byte[] array) {
 		if (!loaded) {
 			try {
 				provider.fd.getChannel().read(ByteBuffer.wrap(array), start);
 			} catch (IOException e) {
 				throw new RuntimeException("Cannot fault in window", e);
+			} finally {
+				WindowCache.markLoaded(this);
 			}
 			loaded = true;
 		}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java
index a6757e8..5beb79b 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java
@@ -107,4 +107,8 @@ void inflateVerify(final ByteBuffer buffer, final int pos,
 		while (!inf.finished() && !inf.needsInput())
 			inf.inflate(verifyGarbageBuffer, 0, verifyGarbageBuffer.length);
 	}
+
+	void ensureLoaded(final ByteBuffer ref) {
+		// Do nothing.
+	}
 }
\ No newline at end of file
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteWindow.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteWindow.java
index 732664b..e957138 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteWindow.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteWindow.java
@@ -217,4 +217,6 @@ final void inflateVerify(T ref, long pos, Inflater inf)
 
 	abstract void inflateVerify(T ref, int pos, Inflater inf)
 			throws DataFormatException;
+
+	abstract void ensureLoaded(T ref);
 }
\ No newline at end of file
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/WindowCache.java b/org.spearce.jgit/src/org/spearce/jgit/lib/WindowCache.java
index 0b9d20c..fc4bab8 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/WindowCache.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/WindowCache.java
@@ -189,7 +189,13 @@ private static synchronized void reconfigureImpl(final int packedGitLimit,
 	 *             the window was not found in the cache and the given provider
 	 *             was unable to load the window on demand.
 	 */
-	public static synchronized final void get(final WindowCursor curs,
+	public static final void get(final WindowCursor curs,
+			final WindowedFile wp, final long position) throws IOException {
+		getImpl(curs, wp, position);
+		curs.window.ensureLoaded(curs.handle);
+	}
+
+	private static synchronized final void getImpl(final WindowCursor curs,
 			final WindowedFile wp, final long position) throws IOException {
 		final int id = (int) (position >> windowSizeShift);
 		final int idx = hash(wp, id);
@@ -254,6 +260,10 @@ public static synchronized final void get(final WindowCursor curs,
 		insertLRU(e);
 	}
 
+	static synchronized void markLoaded(final ByteWindow w) {
+		w.provider.openCount--;
+	}
+
 	private static void makeMostRecent(ByteWindow<?> e) {
 		if (lruHead != e) {
 			unlinkLRU(e);
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/WindowedFile.java b/org.spearce.jgit/src/org/spearce/jgit/lib/WindowedFile.java
index db8ea88..938f44c 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/WindowedFile.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/WindowedFile.java
@@ -348,5 +348,6 @@ void allocWindow(final WindowCursor curs, final int windowId,
 		final byte[] b = new byte[size];
 		curs.window = new ByteArrayWindow(this, pos, windowId, b);
 		curs.handle = b;
+		openCount++; // Until the window loads, we must stay open.
 	}
 }
-- 
1.6.2.185.g8b635

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux