83 |
83 |
84 package jdk.internal.dynalink; |
84 package jdk.internal.dynalink; |
85 |
85 |
86 import java.lang.invoke.MethodHandle; |
86 import java.lang.invoke.MethodHandle; |
87 import java.lang.invoke.MethodHandles; |
87 import java.lang.invoke.MethodHandles; |
|
88 import java.util.Arrays; |
88 import java.util.Iterator; |
89 import java.util.Iterator; |
89 import java.util.LinkedList; |
90 import java.util.LinkedList; |
90 import java.util.concurrent.atomic.AtomicReference; |
|
91 import jdk.internal.dynalink.linker.GuardedInvocation; |
91 import jdk.internal.dynalink.linker.GuardedInvocation; |
92 import jdk.internal.dynalink.support.AbstractRelinkableCallSite; |
92 import jdk.internal.dynalink.support.AbstractRelinkableCallSite; |
93 import jdk.internal.dynalink.support.Lookup; |
93 import jdk.internal.dynalink.support.Lookup; |
94 |
94 |
95 /** |
95 /** |
110 MethodHandle.class, boolean.class); |
110 MethodHandle.class, boolean.class); |
111 PRUNE_CATCHES = MethodHandles.insertArguments(PRUNE, 2, true); |
111 PRUNE_CATCHES = MethodHandles.insertArguments(PRUNE, 2, true); |
112 PRUNE_SWITCHPOINTS = MethodHandles.insertArguments(PRUNE, 2, false); |
112 PRUNE_SWITCHPOINTS = MethodHandles.insertArguments(PRUNE, 2, false); |
113 } |
113 } |
114 |
114 |
115 private final AtomicReference<LinkedList<GuardedInvocation>> invocations = new AtomicReference<>(); |
115 /** |
|
116 * Contains the invocations currently linked into this call site's target. They are used when we are |
|
117 * relinking to rebuild the guardWithTest chain. Valid values for this field are: {@code null} if there's |
|
118 * no linked invocations, or an instance of {@link GuardedInvocation} if there is exactly one previous |
|
119 * invocation, or an instance of {@code GuardedInvocation[]} if there is more than one previous |
|
120 * invocation. |
|
121 */ |
|
122 private Object invocations; |
116 |
123 |
117 /** |
124 /** |
118 * Creates a new chained call site. |
125 * Creates a new chained call site. |
119 * @param descriptor the descriptor for the call site. |
126 * @param descriptor the descriptor for the call site. |
120 */ |
127 */ |
140 public void resetAndRelink(final GuardedInvocation guardedInvocation, final MethodHandle fallback) { |
147 public void resetAndRelink(final GuardedInvocation guardedInvocation, final MethodHandle fallback) { |
141 relinkInternal(guardedInvocation, fallback, true, false); |
148 relinkInternal(guardedInvocation, fallback, true, false); |
142 } |
149 } |
143 |
150 |
144 private MethodHandle relinkInternal(final GuardedInvocation invocation, final MethodHandle relink, final boolean reset, final boolean removeCatches) { |
151 private MethodHandle relinkInternal(final GuardedInvocation invocation, final MethodHandle relink, final boolean reset, final boolean removeCatches) { |
145 final LinkedList<GuardedInvocation> currentInvocations = invocations.get(); |
152 final Object currentInvocations = invocations; |
146 @SuppressWarnings({ "unchecked", "rawtypes" }) |
153 final LinkedList<GuardedInvocation> newInvocations; |
147 final LinkedList<GuardedInvocation> newInvocations = |
154 if (currentInvocations == null || reset) { |
148 currentInvocations == null || reset ? new LinkedList<>() : (LinkedList)currentInvocations.clone(); |
155 newInvocations = new LinkedList<>(); |
|
156 } else if (currentInvocations instanceof GuardedInvocation) { |
|
157 newInvocations = new LinkedList<>(); |
|
158 newInvocations.add((GuardedInvocation)currentInvocations); |
|
159 } else if (currentInvocations instanceof GuardedInvocation[]) { |
|
160 newInvocations = new LinkedList<>(Arrays.asList(((GuardedInvocation[])currentInvocations))); |
|
161 } else { |
|
162 throw new AssertionError(); |
|
163 } |
149 |
164 |
150 // First, prune the chain of invalidated switchpoints, we always do this |
165 // First, prune the chain of invalidated switchpoints, we always do this |
151 // We also remove any catches if the remove catches flag is set |
166 // We also remove any catches if the remove catches flag is set |
152 for(final Iterator<GuardedInvocation> it = newInvocations.iterator(); it.hasNext();) { |
167 for(final Iterator<GuardedInvocation> it = newInvocations.iterator(); it.hasNext();) { |
153 final GuardedInvocation inv = it.next(); |
168 final GuardedInvocation inv = it.next(); |
175 MethodHandle target = relink; |
190 MethodHandle target = relink; |
176 for(final GuardedInvocation inv: newInvocations) { |
191 for(final GuardedInvocation inv: newInvocations) { |
177 target = inv.compose(target, pruneAndInvokeSwitchPoints, pruneAndInvokeCatches); |
192 target = inv.compose(target, pruneAndInvokeSwitchPoints, pruneAndInvokeCatches); |
178 } |
193 } |
179 |
194 |
180 // If nobody else updated the call site while we were rebuilding the chain, set the target to our chain. In case |
195 switch (newInvocations.size()) { |
181 // we lost the race for multithreaded update, just do nothing. Either the other thread installed the same thing |
196 case 0: |
182 // we wanted to install, or otherwise, we'll be asked to relink again. |
197 invocations = null; |
183 if(invocations.compareAndSet(currentInvocations, newInvocations)) { |
198 break; |
184 setTarget(target); |
199 case 1: |
185 } |
200 invocations = newInvocations.getFirst(); |
|
201 break; |
|
202 default: |
|
203 invocations = newInvocations.toArray(new GuardedInvocation[newInvocations.size()]); |
|
204 } |
|
205 setTarget(target); |
186 return target; |
206 return target; |
187 } |
207 } |
188 |
208 |
189 /** |
209 /** |
190 * Creates a method that rebuilds our call chain, pruning it of any invalidated switchpoints, and then invokes that |
210 * Creates a method that rebuilds our call chain, pruning it of any invalidated switchpoints, and then invokes that |