--- a/jdk/src/share/classes/java/lang/ref/Reference.java Mon May 27 14:00:26 2013 +0100
+++ b/jdk/src/share/classes/java/lang/ref/Reference.java Mon May 27 15:24:53 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -138,8 +138,23 @@
pending = r.discovered;
r.discovered = null;
} else {
+ // The waiting on the lock may cause an OOME because it may try to allocate
+ // exception objects, so also catch OOME here to avoid silent exit of the
+ // reference handler thread.
+ //
+ // Explicitly define the order of the two exceptions we catch here
+ // when waiting for the lock.
+ //
+ // We do not want to try to potentially load the InterruptedException class
+ // (which would be done if this was its first use, and InterruptedException
+ // were checked first) in this situation.
+ //
+ // This may lead to the VM not ever trying to load the InterruptedException
+ // class again.
try {
- lock.wait();
+ try {
+ lock.wait();
+ } catch (OutOfMemoryError x) { }
} catch (InterruptedException x) { }
continue;
}
--- a/jdk/src/windows/classes/sun/nio/fs/WindowsConstants.java Mon May 27 14:00:26 2013 +0100
+++ b/jdk/src/windows/classes/sun/nio/fs/WindowsConstants.java Mon May 27 15:24:53 2013 +0100
@@ -100,6 +100,7 @@
public static final int ERROR_INVALID_LEVEL = 124;
public static final int ERROR_DIR_NOT_EMPTY = 145;
public static final int ERROR_ALREADY_EXISTS = 183;
+ public static final int ERROR_MORE_DATA = 234;
public static final int ERROR_DIRECTORY = 267;
public static final int ERROR_NOTIFY_ENUM_DIR = 1022;
public static final int ERROR_NONE_MAPPED = 1332;
--- a/jdk/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java Mon May 27 14:00:26 2013 +0100
+++ b/jdk/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java Mon May 27 15:24:53 2013 +0100
@@ -973,19 +973,19 @@
* HANDLE CreateIoCompletionPort (
* HANDLE FileHandle,
* HANDLE ExistingCompletionPort,
- * DWORD CompletionKey,
+ * ULONG_PTR CompletionKey,
* DWORD NumberOfConcurrentThreads
* )
*/
static native long CreateIoCompletionPort(long fileHandle, long existingPort,
- int completionKey) throws WindowsException;
+ long completionKey) throws WindowsException;
/**
* GetQueuedCompletionStatus(
* HANDLE CompletionPort,
* LPDWORD lpNumberOfBytesTransferred,
- * LPDWORD lpCompletionKey,
+ * PULONG_PTR lpCompletionKey,
* LPOVERLAPPED *lpOverlapped,
* DWORD dwMilliseconds
*/
@@ -999,12 +999,12 @@
static class CompletionStatus {
private int error;
private int bytesTransferred;
- private int completionKey;
+ private long completionKey;
private CompletionStatus() { }
int error() { return error; }
int bytesTransferred() { return bytesTransferred; }
- int completionKey() { return completionKey; }
+ long completionKey() { return completionKey; }
}
private static native void GetQueuedCompletionStatus0(long completionPort,
CompletionStatus status) throws WindowsException;
@@ -1013,12 +1013,12 @@
* PostQueuedCompletionStatus(
* HANDLE CompletionPort,
* DWORD dwNumberOfBytesTransferred,
- * DWORD dwCompletionKey,
+ * ULONG_PTR dwCompletionKey,
* LPOVERLAPPED lpOverlapped
* )
*/
static native void PostQueuedCompletionStatus(long completionPort,
- int completionKey) throws WindowsException;
+ long completionKey) throws WindowsException;
/**
* ReadDirectoryChangesW(
--- a/jdk/src/windows/classes/sun/nio/fs/WindowsWatchService.java Mon May 27 14:00:26 2013 +0100
+++ b/jdk/src/windows/classes/sun/nio/fs/WindowsWatchService.java Mon May 27 15:24:53 2013 +0100
@@ -41,6 +41,7 @@
class WindowsWatchService
extends AbstractWatchService
{
+ private final static int WAKEUP_COMPLETION_KEY = 0;
private final Unsafe unsafe = Unsafe.getUnsafe();
// background thread to service I/O completion port
@@ -83,7 +84,7 @@
*/
private class WindowsWatchKey extends AbstractWatchKey {
// file key (used to detect existing registrations)
- private FileKey fileKey;
+ private final FileKey fileKey;
// handle to directory
private volatile long handle = INVALID_HANDLE_VALUE;
@@ -223,8 +224,7 @@
FileKey other = (FileKey)obj;
if (this.volSerialNumber != other.volSerialNumber) return false;
if (this.fileIndexHigh != other.fileIndexHigh) return false;
- if (this.fileIndexLow != other.fileIndexLow) return false;
- return true;
+ return this.fileIndexLow == other.fileIndexLow;
}
}
@@ -268,6 +268,7 @@
private static final short OFFSETOF_FILENAME = 12;
// size of per-directory buffer for events (FIXME - make this configurable)
+ // Need to be less than 4*16384 = 65536. DWORD align.
private static final int CHANGES_BUFFER_SIZE = 16 * 1024;
private final WindowsFileSystem fs;
@@ -275,27 +276,28 @@
private final long port;
// maps completion key to WatchKey
- private final Map<Integer,WindowsWatchKey> int2key;
+ private final Map<Integer,WindowsWatchKey> ck2key;
// maps file key to WatchKey
private final Map<FileKey,WindowsWatchKey> fk2key;
// unique completion key for each directory
+ // native completion key capacity is 64 bits on Win64.
private int lastCompletionKey;
Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) {
this.fs = fs;
this.watcher = watcher;
this.port = port;
- this.int2key = new HashMap<Integer,WindowsWatchKey>();
- this.fk2key = new HashMap<FileKey,WindowsWatchKey>();
+ this.ck2key = new HashMap<>();
+ this.fk2key = new HashMap<>();
this.lastCompletionKey = 0;
}
@Override
void wakeup() throws IOException {
try {
- PostQueuedCompletionStatus(port, 0);
+ PostQueuedCompletionStatus(port, WAKEUP_COMPLETION_KEY);
} catch (WindowsException x) {
throw new IOException(x.getMessage());
}
@@ -322,7 +324,6 @@
for (WatchEvent.Modifier modifier: modifiers) {
if (modifier == ExtendedWatchEventModifier.FILE_TREE) {
watchSubtree = true;
- continue;
} else {
if (modifier == null)
return new NullPointerException();
@@ -333,7 +334,7 @@
}
// open directory
- long handle = -1L;
+ long handle;
try {
handle = CreateFile(dir.getPathForWin32Calls(),
FILE_LIST_DIRECTORY,
@@ -347,7 +348,7 @@
boolean registered = false;
try {
// read attributes and check file is a directory
- WindowsFileAttributes attrs = null;
+ WindowsFileAttributes attrs;
try {
attrs = WindowsFileAttributes.readAttributes(handle);
} catch (WindowsException x) {
@@ -370,9 +371,10 @@
return existing;
}
- // unique completion key (skip 0)
+ // Can overflow the int type capacity.
+ // Skip WAKEUP_COMPLETION_KEY value.
int completionKey = ++lastCompletionKey;
- if (completionKey == 0)
+ if (completionKey == WAKEUP_COMPLETION_KEY)
completionKey = ++lastCompletionKey;
// associate handle with completion port
@@ -418,13 +420,13 @@
// 1. remove mapping from old completion key to existing watch key
// 2. release existing key's resources (handle/buffer)
// 3. re-initialize key with new handle/buffer
- int2key.remove(existing.completionKey());
+ ck2key.remove(existing.completionKey());
existing.releaseResources();
watchKey = existing.init(handle, events, watchSubtree, buffer,
countAddress, overlappedAddress, completionKey);
}
// map completion map to watch key
- int2key.put(completionKey, watchKey);
+ ck2key.put(completionKey, watchKey);
registered = true;
return watchKey;
@@ -440,7 +442,7 @@
WindowsWatchKey key = (WindowsWatchKey)obj;
if (key.isValid()) {
fk2key.remove(key.fileKey());
- int2key.remove(key.completionKey());
+ ck2key.remove(key.completionKey());
key.invalidate();
}
}
@@ -449,11 +451,11 @@
@Override
void implCloseAll() {
// cancel all keys
- for (Map.Entry<Integer,WindowsWatchKey> entry: int2key.entrySet()) {
+ for (Map.Entry<Integer, WindowsWatchKey> entry: ck2key.entrySet()) {
entry.getValue().invalidate();
}
fk2key.clear();
- int2key.clear();
+ ck2key.clear();
// close I/O completion port
CloseHandle(port);
@@ -517,7 +519,7 @@
@Override
public void run() {
for (;;) {
- CompletionStatus info = null;
+ CompletionStatus info;
try {
info = GetQueuedCompletionStatus(port);
} catch (WindowsException x) {
@@ -527,7 +529,7 @@
}
// wakeup
- if (info.completionKey() == 0) {
+ if (info.completionKey() == WAKEUP_COMPLETION_KEY) {
boolean shutdown = processRequests();
if (shutdown) {
return;
@@ -536,7 +538,7 @@
}
// map completionKey to get WatchKey
- WindowsWatchKey key = int2key.get(info.completionKey());
+ WindowsWatchKey key = ck2key.get((int)info.completionKey());
if (key == null) {
// We get here when a registration is changed. In that case
// the directory is closed which causes an event with the
@@ -544,38 +546,44 @@
continue;
}
- // ReadDirectoryChangesW failed
- if (info.error() != 0) {
+ boolean criticalError = false;
+ int errorCode = info.error();
+ int messageSize = info.bytesTransferred();
+ if (errorCode == ERROR_NOTIFY_ENUM_DIR) {
// buffer overflow
- if (info.error() == ERROR_NOTIFY_ENUM_DIR) {
- key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
- } else {
- // other error so cancel key
- implCancelKey(key);
- key.signal();
- }
- continue;
- }
+ key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
+ } else if (errorCode != 0 && errorCode != ERROR_MORE_DATA) {
+ // ReadDirectoryChangesW failed
+ criticalError = true;
+ } else {
+ // ERROR_MORE_DATA is a warning about incomplite
+ // data transfer over TCP/UDP stack. For the case
+ // [messageSize] is zero in the most of cases.
- // process the events
- if (info.bytesTransferred() > 0) {
- processEvents(key, info.bytesTransferred());
- } else {
- // insufficient buffer size
- key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
- }
+ if (messageSize > 0) {
+ // process non-empty events.
+ processEvents(key, messageSize);
+ } else if (errorCode == 0) {
+ // insufficient buffer size
+ // not described, but can happen.
+ key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
+ }
- // start read for next batch of changes
- try {
- ReadDirectoryChangesW(key.handle(),
- key.buffer().address(),
- CHANGES_BUFFER_SIZE,
- key.watchSubtree(),
- ALL_FILE_NOTIFY_EVENTS,
- key.countAddress(),
- key.overlappedAddress());
- } catch (WindowsException x) {
- // no choice but to cancel key
+ // start read for next batch of changes
+ try {
+ ReadDirectoryChangesW(key.handle(),
+ key.buffer().address(),
+ CHANGES_BUFFER_SIZE,
+ key.watchSubtree(),
+ ALL_FILE_NOTIFY_EVENTS,
+ key.countAddress(),
+ key.overlappedAddress());
+ } catch (WindowsException x) {
+ // no choice but to cancel key
+ criticalError = true;
+ }
+ }
+ if (criticalError) {
implCancelKey(key);
key.signal();
}
--- a/jdk/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c Mon May 27 14:00:26 2013 +0100
+++ b/jdk/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c Mon May 27 15:24:53 2013 +0100
@@ -162,7 +162,7 @@
}
completionStatus_error = (*env)->GetFieldID(env, clazz, "error", "I");
completionStatus_bytesTransferred = (*env)->GetFieldID(env, clazz, "bytesTransferred", "I");
- completionStatus_completionKey = (*env)->GetFieldID(env, clazz, "completionKey", "I");
+ completionStatus_completionKey = (*env)->GetFieldID(env, clazz, "completionKey", "J");
clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$BackupResult");
if (clazz == NULL) {
@@ -1169,12 +1169,11 @@
JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_CreateIoCompletionPort(JNIEnv* env, jclass this,
- jlong fileHandle, jlong existingPort, jint completionKey)
+ jlong fileHandle, jlong existingPort, jlong completionKey)
{
- ULONG_PTR ck = completionKey;
HANDLE port = CreateIoCompletionPort((HANDLE)jlong_to_ptr(fileHandle),
(HANDLE)jlong_to_ptr(existingPort),
- ck,
+ (ULONG_PTR)completionKey,
0);
if (port == NULL) {
throwWindowsException(env, GetLastError());
@@ -1203,21 +1202,20 @@
(*env)->SetIntField(env, obj, completionStatus_error, ioResult);
(*env)->SetIntField(env, obj, completionStatus_bytesTransferred,
(jint)bytesTransferred);
- (*env)->SetIntField(env, obj, completionStatus_completionKey,
- (jint)completionKey);
-
+ (*env)->SetLongField(env, obj, completionStatus_completionKey,
+ (jlong)completionKey);
}
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_PostQueuedCompletionStatus(JNIEnv* env, jclass this,
- jlong completionPort, jint completionKey)
+ jlong completionPort, jlong completionKey)
{
BOOL res;
res = PostQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort),
(DWORD)0, /* dwNumberOfBytesTransferred */
- (DWORD)completionKey,
+ (ULONG_PTR)completionKey,
NULL); /* lpOverlapped */
if (res == 0) {
throwWindowsException(env, GetLastError());
@@ -1232,7 +1230,17 @@
BOOL res;
BOOL subtree = (watchSubTree == JNI_TRUE) ? TRUE : FALSE;
- ((LPOVERLAPPED)jlong_to_ptr(pOverlapped))->hEvent = NULL;
+ /* Any unused members of [OVERLAPPED] structure should always be initialized to zero
+ before the structure is used in a function call.
+ Otherwise, the function may fail and return ERROR_INVALID_PARAMETER.
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms684342%28v=vs.85%29.aspx
+
+ The [Offset] and [OffsetHigh] members of this structure are not used.
+ http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465%28v=vs.85%29.aspx
+
+ [hEvent] should be zero, other fields are the return values. */
+ ZeroMemory((LPOVERLAPPED)jlong_to_ptr(pOverlapped), sizeof(OVERLAPPED));
+
res = ReadDirectoryChangesW((HANDLE)jlong_to_ptr(hDirectory),
(LPVOID)jlong_to_ptr(bufferAddress),
(DWORD)bufferLength,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ref/OOMEInReferenceHandler.java Mon May 27 15:24:53 2013 +0100
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 7038914
+ * @summary Verify that the reference handler does not die after an OOME allocating the InterruptedException object
+ * @run main/othervm -Xmx16M -XX:-UseTLAB OOMEInReferenceHandler
+ * @author peter.levart@gmail.com
+ */
+
+import java.lang.ref.*;
+
+public class OOMEInReferenceHandler {
+ static Object[] fillHeap() {
+ Object[] first = null, last = null;
+ int size = 1 << 20;
+ while (size > 0) {
+ try {
+ Object[] array = new Object[size];
+ if (first == null) {
+ first = array;
+ } else {
+ last[0] = array;
+ }
+ last = array;
+ } catch (OutOfMemoryError oome) {
+ size = size >>> 1;
+ }
+ }
+ return first;
+ }
+
+ public static void main(String[] args) throws Exception {
+ // preinitialize the InterruptedException class so that the reference handler
+ // does not die due to OOME when loading the class if it is the first use
+ InterruptedException ie = new InterruptedException("dummy");
+
+ ThreadGroup tg = Thread.currentThread().getThreadGroup();
+ for (
+ ThreadGroup tgn = tg;
+ tgn != null;
+ tg = tgn, tgn = tg.getParent()
+ )
+ ;
+
+ Thread[] threads = new Thread[tg.activeCount()];
+ Thread referenceHandlerThread = null;
+ int n = tg.enumerate(threads);
+ for (int i = 0; i < n; i++) {
+ if ("Reference Handler".equals(threads[i].getName())) {
+ referenceHandlerThread = threads[i];
+ }
+ }
+
+ if (referenceHandlerThread == null) {
+ throw new IllegalStateException("Couldn't find Reference Handler thread.");
+ }
+
+ ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
+ Object referent = new Object();
+ WeakReference<Object> weakRef = new WeakReference<>(referent, refQueue);
+
+ Object waste = fillHeap();
+
+ referenceHandlerThread.interrupt();
+
+ // allow referenceHandlerThread some time to throw OOME
+ Thread.sleep(500L);
+
+ // release waste & referent
+ waste = null;
+ referent = null;
+
+ // wait at most 10 seconds for success or failure
+ for (int i = 0; i < 20; i++) {
+ if (refQueue.poll() != null) {
+ // Reference Handler thread still working -> success
+ return;
+ }
+ System.gc();
+ Thread.sleep(500L); // wait a little to allow GC to do it's work before allocating objects
+ if (!referenceHandlerThread.isAlive()) {
+ // Reference Handler thread died -> failure
+ throw new Exception("Reference Handler thread died.");
+ }
+ }
+
+ // no sure answer after 10 seconds
+ throw new IllegalStateException("Reference Handler thread stuck.");
+ }
+}