1 /* 2 * Copyright (c) 2022, 2023, 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 * @test 26 * @summary Test Thread.yield submits the virtual thread task to the expected queue 27 * @requires vm.continuations 28 * @run junit/othervm -Djdk.virtualThreadScheduler.maxPoolSize=1 YieldQueuing 29 */ 30 31 import java.lang.invoke.MethodHandles; 32 import java.util.List; 33 import java.util.concurrent.CopyOnWriteArrayList; 34 import java.util.concurrent.atomic.AtomicBoolean; 35 import java.util.concurrent.locks.LockSupport; 36 37 import org.junit.jupiter.api.Test; 38 import org.junit.jupiter.api.BeforeAll; 39 import static org.junit.jupiter.api.Assertions.*; 40 41 class YieldQueuing { 42 43 @BeforeAll 44 static void setup() throws Exception { 45 // waiting for LockSupport to be initialized can change the scheduling 46 MethodHandles.lookup().ensureInitialized(LockSupport.class); 47 } 48 49 /** 50 * Test Thread.yield submits the task for the current virtual thread to a scheduler 51 * submission queue when there are no tasks in the local queue. 52 */ 53 @Test 54 void testYieldWithEmptyLocalQueue() throws Exception { 55 var list = new CopyOnWriteArrayList<String>(); 56 57 var threadsStarted = new AtomicBoolean(); 58 59 var threadA = Thread.ofVirtual().unstarted(() -> { 60 // pin thread until task for B is in submission queue 61 while (!threadsStarted.get()) { 62 Thread.onSpinWait(); 63 } 64 65 list.add("A"); 66 Thread.yield(); // push task for A to submission queue, B should run 67 list.add("A"); 68 }); 69 70 var threadB = Thread.ofVirtual().unstarted(() -> { 71 list.add("B"); 72 }); 73 74 // push tasks for A and B to submission queue 75 threadA.start(); 76 threadB.start(); 77 78 // release A 79 threadsStarted.set(true); 80 81 // wait for result 82 threadA.join(); 83 threadB.join(); 84 assertEquals(list, List.of("A", "B", "A")); 85 } 86 87 /** 88 * Test Thread.yield submits the task for the current virtual thread to the local 89 * queue when there are tasks in the local queue. 90 */ 91 @Test 92 void testYieldWithNonEmptyLocalQueue() throws Exception { 93 var list = new CopyOnWriteArrayList<String>(); 94 95 var threadsStarted = new AtomicBoolean(); 96 97 var threadA = Thread.ofVirtual().unstarted(() -> { 98 // pin thread until tasks for B and C are in submission queue 99 while (!threadsStarted.get()) { 100 Thread.onSpinWait(); 101 } 102 103 list.add("A"); 104 LockSupport.park(); // B should run 105 list.add("A"); 106 }); 107 108 var threadB = Thread.ofVirtual().unstarted(() -> { 109 list.add("B"); 110 LockSupport.unpark(threadA); // push task for A to local queue 111 Thread.yield(); // push task for B to local queue, A should run 112 list.add("B"); 113 }); 114 115 var threadC = Thread.ofVirtual().unstarted(() -> { 116 list.add("C"); 117 }); 118 119 // push tasks for A, B and C to submission queue 120 threadA.start(); 121 threadB.start(); 122 threadC.start(); 123 124 // release A 125 threadsStarted.set(true); 126 127 // wait for result 128 threadA.join(); 129 threadB.join(); 130 threadC.join(); 131 assertEquals(list, List.of("A", "B", "A", "B", "C")); 132 } 133 }