Path: blob/master/src/java.base/share/classes/sun/nio/ch/Invoker.java
41159 views
/*1* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.ch;2627import java.nio.channels.*;28import java.util.concurrent.*;29import java.security.AccessController;30import sun.security.action.GetIntegerAction;31import jdk.internal.misc.InnocuousThread;3233/**34* Defines static methods to invoke a completion handler or arbitrary task.35*/3637class Invoker {38private Invoker() { }3940// maximum number of completion handlers that may be invoked on the current41// thread before it re-directs invocations to the thread pool. This helps42// avoid stack overflow and lessens the risk of starvation.43@SuppressWarnings("removal")44private static final int maxHandlerInvokeCount = AccessController.doPrivileged(45new GetIntegerAction("sun.nio.ch.maxCompletionHandlersOnStack", 16));4647// Per-thread object with reference to channel group and a counter for48// the number of completion handlers invoked. This should be reset to 049// when all completion handlers have completed.50static class GroupAndInvokeCount {51private final AsynchronousChannelGroupImpl group;52private int handlerInvokeCount;53GroupAndInvokeCount(AsynchronousChannelGroupImpl group) {54this.group = group;55}56AsynchronousChannelGroupImpl group() {57return group;58}59int invokeCount() {60return handlerInvokeCount;61}62void setInvokeCount(int value) {63handlerInvokeCount = value;64}65void resetInvokeCount() {66handlerInvokeCount = 0;67}68void incrementInvokeCount() {69handlerInvokeCount++;70}71}72private static final ThreadLocal<GroupAndInvokeCount> myGroupAndInvokeCount =73new ThreadLocal<GroupAndInvokeCount>() {74@Override protected GroupAndInvokeCount initialValue() {75return null;76}77};7879/**80* Binds this thread to the given group81*/82static void bindToGroup(AsynchronousChannelGroupImpl group) {83myGroupAndInvokeCount.set(new GroupAndInvokeCount(group));84}8586/**87* Returns the GroupAndInvokeCount object for this thread.88*/89static GroupAndInvokeCount getGroupAndInvokeCount() {90return myGroupAndInvokeCount.get();91}9293/**94* Returns true if the current thread is in a channel group's thread pool95*/96static boolean isBoundToAnyGroup() {97return myGroupAndInvokeCount.get() != null;98}99100/**101* Returns true if the current thread is in the given channel's thread102* pool and we haven't exceeded the maximum number of handler frames on103* the stack.104*/105static boolean mayInvokeDirect(GroupAndInvokeCount myGroupAndInvokeCount,106AsynchronousChannelGroupImpl group)107{108if ((myGroupAndInvokeCount != null) &&109(myGroupAndInvokeCount.group() == group) &&110(myGroupAndInvokeCount.invokeCount() < maxHandlerInvokeCount))111{112return true;113}114return false;115}116117/**118* Invoke handler without checking the thread identity or number of handlers119* on the thread stack.120*/121@SuppressWarnings("removal")122static <V,A> void invokeUnchecked(CompletionHandler<V,? super A> handler,123A attachment,124V value,125Throwable exc)126{127if (exc == null) {128handler.completed(value, attachment);129} else {130handler.failed(exc, attachment);131}132133// clear interrupt134Thread.interrupted();135136// clear thread locals when in default thread pool137if (System.getSecurityManager() != null) {138Thread me = Thread.currentThread();139if (me instanceof InnocuousThread) {140GroupAndInvokeCount thisGroupAndInvokeCount = myGroupAndInvokeCount.get();141((InnocuousThread)me).eraseThreadLocals();142if (thisGroupAndInvokeCount != null) {143myGroupAndInvokeCount.set(thisGroupAndInvokeCount);144}145}146}147}148149/**150* Invoke handler assuming thread identity already checked151*/152static <V,A> void invokeDirect(GroupAndInvokeCount myGroupAndInvokeCount,153CompletionHandler<V,? super A> handler,154A attachment,155V result,156Throwable exc)157{158myGroupAndInvokeCount.incrementInvokeCount();159Invoker.invokeUnchecked(handler, attachment, result, exc);160}161162/**163* Invokes the handler. If the current thread is in the channel group's164* thread pool then the handler is invoked directly, otherwise it is165* invoked indirectly.166*/167static <V,A> void invoke(AsynchronousChannel channel,168CompletionHandler<V,? super A> handler,169A attachment,170V result,171Throwable exc)172{173boolean invokeDirect = false;174boolean identityOkay = false;175GroupAndInvokeCount thisGroupAndInvokeCount = myGroupAndInvokeCount.get();176if (thisGroupAndInvokeCount != null) {177if ((thisGroupAndInvokeCount.group() == ((Groupable)channel).group()))178identityOkay = true;179if (identityOkay &&180(thisGroupAndInvokeCount.invokeCount() < maxHandlerInvokeCount))181{182// group match183invokeDirect = true;184}185}186if (invokeDirect) {187invokeDirect(thisGroupAndInvokeCount, handler, attachment, result, exc);188} else {189try {190invokeIndirectly(channel, handler, attachment, result, exc);191} catch (RejectedExecutionException ree) {192// channel group shutdown; fallback to invoking directly193// if the current thread has the right identity.194if (identityOkay) {195invokeDirect(thisGroupAndInvokeCount,196handler, attachment, result, exc);197} else {198throw new ShutdownChannelGroupException();199}200}201}202}203204/**205* Invokes the handler indirectly via the channel group's thread pool.206*/207static <V,A> void invokeIndirectly(AsynchronousChannel channel,208final CompletionHandler<V,? super A> handler,209final A attachment,210final V result,211final Throwable exc)212{213try {214((Groupable)channel).group().executeOnPooledThread(new Runnable() {215public void run() {216GroupAndInvokeCount thisGroupAndInvokeCount =217myGroupAndInvokeCount.get();218if (thisGroupAndInvokeCount != null)219thisGroupAndInvokeCount.setInvokeCount(1);220invokeUnchecked(handler, attachment, result, exc);221}222});223} catch (RejectedExecutionException ree) {224throw new ShutdownChannelGroupException();225}226}227228/**229* Invokes the handler "indirectly" in the given Executor230*/231static <V,A> void invokeIndirectly(final CompletionHandler<V,? super A> handler,232final A attachment,233final V value,234final Throwable exc,235Executor executor)236{237try {238executor.execute(new Runnable() {239public void run() {240invokeUnchecked(handler, attachment, value, exc);241}242});243} catch (RejectedExecutionException ree) {244throw new ShutdownChannelGroupException();245}246}247248/**249* Invokes the given task on the thread pool associated with the given250* channel. If the current thread is in the thread pool then the task is251* invoked directly.252*/253static void invokeOnThreadInThreadPool(Groupable channel,254Runnable task)255{256boolean invokeDirect;257GroupAndInvokeCount thisGroupAndInvokeCount = myGroupAndInvokeCount.get();258AsynchronousChannelGroupImpl targetGroup = channel.group();259if (thisGroupAndInvokeCount == null) {260invokeDirect = false;261} else {262invokeDirect = (thisGroupAndInvokeCount.group == targetGroup);263}264try {265if (invokeDirect) {266task.run();267} else {268targetGroup.executeOnPooledThread(task);269}270} catch (RejectedExecutionException ree) {271throw new ShutdownChannelGroupException();272}273}274275/**276* Invoke handler with completed result. This method does not check the277* thread identity or the number of handlers on the thread stack.278*/279static <V,A> void invokeUnchecked(PendingFuture<V,A> future) {280assert future.isDone();281CompletionHandler<V,? super A> handler = future.handler();282if (handler != null) {283invokeUnchecked(handler,284future.attachment(),285future.value(),286future.exception());287}288}289290/**291* Invoke handler with completed result. If the current thread is in the292* channel group's thread pool then the handler is invoked directly,293* otherwise it is invoked indirectly.294*/295static <V,A> void invoke(PendingFuture<V,A> future) {296assert future.isDone();297CompletionHandler<V,? super A> handler = future.handler();298if (handler != null) {299invoke(future.channel(),300handler,301future.attachment(),302future.value(),303future.exception());304}305}306307/**308* Invoke handler with completed result. The handler is invoked indirectly,309* via the channel group's thread pool.310*/311static <V,A> void invokeIndirectly(PendingFuture<V,A> future) {312assert future.isDone();313CompletionHandler<V,? super A> handler = future.handler();314if (handler != null) {315invokeIndirectly(future.channel(),316handler,317future.attachment(),318future.value(),319future.exception());320}321}322}323324325