| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| public class Main { |
| |
| public static class InnerInitialized { |
| static int staticValue1 = 0; |
| static int staticValue2 = 1; |
| |
| static int $noinline$runHotMethod(boolean doComputation) { |
| if (doComputation) { |
| for (int i = 0; i < 100000; i++) { |
| staticValue1 += staticValue2; |
| } |
| } |
| return staticValue1; |
| } |
| |
| static { |
| // Make $noinline$runHotMethod hot so it gets compiled. The |
| // regression used to be that the JIT would incorrectly update the |
| // resolution entrypoint of the method. The entrypoint needs to stay |
| // the resolution entrypoint otherwise other threads may incorrectly |
| // execute static methods of the class while the class is still being |
| // initialized. |
| for (int i = 0; i < 10; i++) { |
| $noinline$runHotMethod(true); |
| } |
| |
| // Start another thread that will invoke a static method of InnerInitialized. |
| new Thread(new Runnable() { |
| public void run() { |
| for (int i = 0; i < 100000; i++) { |
| $noinline$runInternalHotMethod(false); |
| } |
| // Give some time for the JIT compiler to compile $noinline$runInternalHotMethod. |
| // Only compiled code invoke the entrypoint of an ArtMethod. |
| try { |
| Thread.sleep(1000); |
| } catch (Exception e) { |
| } |
| int value = $noinline$runInternalHotMethod(true); |
| if (value != 42) { |
| throw new Error("Expected 42, got " + value); |
| } |
| } |
| |
| public int $noinline$runInternalHotMethod(boolean invokeStaticMethod) { |
| if (invokeStaticMethod) { |
| // The bug used to be here: the compiled code of $noinline$runInternalHotMethod |
| // would invoke the entrypoint of $noinline$runHotMethod, which was incorrectly |
| // updated to the JIT entrypoint and therefore not hitting the resolution |
| // trampoline which would have waited for the class to be initialized. |
| return $noinline$runHotMethod(false); |
| } |
| return 0; |
| } |
| |
| }).start(); |
| // Give some time for the JIT compiler to compile runHotMethod, and also for the |
| // other thread to invoke $noinline$runHotMethod. |
| // This wait should be longer than the other thread's wait to make sure the other |
| // thread hits the $noinline$runHotMethod call before the initialization of |
| // InnerInitialized is finished. |
| try { |
| Thread.sleep(5000); |
| } catch (Exception e) { |
| } |
| staticValue1 = 42; |
| } |
| } |
| |
| public static void main(String[] args) throws Exception { |
| System.out.println(InnerInitialized.staticValue1); |
| } |
| } |