You need to sign in or sign up before continuing.
提交 6d73e05f 编写于 作者: M martin

6631352: File{OutputStream,Writer} should implement atomic append mode using FILE_APPEND_DATA (win)

Reviewed-by: alanb, iris
上级 420617ce
...@@ -85,7 +85,6 @@ SUNWprivate_1.1 { ...@@ -85,7 +85,6 @@ SUNWprivate_1.1 {
Java_java_io_FileOutputStream_close0; Java_java_io_FileOutputStream_close0;
Java_java_io_FileOutputStream_initIDs; Java_java_io_FileOutputStream_initIDs;
Java_java_io_FileOutputStream_open; Java_java_io_FileOutputStream_open;
Java_java_io_FileOutputStream_openAppend;
Java_java_io_FileOutputStream_write; Java_java_io_FileOutputStream_write;
Java_java_io_FileOutputStream_writeBytes; Java_java_io_FileOutputStream_writeBytes;
Java_java_io_FileSystem_getFileSystem; Java_java_io_FileSystem_getFileSystem;
......
...@@ -58,8 +58,6 @@ class FileOutputStream extends OutputStream ...@@ -58,8 +58,6 @@ class FileOutputStream extends OutputStream
private FileChannel channel= null; private FileChannel channel= null;
private boolean append = false;
private final Object closeLock = new Object(); private final Object closeLock = new Object();
private volatile boolean closed = false; private volatile boolean closed = false;
private static final ThreadLocal<Boolean> runningFinalize = private static final ThreadLocal<Boolean> runningFinalize =
...@@ -200,12 +198,7 @@ class FileOutputStream extends OutputStream ...@@ -200,12 +198,7 @@ class FileOutputStream extends OutputStream
} }
fd = new FileDescriptor(); fd = new FileDescriptor();
fd.incrementAndGetUseCount(); fd.incrementAndGetUseCount();
this.append = append; open(name, append);
if (append) {
openAppend(name);
} else {
open(name);
}
} }
/** /**
...@@ -250,16 +243,12 @@ class FileOutputStream extends OutputStream ...@@ -250,16 +243,12 @@ class FileOutputStream extends OutputStream
} }
/** /**
* Opens a file, with the specified name, for writing. * Opens a file, with the specified name, for overwriting or appending.
* @param name name of file to be opened
*/
private native void open(String name) throws FileNotFoundException;
/**
* Opens a file, with the specified name, for appending.
* @param name name of file to be opened * @param name name of file to be opened
* @param append whether the file is to be opened in append mode
*/ */
private native void openAppend(String name) throws FileNotFoundException; private native void open(String name, boolean append)
throws FileNotFoundException;
/** /**
* Writes the specified byte to this file output stream. Implements * Writes the specified byte to this file output stream. Implements
...@@ -383,7 +372,7 @@ class FileOutputStream extends OutputStream ...@@ -383,7 +372,7 @@ class FileOutputStream extends OutputStream
public FileChannel getChannel() { public FileChannel getChannel() {
synchronized (this) { synchronized (this) {
if (channel == null) { if (channel == null) {
channel = FileChannelImpl.open(fd, false, true, this, append); channel = FileChannelImpl.open(fd, false, true, this);
/* /*
* Increment fd's use count. Invoking the channel's close() * Increment fd's use count. Invoking the channel's close()
......
...@@ -52,39 +52,37 @@ public class FileChannelImpl ...@@ -52,39 +52,37 @@ public class FileChannelImpl
{ {
// Used to make native read and write calls // Used to make native read and write calls
private static NativeDispatcher nd; private static final NativeDispatcher nd;
// Memory allocation size for mapping buffers // Memory allocation size for mapping buffers
private static long allocationGranularity; private static final long allocationGranularity;
// Cached field for MappedByteBuffer.isAMappedBuffer // Cached field for MappedByteBuffer.isAMappedBuffer
private static Field isAMappedBufferField; private static final Field isAMappedBufferField;
// File descriptor // File descriptor
private FileDescriptor fd; private final FileDescriptor fd;
// File access mode (immutable) // File access mode (immutable)
private boolean writable; private final boolean writable;
private boolean readable; private final boolean readable;
private boolean appending;
// Required to prevent finalization of creating stream (immutable) // Required to prevent finalization of creating stream (immutable)
private Object parent; private final Object parent;
// Thread-safe set of IDs of native threads, for signalling // Thread-safe set of IDs of native threads, for signalling
private NativeThreadSet threads = new NativeThreadSet(2); private final NativeThreadSet threads = new NativeThreadSet(2);
// Lock for operations involving position and size // Lock for operations involving position and size
private Object positionLock = new Object(); private final Object positionLock = new Object();
private FileChannelImpl(FileDescriptor fd, boolean readable, private FileChannelImpl(FileDescriptor fd, boolean readable,
boolean writable, Object parent, boolean append) boolean writable, Object parent)
{ {
this.fd = fd; this.fd = fd;
this.readable = readable; this.readable = readable;
this.writable = writable; this.writable = writable;
this.parent = parent; this.parent = parent;
this.appending = append;
} }
// Invoked by getChannel() methods // Invoked by getChannel() methods
...@@ -94,14 +92,7 @@ public class FileChannelImpl ...@@ -94,14 +92,7 @@ public class FileChannelImpl
boolean readable, boolean writable, boolean readable, boolean writable,
Object parent) Object parent)
{ {
return new FileChannelImpl(fd, readable, writable, parent, false); return new FileChannelImpl(fd, readable, writable, parent);
}
public static FileChannel open(FileDescriptor fd,
boolean readable, boolean writable,
Object parent, boolean append)
{
return new FileChannelImpl(fd, readable, writable, parent, append);
} }
private void ensureOpen() throws IOException { private void ensureOpen() throws IOException {
...@@ -134,15 +125,7 @@ public class FileChannelImpl ...@@ -134,15 +125,7 @@ public class FileChannelImpl
// superclass AbstractInterruptibleChannel, but the isOpen logic in // superclass AbstractInterruptibleChannel, but the isOpen logic in
// that method will prevent this method from being reinvoked. // that method will prevent this method from being reinvoked.
// //
if (parent instanceof FileInputStream) ((java.io.Closeable)parent).close();
((FileInputStream)parent).close();
else if (parent instanceof FileOutputStream)
((FileOutputStream)parent).close();
else if (parent instanceof RandomAccessFile)
((RandomAccessFile)parent).close();
else
assert false;
} else { } else {
nd.close(fd); nd.close(fd);
} }
...@@ -218,8 +201,6 @@ public class FileChannelImpl ...@@ -218,8 +201,6 @@ public class FileChannelImpl
if (!isOpen()) if (!isOpen())
return 0; return 0;
ti = threads.add(); ti = threads.add();
if (appending)
position(size());
do { do {
n = IOUtil.write(fd, src, -1, nd, positionLock); n = IOUtil.write(fd, src, -1, nd, positionLock);
} while ((n == IOStatus.INTERRUPTED) && isOpen()); } while ((n == IOStatus.INTERRUPTED) && isOpen());
...@@ -244,8 +225,6 @@ public class FileChannelImpl ...@@ -244,8 +225,6 @@ public class FileChannelImpl
if (!isOpen()) if (!isOpen())
return 0; return 0;
ti = threads.add(); ti = threads.add();
if (appending)
position(size());
do { do {
n = IOUtil.write(fd, srcs, nd); n = IOUtil.write(fd, srcs, nd);
} while ((n == IOStatus.INTERRUPTED) && isOpen()); } while ((n == IOStatus.INTERRUPTED) && isOpen());
...@@ -1051,7 +1030,7 @@ public class FileChannelImpl ...@@ -1051,7 +1030,7 @@ public class FileChannelImpl
private FileKey fileKey; private FileKey fileKey;
FileLockReference(FileLock referent, FileLockReference(FileLock referent,
ReferenceQueue queue, ReferenceQueue<FileLock> queue,
FileKey key) { FileKey key) {
super(referent, queue); super(referent, queue);
this.fileKey = key; this.fileKey = key;
...@@ -1073,7 +1052,7 @@ public class FileChannelImpl ...@@ -1073,7 +1052,7 @@ public class FileChannelImpl
new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>(); new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>();
// reference queue for cleared refs // reference queue for cleared refs
private static ReferenceQueue queue = new ReferenceQueue(); private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
// the enclosing file channel // the enclosing file channel
private FileChannelImpl fci; private FileChannelImpl fci;
......
...@@ -95,7 +95,7 @@ readBytes(JNIEnv *env, jobject this, jbyteArray bytes, ...@@ -95,7 +95,7 @@ readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
fd = GET_FD(this, fid); fd = GET_FD(this, fid);
if (fd == -1) { if (fd == -1) {
JNU_ThrowIOException(env, "Stream Closed"); JNU_ThrowIOException(env, "Stream Closed");
return -1; return -1;
} }
nread = IO_Read(fd, buf, len); nread = IO_Read(fd, buf, len);
......
...@@ -53,13 +53,10 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fdClass) { ...@@ -53,13 +53,10 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fdClass) {
*/ */
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, jstring path) { Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_TRUNC); jstring path, jboolean append) {
} fileOpen(env, this, path, fos_fd,
O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC));
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_openAppend(JNIEnv *env, jobject this, jstring path) {
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_APPEND);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
......
...@@ -39,8 +39,6 @@ ...@@ -39,8 +39,6 @@
jfieldID fos_fd; /* id for jobject 'fd' in java.io.FileOutputStream */ jfieldID fos_fd; /* id for jobject 'fd' in java.io.FileOutputStream */
jfieldID fos_append;
/************************************************************** /**************************************************************
* static methods to store field ID's in initializers * static methods to store field ID's in initializers
*/ */
...@@ -49,7 +47,6 @@ JNIEXPORT void JNICALL ...@@ -49,7 +47,6 @@ JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) { Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) {
fos_fd = fos_fd =
(*env)->GetFieldID(env, fosClass, "fd", "Ljava/io/FileDescriptor;"); (*env)->GetFieldID(env, fosClass, "fd", "Ljava/io/FileDescriptor;");
fos_append = (*env)->GetFieldID(env, fosClass, "append", "Z");
} }
/************************************************************** /**************************************************************
...@@ -57,45 +54,20 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) { ...@@ -57,45 +54,20 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) {
*/ */
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, jstring path) { Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_TRUNC); jstring path, jboolean append) {
} fileOpen(env, this, path, fos_fd,
O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC));
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_openAppend(JNIEnv *env, jobject this, jstring path) {
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_APPEND);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) { Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) {
jboolean append = (*env)->GetBooleanField(env, this, fos_append);
FD fd = GET_FD(this, fos_fd);
if (fd == -1) {
JNU_ThrowIOException(env, "Stream Closed");
return;
}
if (append == JNI_TRUE) {
if (IO_Lseek(fd, 0L, SEEK_END) == -1) {
JNU_ThrowIOExceptionWithLastError(env, "Append failed");
}
}
writeSingle(env, this, byte, fos_fd); writeSingle(env, this, byte, fos_fd);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env, Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
jobject this, jbyteArray bytes, jint off, jint len) { jobject this, jbyteArray bytes, jint off, jint len) {
jboolean append = (*env)->GetBooleanField(env, this, fos_append);
FD fd = GET_FD(this, fos_fd);
if (fd == -1) {
JNU_ThrowIOException(env, "Stream Closed");
return;
}
if (append == JNI_TRUE) {
if (IO_Lseek(fd, 0L, SEEK_END) == -1) {
JNU_ThrowIOExceptionWithLastError(env, "Append failed");
}
}
writeBytes(env, this, bytes, off, len, fos_fd); writeBytes(env, this, bytes, off, len, fos_fd);
} }
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
extern jboolean onNT = JNI_FALSE; extern jboolean onNT = JNI_FALSE;
static int MAX_INPUT_EVENTS = 2000; static DWORD MAX_INPUT_EVENTS = 2000;
void void
initializeWindowsVersion() { initializeWindowsVersion() {
...@@ -190,9 +190,16 @@ pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) { ...@@ -190,9 +190,16 @@ pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) {
jlong jlong
winFileHandleOpen(JNIEnv *env, jstring path, int flags) winFileHandleOpen(JNIEnv *env, jstring path, int flags)
{ {
/* To implement O_APPEND, we use the strategy from
http://msdn2.microsoft.com/en-us/library/aa363858.aspx
"You can get atomic append by opening a file with
FILE_APPEND_DATA access and _without_ FILE_WRITE_DATA access.
If you do this then all writes will ignore the current file
pointer and be done at the end-of file." */
const DWORD access = const DWORD access =
(flags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) : (flags & O_APPEND) ? (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA) :
(flags & O_WRONLY) ? GENERIC_WRITE : (flags & O_WRONLY) ? GENERIC_WRITE :
(flags & O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) :
GENERIC_READ; GENERIC_READ;
const DWORD sharing = const DWORD sharing =
FILE_SHARE_READ | FILE_SHARE_WRITE; FILE_SHARE_READ | FILE_SHARE_WRITE;
...@@ -495,7 +502,7 @@ handleClose(JNIEnv *env, jobject this, jfieldID fid) ...@@ -495,7 +502,7 @@ handleClose(JNIEnv *env, jobject this, jfieldID fid)
FD fd = GET_FD(this, fid); FD fd = GET_FD(this, fid);
HANDLE h = (HANDLE)fd; HANDLE h = (HANDLE)fd;
if (fd == INVALID_HANDLE_VALUE) { if (h == INVALID_HANDLE_VALUE) {
return 0; return 0;
} }
......
/*
* Copyright 2007 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/*
* @test
* @bug 6631352
* @summary Check that appends are atomic
*/
import java.io.File;
import java.io.FileOutputStream;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
public class AtomicAppend {
// Before the fix for
// 6631352: Implement atomic append mode using FILE_APPEND_DATA (win)
// this would fail intermittently on windows
void test(String[] args) throws Throwable {
final int nThreads = 10;
final int writes = 1000;
final File file = new File("foo");
file.delete();
try {
final ExecutorService es = Executors.newFixedThreadPool(nThreads);
for (int i = 0; i < nThreads; i++)
es.execute(new Runnable() { public void run() {
try {
FileOutputStream s = new FileOutputStream(file, true);
for (int j = 0; j < 1000; j++) {
s.write((int) 'x');
s.flush();
}
s.close();
} catch (Throwable t) { unexpected(t); }}});
es.shutdown();
es.awaitTermination(10L, TimeUnit.MINUTES);
equal(file.length(), (long) (nThreads * writes));
} finally {
file.delete();
}
}
//--------------------- Infrastructure ---------------------------
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
new AtomicAppend().instanceMain(args);}
void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册