Skip to content

Commit bc28da4

Browse files
TransactionTest: check activeTxCursor is closed after write tx #286
1 parent 6b48302 commit bc28da4

File tree

2 files changed

+57
-0
lines changed

2 files changed

+57
-0
lines changed

objectbox-java/src/main/java/io/objectbox/Box.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ Cursor<T> getReader() {
8989
return cursor;
9090
}
9191

92+
/**
93+
* Returns if for the calling thread this has a Cursor, if any, for the currently active transaction.
94+
*/
95+
boolean hasActiveTxCursorForCurrentThread() {
96+
return activeTxCursor.get() != null;
97+
}
98+
9299
Cursor<T> getActiveTxCursor() {
93100
Transaction activeTx = store.activeTx.get();
94101
if (activeTx != null) {

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.concurrent.LinkedBlockingQueue;
3131
import java.util.concurrent.ThreadPoolExecutor;
3232
import java.util.concurrent.TimeUnit;
33+
import java.util.concurrent.atomic.AtomicBoolean;
3334
import java.util.concurrent.atomic.AtomicInteger;
3435
import java.util.concurrent.atomic.AtomicReference;
3536

@@ -421,20 +422,67 @@ public void testCallInReadTx() {
421422
assertEquals(1, counts[1]);
422423
}
423424

425+
@Test
426+
public void testRunInTx_closesActiveTxCursor() {
427+
final Box<TestEntity> box = getTestEntityBox();
428+
AtomicBoolean hasCursorInTx = new AtomicBoolean(false);
429+
store.runInTx(() -> {
430+
box.count(); // Call Box API that creates a reader/activeTxCursor
431+
hasCursorInTx.set(box.hasActiveTxCursorForCurrentThread());
432+
});
433+
// Verify a cursor for the active tx was created
434+
assertTrue(hasCursorInTx.get());
435+
// Check it was released
436+
assertFalse(box.hasActiveTxCursorForCurrentThread());
437+
438+
// Verify the same in case the runnable throws
439+
try {
440+
store.runInTx(() -> {
441+
box.count();
442+
throw new IllegalStateException("Throw in transaction");
443+
});
444+
} catch (IllegalStateException ignored) {}
445+
assertFalse(box.hasActiveTxCursorForCurrentThread());
446+
}
447+
448+
@Test
449+
public void testCallInTx_closesActiveTxCursor() throws Exception {
450+
final Box<TestEntity> box = getTestEntityBox();
451+
Boolean hasCursorInTx = store.callInTx(() -> {
452+
box.count(); // Call Box API that creates a reader/activeTxCursor
453+
return box.hasActiveTxCursorForCurrentThread();
454+
});
455+
// Verify a cursor for the active tx was created
456+
assertTrue(hasCursorInTx);
457+
// Check it was released
458+
assertFalse(box.hasActiveTxCursorForCurrentThread());
459+
460+
// Verify the same in case the callable throws
461+
try {
462+
store.callInTx(() -> {
463+
box.count();
464+
throw new IllegalStateException("Throw in transaction");
465+
});
466+
} catch (IllegalStateException ignored) {}
467+
assertFalse(box.hasActiveTxCursorForCurrentThread());
468+
}
469+
424470
@Test
425471
public void testRunInReadTx_closesActiveTxCursor() {
426472
final Box<TestEntity> box = getTestEntityBox();
427473
store.runInReadTx(box::count); // Call Box API that creates a reader/activeTxCursor
428474
// Verify that box does not hang on to the read-only TX: if it would, count() would re-use the cursor/tx from
429475
// above and not see the put object.
430476
putTestEntityAndExpectCount(1);
477+
assertFalse(box.hasActiveTxCursorForCurrentThread());
431478

432479
// Verify the same in case the runnable throws
433480
assertThrows(IllegalStateException.class, () -> store.runInReadTx(() -> {
434481
box.count();
435482
throw new IllegalStateException("Throw in transaction");
436483
}));
437484
putTestEntityAndExpectCount(2);
485+
assertFalse(box.hasActiveTxCursorForCurrentThread());
438486
}
439487

440488
@Test
@@ -444,13 +492,15 @@ public void testCallInReadTx_closesActiveTxCursor() {
444492
// Verify that box does not hang on to the read-only TX: if it would, count() would re-use the cursor/tx from
445493
// above and not see the put object.
446494
putTestEntityAndExpectCount(1);
495+
assertFalse(box.hasActiveTxCursorForCurrentThread());
447496

448497
// Verify the same in case the callable throws
449498
assertThrows(IllegalStateException.class, () -> store.callInReadTx(() -> {
450499
box.count();
451500
throw new IllegalStateException("Throw in transaction");
452501
}));
453502
putTestEntityAndExpectCount(2);
503+
assertFalse(box.hasActiveTxCursorForCurrentThread());
454504
}
455505

456506
private void putTestEntityAndExpectCount(int expectedCount) {

0 commit comments

Comments
 (0)