File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\store\fs\FilePathNioMapped.java.html

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