jdk/src/solaris/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java
changeset 2057 3acf8e5e2ca0
child 3632 399359a027de
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java	Sun Feb 15 12:25:54 2009 +0000
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.util.concurrent.*;
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.net.InetSocketAddress;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Unix implementation of AsynchronousServerSocketChannel
+ */
+
+class UnixAsynchronousServerSocketChannelImpl
+    extends AsynchronousServerSocketChannelImpl
+    implements Port.PollableChannel
+{
+    private final static NativeDispatcher nd = new SocketDispatcher();
+
+    private final Port port;
+    private final int fdVal;
+
+    // flag to indicate an accept is outstanding
+    private final AtomicBoolean accepting = new AtomicBoolean();
+    private void enableAccept() {
+        accepting.set(false);
+    }
+
+    // used to ensure that the context for an asynchronous accept is visible
+    // the pooled thread that handles the I/O event
+    private final Object updateLock = new Object();
+
+    // pending accept
+    private PendingFuture<AsynchronousSocketChannel,Object> pendingAccept;
+
+    // context for permission check when security manager set
+    private AccessControlContext acc;
+
+
+    UnixAsynchronousServerSocketChannelImpl(Port port)
+        throws IOException
+    {
+        super(port);
+
+        try {
+            IOUtil.configureBlocking(fd, false);
+        } catch (IOException x) {
+            nd.close(fd);  // prevent leak
+            throw x;
+        }
+        this.port = port;
+        this.fdVal = IOUtil.fdVal(fd);
+
+        // add mapping from file descriptor to this channel
+        port.register(fdVal, this);
+    }
+
+    // returns and clears the result of a pending accept
+    private PendingFuture<AsynchronousSocketChannel,Object> grabPendingAccept() {
+        synchronized (updateLock) {
+            PendingFuture<AsynchronousSocketChannel,Object> result = pendingAccept;
+            pendingAccept = null;
+            return result;
+        }
+    }
+
+    @Override
+    void implClose() throws IOException {
+        // remove the mapping
+        port.unregister(fdVal);
+
+        // close file descriptor
+        nd.close(fd);
+
+        // if there is a pending accept then complete it
+        final PendingFuture<AsynchronousSocketChannel,Object> result =
+            grabPendingAccept();
+        if (result != null) {
+            // discard the stack trace as otherwise it may appear that implClose
+            // has thrown the exception.
+            AsynchronousCloseException x = new AsynchronousCloseException();
+            x.setStackTrace(new StackTraceElement[0]);
+            result.setFailure(x);
+
+            // invoke by submitting task rather than directly
+            Invoker.invokeIndirectly(result.handler(), result);
+        }
+    }
+
+    @Override
+    public AsynchronousChannelGroupImpl group() {
+        return port;
+    }
+
+    /**
+     * Invoked by event handling thread when listener socket is polled
+     */
+    @Override
+    public void onEvent(int events) {
+        PendingFuture<AsynchronousSocketChannel,Object> result = grabPendingAccept();
+        if (result == null)
+            return; // may have been grabbed by asynchronous close
+
+        // attempt to accept connection
+        FileDescriptor newfd = new FileDescriptor();
+        InetSocketAddress[] isaa = new InetSocketAddress[1];
+        boolean accepted = false;
+        try {
+            begin();
+            int n = accept0(this.fd, newfd, isaa);
+
+            // spurious wakeup, is this possible?
+            if (n == IOStatus.UNAVAILABLE) {
+                synchronized (updateLock) {
+                    this.pendingAccept = result;
+                }
+                port.startPoll(fdVal, Port.POLLIN);
+                return;
+            }
+
+            // connection accepted
+            accepted = true;
+
+        } catch (Throwable x) {
+            if (x instanceof ClosedChannelException)
+                x = new AsynchronousCloseException();
+            enableAccept();
+            result.setFailure(x);
+        } finally {
+            end();
+        }
+
+        // Connection accepted so finish it when not holding locks.
+        AsynchronousSocketChannel child = null;
+        if (accepted) {
+            try {
+                child = finishAccept(newfd, isaa[0], acc);
+                enableAccept();
+                result.setResult(child);
+            } catch (Throwable x) {
+                enableAccept();
+                if (!(x instanceof IOException) && !(x instanceof SecurityException))
+                    x = new IOException(x);
+                result.setFailure(x);
+            }
+        }
+
+        // if an async cancel has already cancelled the operation then
+        // close the new channel so as to free resources
+        if (child != null && result.isCancelled()) {
+            try {
+                child.close();
+            } catch (IOException ignore) { }
+        }
+
+        // invoke the handler
+        Invoker.invoke(result.handler(), result);
+    }
+
+    /**
+     * Completes the accept by creating the AsynchronousSocketChannel for
+     * the given file descriptor and remote address. If this method completes
+     * with an IOException or SecurityException then the channel/file descriptor
+     * will be closed.
+     */
+    private AsynchronousSocketChannel finishAccept(FileDescriptor newfd,
+                                                   final InetSocketAddress remote,
+                                                   AccessControlContext acc)
+        throws IOException, SecurityException
+    {
+        AsynchronousSocketChannel ch = null;
+        try {
+            ch = new UnixAsynchronousSocketChannelImpl(port, newfd, remote);
+        } catch (IOException x) {
+            nd.close(newfd);
+            throw x;
+        }
+
+        // permission check must always be in initiator's context
+        try {
+            if (acc != null) {
+                AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                    public Void run() {
+                        SecurityManager sm = System.getSecurityManager();
+                        if (sm != null) {
+                            sm.checkAccept(remote.getAddress().getHostAddress(),
+                                           remote.getPort());
+                        }
+                        return null;
+                    }
+                }, acc);
+            } else {
+                SecurityManager sm = System.getSecurityManager();
+                if (sm != null) {
+                    sm.checkAccept(remote.getAddress().getHostAddress(),
+                                   remote.getPort());
+                }
+            }
+        } catch (SecurityException x) {
+            try {
+                ch.close();
+            } catch (IOException ignore) { }
+            throw x;
+        }
+        return ch;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <A> Future<AsynchronousSocketChannel> accept(A attachment,
+        final CompletionHandler<AsynchronousSocketChannel,? super A> handler)
+    {
+        // complete immediately if channel is closed
+        if (!isOpen()) {
+            CompletedFuture<AsynchronousSocketChannel,A> result = CompletedFuture
+                .withFailure(this, new ClosedChannelException(), attachment);
+            Invoker.invokeIndirectly(handler, result);
+            return result;
+        }
+        if (localAddress == null)
+            throw new NotYetBoundException();
+
+        // cancel was invoked with pending accept so connection may have been
+        // dropped.
+        if (isAcceptKilled())
+            throw new RuntimeException("Accept not allowed due cancellation");
+
+        // check and set flag to prevent concurrent accepting
+        if (!accepting.compareAndSet(false, true))
+            throw new AcceptPendingException();
+
+        // attempt accept
+        AbstractFuture<AsynchronousSocketChannel,A> result = null;
+        FileDescriptor newfd = new FileDescriptor();
+        InetSocketAddress[] isaa = new InetSocketAddress[1];
+        try {
+            begin();
+
+            int n = accept0(this.fd, newfd, isaa);
+            if (n == IOStatus.UNAVAILABLE) {
+                // no connection to accept
+                result = new PendingFuture<AsynchronousSocketChannel,A>(this, handler, attachment);
+
+                // need calling context when there is security manager as
+                // permission check may be done in a different thread without
+                // any application call frames on the stack
+                synchronized (this) {
+                    this.acc = (System.getSecurityManager() == null) ?
+                        null : AccessController.getContext();
+                    this.pendingAccept =
+                        (PendingFuture<AsynchronousSocketChannel,Object>)result;
+                }
+
+                // register for connections
+                port.startPoll(fdVal, Port.POLLIN);
+                return result;
+            }
+        } catch (Throwable x) {
+            // accept failed
+            if (x instanceof ClosedChannelException)
+                x = new AsynchronousCloseException();
+            result = CompletedFuture.withFailure(this, x, attachment);
+        } finally {
+            end();
+        }
+
+        // connection accepted immediately
+        if (result == null) {
+            try {
+                AsynchronousSocketChannel ch = finishAccept(newfd, isaa[0], null);
+                result = CompletedFuture.withResult(this, ch, attachment);
+            } catch (Throwable x) {
+                result = CompletedFuture.withFailure(this, x, attachment);
+            }
+        }
+
+        // re-enable accepting and invoke handler
+        enableAccept();
+        Invoker.invokeIndirectly(handler, result);
+        return result;
+    }
+
+    // -- Native methods --
+
+    private static native void initIDs();
+
+    // Accepts a new connection, setting the given file descriptor to refer to
+    // the new socket and setting isaa[0] to the socket's remote address.
+    // Returns 1 on success, or IOStatus.UNAVAILABLE.
+    //
+    private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
+                               InetSocketAddress[] isaa)
+        throws IOException;
+
+    static {
+        Util.load();
+        initIDs();
+    }
+}