1 /*
2  * Copyright (c) 2012, 2018, 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 
24 
25 package org.graalvm.compiler.replacements.test;
26 
27 import org.junit.Test;
28 
29 import org.graalvm.compiler.core.test.GraalCompilerTest;
30 import org.graalvm.compiler.nodes.ValueNode;
31 import org.graalvm.compiler.replacements.BoxingSnippets;
32 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
33 
34 public class MonitorTest extends GraalCompilerTest {
35 
36     @Test
test0()37     public void test0() {
38         test("lockObjectSimple", new Object(), new Object());
39         test("lockObjectSimple", new Object(), null);
40         test("lockObjectSimple", null, null);
41     }
42 
43     @Test
test01()44     public void test01() {
45         test("lockThisSimple", "test1", new Object());
46         test("lockThisSimple", "test1", null);
47     }
48 
49     @Test
test02()50     public void test02() {
51         test("lockObjectSimple", null, "test1");
52     }
53 
54     @Test
test101()55     public void test101() {
56         test("lockObject", new Object(), "test1", new String[1]);
57     }
58 
59     @Test
test102()60     public void test102() {
61         test("lockObject", null, "test1_1", new String[1]);
62     }
63 
64     @Test
test2()65     public void test2() {
66         test("lockThis", "test2", new String[1]);
67     }
68 
69     /**
70      * Tests monitor operations on {@link PartialEscapePhase virtual objects}.
71      */
72     @Test
test3()73     public void test3() {
74         test("lockLocalObject", "test3", new String[1]);
75     }
76 
77     /**
78      * Tests recursive locking of objects which should be biasable.
79      */
80     @Test
test4()81     public void test4() {
82         Chars src = new Chars("1234567890".toCharArray());
83         Chars dst = new Chars(src.data.length);
84         test("copyObj", src, dst, 100);
85     }
86 
87     /**
88      * Tests recursive locking of objects which do not appear to be biasable.
89      */
90     @Test
test5()91     public void test5() {
92         char[] src = "1234567890".toCharArray();
93         char[] dst = new char[src.length];
94         test("copyArr", src, dst, 100);
95     }
96 
97     /**
98      * Extends {@link #test4()} with contention.
99      */
100     @Test
test6()101     public void test6() {
102         Chars src = new Chars("1234567890".toCharArray());
103         Chars dst = new Chars(src.data.length);
104         int n = Runtime.getRuntime().availableProcessors();
105         testN(n, "copyObj", src, dst, 100);
106     }
107 
108     /**
109      * Extends {@link #test5()} with contention.
110      */
111     @Test
test7()112     public void test7() {
113         char[] src = "1234567890".toCharArray();
114         char[] dst = new char[src.length];
115         int n = Math.min(32, Runtime.getRuntime().availableProcessors());
116         testN(n, "copyArr", src, dst, 100);
117     }
118 
setAndGet(String[] box, String value)119     private static String setAndGet(String[] box, String value) {
120         synchronized (box) {
121             box[0] = null;
122         }
123 
124         // Do a GC while a object is locked (by the caller)
125         System.gc();
126 
127         synchronized (box) {
128             box[0] = value;
129         }
130         return box[0];
131     }
132 
lockObjectSimple(Object o, Object value)133     public static Object lockObjectSimple(Object o, Object value) {
134         synchronized (o) {
135             value.hashCode();
136             return value;
137         }
138     }
139 
lockThisSimple(String value, Object o)140     public String lockThisSimple(String value, Object o) {
141         synchronized (this) {
142             synchronized (value) {
143                 o.hashCode();
144                 return value;
145             }
146         }
147     }
148 
lockObject(Object o, String value, String[] box)149     public static String lockObject(Object o, String value, String[] box) {
150         synchronized (o) {
151             return setAndGet(box, value);
152         }
153     }
154 
lockThis(String value, String[] box)155     public String lockThis(String value, String[] box) {
156         synchronized (this) {
157             return setAndGet(box, value);
158         }
159     }
160 
lockLocalObject(String value, String[] box)161     public static String lockLocalObject(String value, String[] box) {
162         Object o = new Object();
163         synchronized (o) {
164             return setAndGet(box, value);
165         }
166     }
167 
168     static class Chars {
169 
170         final char[] data;
171 
Chars(int size)172         Chars(int size) {
173             this.data = new char[size];
174         }
175 
Chars(char[] data)176         Chars(char[] data) {
177             this.data = data;
178         }
179     }
180 
copyObj(Chars src, Chars dst, int n)181     public static String copyObj(Chars src, Chars dst, int n) {
182         for (int j = 0; j < n; j++) {
183             for (int i = 0; i < src.data.length; i++) {
184                 synchronized (src) {
185                     synchronized (dst) {
186                         synchronized (src) {
187                             synchronized (dst) {
188                                 dst.data[i] = src.data[i];
189                             }
190                         }
191                     }
192                 }
193             }
194         }
195         return new String(dst.data);
196     }
197 
copyArr(char[] src, char[] dst, int n)198     public static String copyArr(char[] src, char[] dst, int n) {
199         for (int j = 0; j < n; j++) {
200             for (int i = 0; i < src.length; i++) {
201                 synchronized (src) {
202                     synchronized (dst) {
203                         synchronized (src) {
204                             synchronized (dst) {
205                                 dst[i] = src[i];
206                             }
207                         }
208                     }
209                 }
210             }
211         }
212         return new String(dst);
213     }
214 
lockBoxedLong(long value)215     public static String lockBoxedLong(long value) {
216         Long lock = value;
217         synchronized (lock) {
218             return lock.toString();
219         }
220     }
221 
222     /**
223      * Reproduces issue reported in https://github.com/graalvm/graal-core/issues/201. The stamp in
224      * the PiNode returned by {@link BoxingSnippets#longValueOf} was overwritten when the node was
225      * subsequently canonicalized because {@code PiNode.computeValue()} ignored the
226      * {@link ValueNode#stamp(org.graalvm.compiler.nodes.NodeView)} field and used the
227      * {@code PiNode.piStamp} field.
228      */
229     @Test
test8()230     public void test8() {
231         test("lockBoxedLong", 5L);
232         test("lockBoxedLong", Long.MAX_VALUE - 1);
233         test("lockBoxedLong", Long.MIN_VALUE + 1);
234     }
235 }
236