1
1
/*
2
- * Copyright (c) 2015, 2019 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2015, 2021 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
39
39
import java .util .HashSet ;
40
40
import java .util .Set ;
41
41
import java .util .Spliterator ;
42
+ import java .util .concurrent .atomic .AtomicInteger ;
42
43
import java .util .function .Consumer ;
43
44
45
+ import jdk .internal .access .SharedSecrets ;
46
+ import jdk .internal .access .JavaNioAccess ;
47
+
44
48
/**
45
49
* A file-based lines spliterator, leveraging a shared mapped byte buffer and
46
50
* associated file channel, covering lines of a file for character encodings
@@ -84,19 +88,31 @@ final class FileChannelLinesSpliterator implements Spliterator<String> {
84
88
// Non-null when traversing
85
89
private BufferedReader reader ;
86
90
91
+ // Number of references to the shared mapped buffer. Initialized to unity
92
+ // when the buffer is created by the root spliterator. Incremented in the
93
+ // sub-spliterator constructor. Decremented when 'buffer' transitions from
94
+ // non-null to null, either when traversing begins or if the spliterator is
95
+ // closed before traversal. If the count is zero after decrementing, then
96
+ // the buffer is unmapped.
97
+ private final AtomicInteger bufRefCount ;
98
+
87
99
FileChannelLinesSpliterator (FileChannel fc , Charset cs , int index , int fence ) {
88
100
this .fc = fc ;
89
101
this .cs = cs ;
90
102
this .index = index ;
91
103
this .fence = fence ;
104
+ this .bufRefCount = new AtomicInteger ();
92
105
}
93
106
94
- private FileChannelLinesSpliterator (FileChannel fc , Charset cs , int index , int fence , ByteBuffer buffer ) {
107
+ private FileChannelLinesSpliterator (FileChannel fc , Charset cs , int index ,
108
+ int fence , ByteBuffer buffer , AtomicInteger bufRefCount ) {
95
109
this .fc = fc ;
96
- this .buffer = buffer ;
97
110
this .cs = cs ;
98
111
this .index = index ;
99
112
this .fence = fence ;
113
+ this .buffer = buffer ;
114
+ this .bufRefCount = bufRefCount ;
115
+ this .bufRefCount .incrementAndGet ();
100
116
}
101
117
102
118
@ Override
@@ -167,7 +183,7 @@ public void close() throws IOException {
167
183
private String readLine () {
168
184
if (reader == null ) {
169
185
reader = getBufferedReader ();
170
- buffer = null ;
186
+ unmap () ;
171
187
}
172
188
173
189
try {
@@ -178,13 +194,6 @@ private String readLine() {
178
194
}
179
195
180
196
private ByteBuffer getMappedByteBuffer () {
181
- // TODO can the mapped byte buffer be explicitly unmapped?
182
- // It's possible, via a shared-secret mechanism, when either
183
- // 1) the spliterator starts traversing, although traversal can
184
- // happen concurrently for mulitple spliterators, so care is
185
- // needed in this case; or
186
- // 2) when the stream is closed using some shared holder to pass
187
- // the mapped byte buffer when it is created.
188
197
try {
189
198
return fc .map (FileChannel .MapMode .READ_ONLY , 0 , fence );
190
199
} catch (IOException e ) {
@@ -201,6 +210,7 @@ public Spliterator<String> trySplit() {
201
210
ByteBuffer b ;
202
211
if ((b = buffer ) == null ) {
203
212
b = buffer = getMappedByteBuffer ();
213
+ bufRefCount .set (1 );
204
214
}
205
215
206
216
final int hi = fence , lo = index ;
@@ -246,7 +256,8 @@ public Spliterator<String> trySplit() {
246
256
247
257
// The left spliterator will have the line-separator at the end
248
258
return (mid > lo && mid < hi )
249
- ? new FileChannelLinesSpliterator (fc , cs , lo , index = mid , b )
259
+ ? new FileChannelLinesSpliterator (fc , cs , lo , index = mid ,
260
+ b , bufRefCount )
250
261
: null ;
251
262
}
252
263
@@ -267,4 +278,22 @@ public long getExactSizeIfKnown() {
267
278
public int characteristics () {
268
279
return Spliterator .ORDERED | Spliterator .NONNULL ;
269
280
}
281
+
282
+ private void unmap () {
283
+ if (buffer != null ) {
284
+ ByteBuffer b = buffer ;
285
+ buffer = null ;
286
+ if (bufRefCount .decrementAndGet () == 0 ) {
287
+ JavaNioAccess nioAccess = SharedSecrets .getJavaNioAccess ();
288
+ try {
289
+ nioAccess .unmapper (b ).unmap ();
290
+ } catch (UnsupportedOperationException ignored ) {
291
+ }
292
+ }
293
+ }
294
+ }
295
+
296
+ void close () {
297
+ unmap ();
298
+ }
270
299
}
0 commit comments