1 /* |
|
2 * Copyright (c) 2010, 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 package java.dyn; |
|
27 |
|
28 /** |
|
29 * <p> |
|
30 * A {@code Switcher} is an object which can publish state transitions to other threads. |
|
31 * A switcher is initially in the <em>valid</em> state, but may at any time be |
|
32 * changed to the <em>invalid</em> state. Invalidation cannot be reversed. |
|
33 * <p> |
|
34 * A single switcher may be used to create any number of guarded method handle pairs. |
|
35 * Each guarded pair is wrapped in a new method handle {@code M}, |
|
36 * which is permanently associated with the switcher that created it. |
|
37 * Each pair consists of a target {@code T} and a fallback {@code F}. |
|
38 * While the switcher is valid, invocations to {@code M} are delegated to {@code T}. |
|
39 * After it is invalidated, invocations are delegated to {@code F}. |
|
40 * <p> |
|
41 * Invalidation is global and immediate, as if the switcher contained a |
|
42 * volatile boolean variable consulted on every call to {@code M}. |
|
43 * The invalidation is also permanent, which means the switcher |
|
44 * can change state only once. |
|
45 * <p> |
|
46 * Here is an example of a switcher in action: |
|
47 * <blockquote><pre> |
|
48 MethodType MT_str2 = MethodType.methodType(String.class, String.class); |
|
49 MethodHandle MH_strcat = MethodHandles.lookup() |
|
50 .findVirtual(String.class, "concat", MT_str2); |
|
51 Switcher switcher = new Switcher(); |
|
52 // the following steps may be repeated to re-use the same switcher: |
|
53 MethodHandle worker1 = strcat; |
|
54 MethodHandle worker2 = MethodHandles.permuteArguments(strcat, MT_str2, 1, 0); |
|
55 MethodHandle worker = switcher.guardWithTest(worker1, worker2); |
|
56 assertEquals("method", (String) worker.invokeExact("met", "hod")); |
|
57 switcher.invalidate(); |
|
58 assertEquals("hodmet", (String) worker.invokeExact("met", "hod")); |
|
59 * </pre></blockquote> |
|
60 * <p> |
|
61 * <em>Implementation Note:</em> |
|
62 * A switcher behaves as if implemented on top of {@link MutableCallSite}, |
|
63 * approximately as follows: |
|
64 * <blockquote><pre> |
|
65 public class Switcher { |
|
66 private static final MethodHandle |
|
67 K_true = MethodHandles.constant(boolean.class, true), |
|
68 K_false = MethodHandles.constant(boolean.class, false); |
|
69 private final MutableCallSite mcs; |
|
70 private final MethodHandle mcsInvoker; |
|
71 public Switcher() { |
|
72 this.mcs = new MutableCallSite(K_true); |
|
73 this.mcsInvoker = mcs.dynamicInvoker(); |
|
74 } |
|
75 public MethodHandle guardWithTest( |
|
76 MethodHandle target, MethodHandle fallback) { |
|
77 // Note: mcsInvoker is of type boolean(). |
|
78 // Target and fallback may take any arguments, but must have the same type. |
|
79 return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback); |
|
80 } |
|
81 public static void invalidateAll(Switcher[] switchers) { |
|
82 List<MutableCallSite> mcss = new ArrayList<>(); |
|
83 for (Switcher s : switchers) mcss.add(s.mcs); |
|
84 for (MutableCallSite mcs : mcss) mcs.setTarget(K_false); |
|
85 MutableCallSite.sync(mcss.toArray(new MutableCallSite[0])); |
|
86 } |
|
87 } |
|
88 * </pre></blockquote> |
|
89 * @author Remi Forax, JSR 292 EG |
|
90 */ |
|
91 public class Switcher { |
|
92 private static final MethodHandle |
|
93 K_true = MethodHandles.constant(boolean.class, true), |
|
94 K_false = MethodHandles.constant(boolean.class, false); |
|
95 |
|
96 private final MutableCallSite mcs; |
|
97 private final MethodHandle mcsInvoker; |
|
98 |
|
99 /** Create a switcher. */ |
|
100 public Switcher() { |
|
101 this.mcs = new MutableCallSite(K_true); |
|
102 this.mcsInvoker = mcs.dynamicInvoker(); |
|
103 } |
|
104 |
|
105 /** |
|
106 * Return a method handle which always delegates either to the target or the fallback. |
|
107 * The method handle will delegate to the target exactly as long as the switcher is valid. |
|
108 * After that, it will permanently delegate to the fallback. |
|
109 * <p> |
|
110 * The target and fallback must be of exactly the same method type, |
|
111 * and the resulting combined method handle will also be of this type. |
|
112 * @see MethodHandles#guardWithTest |
|
113 */ |
|
114 public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) { |
|
115 if (mcs.getTarget() == K_false) |
|
116 return fallback; // already invalid |
|
117 return MethodHandles.guardWithTest(mcsInvoker, target, fallback); |
|
118 } |
|
119 |
|
120 /** Set all of the given switchers into the invalid state. */ |
|
121 public static void invalidateAll(Switcher[] switchers) { |
|
122 MutableCallSite[] sites = new MutableCallSite[switchers.length]; |
|
123 int fillp = 0; |
|
124 for (Switcher switcher : switchers) { |
|
125 sites[fillp++] = switcher.mcs; |
|
126 switcher.mcs.setTarget(K_false); |
|
127 } |
|
128 MutableCallSite.sync(sites); |
|
129 } |
|
130 } |
|