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