Package org.jboss.netty.channel.socket.nio

Source Code of org.jboss.netty.channel.socket.nio.NioProviderMetadata$SelectorLoop

/*
* JBoss, Home of Professional Open Source
*
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* by the @author tags. See the COPYRIGHT.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.netty.channel.socket.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
import org.jboss.netty.util.SystemPropertyUtil;
import org.jboss.netty.util.ThreadRenamingRunnable;

/**
* Provides information which is specific to a NIO service provider
* implementation.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev: 531 $, $Date: 2008-11-28 14:28:50 +0900 (Fri, 28 Nov 2008) $
*
*/
class NioProviderMetadata {
    static final InternalLogger logger =
        InternalLoggerFactory.getInstance(NioProviderMetadata.class);

    private static final String CONSTRAINT_LEVEL_PROPERTY =
        "java.nio.channels.spi.constraintLevel";

    private static final long AUTODETECTION_TIMEOUT = 7000L;

    /**
     * 0 - no need to wake up to get / set interestOps (most cases)
     * 1 - no need to wake up to get interestOps, but need to wake up to set.
     * 2 - need to wake up to get / set interestOps    (old providers)
     */
    static final int CONSTRAINT_LEVEL;

    static {
        int constraintLevel = -1;

        // Use the system property if possible.
        try {
            String value = SystemPropertyUtil.get(CONSTRAINT_LEVEL_PROPERTY, "-1");
            constraintLevel = Integer.parseInt(value);
            if (constraintLevel < 0 || constraintLevel > 2) {
                constraintLevel = -1;
            } else {
                logger.debug(
                        "Using the specified NIO constraint level: " +
                        constraintLevel);
            }
        } catch (Exception e) {
            // format error
        }

        if (constraintLevel < 0) {
            constraintLevel = detectConstraintLevelFromSystemProperties();

            if (constraintLevel < 0) {
                logger.debug(
                        "Couldn't get the NIO constraint level from the system properties.");
                ConstraintLevelAutodetector autodetector =
                    new ConstraintLevelAutodetector();

                try {
                    constraintLevel = autodetector.autodetectWithTimeout();
                } catch (Exception e) {
                    // Probably because of security manager - try again without
                    // creating a new thread directly.
                    constraintLevel = autodetector.autodetectWithoutTimeout();
                }
            }

            if (constraintLevel < 0) {
                constraintLevel = 2;
                logger.warn(
                        "Failed to autodetect the NIO constraint level; " +
                        "using the safest level (2)");
            } else if (constraintLevel != 0) {
                logger.info(
                        "Using the autodetected NIO constraint level: " +
                        constraintLevel +
                        " (Use better NIO provider for better performance)");
            } else {
                logger.debug(
                        "Using the autodetected NIO constraint level: " +
                        constraintLevel);
            }
        }

        CONSTRAINT_LEVEL = constraintLevel;

        if (CONSTRAINT_LEVEL < 0 || CONSTRAINT_LEVEL > 2) {
            throw new Error(
                    "Unexpected NIO constraint level: " +
                    CONSTRAINT_LEVEL + ", please report this error.");
        }
    }

