Mercurial > hg > graal-compiler
comparison graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ThreadSafetyTest.java @ 14629:ba52fbec5b6c
Truffle: atomic node rewriting
make Node#replace thread-safe
add Node#atomic helper method for atomic tree operations
add basic test for thread-safety
author | Andreas Woess <andreas.woess@jku.at> |
---|---|
date | Thu, 20 Mar 2014 01:29:19 +0100 |
parents | |
children | 69375786ef70 |
comparison
equal
deleted
inserted
replaced
14628:a08b8694f556 | 14629:ba52fbec5b6c |
---|---|
1 /* | |
2 * Copyright (c) 2014, 2014, 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. | |
8 * | |
9 * This code is distributed in the hope that it will be useful, but WITHOUT | |
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 * version 2 for more details (a copy is included in the LICENSE file that | |
13 * accompanied this code). | |
14 * | |
15 * You should have received a copy of the GNU General Public License version | |
16 * 2 along with this work; if not, write to the Free Software Foundation, | |
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 * | |
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
20 * or visit www.oracle.com if you need additional information or have any | |
21 * questions. | |
22 */ | |
23 package com.oracle.truffle.api.test; | |
24 | |
25 import static org.junit.Assert.*; | |
26 | |
27 import java.io.*; | |
28 import java.util.*; | |
29 import java.util.concurrent.*; | |
30 import java.util.concurrent.atomic.*; | |
31 | |
32 import org.junit.*; | |
33 | |
34 import com.oracle.truffle.api.*; | |
35 import com.oracle.truffle.api.frame.*; | |
36 import com.oracle.truffle.api.nodes.*; | |
37 | |
38 /** | |
39 * Test node rewriting in a tree shared across multiple threads (run with -ea). | |
40 */ | |
41 public class ThreadSafetyTest { | |
42 | |
43 @Test | |
44 public void test() throws InterruptedException { | |
45 TruffleRuntime runtime = Truffle.getRuntime(); | |
46 TestRootNode rootNode1 = new TestRootNode(new RewritingNode(new RewritingNode(new RewritingNode(new RewritingNode(new RewritingNode(new ConstNode(42))))))); | |
47 final CallTarget target1 = runtime.createCallTarget(rootNode1); | |
48 NodeUtil.verify(rootNode1); | |
49 | |
50 RecursiveCallNode callNode = new RecursiveCallNode(new ConstNode(42)); | |
51 TestRootNode rootNode2 = new TestRootNode(new RewritingNode(new RewritingNode(new RewritingNode(new RewritingNode(new RewritingNode(callNode)))))); | |
52 final CallTarget target2 = runtime.createCallTarget(rootNode2); | |
53 callNode.setCallNode(runtime.createCallNode(target2)); | |
54 NodeUtil.verify(rootNode2); | |
55 | |
56 testTarget(target1, 47, 1_000_000); | |
57 testTarget(target2, 72, 1_000_000); | |
58 } | |
59 | |
60 private static void testTarget(final CallTarget target, final int expectedResult, final int numberOfIterations) throws InterruptedException { | |
61 ExecutorService executorService = Executors.newFixedThreadPool(20); | |
62 final AtomicInteger ai = new AtomicInteger(); | |
63 for (int i = 0; i < numberOfIterations; i++) { | |
64 executorService.submit(new Runnable() { | |
65 public void run() { | |
66 try { | |
67 Object result = target.call(new TestArguments(5)); | |
68 assertEquals(expectedResult, result); | |
69 ai.incrementAndGet(); | |
70 } catch (Throwable t) { | |
71 PrintStream out = System.out; | |
72 out.println(t); | |
73 } | |
74 } | |
75 }); | |
76 } | |
77 executorService.shutdown(); | |
78 executorService.awaitTermination(30, TimeUnit.SECONDS); | |
79 assertEquals(numberOfIterations, ai.get()); | |
80 } | |
81 | |
82 static class TestArguments extends Arguments { | |
83 final int arg; | |
84 | |
85 public TestArguments(int arg) { | |
86 this.arg = arg; | |
87 } | |
88 } | |
89 | |
90 static class TestRootNode extends RootNode { | |
91 | |
92 @Child private ValueNode child; | |
93 | |
94 public TestRootNode(ValueNode child) { | |
95 super(null); | |
96 this.child = child; | |
97 } | |
98 | |
99 @Override | |
100 public Object execute(VirtualFrame frame) { | |
101 return child.execute(frame); | |
102 } | |
103 } | |
104 | |
105 abstract static class ValueNode extends Node { | |
106 | |
107 public ValueNode() { | |
108 super(null); | |
109 } | |
110 | |
111 abstract int execute(VirtualFrame frame); | |
112 } | |
113 | |
114 static class RewritingNode extends ValueNode { | |
115 | |
116 @Child private ValueNode child; | |
117 private final Random random; | |
118 | |
119 public RewritingNode(ValueNode child) { | |
120 this(child, new Random()); | |
121 } | |
122 | |
123 public RewritingNode(ValueNode child, Random random) { | |
124 this.child = child; | |
125 this.random = random; | |
126 } | |
127 | |
128 @Override | |
129 int execute(VirtualFrame frame) { | |
130 boolean replace = random.nextBoolean(); | |
131 if (replace) { | |
132 ValueNode newNode = this.replace(new OtherRewritingNode(child, random)); | |
133 return newNode.execute(frame); | |
134 } | |
135 return 1 + child.execute(frame); | |
136 } | |
137 } | |
138 | |
139 static class OtherRewritingNode extends ValueNode { | |
140 | |
141 @Child private ValueNode child; | |
142 private final Random random; | |
143 | |
144 public OtherRewritingNode(ValueNode child, Random random) { | |
145 this.child = child; | |
146 this.random = random; | |
147 } | |
148 | |
149 @Override | |
150 int execute(VirtualFrame frame) { | |
151 boolean replace = random.nextBoolean(); | |
152 if (replace) { | |
153 ValueNode newNode = this.replace(new RewritingNode(child, random)); | |
154 return newNode.execute(frame); | |
155 } | |
156 return 1 + child.execute(frame); | |
157 } | |
158 } | |
159 | |
160 static class ConstNode extends ValueNode { | |
161 | |
162 private final int value; | |
163 | |
164 ConstNode(int value) { | |
165 this.value = value; | |
166 } | |
167 | |
168 @Override | |
169 int execute(VirtualFrame frame) { | |
170 return value; | |
171 } | |
172 } | |
173 | |
174 static class RecursiveCallNode extends ValueNode { | |
175 @Child CallNode callNode; | |
176 @Child private ValueNode valueNode; | |
177 | |
178 RecursiveCallNode(ValueNode value) { | |
179 this.valueNode = value; | |
180 } | |
181 | |
182 @Override | |
183 int execute(VirtualFrame frame) { | |
184 int arg = frame.getArguments(TestArguments.class).arg; | |
185 if (arg > 0) { | |
186 return (int) callNode.call(frame.pack(), new TestArguments(arg - 1)); | |
187 } else { | |
188 return valueNode.execute(frame); | |
189 } | |
190 } | |
191 | |
192 void setCallNode(CallNode callNode) { | |
193 this.callNode = insert(callNode); | |
194 } | |
195 } | |
196 } |