-
Bug
-
Resolution: Fixed
-
P3
-
6
-
b118
-
x86
-
windows_xp
-
Verified
OPERATING SYSTEM(S):
Windows
FULL JDK VERSION(S):
All 5.0 and 6 JDKs
DESCRIPTION:
1. Compile the provided testcase ForceFlushTest.java
2. Start Filemon.exe utility (Can be downloaded from
http://technet.microsoft.com/en-us/sysinternals/bb896642.aspx)
3. Run "java ForceFlushTest 1" to create a file and use
MappedByteBuffer.force() to ensure all data is written to it.
4. Observe that there is no call to FLUSH in the filemon output
5. Run "java ForceFlushTest 2" to create a file and use
FileChannel.force() to ensure all data is written to it.
6. Observe that there *is* a call to FLUSH in the filemon output
The behaviour of MappedByteBuffer.force() does not tally with its Javadoc, which states:
"Forces any changes made to this buffer's content to be written to the
storage device containing the mapped file. If the file mapped into
this buffer resides on a local storage device then when this method
returns it is guaranteed that all changes made to the buffer since it
was created, or since this method was last invoked, will have been
written to that device."
The problem seems to be that the implementation for MappedByteBuffer.force() on Windows does not follow the Windows API documentation, which states:
"To flush all the dirty pages plus the metadata for the file and ensure
that they are physically written to disk, call FlushViewOfFile and
then call the FlushFileBuffers function."
The MappedByteBuffer.force() implementation calls FlushViewOfFile(), but does not call FlushFileBuffers() immediately afterwards.
This could cause serious problems with applications that require content to be written to files to guarantee consistency in the event of power failures etc.
TESTCASE SOURCE:
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.Arrays;
public class ForceFlushTest {
private static final String filename = "c:\\temp\\file1";
public static void main(String[] args) throws Exception {
if (args.length != 1 || 1 > Integer.parseInt(args[0]) || Integer.parseInt(args[0]) > 2 ) {
System.out.println("Usage: java ForceFlushTest 1|2");
System.out.println("Where: 1 = use MappedByteBuffer.force(), 2 = use FileChannel.force()");
System.exit(1);
}
int mode = Integer.parseInt(args[0]);
ByteBuffer buff = ByteBuffer.allocate(4096);
Arrays.fill(buff.array(), (byte)' ');
RandomAccessFile mapFile = new RandomAccessFile(filename, "rw");
FileChannel mapChl = mapFile.getChannel();
MappedByteBuffer mappedBuffer = mapChl.map(MapMode.READ_WRITE, 0, 1024*1024);
byte[] writeBuff = new byte[1024];
for (int i = 0; i < writeBuff.length; i++) {
writeBuff[i] = (byte)('a' + i%26);
}
for (int i = 0; i<1024; i++) {
mappedBuffer.rewind();
mappedBuffer.put(writeBuff);
switch (mode) {
case 1:
mappedBuffer.force(); // Doesn't cause a FLUSH
break;
case 2:
mapChl.force(false); // Does cause a FLUSH
break;
}
}
}
}
Windows
FULL JDK VERSION(S):
All 5.0 and 6 JDKs
DESCRIPTION:
1. Compile the provided testcase ForceFlushTest.java
2. Start Filemon.exe utility (Can be downloaded from
http://technet.microsoft.com/en-us/sysinternals/bb896642.aspx)
3. Run "java ForceFlushTest 1" to create a file and use
MappedByteBuffer.force() to ensure all data is written to it.
4. Observe that there is no call to FLUSH in the filemon output
5. Run "java ForceFlushTest 2" to create a file and use
FileChannel.force() to ensure all data is written to it.
6. Observe that there *is* a call to FLUSH in the filemon output
The behaviour of MappedByteBuffer.force() does not tally with its Javadoc, which states:
"Forces any changes made to this buffer's content to be written to the
storage device containing the mapped file. If the file mapped into
this buffer resides on a local storage device then when this method
returns it is guaranteed that all changes made to the buffer since it
was created, or since this method was last invoked, will have been
written to that device."
The problem seems to be that the implementation for MappedByteBuffer.force() on Windows does not follow the Windows API documentation, which states:
"To flush all the dirty pages plus the metadata for the file and ensure
that they are physically written to disk, call FlushViewOfFile and
then call the FlushFileBuffers function."
The MappedByteBuffer.force() implementation calls FlushViewOfFile(), but does not call FlushFileBuffers() immediately afterwards.
This could cause serious problems with applications that require content to be written to files to guarantee consistency in the event of power failures etc.
TESTCASE SOURCE:
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.Arrays;
public class ForceFlushTest {
private static final String filename = "c:\\temp\\file1";
public static void main(String[] args) throws Exception {
if (args.length != 1 || 1 > Integer.parseInt(args[0]) || Integer.parseInt(args[0]) > 2 ) {
System.out.println("Usage: java ForceFlushTest 1|2");
System.out.println("Where: 1 = use MappedByteBuffer.force(), 2 = use FileChannel.force()");
System.exit(1);
}
int mode = Integer.parseInt(args[0]);
ByteBuffer buff = ByteBuffer.allocate(4096);
Arrays.fill(buff.array(), (byte)' ');
RandomAccessFile mapFile = new RandomAccessFile(filename, "rw");
FileChannel mapChl = mapFile.getChannel();
MappedByteBuffer mappedBuffer = mapChl.map(MapMode.READ_WRITE, 0, 1024*1024);
byte[] writeBuff = new byte[1024];
for (int i = 0; i < writeBuff.length; i++) {
writeBuff[i] = (byte)('a' + i%26);
}
for (int i = 0; i<1024; i++) {
mappedBuffer.rewind();
mappedBuffer.put(writeBuff);
switch (mode) {
case 1:
mappedBuffer.force(); // Doesn't cause a FLUSH
break;
case 2:
mapChl.force(false); // Does cause a FLUSH
break;
}
}
}
}