    private static int detectConstraintLevelFromSystemProperties() {
        String version = SystemPropertyUtil.get("java.specification.version");
        String os = SystemPropertyUtil.get("os.name");
        String vendor = SystemPropertyUtil.get("java.vm.vendor");
        String provider;
        try {
            provider = SelectorProvider.provider().getClass().getName();
        } catch (Exception e) {
            // Perhaps security exception.
            provider = null;
        }

        if (version == null || os == null || vendor == null || provider == null) {
            return -1;
        }

        os = os.toLowerCase();
        vendor = vendor.toLowerCase();

        // Sun JVM
        if (vendor.indexOf("sun") >= 0) {
            // Linux
            if (os.indexOf("linux") >= 0) {
                if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
                    provider.equals("sun.nio.ch.PollSelectorProvider")) {
                    return 0;
                }

            // Windows
            } else if (os.indexOf("windows") >= 0) {
                if (provider.equals("sun.nio.ch.WindowsSelectorProvider")) {
                    return 0;
                }

            // Solaris
            } else if (os.indexOf("sun") >= 0 || os.indexOf("solaris") >= 0) {
                if (provider.equals("sun.nio.ch.DevPollSelectorProvider")) {
                    return 0;
                }
            }
        // Apple JVM
        } else if (vendor.indexOf("apple") >= 0) {
            // Mac OS
            if (os.indexOf("mac") >= 0 && os.indexOf("os") >= 0) {
                if (provider.equals("sun.nio.ch.KQueueSelectorProvider")) {
                    return 0;
                }
            }
        // IBM
        } else if (vendor.indexOf("ibm") >= 0) {
            // Linux
            if (os.indexOf("linux") >= 0) {
                if (version.equals("1.5") || version.matches("^1\\.5\\D.*$")) {
                    if (provider.equals("sun.nio.ch.PollSelectorProvider")) {
                        return 1;
                    }
                } else if (version.equals("1.6") || version.matches("^1\\.6\\D.*$")) {
                    if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
                        provider.equals("sun.nio.ch.PollSelectorProvider")) {
                        return 2;
                    }
                }

            // AIX
            } if (os.indexOf("aix") >= 0) {
                if (version.equals("1.5") || version.matches("^1\\.5\\D.*$")) {
                    if (provider.equals("sun.nio.ch.PollSelectorProvider")) {
                        return 1;
                    }
                } else if (version.equals("1.6") || version.matches("^1\\.6\\D.*$")) {
                    if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
                        provider.equals("sun.nio.ch.PollSelectorProvider")) {
                        return 2;
                    }
                }
            }
        // BEA
        } else if (vendor.indexOf("bea") >= 0 || vendor.indexOf("oracle") >= 0) {
            // Linux
            if (os.indexOf("linux") >= 0) {
                if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
                    provider.equals("sun.nio.ch.PollSelectorProvider")) {
                    return 0;
                }

            // Windows
            } else if (os.indexOf("windows") >= 0) {
                if (provider.equals("sun.nio.ch.WindowsSelectorProvider")) {
                    return 0;
                }
            }
        }

        // Others (untested)
        return -1;
    }


    private static final class ConstraintLevelAutodetector {

        ConstraintLevelAutodetector() {
            super();
        }

        int autodetectWithTimeout() {
            final BlockingQueue<Integer> resultQueue = new LinkedBlockingQueue<Integer>();
            Runnable detector = new ThreadRenamingRunnable(new Runnable() {
                public void run() {
                    int level = -1;
                    try {
                        level = autodetectWithoutTimeout();
                    } finally {
                        resultQueue.offer(Integer.valueOf(level));
                    }
                }
            }, "NIO constraint level detector");

            Thread detectorThread = new Thread(detector);
            detectorThread.start();

            for (;;) {
                try {
                    Integer result = resultQueue.poll(AUTODETECTION_TIMEOUT, TimeUnit.MILLISECONDS);
                    if (result == null) {
                        logger.warn("NIO constraint level autodetection timed out.");
                        return -1;
                    } else {
                        return result.intValue();
                    }
                } catch (InterruptedException e) {
                    // Ignored
                }
            }
        }

        int autodetectWithoutTimeout() {
            final int constraintLevel;
            ExecutorService executor = Executors.newCachedThreadPool();
            boolean success;
            long startTime;
            int interestOps;

            ServerSocketChannel ch = null;
            SelectorLoop loop = null;

            try {
                // Open a channel.
                ch = ServerSocketChannel.open();

                // Configure the channel
                try {
                    ch.socket().bind(new InetSocketAddress(0));
                    ch.configureBlocking(false);
                } catch (IOException e) {
                    logger.warn("Failed to configure a temporary socket.", e);
                    return -1;
                }

                // Prepare the selector loop.
                try {
                    loop = new SelectorLoop();
                } catch (IOException e) {
                    logger.warn("Failed to open a temporary selector.", e);
                    return -1;
                }

                // Register the channel
                try {
                    ch.register(loop.selector, 0);
                } catch (ClosedChannelException e) {
                    logger.warn("Failed to register a temporary selector.", e);
                    return -1;
                }

                SelectionKey key = ch.keyFor(loop.selector);

                // Start the selector loop.
                executor.execute(loop);

                // Level 0
                success = true;
                for (int i = 0; i < 10; i ++) {

                    // Increase the probability of calling interestOps
                    // while select() is running.
                    do {
                        while (!loop.selecting) {
                            Thread.yield();
                        }

                        // Wait a little bit more.
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            // Ignore
                        }
                    } while (!loop.selecting);

                    startTime = System.nanoTime();
                    key.interestOps(key.interestOps() | SelectionKey.OP_ACCEPT);
                    key.interestOps(key.interestOps() & ~SelectionKey.OP_ACCEPT);

                    if (System.nanoTime() - startTime >= 500000000L) {
                        success = false;
                        break;
                    }
                }

                if (success) {
                    constraintLevel = 0;
                } else {
                    // Level 1
                    success = true;
                    for (int i = 0; i < 10; i ++) {

                        // Increase the probability of calling interestOps
                        // while select() is running.
                        do {
                            while (!loop.selecting) {
                                Thread.yield();
                            }

                            // Wait a little bit more.
                            try {
                                Thread.sleep(50);
                            } catch (InterruptedException e) {
                                // Ignore
                            }
                        } while (!loop.selecting);

                        startTime = System.nanoTime();
                        interestOps = key.interestOps();
                        synchronized (loop) {
                            loop.selector.wakeup();
                            key.interestOps(interestOps | SelectionKey.OP_ACCEPT);
                            key.interestOps(interestOps & ~SelectionKey.OP_ACCEPT);
                        }

                        if (System.nanoTime() - startTime >= 500000000L) {
                            success = false;
                            break;
                        }
                    }
                    if (success) {
                        constraintLevel = 1;
                    } else {
                        constraintLevel = 2;
                    }
                }
            } catch (IOException e) {
                return -1;
            } finally {
                if (ch != null) {
                    try {
                        ch.close();
                    } catch (IOException e) {
                        logger.warn("Failed to close a temporary socket.", e);
                    }
                }

                if (loop != null) {
                    loop.done = true;
                    executor.shutdownNow();
                    try {
                        for (;;) {
                            loop.selector.wakeup();
                            try {
                                if (executor.awaitTermination(1, TimeUnit.SECONDS)) {
                                    break;
                                }
                            } catch (InterruptedException e) {
                                // Ignore
                            }
                        }
                    } catch (Exception e) {
                        // Perhaps security exception.
                    }

                    try {
                        loop.selector.close();
                    } catch (IOException e) {
                        logger.warn("Failed to close a temporary selector.", e);
                    }
                }
            }

            return constraintLevel;
        }
    }

    private static final class SelectorLoop implements Runnable {
        final Selector selector;
        volatile boolean done;
        volatile boolean selecting; // Just an approximation

        SelectorLoop() throws IOException {
            selector = Selector.open();
        }

        public void run() {
            while (!done) {
                synchronized (this) {
                    // Guard
                }
                try {
                    selecting = true;
                    try {
                        selector.select(1000);
                    } finally {
                        selecting = false;
                    }

                    Set<SelectionKey> keys = selector.selectedKeys();
                    for (SelectionKey k: keys) {
                        k.interestOps(0);
                    }
                    keys.clear();
                } catch (IOException e) {
                    logger.warn("Failed to wait for a temporary selector.", e);
                }
            }
        }
    }
}
TOP

Related Classes of org.jboss.netty.channel.socket.nio.NioProviderMetadata$SelectorLoop

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.