Skip to content

Commit

Permalink
4833719: (bf) Views of MappedByteBuffers are not MappedByteBuffers, a…
Browse files Browse the repository at this point in the history
…nd cannot be forced

Reviewed-by: adinn
  • Loading branch information
Brian Burkhalter committed Mar 25, 2021
1 parent 8307aa6 commit b006f22
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 13 deletions.
57 changes: 46 additions & 11 deletions src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
Original file line number Diff line number Diff line change
Expand Up @@ -198,19 +198,30 @@ class Direct$Type$Buffer$RW$$BO$
// For duplicates and slices
//
Direct$Type$Buffer$RW$$BO$(DirectBuffer db, // package-private
int mark, int pos, int lim, int cap,
int off, MemorySegmentProxy segment)
int mark, int pos, int lim, int cap, int off,
#if[byte]
FileDescriptor fd, boolean isSync,
#end[byte]
MemorySegmentProxy segment)
{
#if[rw]
super(mark, pos, lim, cap, segment);
super(mark, pos, lim, cap,
#if[byte]
fd, isSync,
#end[byte]
segment);
address = ((Buffer)db).address + off;
#if[byte]
cleaner = null;
#end[byte]
Object attachment = db.attachment();
att = (attachment == null ? db : attachment);
#else[rw]
super(db, mark, pos, lim, cap, off, segment);
super(db, mark, pos, lim, cap, off,
#if[byte]
fd, isSync,
#end[byte]
segment);
this.isReadOnly = true;
#end[rw]
}
Expand All @@ -220,34 +231,53 @@ class Direct$Type$Buffer$RW$$BO$
return null;
}

public $Type$Buffer slice() {
public {#if[byte]?Mapped$Type$Buffer:$Type$Buffer} slice() {
int pos = this.position();
int lim = this.limit();
int rem = (pos <= lim ? lim - pos : 0);
int off = (pos << $LG_BYTES_PER_VALUE$);
assert (off >= 0);
return new Direct$Type$Buffer$RW$$BO$(this, -1, 0, rem, rem, off, segment);
return new Direct$Type$Buffer$RW$$BO$(this,
-1,
0,
rem,
rem,
off,
#if[byte]
fileDescriptor(),
isSync(),
#end[byte]
segment);
}

@Override
public $Type$Buffer slice(int index, int length) {
public {#if[byte]?Mapped$Type$Buffer:$Type$Buffer} slice(int index, int length) {
Objects.checkFromIndexSize(index, length, limit());
return new Direct$Type$Buffer$RW$$BO$(this,
-1,
0,
length,
length,
index << $LG_BYTES_PER_VALUE$,
#if[byte]
fileDescriptor(),
isSync(),
#end[byte]
segment);
}

public $Type$Buffer duplicate() {
public {#if[byte]?Mapped$Type$Buffer:$Type$Buffer} duplicate() {
return new Direct$Type$Buffer$RW$$BO$(this,
this.markValue(),
this.position(),
this.limit(),
this.capacity(),
0, segment);
0,
#if[byte]
fileDescriptor(),
isSync(),
#end[byte]
segment);
}

public $Type$Buffer asReadOnlyBuffer() {
Expand All @@ -257,7 +287,12 @@ class Direct$Type$Buffer$RW$$BO$
this.position(),
this.limit(),
this.capacity(),
0, segment);
0,
#if[byte]
fileDescriptor(),
isSync(),
#end[byte]
segment);
#else[rw]
return duplicate();
#end[rw]
Expand Down Expand Up @@ -506,7 +541,7 @@ class Direct$Type$Buffer$RW$$BO$
#end[rw]
}

