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 }