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; } } } }
|