attila@90: /* attila@90: * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. attila@90: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. attila@90: * attila@90: * This code is free software; you can redistribute it and/or modify it attila@90: * under the terms of the GNU General Public License version 2 only, as attila@90: * published by the Free Software Foundation. Oracle designates this attila@90: * particular file as subject to the "Classpath" exception as provided attila@90: * by Oracle in the LICENSE file that accompanied this code. attila@90: * attila@90: * This code is distributed in the hope that it will be useful, but WITHOUT attila@90: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or attila@90: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License attila@90: * version 2 for more details (a copy is included in the LICENSE file that attila@90: * accompanied this code). attila@90: * attila@90: * You should have received a copy of the GNU General Public License version attila@90: * 2 along with this work; if not, write to the Free Software Foundation, attila@90: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. attila@90: * attila@90: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA attila@90: * or visit www.oracle.com if you need additional information or have any attila@90: * questions. attila@90: */ attila@90: attila@90: /* attila@90: * This file is available under and governed by the GNU General Public attila@90: * License version 2 only, as published by the Free Software Foundation. attila@90: * However, the following notice accompanied the original version of this attila@90: * file, and Oracle licenses the original version of this file under the BSD attila@90: * license: attila@90: */ attila@90: /* attila@90: Copyright 2009-2013 Attila Szegedi attila@90: attila@90: Licensed under both the Apache License, Version 2.0 (the "Apache License") attila@90: and the BSD License (the "BSD License"), with licensee being free to attila@90: choose either of the two at their discretion. attila@90: attila@90: You may not use this file except in compliance with either the Apache attila@90: License or the BSD License. attila@90: attila@90: If you choose to use this file in compliance with the Apache License, the attila@90: following notice applies to you: attila@90: attila@90: You may obtain a copy of the Apache License at attila@90: attila@90: http://www.apache.org/licenses/LICENSE-2.0 attila@90: attila@90: Unless required by applicable law or agreed to in writing, software attila@90: distributed under the License is distributed on an "AS IS" BASIS, attila@90: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or attila@90: implied. See the License for the specific language governing attila@90: permissions and limitations under the License. attila@90: attila@90: If you choose to use this file in compliance with the BSD License, the attila@90: following notice applies to you: attila@90: attila@90: Redistribution and use in source and binary forms, with or without attila@90: modification, are permitted provided that the following conditions are attila@90: met: attila@90: * Redistributions of source code must retain the above copyright attila@90: notice, this list of conditions and the following disclaimer. attila@90: * Redistributions in binary form must reproduce the above copyright attila@90: notice, this list of conditions and the following disclaimer in the attila@90: documentation and/or other materials provided with the distribution. attila@90: * Neither the name of the copyright holder nor the names of attila@90: contributors may be used to endorse or promote products derived from attila@90: this software without specific prior written permission. attila@90: attila@90: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS attila@90: IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED attila@90: TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A attila@90: PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER attila@90: BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR attila@90: CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF attila@90: SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR attila@90: BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, attila@90: WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR attila@90: OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF attila@90: ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. attila@90: */ attila@90: attila@90: package jdk.internal.dynalink; attila@90: attila@90: import java.lang.invoke.MethodHandle; attila@90: import java.lang.invoke.MethodHandles; attila@90: import java.util.Iterator; attila@90: import java.util.LinkedList; attila@90: import java.util.concurrent.atomic.AtomicReference; attila@90: import jdk.internal.dynalink.linker.GuardedInvocation; attila@90: import jdk.internal.dynalink.support.AbstractRelinkableCallSite; attila@488: import jdk.internal.dynalink.support.Lookup; attila@90: attila@90: /** attila@90: * A relinkable call site that maintains a chain of linked method handles. In the default implementation, up to 8 method attila@90: * handles can be chained, cascading from one to the other through attila@90: * {@link MethodHandles#guardWithTest(MethodHandle, MethodHandle, MethodHandle)}. When this call site has to link a new attila@90: * method handle and the length of the chain is already at the maximum, it will throw away the oldest method handle. attila@90: * Switchpoint-invalidated handles in the chain are removed eagerly (on each linking request, and whenever a attila@90: * switchpoint-invalidated method handle is traversed during invocation). There is currently no profiling attila@90: * attached to the handles in the chain, so they are never reordered based on usage; the most recently linked method attila@90: * handle is always at the start of the chain. attila@90: */ attila@90: public class ChainedCallSite extends AbstractRelinkableCallSite { attila@963: private static final MethodHandle PRUNE_CATCHES = attila@963: MethodHandles.insertArguments( attila@963: Lookup.findOwnSpecial( attila@963: MethodHandles.lookup(), attila@963: "prune", attila@963: MethodHandle.class, attila@963: MethodHandle.class, attila@963: boolean.class), attila@963: 2, attila@963: true); attila@963: attila@963: private static final MethodHandle PRUNE_SWITCHPOINTS = attila@963: MethodHandles.insertArguments( attila@963: Lookup.findOwnSpecial( attila@963: MethodHandles.lookup(), attila@963: "prune", attila@963: MethodHandle.class, attila@963: MethodHandle.class, attila@963: boolean.class), attila@963: 2, attila@963: false); attila@488: attila@90: private final AtomicReference> invocations = new AtomicReference<>(); attila@90: attila@90: /** attila@90: * Creates a new chained call site. attila@90: * @param descriptor the descriptor for the call site. attila@90: */ attila@962: public ChainedCallSite(final CallSiteDescriptor descriptor) { attila@90: super(descriptor); attila@90: } attila@90: attila@90: /** attila@90: * The maximum number of method handles in the chain. Defaults to 8. You can override it in a subclass if you need attila@90: * to change the value. If your override returns a value less than 1, the code will break. attila@90: * @return the maximum number of method handles in the chain. attila@90: */ attila@90: protected int getMaxChainLength() { attila@90: return 8; attila@90: } attila@90: attila@90: @Override attila@962: public void relink(final GuardedInvocation guardedInvocation, final MethodHandle fallback) { attila@963: relinkInternal(guardedInvocation, fallback, false, false); attila@90: } attila@90: attila@90: @Override attila@962: public void resetAndRelink(final GuardedInvocation guardedInvocation, final MethodHandle fallback) { attila@963: relinkInternal(guardedInvocation, fallback, true, false); attila@90: } attila@90: attila@963: private MethodHandle relinkInternal(final GuardedInvocation invocation, final MethodHandle relink, final boolean reset, final boolean removeCatches) { attila@90: final LinkedList currentInvocations = invocations.get(); attila@90: @SuppressWarnings({ "unchecked", "rawtypes" }) attila@90: final LinkedList newInvocations = attila@90: currentInvocations == null || reset ? new LinkedList<>() : (LinkedList)currentInvocations.clone(); attila@90: attila@963: // First, prune the chain of invalidated switchpoints, we always do this attila@963: // We also remove any catches if the remove catches flag is set attila@962: for(final Iterator it = newInvocations.iterator(); it.hasNext();) { attila@963: final GuardedInvocation inv = it.next(); attila@963: if(inv.hasBeenInvalidated() || (removeCatches && inv.getException() != null)) { attila@90: it.remove(); attila@90: } attila@90: } attila@90: attila@90: // prune() is allowed to invoke this method with invocation == null meaning we're just pruning the chain and not attila@90: // adding any new invocations to it. attila@90: if(invocation != null) { attila@90: // Remove oldest entry if we're at max length attila@90: if(newInvocations.size() == getMaxChainLength()) { attila@90: newInvocations.removeFirst(); attila@90: } attila@90: newInvocations.addLast(invocation); attila@90: } attila@90: attila@90: // prune-and-invoke is used as the fallback for invalidated switchpoints. If a switchpoint gets invalidated, we attila@90: // rebuild the chain and get rid of all invalidated switchpoints instead of letting them linger. attila@963: final MethodHandle pruneAndInvokeSwitchPoints = makePruneAndInvokeMethod(relink, getPruneSwitchpoints()); attila@963: final MethodHandle pruneAndInvokeCatches = makePruneAndInvokeMethod(relink, getPruneCatches()); attila@90: attila@90: // Fold the new chain attila@90: MethodHandle target = relink; attila@962: for(final GuardedInvocation inv: newInvocations) { attila@963: target = inv.compose(target, pruneAndInvokeSwitchPoints, pruneAndInvokeCatches); attila@90: } attila@90: attila@90: // If nobody else updated the call site while we were rebuilding the chain, set the target to our chain. In case attila@90: // we lost the race for multithreaded update, just do nothing. Either the other thread installed the same thing attila@90: // we wanted to install, or otherwise, we'll be asked to relink again. attila@90: if(invocations.compareAndSet(currentInvocations, newInvocations)) { attila@90: setTarget(target); attila@90: } attila@90: return target; attila@90: } attila@90: attila@90: /** attila@963: * Get the switchpoint pruning function for a chained call site attila@963: * @return function that removes invalidated switchpoints tied to callsite guard chain and relinks attila@963: */ attila@963: protected MethodHandle getPruneSwitchpoints() { attila@963: return PRUNE_SWITCHPOINTS; attila@963: } attila@963: attila@963: /** attila@963: * Get the catch pruning function for a chained call site attila@963: * @return function that removes all catches tied to callsite guard chain and relinks attila@963: */ attila@963: protected MethodHandle getPruneCatches() { attila@963: return PRUNE_CATCHES; attila@963: } attila@963: attila@963: /** attila@90: * Creates a method that rebuilds our call chain, pruning it of any invalidated switchpoints, and then invokes that attila@90: * chain. attila@90: * @param relink the ultimate fallback for the chain (the {@code DynamicLinker}'s relink). attila@90: * @return a method handle for prune-and-invoke attila@90: */ attila@963: private MethodHandle makePruneAndInvokeMethod(final MethodHandle relink, final MethodHandle prune) { attila@90: // Bind prune to (this, relink) attila@963: final MethodHandle boundPrune = MethodHandles.insertArguments(prune, 0, this, relink); attila@90: // Make it ignore all incoming arguments attila@90: final MethodHandle ignoreArgsPrune = MethodHandles.dropArguments(boundPrune, 0, type().parameterList()); attila@90: // Invoke prune, then invoke the call site target with original arguments attila@90: return MethodHandles.foldArguments(MethodHandles.exactInvoker(type()), ignoreArgsPrune); attila@90: } attila@90: attila@90: @SuppressWarnings("unused") attila@963: private MethodHandle prune(final MethodHandle relink, final boolean catches) { attila@963: return relinkInternal(null, relink, false, catches); attila@90: } attila@101: }