1 /*
   2  * Copyright (c) 2008, 2009, 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.spi.AsynchronousChannelProvider;
  29 import java.nio.channels.*;
  30 import java.io.IOException;
  31 import java.io.Closeable;
  32 import java.io.FileDescriptor;
  33 import java.util.Map;
  34 import java.util.HashMap;
  35 import java.util.concurrent.locks.ReadWriteLock;
  36 import java.util.concurrent.locks.ReentrantReadWriteLock;
  37 
  38 /**
  39  * Base implementation of AsynchronousChannelGroupImpl for Unix systems.
  40  */
  41 
  42 abstract class Port extends AsynchronousChannelGroupImpl {
  43     static final short POLLIN       = 0x0001;
  44     static final short POLLOUT      = 0x0004;
  45     static final short POLLERR      = 0x0008;
  46     static final short POLLHUP      = 0x0010;
  47 
  48     /**
  49      * Implemented by clients registered with this port.
  50      */
  51     interface PollableChannel extends Closeable {
  52         void onEvent(int events, boolean mayInvokeDirect);
  53     }
  54 
  55     // maps fd to "pollable" channel
  56     protected final ReadWriteLock fdToChannelLock = new ReentrantReadWriteLock();
  57     protected final Map<Integer,PollableChannel> fdToChannel =
  58         new HashMap<Integer,PollableChannel>();
  59 
  60 
  61     Port(AsynchronousChannelProvider provider, ThreadPool pool) {
  62         super(provider, pool);
  63     }
  64 
  65     /**
  66      * Register channel identified by its file descriptor
  67      */
  68     final void register(int fd, PollableChannel ch) {
  69         fdToChannelLock.writeLock().lock();
  70         try {
  71             if (isShutdown())
  72                 throw new ShutdownChannelGroupException();
  73             fdToChannel.put(Integer.valueOf(fd), ch);
  74         } finally {
  75             fdToChannelLock.writeLock().unlock();
  76         }
  77     }
  78 
  79     /**
  80      * Callback method for implementations that need special handling when fd is
  81      * removed (currently only needed in the AIX-Port - see AixPollPort.java).
  82      */
  83     protected void preUnregister(int fd) {
  84         // Do nothing by default.
  85     }
  86 
  87     /**
  88      * Unregister channel identified by its file descriptor
  89      */
  90     final void unregister(int fd) {
  91         boolean checkForShutdown = false;
  92 
  93         preUnregister(fd);
  94 
  95         fdToChannelLock.writeLock().lock();
  96         try {
  97             fdToChannel.remove(Integer.valueOf(fd));
  98 
  99             // last key to be removed so check if group is shutdown
 100             if (fdToChannel.isEmpty())
 101                 checkForShutdown = true;
 102 
 103         } finally {
 104             fdToChannelLock.writeLock().unlock();
 105         }
 106 
 107         // continue shutdown
 108         if (checkForShutdown && isShutdown()) {
 109             try {
 110                 shutdownNow();
 111             } catch (IOException ignore) { }
 112         }
 113     }
 114     /**
 115      * Register file descriptor with polling mechanism for given events.
 116      * The implementation should translate the events as required.
 117      */
 118     abstract void startPoll(int fd, int events);
 119 
 120     @Override
 121     final boolean isEmpty() {
 122         fdToChannelLock.writeLock().lock();
 123         try {
 124             return fdToChannel.isEmpty();
 125         } finally {
 126             fdToChannelLock.writeLock().unlock();
 127         }
 128     }
 129 
 130     @Override
 131     final Object attachForeignChannel(final Channel channel, FileDescriptor fd) {
 132         int fdVal = IOUtil.fdVal(fd);
 133         register(fdVal, new PollableChannel() {
 134             public void onEvent(int events, boolean mayInvokeDirect) { }
 135             public void close() throws IOException {
 136                 channel.close();
 137             }
 138         });
 139         return Integer.valueOf(fdVal);
 140     }
 141 
 142     @Override
 143     final void detachForeignChannel(Object key) {
 144         unregister((Integer)key);
 145     }
 146 
 147     @Override
 148     final void closeAllChannels() {
 149         /**
 150          * Close channels in batches of up to 128 channels. This allows close
 151          * to remove the channel from the map without interference.
 152          */
 153         final int MAX_BATCH_SIZE = 128;
 154         PollableChannel channels[] = new PollableChannel[MAX_BATCH_SIZE];
 155         int count;
 156         do {
 157             // grab a batch of up to 128 channels
 158             fdToChannelLock.writeLock().lock();
 159             count = 0;
 160             try {
 161                 for (Integer fd: fdToChannel.keySet()) {
 162                     channels[count++] = fdToChannel.get(fd);
 163                     if (count >= MAX_BATCH_SIZE)
 164                         break;
 165                 }
 166             } finally {
 167                 fdToChannelLock.writeLock().unlock();
 168             }
 169 
 170             // close them
 171             for (int i=0; i<count; i++) {
 172                 try {
 173                     channels[i].close();
 174                 } catch (IOException ignore) { }
 175             }
 176         } while (count > 0);
 177     }
 178 }