1 /*
2 * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.nio.ch;
27
28 import java.nio.channels;
29 import java.util;
30 import sun.misc.Unsafe;
31
32 /**
33 * Maintains a mapping of pending I/O requests (identified by the address of
34 * an OVERLAPPED structure) to Futures.
35 */
36
37 class PendingIoCache {
38 private static final Unsafe unsafe = Unsafe.getUnsafe();
39 private static final int addressSize = unsafe.addressSize();
40
41 private static int dependsArch(int value32, int value64) {
42 return (addressSize == 4) ? value32 : value64;
43 }
44
45 /*
46 * typedef struct _OVERLAPPED {
47 * DWORD Internal;
48 * DWORD InternalHigh;
49 * DWORD Offset;
50 * DWORD OffsetHigh;
51 * HANDLE hEvent;
52 * } OVERLAPPED;
53 */
54 private static final int SIZEOF_OVERLAPPED = dependsArch(20, 32);
55
56 // set to true when closed
57 private boolean closed;
58
59 // set to true when thread is waiting for all I/O operations to complete
60 private boolean closePending;
61
62 // maps OVERLAPPED to PendingFuture
63 private final Map<Long,PendingFuture> pendingIoMap =
64 new HashMap<Long,PendingFuture>();
65
66 // per-channel cache of OVERLAPPED structures
67 private long[] overlappedCache = new long[4];
68 private int overlappedCacheCount = 0;
69
70 PendingIoCache() {
71 }
72
73 long add(PendingFuture<?,?> result) {
74 synchronized (this) {
75 if (closed)
76 throw new AssertionError("Should not get here");
77 long ov;
78 if (overlappedCacheCount > 0) {
79 ov = overlappedCache[--overlappedCacheCount];
80 } else {
81 ov = unsafe.allocateMemory(SIZEOF_OVERLAPPED);
82 }
83 pendingIoMap.put(ov, result);
84 return ov;
85 }
86 }
87
88 @SuppressWarnings("unchecked")
89 <V,A> PendingFuture<V,A> remove(long overlapped) {
90 synchronized (this) {
91 PendingFuture<V,A> res = pendingIoMap.remove(overlapped);
92 if (res != null) {
93 if (overlappedCacheCount < overlappedCache.length) {
94 overlappedCache[overlappedCacheCount++] = overlapped;
95 } else {
96 // cache full or channel closing
97 unsafe.freeMemory(overlapped);
98 }
99 // notify closing thread.
100 if (closePending) {
101 this.notifyAll();
102 }
103 }
104 return res;
105 }
106 }
107
108 void close() {
109 synchronized (this) {
110 if (closed)
111 return;
112
113 // handle case where I/O operations that have not completed.
114 if (!pendingIoMap.isEmpty())
115 clearPendingIoMap();
116
117 // release memory for any cached OVERLAPPED structures
118 while (overlappedCacheCount > 0) {
119 unsafe.freeMemory( overlappedCache[--overlappedCacheCount] );
120 }
121
122 // done
123 closed = true;
124 }
125 }
126
127 private void clearPendingIoMap() {
128 assert Thread.holdsLock(this);
129
130 // wait up to 50ms for the I/O operations to complete
131 closePending = true;
132 try {
133 this.wait(50);
134 } catch (InterruptedException x) {
135 Thread.currentThread().interrupt();
136 }
137 closePending = false;
138 if (pendingIoMap.isEmpty())
139 return;
140
141 // cause all pending I/O operations to fail
142 // simulate the failure of all pending I/O operations.
143 for (Long ov: pendingIoMap.keySet()) {
144 PendingFuture<?,?> result = pendingIoMap.get(ov);
145 assert !result.isDone();
146
147 // make I/O port aware of the stale OVERLAPPED structure
148 Iocp iocp = (Iocp)((Groupable)result.channel()).group();
149 iocp.makeStale(ov);
150
151 // execute a task that invokes the result handler's failed method
152 final Iocp.ResultHandler rh = (Iocp.ResultHandler)result.getContext();
153 Runnable task = new Runnable() {
154 public void run() {
155 rh.failed(-1, new AsynchronousCloseException());
156 }
157 };
158 iocp.executeOnPooledThread(task);
159 }
160 pendingIoMap.clear();
161 }
162 }