From a2608c598fa5282f32ca7df92ba410d933e009eb Mon Sep 17 00:00:00 2001 From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca> Date: Sun, 26 Nov 2023 02:36:24 -0400 Subject: [PATCH 1/6] refactor: Decompose conditional --- .../src/main/java/io/objectbox/Box.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/objectbox-java/src/main/java/io/objectbox/Box.java b/objectbox-java/src/main/java/io/objectbox/Box.java index 128ffa80..ffc3e81c 100644 --- a/objectbox-java/src/main/java/io/objectbox/Box.java +++ b/objectbox-java/src/main/java/io/objectbox/Box.java @@ -138,14 +138,20 @@ void releaseWriter(Cursor<T> cursor) { } void releaseReader(Cursor<T> cursor) { - // NOP if TX is ongoing - if (activeTxCursor.get() == null) { - Transaction tx = cursor.getTx(); - if (tx.isClosed() || tx.isRecycled() || !tx.isReadOnly()) { - throw new IllegalStateException("Illegal reader TX state"); - } - tx.recycle(); + if (activeTxCursor.get() != null) { + return; // NOP if TX is ongoing } + + Transaction tx = cursor.getTx(); + if (!isValidTransactionToRelease(tx)) { + throw new IllegalStateException("Illegal reader TX state"); + } + + tx.recycle(); + } + + private boolean isValidTransactionToRelease(Transaction tx) { + return !tx.isClosed() && !tx.isRecycled() && tx.isReadOnly(); } /** From e8ea8e84a7d0e370812836b985e0eac366ffbb8d Mon Sep 17 00:00:00 2001 From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca> Date: Sun, 26 Nov 2023 02:40:22 -0400 Subject: [PATCH 2/6] refactor: Introduce explaining variable --- objectbox-java/src/main/java/io/objectbox/BoxStore.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/objectbox-java/src/main/java/io/objectbox/BoxStore.java b/objectbox-java/src/main/java/io/objectbox/BoxStore.java index 04b45240..66cdb50e 100644 --- a/objectbox-java/src/main/java/io/objectbox/BoxStore.java +++ b/objectbox-java/src/main/java/io/objectbox/BoxStore.java @@ -373,16 +373,18 @@ static boolean isFileOpen(final String canonicalPath) { } static boolean isFileOpenSync(String canonicalPath, boolean runFinalization) { + int Num_Of_Retries = 5; + int Timeout = 100; // in millis synchronized (openFiles) { int tries = 0; - while (tries < 5 && openFiles.contains(canonicalPath)) { + while (tries < Num_Of_Retries && openFiles.contains(canonicalPath)) { tries++; System.gc(); if (runFinalization && tries > 1) System.runFinalization(); System.gc(); if (runFinalization && tries > 1) System.runFinalization(); try { - openFiles.wait(100); + openFiles.wait(Timeout); } catch (InterruptedException e) { // Ignore } From 81fa2366592fe813b824221c242eab976c084b70 Mon Sep 17 00:00:00 2001 From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca> Date: Sun, 26 Nov 2023 03:10:43 -0400 Subject: [PATCH 3/6] refactor: Extract openFileOnDiffThread method --- .../src/main/java/io/objectbox/BoxStore.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/objectbox-java/src/main/java/io/objectbox/BoxStore.java b/objectbox-java/src/main/java/io/objectbox/BoxStore.java index 66cdb50e..4b34d178 100644 --- a/objectbox-java/src/main/java/io/objectbox/BoxStore.java +++ b/objectbox-java/src/main/java/io/objectbox/BoxStore.java @@ -342,6 +342,23 @@ static void verifyNotAlreadyOpen(String canonicalPath) { } } + + private static void openFileOnDiffThread(final String canonicalPath){ + // Use a thread to avoid finalizers that block us + Thread thread = new Thread(() -> { + isFileOpenSync(canonicalPath, true); + openFilesCheckerThread = null; // Clean ref to itself + }); + thread.setDaemon(true); + openFilesCheckerThread = thread; + thread.start(); + try { + thread.join(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + /** Also retries up to 500ms to improve GC race condition situation. */ static boolean isFileOpen(final String canonicalPath) { synchronized (openFiles) { @@ -349,27 +366,14 @@ static boolean isFileOpen(final String canonicalPath) { } Thread checkerThread = BoxStore.openFilesCheckerThread; if (checkerThread == null || !checkerThread.isAlive()) { - // Use a thread to avoid finalizers that block us - checkerThread = new Thread(() -> { - isFileOpenSync(canonicalPath, true); - BoxStore.openFilesCheckerThread = null; // Clean ref to itself - }); - checkerThread.setDaemon(true); - - BoxStore.openFilesCheckerThread = checkerThread; - checkerThread.start(); - try { - checkerThread.join(500); - } catch (InterruptedException e) { - e.printStackTrace(); + openFileOnDiffThread(canonicalPath); + synchronized (openFiles) { + return openFiles.contains(canonicalPath); } } else { // Waiting for finalizers are blocking; only do that in the thread ^ return isFileOpenSync(canonicalPath, false); } - synchronized (openFiles) { - return openFiles.contains(canonicalPath); - } } static boolean isFileOpenSync(String canonicalPath, boolean runFinalization) { From 3ccf70505b6119851a4d5a3927cf7e6d4e44240e Mon Sep 17 00:00:00 2001 From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca> Date: Sun, 26 Nov 2023 05:20:10 -0400 Subject: [PATCH 4/6] refactor: Extract cache class --- .../flatbuffers/UTF8EncDecCache.java | 18 ++++++++++++++++ .../io/objectbox/flatbuffers/Utf8Old.java | 21 +++++-------------- 2 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 objectbox-java/src/main/java/io/objectbox/flatbuffers/UTF8EncDecCache.java diff --git a/objectbox-java/src/main/java/io/objectbox/flatbuffers/UTF8EncDecCache.java b/objectbox-java/src/main/java/io/objectbox/flatbuffers/UTF8EncDecCache.java new file mode 100644 index 00000000..384741b4 --- /dev/null +++ b/objectbox-java/src/main/java/io/objectbox/flatbuffers/UTF8EncDecCache.java @@ -0,0 +1,18 @@ +package io.objectbox.flatbuffers; + +import java.nio.ByteBuffer; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; + +public class UTF8EncDecCache { + final CharsetEncoder encoder; + final CharsetDecoder decoder; + CharSequence lastInput = null; + ByteBuffer lastOutput = null; + + UTF8EncDecCache() { + encoder = StandardCharsets.UTF_8.newEncoder(); + decoder = StandardCharsets.UTF_8.newDecoder(); + } +} diff --git a/objectbox-java/src/main/java/io/objectbox/flatbuffers/Utf8Old.java b/objectbox-java/src/main/java/io/objectbox/flatbuffers/Utf8Old.java index acb91738..e74a8721 100644 --- a/objectbox-java/src/main/java/io/objectbox/flatbuffers/Utf8Old.java +++ b/objectbox-java/src/main/java/io/objectbox/flatbuffers/Utf8Old.java @@ -24,33 +24,22 @@ import java.nio.charset.CoderResult; import java.nio.charset.StandardCharsets; + /** * This class implements the Utf8 API using the Java Utf8 encoder. Use * Utf8.setDefault(new Utf8Old()); to use it. */ public class Utf8Old extends Utf8 { - private static class Cache { - final CharsetEncoder encoder; - final CharsetDecoder decoder; - CharSequence lastInput = null; - ByteBuffer lastOutput = null; - - Cache() { - encoder = StandardCharsets.UTF_8.newEncoder(); - decoder = StandardCharsets.UTF_8.newDecoder(); - } - } - - private static final ThreadLocal<Cache> CACHE = - ThreadLocal.withInitial(() -> new Cache()); + private static final ThreadLocal<UTF8EncDecCache> CACHE = + ThreadLocal.withInitial(() -> new UTF8EncDecCache()); // Play some games so that the old encoder doesn't pay twice for computing // the length of the encoded string. @Override public int encodedLength(CharSequence in) { - final Cache cache = CACHE.get(); + final UTF8EncDecCache cache = CACHE.get(); int estimated = (int) (in.length() * cache.encoder.maxBytesPerChar()); if (cache.lastOutput == null || cache.lastOutput.capacity() < estimated) { cache.lastOutput = ByteBuffer.allocate(Math.max(128, estimated)); @@ -73,7 +62,7 @@ public int encodedLength(CharSequence in) { @Override public void encodeUtf8(CharSequence in, ByteBuffer out) { - final Cache cache = CACHE.get(); + final UTF8EncDecCache cache = CACHE.get(); if (cache.lastInput != in) { // Update the lastOutput to match our input, although flatbuffer should // never take this branch. From 9193521d06f6bb36014b6ed854acd24c17aabd56 Mon Sep 17 00:00:00 2001 From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca> Date: Sun, 26 Nov 2023 05:45:34 -0400 Subject: [PATCH 5/6] refactor: pull-up alias variable to QueryConditionIml class --- .../io/objectbox/query/PropertyQueryConditionImpl.java | 6 +++--- .../java/io/objectbox/query/QueryConditionImpl.java | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java b/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java index 444fb290..0cf03455 100644 --- a/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java +++ b/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java @@ -16,7 +16,6 @@ public abstract class PropertyQueryConditionImpl<T> extends QueryConditionImpl<T> implements PropertyQueryCondition<T> { // Note: Expose for DAOcompat public final Property<T> property; - private String alias; PropertyQueryConditionImpl(Property<T> property) { this.property = property; @@ -24,7 +23,7 @@ public abstract class PropertyQueryConditionImpl<T> extends QueryConditionImpl<T @Override public QueryCondition<T> alias(String name) { - this.alias = name; + setAlias(name); return this; } @@ -32,7 +31,8 @@ public QueryCondition<T> alias(String name) { @Override public void apply(QueryBuilder<T> builder) { applyCondition(builder); - if (alias != null && alias.length() != 0) { + String alias = getAlias(); + if (alias != null && !alias.isEmpty()) { builder.parameterAlias(alias); } } diff --git a/objectbox-java/src/main/java/io/objectbox/query/QueryConditionImpl.java b/objectbox-java/src/main/java/io/objectbox/query/QueryConditionImpl.java index 5fe8bc61..e643a755 100644 --- a/objectbox-java/src/main/java/io/objectbox/query/QueryConditionImpl.java +++ b/objectbox-java/src/main/java/io/objectbox/query/QueryConditionImpl.java @@ -8,6 +8,16 @@ */ abstract class QueryConditionImpl<T> implements QueryCondition<T> { + private String alias; + + public void setAlias(String alias) { + this.alias = alias; + } + + public String getAlias() { + return alias; + } + @Override public QueryCondition<T> and(QueryCondition<T> queryCondition) { return new AndCondition<>(this, (QueryConditionImpl<T>) queryCondition); From 9d496012aa9b59e682c7934e6bac0a6f159eb2a2 Mon Sep 17 00:00:00 2001 From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca> Date: Sun, 26 Nov 2023 06:19:27 -0400 Subject: [PATCH 6/6] refactor: move field to supper class --- .../objectbox/relation/ArrayListFactory.java | 16 ++++++++++++++ .../relation/CopyOnWriteArrayListFactory.java | 16 ++++++++++++++ .../io/objectbox/relation/ListFactory.java | 22 +++++-------------- .../java/io/objectbox/relation/ToMany.java | 1 - 4 files changed, 38 insertions(+), 17 deletions(-) create mode 100644 objectbox-java/src/main/java/io/objectbox/relation/ArrayListFactory.java create mode 100644 objectbox-java/src/main/java/io/objectbox/relation/CopyOnWriteArrayListFactory.java diff --git a/objectbox-java/src/main/java/io/objectbox/relation/ArrayListFactory.java b/objectbox-java/src/main/java/io/objectbox/relation/ArrayListFactory.java new file mode 100644 index 00000000..21f1dc93 --- /dev/null +++ b/objectbox-java/src/main/java/io/objectbox/relation/ArrayListFactory.java @@ -0,0 +1,16 @@ +package io.objectbox.relation; + +import java.util.ArrayList; +import java.util.List; + +class ArrayListFactory extends ListFactory { + + ArrayListFactory() { + super(8247662514375611729L); + } + + @Override + public <T> List<T> createList() { + return new ArrayList<>(); + } +} diff --git a/objectbox-java/src/main/java/io/objectbox/relation/CopyOnWriteArrayListFactory.java b/objectbox-java/src/main/java/io/objectbox/relation/CopyOnWriteArrayListFactory.java new file mode 100644 index 00000000..78326c26 --- /dev/null +++ b/objectbox-java/src/main/java/io/objectbox/relation/CopyOnWriteArrayListFactory.java @@ -0,0 +1,16 @@ +package io.objectbox.relation; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +class CopyOnWriteArrayListFactory extends ListFactory { + + CopyOnWriteArrayListFactory() { + super(1888039726372206411L); + } + + @Override + public <T> List<T> createList() { + return new CopyOnWriteArrayList<>(); + } +} \ No newline at end of file diff --git a/objectbox-java/src/main/java/io/objectbox/relation/ListFactory.java b/objectbox-java/src/main/java/io/objectbox/relation/ListFactory.java index b7a12a98..765d2342 100644 --- a/objectbox-java/src/main/java/io/objectbox/relation/ListFactory.java +++ b/objectbox-java/src/main/java/io/objectbox/relation/ListFactory.java @@ -24,24 +24,14 @@ import io.objectbox.annotation.apihint.Experimental; @Experimental -public interface ListFactory extends Serializable { - <T> List<T> createList(); +public class ListFactory implements Serializable { + private final long serialVersionUID; + ListFactory(long serialVersionUID){ + this.serialVersionUID = serialVersionUID; - class ArrayListFactory implements ListFactory { - private static final long serialVersionUID = 8247662514375611729L; - - @Override - public <T> List<T> createList() { - return new ArrayList<>(); - } } - class CopyOnWriteArrayListFactory implements ListFactory { - private static final long serialVersionUID = 1888039726372206411L; - - @Override - public <T> List<T> createList() { - return new CopyOnWriteArrayList<>(); - } + <T> List<T> createList(){ + throw new RuntimeException("Have to implement createList method"); } } diff --git a/objectbox-java/src/main/java/io/objectbox/relation/ToMany.java b/objectbox-java/src/main/java/io/objectbox/relation/ToMany.java index b387c0df..e18089a3 100644 --- a/objectbox-java/src/main/java/io/objectbox/relation/ToMany.java +++ b/objectbox-java/src/main/java/io/objectbox/relation/ToMany.java @@ -44,7 +44,6 @@ import io.objectbox.internal.ToManyGetter; import io.objectbox.internal.ToOneGetter; import io.objectbox.query.QueryFilter; -import io.objectbox.relation.ListFactory.CopyOnWriteArrayListFactory; import static java.lang.Boolean.TRUE;