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 }