Skip to content

Commit ff66286

Browse files
greenrobotgreenrobot-team
authored andcommitted
testWriteTxBlocksOtherWriteTx(): provoke races with random close order #282
1 parent f6480f5 commit ff66286

File tree

1 file changed

+31
-0
lines changed

1 file changed

+31
-0
lines changed

tests/objectbox-java-test/src/test/java/io/objectbox/CursorTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.junit.Test;
2020

2121
import java.util.concurrent.CountDownLatch;
22+
import java.util.concurrent.ThreadLocalRandom;
2223
import java.util.concurrent.TimeUnit;
2324

2425
import io.objectbox.annotation.IndexType;
@@ -227,24 +228,54 @@ public void testClose() {
227228
}
228229
}
229230

231+
/**
232+
* Begin the first write-TX and ensure the second one blocks until the first one is closed.
233+
* A secondary test goal is to check races of a closing TX and a closing store.
234+
*/
230235
@Test
231236
public void testWriteTxBlocksOtherWriteTx() throws InterruptedException {
237+
// To change the likelihood of the TX vs store closing race, close the store using one of 3 different variants.
238+
// Assign and print the randomly chosen variant beforehand so it does not mess with thread timings later.
239+
// Warning: test variant 2 only manually, it will close the write-TX from a non-owner thread (in BoxStore.close)
240+
// where the native database is expected to panic.
241+
int closeStoreVariant = ThreadLocalRandom.current().nextInt(2 /* 3 - test variant 2 manually, see above */);
242+
System.out.println("Closing store variant: " + closeStoreVariant);
243+
232244
long time = System.currentTimeMillis();
233245
Transaction tx = store.beginTx();
234246
long duration = System.currentTimeMillis() - time; // Usually 0 on desktop
235247
final CountDownLatch latchBeforeBeginTx = new CountDownLatch(1);
236248
final CountDownLatch latchAfterBeginTx = new CountDownLatch(1);
249+
final CountDownLatch latchCloseStoreInMainThread = closeStoreVariant != 0 ? new CountDownLatch(1) : null;
237250
new Thread(() -> {
238251
latchBeforeBeginTx.countDown();
239252
Transaction tx2 = store.beginTx();
240253
latchAfterBeginTx.countDown();
254+
255+
if (closeStoreVariant != 0) {
256+
try {
257+
assertTrue(latchCloseStoreInMainThread.await(5, TimeUnit.SECONDS));
258+
} catch (InterruptedException e) {
259+
throw new RuntimeException("Interrupted", e);
260+
}
261+
}
241262
tx2.close();
242263
}).start();
243264
assertTrue(latchBeforeBeginTx.await(1, TimeUnit.SECONDS));
244265
long waitTime = 100 + duration * 10;
245266
assertFalse(latchAfterBeginTx.await(waitTime, TimeUnit.MILLISECONDS));
246267
tx.close();
247268
assertTrue(latchAfterBeginTx.await(waitTime * 2, TimeUnit.MILLISECONDS));
269+
270+
// closeStoreVariant == 0: not latch waiting, close store when tearing down test
271+
if (closeStoreVariant == 1) {
272+
// This variant tries to close the store and the TX at the same time.
273+
latchCloseStoreInMainThread.countDown();
274+
store.close(); // Maybe this runs a tiny bit before the close() in teardown(?)
275+
} else if (closeStoreVariant == 2) {
276+
store.close(); // Enforces closing the store before the TX.
277+
latchCloseStoreInMainThread.countDown();
278+
}
248279
}
249280

250281
@Test

0 commit comments

Comments
 (0)