@@ -289,6 +289,7 @@ public static void main(String[] args) {
289289 // Relocking test cases
290290 new EARelockingSimpleTarget () .run ();
291291 new EARelockingSimpleWithAccessInOtherThreadTarget () .run ();
292+ new EARelockingSimpleWithAccessInOtherThread_02_DynamicCall_Target () .run ();
292293 new EARelockingRecursiveTarget () .run ();
293294 new EARelockingNestedInflatedTarget () .run ();
294295 new EARelockingNestedInflated_02Target () .run ();
@@ -413,6 +414,7 @@ protected void runTests() throws Exception {
413414 // Relocking test cases
414415 new EARelockingSimple () .run (this );
415416 new EARelockingSimpleWithAccessInOtherThread () .run (this );
417+ new EARelockingSimpleWithAccessInOtherThread_02_DynamicCall () .run (this );
416418 new EARelockingRecursive () .run (this );
417419 new EARelockingNestedInflated () .run (this );
418420 new EARelockingNestedInflated_02 () .run (this );
@@ -1851,6 +1853,95 @@ public int getExpectedIResult() {
18511853
18521854/////////////////////////////////////////////////////////////////////////////
18531855
1856+ // The debugger reads and publishes an object with eliminated locking to an instance field.
1857+ // A 2nd thread in the debuggee finds it there and changes its state using a synchronized method.
1858+ // Without eager relocking the accesses are unsynchronized which can be observed.
1859+ // This is a variant of EARelockingSimpleWithAccessInOtherThread with a dynamic call (not devirtualized).
1860+ class EARelockingSimpleWithAccessInOtherThread_02_DynamicCall extends EATestCaseBaseDebugger {
1861+
1862+ public void runTestCase () throws Exception {
1863+ BreakpointEvent bpe = resumeTo (TARGET_TESTCASE_BASE_NAME , "dontinline_brkpt" , "()V" );
1864+ printStack (bpe .thread ());
1865+ String l1ClassName = EARelockingSimpleWithAccessInOtherThread_02_DynamicCall_Target .SyncCounter .class .getName ();
1866+ ObjectReference ctr = getLocalRef (bpe .thread ().frame (2 ), l1ClassName , "l1" );
1867+ setField (testCase , "sharedCounter" , ctr );
1868+ terminateEndlessLoop ();
1869+ }
1870+ }
1871+
1872+ class EARelockingSimpleWithAccessInOtherThread_02_DynamicCall_Target extends EATestCaseBaseTarget {
1873+
1874+ public static final BrkPtDispatchA [] disp =
1875+ {new BrkPtDispatchA (), new BrkPtDispatchB (), new BrkPtDispatchC (), new BrkPtDispatchD ()};
1876+
1877+ public static class BrkPtDispatchA {
1878+ public EATestCaseBaseTarget testCase ;
1879+ public void dontinline_brkpt () { testCase .dontinline_brkpt (); }
1880+ }
1881+
1882+ public static class BrkPtDispatchB extends BrkPtDispatchA {
1883+ @ Override
1884+ public void dontinline_brkpt () { testCase .dontinline_brkpt (); }
1885+ }
1886+
1887+ public static class BrkPtDispatchC extends BrkPtDispatchA {
1888+ @ Override
1889+ public void dontinline_brkpt () { testCase .dontinline_brkpt (); }
1890+ }
1891+
1892+ public static class BrkPtDispatchD extends BrkPtDispatchA {
1893+ @ Override
1894+ public void dontinline_brkpt () {
1895+ testCase .dontinline_brkpt ();
1896+ }
1897+ }
1898+
1899+ public static class SyncCounter {
1900+ private int val ;
1901+ public synchronized int inc () { return val ++; }
1902+ }
1903+
1904+ public volatile SyncCounter sharedCounter ;
1905+
1906+ @ Override
1907+ public void setUp () {
1908+ super .setUp ();
1909+ testMethodDepth = 2 ;
1910+ for (BrkPtDispatchA d : disp ) {
1911+ d .testCase = this ;
1912+ }
1913+ doLoop = true ;
1914+ new Thread (() -> {
1915+ while (doLoop ) {
1916+ SyncCounter ctr = sharedCounter ;
1917+ if (ctr != null ) {
1918+ ctr .inc ();
1919+ }
1920+ }
1921+ }).start ();
1922+ }
1923+
1924+ public int dispCount ;
1925+ public void dontinline_testMethod () {
1926+ SyncCounter l1 = new SyncCounter ();
1927+ synchronized (l1 ) { // Eliminated locking
1928+ l1 .inc ();
1929+ // Use different types for the subsequent call to prevent devirtualization.
1930+ BrkPtDispatchA d = disp [(dispCount ++) & 3 ];
1931+ d .dontinline_brkpt (); // Dynamic call. Debugger publishes l1 to sharedCounter.
1932+ iResult = l1 .inc (); // Changes by the 2nd thread will be observed if l1
1933+ // was not relocked before passing it to the debugger.
1934+ }
1935+ }
1936+
1937+ @ Override
1938+ public int getExpectedIResult () {
1939+ return 1 ;
1940+ }
1941+ }
1942+
1943+ /////////////////////////////////////////////////////////////////////////////
1944+
18541945// Test recursive locking
18551946class EARelockingRecursiveTarget extends EATestCaseBaseTarget {
18561947
0 commit comments