EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.store.fs]

COVERAGE SUMMARY FOR SOURCE FILE [FilePathNioMapped.java]

nameclass, %method, %block, %line, %
FilePathNioMapped.java100% (2/2)100% (18/18)74%  (340/457)78%  (87.8/113)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FileNioMapped100% (1/1)100% (15/15)73%  (322/439)77%  (84.8/110)
unMap (): void 100% (1/1)41%  (38/93)56%  (14/25)
checkFileSizeLimit (long): void 100% (1/1)50%  (5/10)67%  (2/3)
reMap (): void 100% (1/1)64%  (54/84)73%  (11/15)
read (ByteBuffer): int 100% (1/1)70%  (55/79)58%  (11/19)
setFileLength (long): void 100% (1/1)92%  (35/38)90%  (11.8/13)
FileNioMapped (String, String): void 100% (1/1)100% (26/26)100% (8/8)
force (boolean): void 100% (1/1)100% (9/9)100% (3/3)
implCloseChannel (): void 100% (1/1)100% (12/12)100% (5/5)
position (): long 100% (1/1)100% (4/4)100% (1/1)
position (long): FileChannel 100% (1/1)100% (8/8)100% (3/3)
size (): long 100% (1/1)100% (3/3)100% (1/1)
toString (): String 100% (1/1)100% (10/10)100% (1/1)
truncate (long): FileChannel 100% (1/1)100% (18/18)100% (5/5)
tryLock (long, long, boolean): FileLock 100% (1/1)100% (8/8)100% (1/1)
write (ByteBuffer): int 100% (1/1)100% (37/37)100% (7/7)
     
