@@ -289,6 +289,7 @@ public static void main(String[] args) {
289
289
// Relocking test cases
290
290
new EARelockingSimpleTarget () .run ();
291
291
new EARelockingSimpleWithAccessInOtherThreadTarget () .run ();
292
+ new EARelockingSimpleWithAccessInOtherThread_02_DynamicCall_Target () .run ();
292
293
new EARelockingRecursiveTarget () .run ();
293
294
new EARelockingNestedInflatedTarget () .run ();
294
295
new EARelockingNestedInflated_02Target () .run ();
@@ -413,6 +414,7 @@ protected void runTests() throws Exception {
413
414
// Relocking test cases
414
415
new EARelockingSimple () .run (this );
415
416
new EARelockingSimpleWithAccessInOtherThread () .run (this );
417
+ new EARelockingSimpleWithAccessInOtherThread_02_DynamicCall () .run (this );
416
418
new EARelockingRecursive () .run (this );
417
419
new EARelockingNestedInflated () .run (this );
418
420
new EARelockingNestedInflated_02 () .run (this );
@@ -1851,6 +1853,95 @@ public int getExpectedIResult() {
1851
1853
1852
1854
/////////////////////////////////////////////////////////////////////////////
1853
1855
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
+
1854
1945
// Test recursive locking
1855
1946
class EARelockingRecursiveTarget extends EATestCaseBaseTarget {
1856
1947
0 commit comments