public $Type$Buffer compact() {
public {#if[byte]?Mapped$Type$Buffer:$Type$Buffer} compact() {
#if[rw]
int pos = position();
int lim = limit();
Expand Down
54 changes: 52 additions & 2 deletions src/java.base/share/classes/java/nio/MappedByteBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,20 @@ public void unmap() {
* @return true if the file was mapped using one of the sync map
* modes, otherwise false.
*/
private boolean isSync() {
final boolean isSync() { // package-private
return isSync;
}

/**
* Returns the {@code FileDescriptor} associated with this
* {@code MappedByteBuffer}.
*
* @return the buffer's file descriptor; may be {@code null}
*/
final FileDescriptor fileDescriptor() { // package-private
return fd;
}

/**
* Tells whether or not this buffer's content is resident in physical
* memory.
Expand Down Expand Up @@ -205,7 +215,10 @@ public final MappedByteBuffer load() {

/**
* Forces any changes made to this buffer's content to be written to the
* storage device containing the mapped file.
* storage device containing the mapped file. The region starts at index
* zero in this buffer and is {@code capacity()} bytes. An invocation of
* this method behaves in exactly the same way as the invocation
* {@link force(int,int) force(0,capacity())}.
*
* <p> If the file mapped into this buffer resides on a local storage
* device then when this method returns it is guaranteed that all changes
Expand Down Expand Up @@ -362,4 +375,41 @@ public final MappedByteBuffer rewind() {
super.rewind();
return this;
}

/**
* {@inheritDoc}
*
* <p> Reading bytes into physical memory by invoking {@code load()} on the
* returned buffer, or writing bytes to the storage device by invoking
* {@code force()} on the returned buffer, will only act on the sub-range
* of this buffer that the returned buffer represents, namely
* {@code [position(),limit())}.
*/
@Override
public abstract MappedByteBuffer slice();

/**
* {@inheritDoc}
*
* <p> Reading bytes into physical memory by invoking {@code load()} on the
* returned buffer, or writing bytes to the storage device by invoking
* {@code force()} on the returned buffer, will only act on the sub-range
* of this buffer that the returned buffer represents, namely
* {@code [index,index+length)}, where {@code index} and {@code length} are
* assumed to satisfy the preconditions.
*/
@Override
public abstract MappedByteBuffer slice(int index, int length);

/**
* {@inheritDoc}
*/
@Override
public abstract MappedByteBuffer duplicate();

/**
* {@inheritDoc}
*/
@Override
public abstract MappedByteBuffer compact();
}
116 changes: 116 additions & 0 deletions test/jdk/java/nio/MappedByteBuffer/ForceViews.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2021, 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 4833719
* @summary Verify MappedByteBuffer force on compact, duplicate, and slice views
* @run testng ForceViews
*/
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.nio.channels.FileChannel;
import static java.nio.channels.FileChannel.MapMode.*;
import java.nio.file.Path;
import static java.nio.file.StandardOpenOption.*;
import java.util.function.BiFunction;

import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class ForceViews {

static record Segment(int position, int length) {}

private FileChannel fc;

@BeforeTest(alwaysRun=true)
public void openChannel() throws IOException {
Path file = Path.of(System.getProperty("test.src", "."), "junk");
fc = FileChannel.open(file, CREATE_NEW, READ, WRITE, DELETE_ON_CLOSE);
ByteBuffer buf = ByteBuffer.wrap(new byte[1024]);
fc.write(buf);
fc.position(0);
}

@AfterTest(alwaysRun=true)
public void closeChannel() throws IOException {
fc.close();
}

@DataProvider
public Object[][] provider() throws IOException {
BiFunction<MappedByteBuffer,Segment,MappedByteBuffer> absSlice =
(m, s) -> { return m.slice(s.position, s.length); };
BiFunction<MappedByteBuffer,Segment,MappedByteBuffer> relSlice =
(m, s) -> { m.position(s.position); m.limit(s.position + s.length);
return m.slice(); };
BiFunction<MappedByteBuffer,Segment,MappedByteBuffer> duplicate=
(m, s) -> { return m.duplicate(); };
BiFunction<MappedByteBuffer,Segment,MappedByteBuffer> compact =
(m, s) -> { return m.compact(); };

Object[][] result = new Object[][] {
{"Absolute slice", fc, 256, 512, 128, 128, 32, 32, absSlice},
{"Relative slice", fc, 256, 512, 0, 128, 32, 32, relSlice},
{"Duplicate", fc, 256, 512, 0, 256, 32, 32, duplicate},
{"Compact", fc, 256, 512, 0, 256, 32, 32, compact}
};

return result;
}

@Test(dataProvider = "provider")
public void test(String tst, FileChannel fc, int mapPosition, int mapLength,
int sliceIndex, int sliceLength, int regionOffset, int regionLength,
BiFunction<MappedByteBuffer,Segment,MappedByteBuffer> f)
throws Exception {
MappedByteBuffer mbb = fc.map(READ_WRITE, mapPosition, mapLength);
mbb = f.apply(mbb, new Segment(sliceIndex, sliceLength));
for (int i = regionOffset; i < regionOffset + regionLength; i++) {
mbb.put(i, (byte)i);
}
mbb.force(regionOffset, regionOffset + regionLength);

int fcPos = mapPosition + sliceIndex + regionOffset;
int mbbPos = regionOffset;
int length = regionLength;

ByteBuffer buf = ByteBuffer.allocate(length);
fc.position(fcPos);
fc.read(buf);
for (int i = 0; i < length; i++) {
int fcVal = buf.get(i);
int mbbVal = mbb.get(mbbPos + i);
int val = regionOffset + i;
Assert.assertTrue(fcVal == val && mbbVal == val,
String.format("%s: i %d, fcVal %d, mbbVal %d, val %d",
tst, i, fcVal, mbbVal, val));
}
}
}

1 comment on commit b006f22

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.