1/*
2 * Copyright (c) 2008, 2012, 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
26package sun.nio.ch;
27
28import java.util.concurrent.*;
29import java.security.AccessController;
30import java.security.PrivilegedAction;
31import sun.security.action.GetPropertyAction;
32import sun.security.action.GetIntegerAction;
33import jdk.internal.misc.InnocuousThread;
34
35/**
36 * Encapsulates a thread pool associated with a channel group.
37 */
38
39public class ThreadPool {
40    private static final String DEFAULT_THREAD_POOL_THREAD_FACTORY =
41        "java.nio.channels.DefaultThreadPool.threadFactory";
42    private static final String DEFAULT_THREAD_POOL_INITIAL_SIZE =
43        "java.nio.channels.DefaultThreadPool.initialSize";
44
45    private final ExecutorService executor;
46
47    // indicates if thread pool is fixed size
48    private final boolean isFixed;
49
50    // indicates the pool size (for a fixed thread pool configuratin this is
51    // the maximum pool size; for other thread pools it is the initial size)
52    private final int poolSize;
53
54    private ThreadPool(ExecutorService executor,
55                       boolean isFixed,
56                       int poolSize)
57    {
58        this.executor = executor;
59        this.isFixed = isFixed;
60        this.poolSize = poolSize;
61    }
62
63    ExecutorService executor() {
64        return executor;
65    }
66
67    boolean isFixedThreadPool() {
68        return isFixed;
69    }
70
71    int poolSize() {
72        return poolSize;
73    }
74
75    static ThreadFactory defaultThreadFactory() {
76        if (System.getSecurityManager() == null) {
77            return (Runnable r) -> {
78                Thread t = new Thread(r);
79                t.setDaemon(true);
80                return t;
81            };
82        } else {
83            return (Runnable r) -> {
84                PrivilegedAction<Thread> action = () -> {
85                    Thread t = InnocuousThread.newThread(r);
86                    t.setDaemon(true);
87                    return t;
88               };
89               return AccessController.doPrivileged(action);
90           };
91        }
92    }
93
94    private static class DefaultThreadPoolHolder {
95        static final ThreadPool defaultThreadPool = createDefault();
96    }
97
98    // return the default (system-wide) thread pool
99    static ThreadPool getDefault() {
100        return DefaultThreadPoolHolder.defaultThreadPool;
101    }
102
103    // create thread using default settings (configured by system properties)
104    static ThreadPool createDefault() {
105        // default the number of fixed threads to the hardware core count
106        int initialSize = getDefaultThreadPoolInitialSize();
107        if (initialSize < 0)
108            initialSize = Runtime.getRuntime().availableProcessors();
109        // default to thread factory that creates daemon threads
110        ThreadFactory threadFactory = getDefaultThreadPoolThreadFactory();
111        if (threadFactory == null)
112            threadFactory = defaultThreadFactory();
113        // create thread pool
114        ExecutorService executor = Executors.newCachedThreadPool(threadFactory);
115        return new ThreadPool(executor, false, initialSize);
116    }
117
118    // create using given parameters
119    static ThreadPool create(int nThreads, ThreadFactory factory) {
120        if (nThreads <= 0)
121            throw new IllegalArgumentException("'nThreads' must be > 0");
122        ExecutorService executor = Executors.newFixedThreadPool(nThreads, factory);
123        return new ThreadPool(executor, true, nThreads);
124    }
125
126    // wrap a user-supplied executor
127    public static ThreadPool wrap(ExecutorService executor, int initialSize) {
128        if (executor == null)
129            throw new NullPointerException("'executor' is null");
130        // attempt to check if cached thread pool
131        if (executor instanceof ThreadPoolExecutor) {
132            int max = ((ThreadPoolExecutor)executor).getMaximumPoolSize();
133            if (max == Integer.MAX_VALUE) {
134                if (initialSize < 0) {
135                    initialSize = Runtime.getRuntime().availableProcessors();
136                } else {
137                   // not a cached thread pool so ignore initial size
138                    initialSize = 0;
139                }
140            }
141        } else {
142            // some other type of thread pool
143            if (initialSize < 0)
144                initialSize = 0;
145        }
146        return new ThreadPool(executor, false, initialSize);
147    }
148
149    private static int getDefaultThreadPoolInitialSize() {
150        String propValue = AccessController.doPrivileged(new
151            GetPropertyAction(DEFAULT_THREAD_POOL_INITIAL_SIZE));
152        if (propValue != null) {
153            try {
154                return Integer.parseInt(propValue);
155            } catch (NumberFormatException x) {
156                throw new Error("Value of property '" + DEFAULT_THREAD_POOL_INITIAL_SIZE +
157                    "' is invalid: " + x);
158            }
159        }
160        return -1;
161    }
162
163    private static ThreadFactory getDefaultThreadPoolThreadFactory() {
164        String propValue = AccessController.doPrivileged(new
165            GetPropertyAction(DEFAULT_THREAD_POOL_THREAD_FACTORY));
166        if (propValue != null) {
167            try {
168                @SuppressWarnings("deprecation")
169                Object tmp = Class
170                    .forName(propValue, true, ClassLoader.getSystemClassLoader()).newInstance();
171                return (ThreadFactory)tmp;
172            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException x) {
173                throw new Error(x);
174            }
175        }
176        return null;
177    }
178}
179