1 /* 2 * Copyright (c) 2008, 2017, 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 jdk.internal.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 @SuppressWarnings("rawtypes") 64 private final Map<Long,PendingFuture> pendingIoMap = 65 new HashMap<Long,PendingFuture>(); 66 67 // keys of former pendingIoMap entries which were invalidated 68 private final Set<Long> invalidOverlapped = new HashSet<Long>(); 69 70 // per-channel cache of OVERLAPPED structures 71 private long[] overlappedCache = new long[4]; 72 private int overlappedCacheCount = 0; 73 74 PendingIoCache() { 75 } 76 77 long add(PendingFuture<?,?> result) { 78 synchronized (this) { 79 if (closed) 80 throw new AssertionError("Should not get here"); 81 long ov; 82 if (overlappedCacheCount > 0) { 83 ov = overlappedCache[--overlappedCacheCount]; 84 } else { 85 ov = unsafe.allocateMemory(SIZEOF_OVERLAPPED); 86 } 87 pendingIoMap.put(ov, result); 88 return ov; 89 } 90 } 91 92 @SuppressWarnings("unchecked") 93 void invalidate(long overlapped) { 94 synchronized (this) { 95 if (pendingIoMap.remove(overlapped) != null) { 96 invalidOverlapped.add(overlapped); 97 } 98 } 99 } 100 101 @SuppressWarnings("unchecked") 102 <V,A> PendingFuture<V,A> remove(long overlapped) { 103 synchronized (this) { 104 PendingFuture<V,A> res = pendingIoMap.remove(overlapped); 105 if (res != null || invalidOverlapped.remove(overlapped)) { 106 if (overlappedCacheCount < overlappedCache.length) { 107 overlappedCache[overlappedCacheCount++] = overlapped; 108 } else { 109 // cache full or channel closing 110 unsafe.freeMemory(overlapped); 111 } 112 } 113 if (res != null && closePending) { 114 // notify closing thread. 115 this.notifyAll(); 116 } 117 return res; 118 } 119 } 120 121 void close() { 122 synchronized (this) { 123 if (closed) 124 return; 125 126 // handle case where I/O operations that have not completed. 127 if (!pendingIoMap.isEmpty()) 128 clearPendingIoMap(); 129 130 // release memory for any cached OVERLAPPED structures 131 while (overlappedCacheCount > 0) { 132 unsafe.freeMemory( overlappedCache[--overlappedCacheCount] ); 133 } 134 135 // release memory for any invalidated OVERLAPPED structures 136 invalidOverlapped.stream().forEach(ov -> unsafe.freeMemory(ov)); 137 138 // done 139 closed = true; 140 } 141 } 142 143 private void clearPendingIoMap() { 144 assert Thread.holdsLock(this); 145 146 // wait up to 50ms for the I/O operations to complete 147 closePending = true; 148 try { 149 this.wait(50); 150 } catch (InterruptedException x) { 151 Thread.currentThread().interrupt(); 152 } 153 closePending = false; 154 if (pendingIoMap.isEmpty()) 155 return; 156 157 // cause all pending I/O operations to fail 158 // simulate the failure of all pending I/O operations. 159 for (Long ov: pendingIoMap.keySet()) { 160 PendingFuture<?,?> result = pendingIoMap.get(ov); 161 assert !result.isDone(); 162 163 // make I/O port aware of the stale OVERLAPPED structure 164 Iocp iocp = (Iocp)((Groupable)result.channel()).group(); 165 iocp.makeStale(ov); 166 167 // execute a task that invokes the result handler's failed method 168 final Iocp.ResultHandler rh = (Iocp.ResultHandler)result.getContext(); 169 Runnable task = new Runnable() { 170 public void run() { 171 rh.failed(-1, new AsynchronousCloseException()); 172 } 173 }; 174 iocp.executeOnPooledThread(task); 175 } 176 pendingIoMap.clear(); 177 } 178 }