class FilePathNioMapped100% (1/1)100% (3/3)100% (18/18)100% (3/3)
FilePathNioMapped (): void 100% (1/1)100% (3/3)100% (1/1)
getScheme (): String 100% (1/1)100% (2/2)100% (1/1)
open (String): FileChannel 100% (1/1)100% (13/13)100% (1/1)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 */
6package org.h2.store.fs;
7 
8import java.io.EOFException;
9import java.io.IOException;
10import java.io.RandomAccessFile;
11import java.lang.ref.WeakReference;
12import java.lang.reflect.Method;
13import java.nio.BufferUnderflowException;
14import java.nio.ByteBuffer;
15import java.nio.MappedByteBuffer;
16import java.nio.channels.FileChannel;
17import java.nio.channels.FileLock;
18import java.nio.channels.NonWritableChannelException;
19 
20import org.h2.engine.SysProperties;
21 
22/**
23 * This file system stores files on disk and uses java.nio to access the files.
24 * This class used memory mapped files.
25 */
26public class FilePathNioMapped extends FilePathNio {
27 
28    @Override
29    public FileChannel open(String mode) throws IOException {
30        return new FileNioMapped(name.substring(getScheme().length() + 1), mode);
31    }
32 
33    @Override
34    public String getScheme() {
35        return "nioMapped";
36    }
37 
38}
39 
40/**
41 * Uses memory mapped files.
42 * The file size is limited to 2 GB.
43 */
44class FileNioMapped extends FileBase {
45 
46    private static final long GC_TIMEOUT_MS = 10000;
47    private final String name;
48    private final MapMode mode;
49    private RandomAccessFile file;
50    private MappedByteBuffer mapped;
51    private long fileLength;
52 
53    /**
54     * The position within the file. Can't use the position of the mapped buffer
55     * because it doesn't support seeking past the end of the file.
56     */
57    private int pos;
58 
59    FileNioMapped(String fileName, String mode) throws IOException {
60        if ("r".equals(mode)) {
61            this.mode = MapMode.READ_ONLY;
62        } else {
63            this.mode = MapMode.READ_WRITE;
64        }
65        this.name = fileName;
66        file = new RandomAccessFile(fileName, mode);
67        reMap();
68    }
69 
70    private void unMap() throws IOException {
71        if (mapped == null) {
72            return;
73        }
74        // first write all data
75        mapped.force();
76 
77        // need to dispose old direct buffer, see bug
78        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038
79 
80        boolean useSystemGc = true;
81        if (SysProperties.NIO_CLEANER_HACK) {
82            try {
83                Method cleanerMethod = mapped.getClass().getMethod("cleaner");
84                cleanerMethod.setAccessible(true);
85                Object cleaner = cleanerMethod.invoke(mapped);
86                if (cleaner != null) {
87                    Method clearMethod = cleaner.getClass().getMethod("clean");
88                    clearMethod.invoke(cleaner);
89                }
90                useSystemGc = false;
91            } catch (Throwable e) {
92                // useSystemGc is already true
93            } finally {
94                mapped = null;
95            }
96        }
97        if (useSystemGc) {
98            WeakReference<MappedByteBuffer> bufferWeakRef =
99                    new WeakReference<MappedByteBuffer>(mapped);
100            mapped = null;
101            long start = System.currentTimeMillis();
102            while (bufferWeakRef.get() != null) {
103                if (System.currentTimeMillis() - start > GC_TIMEOUT_MS) {
104                    throw new IOException("Timeout (" + GC_TIMEOUT_MS
105                            + " ms) reached while trying to GC mapped buffer");
106                }
107                System.gc();
108                Thread.yield();
109            }
110        }
111    }
112 
113    /**
114     * Re-map byte buffer into memory, called when file size has changed or file
115     * was created.
116     */
117    private void reMap() throws IOException {
118        int oldPos = 0;
119        if (mapped != null) {
120            oldPos = pos;
121            unMap();
122        }
123        fileLength = file.length();
124        checkFileSizeLimit(fileLength);
125        // maps new MappedByteBuffer; the old one is disposed during GC
126        mapped = file.getChannel().map(mode, 0, fileLength);
127        int limit = mapped.limit();
128        int capacity = mapped.capacity();
129        if (limit < fileLength || capacity < fileLength) {
130            throw new IOException("Unable to map: length=" + limit +
131                    " capacity=" + capacity + " length=" + fileLength);
132        }
133        if (SysProperties.NIO_LOAD_MAPPED) {
134            mapped.load();
135        }
136        this.pos = Math.min(oldPos, (int) fileLength);
137    }
138 
139    private static void checkFileSizeLimit(long length) throws IOException {
140        if (length > Integer.MAX_VALUE) {
141            throw new IOException(
142                    "File over 2GB is not supported yet when using this file system");
143        }
144    }
145 
146    @Override
147    public void implCloseChannel() throws IOException {
148        if (file != null) {
149            unMap();
150            file.close();
151            file = null;
152        }
153    }
154 
155    @Override
156    public long position() {
157        return pos;
158    }
159 
160    @Override
161    public String toString() {
162        return "nioMapped:" + name;
163    }
164 
165    @Override
166    public synchronized long size() throws IOException {
167        return fileLength;
168    }
169 
170    @Override
171    public synchronized int read(ByteBuffer dst) throws IOException {
172        try {
173            int len = dst.remaining();
174            if (len == 0) {
175                return 0;
176            }
177            len = (int) Math.min(len, fileLength - pos);
178            if (len <= 0) {
179                return -1;
180            }
181            mapped.position(pos);
182            mapped.get(dst.array(), dst.arrayOffset() + dst.position(), len);
183            dst.position(dst.position() + len);
184            pos += len;
185            return len;
186        } catch (IllegalArgumentException e) {
187            EOFException e2 = new EOFException("EOF");
188            e2.initCause(e);
189            throw e2;
190        } catch (BufferUnderflowException e) {
191            EOFException e2 = new EOFException("EOF");
192            e2.initCause(e);
193            throw e2;
194        }
195    }
196 
197    @Override
198    public FileChannel position(long pos) throws IOException {
199        checkFileSizeLimit(pos);
200        this.pos = (int) pos;
201        return this;
202    }
203 
204    @Override
205    public synchronized FileChannel truncate(long newLength) throws IOException {
206        // compatibility with JDK FileChannel#truncate
207        if (mode == MapMode.READ_ONLY) {
208            throw new NonWritableChannelException();
209        }
210        if (newLength < size()) {
211            setFileLength(newLength);
212        }
213        return this;
214    }
215 
216    public synchronized void setFileLength(long newLength) throws IOException {
217        checkFileSizeLimit(newLength);
218        int oldPos = pos;
219        unMap();
220        for (int i = 0;; i++) {
221            try {
222                file.setLength(newLength);
223                break;
224            } catch (IOException e) {
225                if (i > 16 || e.toString().indexOf("user-mapped section open") < 0) {
226                    throw e;
227                }
228            }
229            System.gc();
230        }
231        reMap();
232        pos = (int) Math.min(newLength, oldPos);
233    }
234 
235    @Override
236    public void force(boolean metaData) throws IOException {
237        mapped.force();
238        file.getFD().sync();
239    }
240 
241    @Override
242    public synchronized int write(ByteBuffer src) throws IOException {
243        int len = src.remaining();
244        // check if need to expand file
245        if (mapped.capacity() < pos + len) {
246            setFileLength(pos + len);
247        }
248        mapped.position(pos);
249        mapped.put(src);
250        pos += len;
251        return len;
252    }
253 
254    @Override
255    public synchronized FileLock tryLock(long position, long size,
256            boolean shared) throws IOException {
257        return file.getChannel().tryLock(position, size, shared);
258    }
259 
260}

[all classes][org.h2.store.fs]
EMMA 2.0.5312 (C) Vladimir Roubtsov