MappedByteBuffer.java revision 126ab1b546c71137a97cef68cc89267e7f7be634
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package java.nio;
18
19import java.nio.channels.FileChannel.MapMode;
20import libcore.io.ErrnoException;
21import libcore.io.Libcore;
22import libcore.io.Memory;
23import static libcore.io.OsConstants.*;
24
25/**
26 * {@code MappedByteBuffer} is a special kind of direct byte buffer which maps a
27 * region of file to memory.
28 * <p>
29 * {@code MappedByteBuffer} can be created by calling
30 * {@link java.nio.channels.FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long) FileChannel.map}.
31 * Once created, the mapping between the byte buffer and the file region remains
32 * valid until the byte buffer is garbage collected.
33 * <p>
34 * All or part of a {@code MappedByteBuffer}'s content may change or become
35 * inaccessible at any time, since the mapped file region can be modified by
36 * another thread or process at any time. If this happens, the behavior of the
37 * {@code MappedByteBuffer} is undefined.
38 */
39public abstract class MappedByteBuffer extends ByteBuffer {
40
41    final DirectByteBuffer wrapped;
42
43    private final MapMode mapMode;
44
45    MappedByteBuffer(ByteBuffer directBuffer) {
46        super(directBuffer.capacity, directBuffer.block);
47        if (!directBuffer.isDirect()) {
48            throw new IllegalArgumentException("directBuffer is not a direct buffer: " + directBuffer);
49        }
50        this.wrapped = (DirectByteBuffer) directBuffer;
51        this.mapMode = null;
52    }
53
54    MappedByteBuffer(MemoryBlock block, int capacity, int offset, MapMode mapMode) {
55        super(capacity, block);
56        this.mapMode = mapMode;
57        if (mapMode == MapMode.READ_ONLY) {
58            wrapped = new ReadOnlyDirectByteBuffer(block, capacity, offset);
59        } else {
60            wrapped = new ReadWriteDirectByteBuffer(block, capacity, offset);
61        }
62    }
63
64    /**
65     * Returns true if there is a high probability that every page of this buffer is currently
66     * loaded in RAM, meaning that accesses will not cause a page fault. It is impossible to give
67     * a strong guarantee since this is only a snapshot of a dynamic situation.
68     */
69    public final boolean isLoaded() {
70        long address = block.toInt();
71        long size = block.getSize();
72        if (size == 0) {
73            return true;
74        }
75
76        try {
77            int pageSize = (int) Libcore.os.sysconf(_SC_PAGE_SIZE);
78            int pageOffset = (int) (address % pageSize);
79            address -= pageOffset;
80            size += pageOffset;
81            int pageCount = (int) ((size + pageSize - 1) / pageSize);
82            byte[] vector = new byte[pageCount];
83            Libcore.os.mincore(address, size, vector);
84            for (int i = 0; i < vector.length; ++i) {
85                if ((vector[i] & 1) != 1) {
86                    return false;
87                }
88            }
89            return true;
90        } catch (ErrnoException errnoException) {
91            return false;
92        }
93    }
94
95    /**
96     * Attempts to load every page of this buffer into RAM. See {@link #isLoaded}.
97     * @return this buffer.
98     */
99    public final MappedByteBuffer load() {
100        try {
101            Libcore.os.mlock(block.toInt(), block.getSize());
102            Libcore.os.munlock(block.toInt(), block.getSize());
103        } catch (ErrnoException ignored) {
104        }
105        return this;
106    }
107
108    /**
109     * Writes all changes of the buffer to the mapped file. If the mapped file
110     * is stored on a local device, it is guaranteed that the changes are
111     * written to the file. No such guarantee is given if the file is located on
112     * a remote device.
113     *
114     * @return this buffer.
115     */
116    public final MappedByteBuffer force() {
117        if (mapMode == MapMode.READ_WRITE) {
118            try {
119                Libcore.os.msync(block.toInt(), block.getSize(), MS_SYNC);
120            } catch (ErrnoException errnoException) {
121                // The RI doesn't throw, presumably on the assumption that you can't get into
122                // a state where msync(2) could return an error.
123                throw new AssertionError(errnoException);
124            }
125        }
126        return this;
127    }
128}
129