1 /* 2 * Copyright (c) 2007, 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 package nsk.share.locks; 24 25 import java.util.*; 26 import java.util.concurrent.locks.ReentrantLock; 27 28 import nsk.share.Consts; 29 import nsk.share.Log; 30 import nsk.share.TestBug; 31 import nsk.share.TestJNIError; 32 import nsk.share.Wicket; 33 34 /* 35 Thread with possibility acquiring monitors in different ways: 36 - entering synchronized method 37 - entering synchronized method for thread object itself 38 - entering synchronized static method 39 - entering synchronized method for thread class itself 40 - entering synchronized block on non-static object 41 - entering synchronized block on non-static on thread object itself 42 - entering synchronized block on static object 43 - entering synchronized block on static thread object itself 44 - JNI MonitorEnter. 45 46 Description of required thread stack should be passed to LockingThread in constructor. 47 When started locking thread create required stack and sleep until not interrupted. 48 49 LockingThread can relinquish acquired monitors in follows ways: 50 - relinquish single monitor through Object.wait - relinquishMonitor(int monitorIndex), 51 - relinquish single monitor through exiting from synchronized blocks/methods or through JNI MonitorExit - exitSingleFrame(), 52 - relinquish all monitors(exit from all synchronized blocks/methods) - stopLockingThread() 53 54 Debug information about each acquired/relinquished monitor is stored and can be obtained through getMonitorsInfo(). 55 56 To be sure that LockingThread have reached required state call method LockingThread.waitState(). 57 58 Usage example: 59 60 List<String> stackFramesDescription = new ArrayList<String>(); 61 stackFramesDescription.add(LockingThread.SYNCHRONIZED_METHOD); 62 stackFramesDescription.add(LockingThread.SYNCHRONIZED_OBJECT_BLOCK); 63 64 LockingThread lockingThread = new LockingThread(log, stackFramesDescription); 65 66 lockingThread.start(); 67 68 // after calling waitState() LockingThread should complete stack creation 69 lockingThread.waitState(); 70 71 lockingThread.exitSingleFrame(); 72 73 // after calling waitState() LockingThread should complete exit from stack frame 74 lockingThread.waitState(); 75 */ 76 public class LockingThread extends Thread { 77 // native part uses TestJNIError class 78 private static final Class<?> jniErrorKlass = TestJNIError.class; 79 static { 80 try { 81 System.loadLibrary("LockingThread"); 82 } catch (UnsatisfiedLinkError e) { 83 System.out.println("Unexpected UnsatisfiedLinkError on loading library 'LockingThread'"); 84 e.printStackTrace(System.out); 85 System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED); 86 } 87 } 88 89 /* 90 * Information about acquired monitor 91 */ 92 public static class DebugMonitorInfo { 93 public DebugMonitorInfo(Object monitor, int stackDepth, Thread thread, boolean isNative) { 94 this.monitor = monitor; 95 this.stackDepth = stackDepth; 96 this.thread = thread; 97 this.isNative = isNative; 98 } 99 100 public Object monitor; 101 102 public int stackDepth; 103 104 public Thread thread; 105 106 boolean isNative; 107 } 108 109 // acquire JNI monitor through JNIMonitorEnter() 110 public static final String JNI_MONITOR_ENTER = "JNI_MONITOR_ENTER"; 111 112 // entering synchronized static method 113 public static final String SYNCHRONIZED_STATIC_METHOD = "SYNCHRONIZED_STATIC_METHOD"; 114 115 // entering synchronized static method for thread class itself 116 public static final String SYNCHRONIZED_STATIC_THREAD_METHOD = "SYNCHRONIZED_STATIC_THREAD_METHOD"; 117 118 // entering synchronized method 119 public static final String SYNCHRONIZED_METHOD = "SYNCHRONIZED_METHOD"; 120 121 // entering synchronized method for thread object itself 122 public static final String SYNCHRONIZED_THREAD_METHOD = "SYNCHRONIZED_THREAD_METHOD"; 123 124 // entering synchronized block for thread object itself 125 public static final String SYNCHRONIZED_THIS_BLOCK = "SYNCHRONIZED_THIS_BLOCK"; 126 127 // entering synchronized block 128 public static final String SYNCHRONIZED_OBJECT_BLOCK = "SYNCHRONIZED_OBJECT_BLOCK"; 129 130 // entering synchronized block on static object 131 public static final String SYNCHRONIZED_BLOCK_STATIC_OBJECT = "SYNCHRONIZED_BLOCK_STATIC_OBJECT"; 132 133 // entering synchronized block on static thread object itself 134 public static final String SYNCHRONIZED_BLOCK_STATIC_THREAD_OBJECT = "SYNCHRONIZED_BLOCK_STATIC_THREAD_OBJECT"; 135 136 // entering frame without monitor acquiring 137 public static final String FRAME_WITHOUT_LOCK = "FRAME_WITHOUT_LOCK"; 138 139 // all acquired monitors 140 private List<DebugMonitorInfo> monitorsInfo = new ArrayList<DebugMonitorInfo>(); 141 142 // This parameter should be passed in constructor 143 // It describe how many locks and in which way LockingThread should acquire 144 private List<String> stackFramesDescription; 145 146 private Log log; 147 148 // is during LockingThread's operations any errors occurred 149 private boolean executedWithErrors; 150 151 public boolean isExecutedWithErrors() { 152 return executedWithErrors; 153 } 154 155 public LockingThread(Log log, List<String> stackFramesDescription) { 156 this.log = log; 157 this.stackFramesDescription = stackFramesDescription; 158 } 159 160 // return array containing all acquired monitors 161 public DebugMonitorInfo[] getMonitorsInfo(boolean returnJNIMonitors) { 162 Map<Object, DebugMonitorInfo> result = new HashMap<Object, DebugMonitorInfo>(); 163 164 for (int i = monitorsInfo.size() - 1; i >= 0; i--) { 165 DebugMonitorInfo monitorInfo = monitorsInfo.get(i); 166 167 if ((returnJNIMonitors || !monitorInfo.isNative) && 168 169 // don't return relinquished monitors 170 (monitorInfo.monitor != relinquishedMonitor) && 171 172 // return only last monitor occurrence 173 !result.containsKey(monitorInfo.monitor)) { 174 result.put(monitorInfo.monitor, monitorInfo); 175 } 176 } 177 178 return result.values().toArray(new DebugMonitorInfo[] {}); 179 } 180 181 void log(String message) { 182 log.display(Thread.currentThread().getName() + ": " + message); 183 } 184 185 // add debug information about acquired monitor 186 void addMonitorInfo(DebugMonitorInfo monitorInfo) { 187 monitorsInfo.add(monitorInfo); 188 } 189 190 // remove debug information about acquired monitor (also should update information about stack depth) 191 void removeMonitorInfo(DebugMonitorInfo removedMonitor) { 192 for (DebugMonitorInfo monitor : monitorsInfo) { 193 if (monitor.stackDepth > removedMonitor.stackDepth) 194 monitor.stackDepth -= 2; 195 } 196 197 monitorsInfo.remove(removedMonitor); 198 } 199 200 // used for stack frames creation 201 private int currentIndex; 202 203 // Recursive function used for stack frames creation 204 205 // For example if LockingThread should acquire 1 monitor through synchronized block 206 // and 1 monitor through synchronized method pass list with values SYNCHRONIZED_METHOD and SYNCHRONIZED_OBJECT_BLOCK 207 // to the constructor and after running LockingThread will have following stack frames: 208 209 // run() 210 // createStackFrame() 211 // ClassWithSynchronizedMethods().synchronizedMethod() // monitor for instance of ClassWithSynchronizedMethods is acquired here 212 // createStackFrame() 213 // synchronizedObjectBlock() // monitor for instance of Object is acquired here 214 // createStackFrame() 215 // doWait() 216 // sleep() 217 218 // When LockingThread have created required stack frame it calls method doWait() and sleep(Long.MAX_VALUE) 219 220 // If LockingThread should relinquish one of the acquired monitors it should be interrupted and after 221 // interrupting should call 'wait()' for specified monitor, and for this example LockingThread will have 222 // following stack frames: 223 224 // run() 225 // createStackFrame() 226 // ClassWithSynchronizedMethods().synchronizedMethod() // monitor for instance of ClassWithSynchronizedMethods is acquired here 227 // createStackFrame() 228 // synchronizedObjectBlock() // monitor for instance of Object is acquired here 229 // createStackFrame() 230 // doWait() 231 // relinquishedMonitor.wait() 232 233 // LockingThread still holds all other locks because of it didn't exit from corresponding synchronized methods and blocks. 234 // To let LockingThread acquire relinquished monitor 'relinquishedMonitor.notifyAll()' should be called, after this 235 // LockingThread will acquire this monitor again because of it still in corresponding synchronized method or block and 236 // it will have again such stack frames: 237 238 // run() 239 // createStackFrame() 240 // ClassWithSynchronizedMethods().synchronizedMethod() // monitor for instance of ClassWithSynchronizedMethods is acquired here 241 // createStackFrame() 242 // synchronizedObjectBlock() // monitor for instance of Object is acquired here 243 // createStackFrame() 244 // doWait() 245 // sleep() 246 void createStackFrame() { 247 if (currentIndex < stackFramesDescription.size()) { 248 String frameDescription = stackFramesDescription.get(currentIndex); 249 250 currentIndex++; 251 252 if (frameDescription.equals(JNI_MONITOR_ENTER)) { 253 // for JNI monitors -1 is returned as stack depth 254 int currentStackDepth = -1; 255 Object object = new Object(); 256 DebugMonitorInfo monitorInfo = new DebugMonitorInfo(object, currentStackDepth, this, true); 257 addMonitorInfo(monitorInfo); 258 log("Enter JNI monitor"); 259 nativeJNIMonitorEnter(object); 260 log("Exit JNI monitor"); 261 removeMonitorInfo(monitorInfo); 262 } else if (frameDescription.equals(SYNCHRONIZED_BLOCK_STATIC_OBJECT)) { 263 synchronizedBlockStaticObject(); 264 } else if (frameDescription.equals(SYNCHRONIZED_BLOCK_STATIC_THREAD_OBJECT)) { 265 synchronizedBlockStaticThreadObject(); 266 } else if (frameDescription.equals(SYNCHRONIZED_METHOD)) { 267 new ClassWithSynchronizedMethods().synchronizedMethod(this); 268 } else if (frameDescription.equals(SYNCHRONIZED_THREAD_METHOD)) { 269 synchronizedMethod(); 270 } else if (frameDescription.equals(SYNCHRONIZED_STATIC_METHOD)) { 271 ClassWithSynchronizedMethods.synchronizedStaticMethod(this); 272 } else if (frameDescription.equals(SYNCHRONIZED_STATIC_THREAD_METHOD)) { 273 synchronizedStaticMethod(this); 274 } else if (frameDescription.equals(SYNCHRONIZED_THIS_BLOCK)) { 275 synchronizedThisBlock(); 276 } else if (frameDescription.equals(SYNCHRONIZED_OBJECT_BLOCK)) { 277 synchronizedObjectBlock(); 278 } else if (frameDescription.equals(FRAME_WITHOUT_LOCK)) { 279 frameWithoutLock(); 280 } else 281 throw new TestBug("Invalid stack frame description: " + frameDescription); 282 } else { 283 // required stack is created 284 ready(); 285 doWait(); 286 } 287 288 if (exitSingleFrame) { 289 if (currentIndex-- < stackFramesDescription.size()) { 290 // exit from single synchronized block/method 291 ready(); 292 doWait(); 293 } 294 } 295 } 296 297 public Object getRelinquishedMonitor() { 298 return relinquishedMonitor; 299 } 300 301 private Object relinquishedMonitor; 302 303 private Wicket waitStateWicket = new Wicket(); 304 305 private Thread.State requiredState; 306 307 public void waitState() { 308 // try wait with timeout to avoid possible hanging (if LockingThread have finished execution because of uncaught exception) 309 int result = waitStateWicket.waitFor(60000); 310 311 if (result != 0) { 312 throw new TestBug("Locking thread can't reach required state (waitStateWicket wasn't unlocked)"); 313 } 314 315 if (requiredState == null) 316 throw new TestBug("Required state not specified"); 317 318 long startTime = System.currentTimeMillis(); 319 320 // additional check to be sure that LockingThread acquired state 321 while (this.getState() != requiredState) { 322 323 // try wait with timeout to avoid possible hanging if something will go wrong 324 if ((System.currentTimeMillis() - startTime) > 60000) { 325 throw new TestBug("Locking thread can't reach required state (state: " + requiredState + " wasn't reached) in 1 minute"); 326 } 327 328 Thread.yield(); 329 } 330 331 requiredState = null; 332 333 Object relinquishedMonitor = getRelinquishedMonitor(); 334 /* 335 * Changing thread state and release of lock is not single/atomic operation. 336 * As result there is a potential race when thread state (LockingThread) has 337 * been changed but the lock has not been released yet. To avoid this current 338 * thread is trying to acquire the same monitor, so current thread proceeds 339 * execution only when monitor has been really relinquished by LockingThread. 340 */ 341 if (relinquishedMonitor != null) { 342 synchronized (relinquishedMonitor) { 343 } 344 } 345 } 346 347 // LockingThread acquired required state 348 private void ready() { 349 waitStateWicket.unlockAll(); 350 } 351 352 // is LockingThread should relinquish single monitor 353 private volatile boolean relinquishMonitor; 354 355 private int relinquishedMonitorIndex; 356 357 // relinquish single monitor with given index through Object.wait() 358 public void relinquishMonitor(int index) { 359 if (index >= monitorsInfo.size()) { 360 throw new TestBug("Invalid monitor index: " + index); 361 } 362 363 requiredState = Thread.State.WAITING; 364 waitStateWicket = new Wicket(); 365 relinquishMonitor = true; 366 relinquishedMonitorIndex = index; 367 368 interrupt(); 369 370 DebugMonitorInfo monitorInfo = monitorsInfo.get(relinquishedMonitorIndex); 371 372 if (monitorInfo == null) 373 throw new TestBug("Invalid monitor index: " + relinquishedMonitorIndex); 374 } 375 376 public void acquireRelinquishedMonitor() { 377 if (relinquishedMonitor == null) { 378 throw new TestBug("There is no relinquished monitor"); 379 } 380 381 // Set requiredState to 'Thread.State.TIMED_WAITING' because of LockingThread call 382 // Thread.sleep(Long.MAX_VALUE) after monitor acquiring 383 requiredState = Thread.State.TIMED_WAITING; 384 385 waitStateWicket = new Wicket(); 386 relinquishMonitor = false; 387 388 synchronized (relinquishedMonitor) { 389 relinquishedMonitor.notifyAll(); 390 } 391 } 392 393 public void stopLockingThread() { 394 requiredState = Thread.State.TIMED_WAITING; 395 396 waitStateWicket = new Wicket(); 397 exitSingleFrame = false; 398 399 interrupt(); 400 } 401 402 // is thread should exit from single synchronized block/method 403 private boolean exitSingleFrame; 404 405 public void exitSingleFrame() { 406 requiredState = Thread.State.TIMED_WAITING; 407 408 waitStateWicket = new Wicket(); 409 exitSingleFrame = true; 410 411 interrupt(); 412 } 413 414 // LockingThread call this method when required state is reached 415 private void doWait() { 416 while (true) { 417 try { 418 Thread.sleep(Long.MAX_VALUE); 419 } catch (InterruptedException e) { 420 // expected exception, LockingThread should be interrupted to change state 421 } 422 423 // if single monitor should be relinquished through Object.wait() 424 if (relinquishMonitor) { 425 try { 426 DebugMonitorInfo monitorInfo = monitorsInfo.get(relinquishedMonitorIndex); 427 428 if (monitorInfo == null) 429 throw new TestBug("Invalid monitor index: " + relinquishedMonitorIndex); 430 431 relinquishedMonitor = monitorInfo.monitor; 432 433 log("Relinquish monitor: " + relinquishedMonitor); 434 435 // monitor is relinquished 436 ready(); 437 438 // Really monitor is relinquished only when LockingThread calls relinquishedMonitor.wait(0) below, 439 // but to be sure that LockingThread have reached required state method waitState() should be called 440 // and this method waits when LockingThred change state to 'Thread.State.WAITING' 441 442 while (relinquishMonitor) 443 relinquishedMonitor.wait(0); 444 445 log("Acquire relinquished monitor: " + relinquishedMonitor); 446 } catch (Exception e) { 447 executedWithErrors = true; 448 log("Unexpected exception: " + e); 449 e.printStackTrace(log.getOutStream()); 450 } 451 452 relinquishedMonitor = null; 453 454 // monitor is acquired again 455 //(becuase we still are located in the frame where lock was acquired before we relinquished it) 456 ready(); 457 } else 458 // exit from frame 459 break; 460 } 461 } 462 463 public void run() { 464 // LockingThread call Thread.sleep() when required stack frame was created 465 requiredState = Thread.State.TIMED_WAITING; 466 467 createStackFrame(); 468 469 // thread relinquished all monitors 470 ready(); 471 doWait(); 472 } 473 474 static synchronized void synchronizedStaticMethod(LockingThread lockingThread) { 475 int currentStackDepth = lockingThread.expectedDepth(); 476 477 lockingThread.log("Enter synchronized static thread method"); 478 479 DebugMonitorInfo monitorInfo = new DebugMonitorInfo(LockingThread.class, currentStackDepth, lockingThread, false); 480 lockingThread.addMonitorInfo(monitorInfo); 481 lockingThread.createStackFrame(); 482 lockingThread.removeMonitorInfo(monitorInfo); 483 484 lockingThread.log("Exit synchronized static thread method"); 485 } 486 487 // calculate stack depth at which monitor was acquired 488 int expectedDepth() { 489 // for each monitor call 2 methods: createStackFrame() and method which acquire monitor 490 // + when stack creation is finished call 3 methods: createStackFrame()->doWait()->sleep() 491 return (stackFramesDescription.size() - currentIndex) * 2 + 3; 492 } 493 494 private native void nativeJNIMonitorEnter(Object object); 495 496 synchronized void synchronizedMethod() { 497 int currentStackDepth = expectedDepth(); 498 499 log("Enter synchronized thread method"); 500 501 DebugMonitorInfo monitorInfo = new DebugMonitorInfo(this, currentStackDepth, this, false); 502 addMonitorInfo(monitorInfo); 503 createStackFrame(); 504 removeMonitorInfo(monitorInfo); 505 506 log("Exit synchronized thread method"); 507 } 508 509 void synchronizedThisBlock() { 510 int currentStackDepth = expectedDepth(); 511 512 log("Enter synchronized(this) block"); 513 514 synchronized (this) { 515 DebugMonitorInfo monitorInfo = new DebugMonitorInfo(this, currentStackDepth, this, false); 516 addMonitorInfo(monitorInfo); 517 createStackFrame(); 518 removeMonitorInfo(monitorInfo); 519 } 520 521 log("Exit synchronized(this) block"); 522 } 523 524 private static Object staticObject; 525 526 // 'staticObjectInitializingLock' is used in synchronizedBlockStaticObject() and synchronizedBlockStaticThreadObject(). 527 // In this methods LockingThread initializes static object and enters in synchronized block 528 // for this static object, this actions are not thread safe(because of static fields are used) and 'staticObjectInitializingLock' 529 // is used to prevent races 530 private static ReentrantLock staticObjectInitializingLock = new ReentrantLock(); 531 532 void synchronizedBlockStaticObject() { 533 int currentStackDepth = expectedDepth(); 534 535 // initializing of 'staticObject' and entering to the synchronized(staticObject) block should be thread safe 536 staticObjectInitializingLock.lock(); 537 538 staticObject = new Object(); 539 540 log("Enter synchronized(static object) block"); 541 542 synchronized (staticObject) { 543 // thread unsafe actions was done 544 staticObjectInitializingLock.unlock(); 545 546 DebugMonitorInfo monitorInfo = new DebugMonitorInfo(staticObject, currentStackDepth, this, false); 547 addMonitorInfo(monitorInfo); 548 createStackFrame(); 549 removeMonitorInfo(monitorInfo); 550 } 551 552 log("Exit synchronized(static object) block"); 553 } 554 555 private static LockingThread staticLockingThread; 556 557 void synchronizedBlockStaticThreadObject() { 558 int currentStackDepth = expectedDepth(); 559 560 // initializing of 'staticLockingThread' and entering to the synchronized(staticLockingThread) block should be thread safe 561 staticObjectInitializingLock.lock(); 562 563 staticLockingThread = this; 564 565 log("Enter synchronized(static thread object) block"); 566 567 synchronized (staticLockingThread) { 568 // thread unsafe actions was done 569 staticObjectInitializingLock.unlock(); 570 571 DebugMonitorInfo monitorInfo = new DebugMonitorInfo(staticLockingThread, currentStackDepth, this, false); 572 addMonitorInfo(monitorInfo); 573 createStackFrame(); 574 removeMonitorInfo(monitorInfo); 575 } 576 577 log("Exit synchronized(static thread object) block"); 578 } 579 580 void synchronizedObjectBlock() { 581 int currentStackDepth = expectedDepth(); 582 583 Object object = new Object(); 584 585 log("Enter synchronized(object) block"); 586 587 synchronized (object) { 588 DebugMonitorInfo monitorInfo = new DebugMonitorInfo(object, currentStackDepth, this, false); 589 addMonitorInfo(monitorInfo); 590 createStackFrame(); 591 removeMonitorInfo(monitorInfo); 592 } 593 594 log("Exit synchronized(object) block"); 595 } 596 597 private void frameWithoutLock() { 598 log("Enter frameWithoutLock"); 599 600 createStackFrame(); 601 602 for (DebugMonitorInfo monitor : monitorsInfo) 603 monitor.stackDepth -= 2; 604 605 log("Exit frameWithoutLock"); 606 } 607 } 608 609 //class containing synchronized method and synchronized static method, used by LockingThread 610 class ClassWithSynchronizedMethods { 611 public synchronized void synchronizedMethod(LockingThread lockingThread) { 612 int currentStackDepth = lockingThread.expectedDepth(); 613 614 lockingThread.log("Enter synchronized method"); 615 616 LockingThread.DebugMonitorInfo monitorInfo = new LockingThread.DebugMonitorInfo(this, currentStackDepth, lockingThread, false); 617 lockingThread.addMonitorInfo(monitorInfo); 618 lockingThread.createStackFrame(); 619 lockingThread.removeMonitorInfo(monitorInfo); 620 621 lockingThread.log("Exit synchronized method"); 622 } 623 624 public static synchronized void synchronizedStaticMethod(LockingThread lockingThread) { 625 int currentStackDepth = lockingThread.expectedDepth(); 626 627 lockingThread.log("Enter synchronized static method"); 628 629 LockingThread.DebugMonitorInfo monitorInfo = new LockingThread.DebugMonitorInfo(ClassWithSynchronizedMethods.class, currentStackDepth, 630 lockingThread, false); 631 lockingThread.addMonitorInfo(monitorInfo); 632 lockingThread.createStackFrame(); 633 lockingThread.removeMonitorInfo(monitorInfo); 634 635 lockingThread.log("Exit synchronized static method"); 636 } 637 }