338 lines · 10.7 KB · Java
RawDownload
/* * Copyright (c) 2008, 2009, Oracle and/or its affiliates. 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.nio.ch; import java.nio.channels.Channel;import java.nio.channels.AsynchronousChannelGroup;import java.nio.channels.spi.AsynchronousChannelProvider;import java.io.IOException;import java.io.FileDescriptor;import java.util.Queue;import java.util.concurrent.*;import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.atomic.AtomicBoolean;import java.security.PrivilegedAction;import java.security.AccessController;import java.security.AccessControlContext;import sun.security.action.GetIntegerAction; /** * Base implementation of AsynchronousChannelGroup */ abstract class AsynchronousChannelGroupImpl    extends AsynchronousChannelGroup implements Executor{    // number of internal threads handling I/O events when using an unbounded    // thread pool. Internal threads do not dispatch to completion handlers.    private static final int internalThreadCount = AccessController.doPrivileged(        new GetIntegerAction("sun.nio.ch.internalThreadPoolSize", 1));     // associated thread pool    private final ThreadPool pool;     // number of tasks running (including internal)    private final AtomicInteger threadCount = new AtomicInteger();     // associated Executor for timeouts    private ScheduledThreadPoolExecutor timeoutExecutor;     // task queue for when using a fixed thread pool. In that case, thread    // waiting on I/O events must be awokon to poll tasks from this queue.    private final Queue<Runnable> taskQueue;     // group shutdown    private final AtomicBoolean shutdown = new AtomicBoolean();    private final Object shutdownNowLock = new Object();    private volatile boolean terminateInitiated;     AsynchronousChannelGroupImpl(AsynchronousChannelProvider provider,                                 ThreadPool pool)    {        super(provider);        this.pool = pool;         if (pool.isFixedThreadPool()) {            taskQueue = new ConcurrentLinkedQueue<Runnable>();        } else {            taskQueue = null;   // not used        }         // use default thread factory as thread should not be visible to        // application (it doesn't execute completion handlers).        this.timeoutExecutor = (ScheduledThreadPoolExecutor)            Executors.newScheduledThreadPool(1, ThreadPool.defaultThreadFactory());        this.timeoutExecutor.setRemoveOnCancelPolicy(true);    }     final ExecutorService executor() {        return pool.executor();    }     final boolean isFixedThreadPool() {        return pool.isFixedThreadPool();    }     final int fixedThreadCount() {        if (isFixedThreadPool()) {            return pool.poolSize();        } else {            return pool.poolSize() + internalThreadCount;        }    }     private Runnable bindToGroup(final Runnable task) {        final AsynchronousChannelGroupImpl thisGroup = this;        return new Runnable() {            public void run() {                Invoker.bindToGroup(thisGroup);                task.run();            }        };    }     private void startInternalThread(final Runnable task) {        AccessController.doPrivileged(new PrivilegedAction<Void>() {            @Override            public Void run() {                // internal threads should not be visible to application so                // cannot use user-supplied thread factory                ThreadPool.defaultThreadFactory().newThread(task).start();                return null;            }         });    }     protected final void startThreads(Runnable task) {        if (!isFixedThreadPool()) {            for (int i=0; i<internalThreadCount; i++) {                startInternalThread(task);                threadCount.incrementAndGet();            }        }        if (pool.poolSize() > 0) {            task = bindToGroup(task);            try {                for (int i=0; i<pool.poolSize(); i++) {                    pool.executor().execute(task);                    threadCount.incrementAndGet();                }            } catch (RejectedExecutionException  x) {                // nothing we can do            }        }    }     final int threadCount() {        return threadCount.get();    }     /**     * Invoked by tasks as they terminate     */    final int threadExit(Runnable task, boolean replaceMe) {        if (replaceMe) {            try {                if (Invoker.isBoundToAnyGroup()) {                    // submit new task to replace this thread                    pool.executor().execute(bindToGroup(task));                } else {                    // replace internal thread                    startInternalThread(task);                }                return threadCount.get();            } catch (RejectedExecutionException x) {                // unable to replace            }        }        return threadCount.decrementAndGet();    }     /**     * Wakes up a thread waiting for I/O events to execute the given task.     */    abstract void executeOnHandlerTask(Runnable task);     /**     * For a fixed thread pool the task is queued to a thread waiting on I/O     * events. For other thread pools we simply submit the task to the thread     * pool.     */    final void executeOnPooledThread(Runnable task) {        if (isFixedThreadPool()) {            executeOnHandlerTask(task);        } else {            pool.executor().execute(bindToGroup(task));        }    }     final void offerTask(Runnable task) {        taskQueue.offer(task);    }     final Runnable pollTask() {        return (taskQueue == null) ? null : taskQueue.poll();    }     final Future<?> schedule(Runnable task, long timeout, TimeUnit unit) {        try {            return timeoutExecutor.schedule(task, timeout, unit);        } catch (RejectedExecutionException rej) {            if (terminateInitiated) {                // no timeout scheduled as group is terminating                return null;            }            throw new AssertionError(rej);        }    }     @Override    public final boolean isShutdown() {        return shutdown.get();    }     @Override    public final boolean isTerminated()  {        return pool.executor().isTerminated();    }     /**     * Returns true if there are no channels in the group     */    abstract boolean isEmpty();     /**     * Attaches a foreign channel to this group.     */    abstract Object attachForeignChannel(Channel channel, FileDescriptor fdo)        throws IOException;     /**     * Detaches a foreign channel from this group.     */    abstract void detachForeignChannel(Object key);     /**     * Closes all channels in the group     */    abstract void closeAllChannels() throws IOException;     /**     * Shutdown all tasks waiting for I/O events.     */    abstract void shutdownHandlerTasks();     private void shutdownExecutors() {        AccessController.doPrivileged(new PrivilegedAction<Void>() {            public Void run() {                pool.executor().shutdown();                timeoutExecutor.shutdown();                return null;            }        });    }     @Override    public final void shutdown() {        if (shutdown.getAndSet(true)) {            // already shutdown            return;        }        // if there are channels in the group then shutdown will continue        // when the last channel is closed        if (!isEmpty()) {            return;        }        // initiate termination (acquire shutdownNowLock to ensure that other        // threads invoking shutdownNow will block).        synchronized (shutdownNowLock) {            if (!terminateInitiated) {                terminateInitiated = true;                shutdownHandlerTasks();                shutdownExecutors();            }        }    }     @Override    public final void shutdownNow() throws IOException {        shutdown.set(true);        synchronized (shutdownNowLock) {            if (!terminateInitiated) {                terminateInitiated = true;                closeAllChannels();                shutdownHandlerTasks();                shutdownExecutors();            }        }    }     /**     * For use by AsynchronousFileChannel to release resources without shutting     * down the thread pool.     */    final void detachFromThreadPool() {        if (shutdown.getAndSet(true))            throw new AssertionError("Already shutdown");        if (!isEmpty())            throw new AssertionError("Group not empty");        shutdownHandlerTasks();    }     @Override    public final boolean awaitTermination(long timeout, TimeUnit unit)        throws InterruptedException    {        return pool.executor().awaitTermination(timeout, unit);    }     /**     * Executes the given command on one of the channel group's pooled threads.     */    @Override    public final void execute(Runnable task) {        SecurityManager sm = System.getSecurityManager();        if (sm != null) {            // when a security manager is installed then the user's task            // must be run with the current calling context            final AccessControlContext acc = AccessController.getContext();            final Runnable delegate = task;            task = new Runnable() {                @Override                public void run() {                    AccessController.doPrivileged(new PrivilegedAction<Void>() {                        @Override                        public Void run() {                            delegate.run();                            return null;                        }                    }, acc);                }            };        }        executeOnPooledThread(task);    }}