ChainedCallSite.java revision 1551:f3b883bec2d0
1/* 2 * Copyright (c) 2010, 2013, 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 26/* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file, and Oracle licenses the original version of this file under the BSD 31 * license: 32 */ 33/* 34 Copyright 2009-2013 Attila Szegedi 35 36 Licensed under both the Apache License, Version 2.0 (the "Apache License") 37 and the BSD License (the "BSD License"), with licensee being free to 38 choose either of the two at their discretion. 39 40 You may not use this file except in compliance with either the Apache 41 License or the BSD License. 42 43 If you choose to use this file in compliance with the Apache License, the 44 following notice applies to you: 45 46 You may obtain a copy of the Apache License at 47 48 http://www.apache.org/licenses/LICENSE-2.0 49 50 Unless required by applicable law or agreed to in writing, software 51 distributed under the License is distributed on an "AS IS" BASIS, 52 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 53 implied. See the License for the specific language governing 54 permissions and limitations under the License. 55 56 If you choose to use this file in compliance with the BSD License, the 57 following notice applies to you: 58 59 Redistribution and use in source and binary forms, with or without 60 modification, are permitted provided that the following conditions are 61 met: 62 * Redistributions of source code must retain the above copyright 63 notice, this list of conditions and the following disclaimer. 64 * Redistributions in binary form must reproduce the above copyright 65 notice, this list of conditions and the following disclaimer in the 66 documentation and/or other materials provided with the distribution. 67 * Neither the name of the copyright holder nor the names of 68 contributors may be used to endorse or promote products derived from 69 this software without specific prior written permission. 70 71 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 72 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 73 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 74 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER 75 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 76 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 77 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 78 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 79 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 80 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 81 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 82*/ 83 84package jdk.dynalink.support; 85 86import java.lang.invoke.MethodHandle; 87import java.lang.invoke.MethodHandles; 88import java.util.Arrays; 89import java.util.Iterator; 90import java.util.LinkedList; 91import jdk.dynalink.CallSiteDescriptor; 92import jdk.dynalink.linker.GuardedInvocation; 93import jdk.dynalink.linker.support.Lookup; 94 95/** 96 * A relinkable call site that implements a polymorphic inline caching strategy. 97 * It remembers up to 8 {@link GuardedInvocation}s it was linked with, and on 98 * each relink request builds a cascading chain of method handles of one 99 * invocation falling back to the next one. The number of remembered invocations 100 * can be customized by overriding {@link #getMaxChainLength()} in a subclass. 101 * When this call site is relinked with a new invocation and the length of the 102 * chain is already at the maximum, it will throw away the oldest invocation. 103 * Invocations with invalidated switch points and ones for which their 104 * invalidating exception triggered are removed eagerly from the chain. The 105 * invocations are never reordered; the most recently linked method handle is 106 * always at the start of the chain and the least recently linked at its end. 107 * The call site can be safely relinked on more than one thread concurrently. 108 * Race conditions in linking are resolved by throwing away the 109 * {@link GuardedInvocation} produced on the losing thread without incorporating 110 * it into the chain, so it can lead to repeated linking for the same arguments. 111 */ 112public class ChainedCallSite extends AbstractRelinkableCallSite { 113 private static final MethodHandle PRUNE_CATCHES; 114 private static final MethodHandle PRUNE_SWITCHPOINTS; 115 static { 116 final MethodHandle PRUNE = Lookup.findOwnSpecial(MethodHandles.lookup(), "prune", MethodHandle.class, 117 MethodHandle.class, boolean.class); 118 PRUNE_CATCHES = MethodHandles.insertArguments(PRUNE, 2, true); 119 PRUNE_SWITCHPOINTS = MethodHandles.insertArguments(PRUNE, 2, false); 120 } 121 122 /** 123 * Contains the invocations currently linked into this call site's target. They are used when we are 124 * relinking to rebuild the guardWithTest chain. Valid values for this field are: {@code null} if there's 125 * no linked invocations, or an instance of {@link GuardedInvocation} if there is exactly one previous 126 * invocation, or an instance of {@code GuardedInvocation[]} if there is more than one previous 127 * invocation. 128 */ 129 private Object invocations; 130 131 /** 132 * Creates a new chained call site. 133 * @param descriptor the descriptor for the call site. 134 */ 135 public ChainedCallSite(final CallSiteDescriptor descriptor) { 136 super(descriptor); 137 } 138 139 /** 140 * The maximum number of method handles in the chain. Defaults to 8. You can 141 * override it in a subclass if you need to change the value. 142 * @return the maximum number of method handles in the chain. The return 143 * value is checked, and if your override returns a value less than 1, a 144 * {@link RuntimeException} will be thrown. 145 */ 146 protected int getMaxChainLength() { 147 return 8; 148 } 149 150 @Override 151 public void relink(final GuardedInvocation guardedInvocation, final MethodHandle relinkAndInvoke) { 152 relinkInternal(guardedInvocation, relinkAndInvoke, false, false); 153 } 154 155 @Override 156 public void resetAndRelink(final GuardedInvocation guardedInvocation, final MethodHandle relinkAndInvoke) { 157 relinkInternal(guardedInvocation, relinkAndInvoke, true, false); 158 } 159 160 private MethodHandle relinkInternal(final GuardedInvocation invocation, final MethodHandle relink, final boolean reset, final boolean removeCatches) { 161 final Object currentInvocations = invocations; 162 final LinkedList<GuardedInvocation> newInvocations; 163 if (currentInvocations == null || reset) { 164 newInvocations = new LinkedList<>(); 165 } else if (currentInvocations instanceof GuardedInvocation) { 166 newInvocations = new LinkedList<>(); 167 newInvocations.add((GuardedInvocation)currentInvocations); 168 } else if (currentInvocations instanceof GuardedInvocation[]) { 169 newInvocations = new LinkedList<>(Arrays.asList(((GuardedInvocation[])currentInvocations))); 170 } else { 171 throw new AssertionError(); 172 } 173 174 // First, prune the chain of invalidated switchpoints, we always do this 175 // We also remove any catches if the remove catches flag is set 176 for(final Iterator<GuardedInvocation> it = newInvocations.iterator(); it.hasNext();) { 177 final GuardedInvocation inv = it.next(); 178 if(inv.hasBeenInvalidated() || (removeCatches && inv.getException() != null)) { 179 it.remove(); 180 } 181 } 182 183 // prune() is allowed to invoke this method with invocation == null meaning we're just pruning the chain and not 184 // adding any new invocations to it. 185 if(invocation != null) { 186 // Remove oldest entry if we're at max length 187 if(newInvocations.size() == checkMaxChainLength(getMaxChainLength())) { 188 newInvocations.removeFirst(); 189 } 190 newInvocations.addLast(invocation); 191 } 192 193 // prune-and-invoke is used as the fallback for invalidated switchpoints. If a switchpoint gets invalidated, we 194 // rebuild the chain and get rid of all invalidated switchpoints instead of letting them linger. 195 final MethodHandle pruneAndInvokeSwitchPoints = makePruneAndInvokeMethod(relink, PRUNE_SWITCHPOINTS); 196 final MethodHandle pruneAndInvokeCatches = makePruneAndInvokeMethod(relink, PRUNE_CATCHES); 197 198 // Fold the new chain 199 MethodHandle target = relink; 200 for(final GuardedInvocation inv: newInvocations) { 201 target = inv.compose(target, pruneAndInvokeSwitchPoints, pruneAndInvokeCatches); 202 } 203 204 switch (newInvocations.size()) { 205 case 0: 206 invocations = null; 207 break; 208 case 1: 209 invocations = newInvocations.getFirst(); 210 break; 211 default: 212 invocations = newInvocations.toArray(new GuardedInvocation[newInvocations.size()]); 213 } 214 setTarget(target); 215 return target; 216 } 217 218 private static int checkMaxChainLength(final int maxChainLength) { 219 if (maxChainLength > 0) { 220 return maxChainLength; 221 } 222 throw new RuntimeException("getMaxChainLength() returned a non-positive value"); 223 224 } 225 /** 226 * Creates a method that rebuilds our call chain, pruning it of any invalidated switchpoints, and then invokes that 227 * chain. 228 * @param relinkAndInvoke the ultimate fallback for the chain passed from the dynamic linker. 229 * @return a method handle for prune-and-invoke 230 */ 231 private MethodHandle makePruneAndInvokeMethod(final MethodHandle relinkAndInvoke, final MethodHandle prune) { 232 // Bind prune to (this, relink) 233 final MethodHandle boundPrune = MethodHandles.insertArguments(prune, 0, this, relinkAndInvoke); 234 // Make it ignore all incoming arguments 235 final MethodHandle ignoreArgsPrune = MethodHandles.dropArguments(boundPrune, 0, type().parameterList()); 236 // Invoke prune, then invoke the call site target with original arguments 237 return MethodHandles.foldArguments(MethodHandles.exactInvoker(type()), ignoreArgsPrune); 238 } 239 240 @SuppressWarnings("unused") 241 private MethodHandle prune(final MethodHandle relink, final boolean catches) { 242 return relinkInternal(null, relink, false, catches); 243 } 244} 245