JDK-6816049 : (bf) MappedByteBuffer.force() method does not flush data correctly
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2009-03-11
  • Updated: 2016-07-03
  • Resolved: 2011-03-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b118Fixed
Description
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;
            }
        }
    }
}

Comments
EVALUATION http://hg.openjdk.java.net/jdk7/build/jdk/rev/3740c2da7cc5
04-12-2010

EVALUATION We've talked to Microsoft about this one, and have proposed that a future release of Windows should provide the equivalent of msync with MS_SYNC semantics. In mean-time the only way we can fix this is for the mapping to keep own handle to the file.
20-10-2010

EVALUATION We will likely need to contact Microsoft to get clarification on exactly when FlushViewOfFile returns. The reason we aren't using FlushFileBuffers is that the mapping is not associated with the FileChannel that created it. That is, the FileChannel can be closed so there isn't a handle available to use FlushFileBuffers as suggested. It could be done for cases where the FileChannel is still open but it would be an incomplete solution.
11-03-2009