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 cursor) { } void releaseReader(Cursor 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(); } /** diff --git a/objectbox-java/src/main/java/io/objectbox/BoxStore.java b/objectbox-java/src/main/java/io/objectbox/BoxStore.java index 04b45240..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,40 +366,29 @@ 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) { + 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 } 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 = - ThreadLocal.withInitial(() -> new Cache()); + private static final ThreadLocal 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. 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 extends QueryConditionImpl implements PropertyQueryCondition { // Note: Expose for DAOcompat public final Property property; - private String alias; PropertyQueryConditionImpl(Property property) { this.property = property; @@ -24,7 +23,7 @@ public abstract class PropertyQueryConditionImpl extends QueryConditionImpl alias(String name) { - this.alias = name; + setAlias(name); return this; } @@ -32,7 +31,8 @@ public QueryCondition alias(String name) { @Override public void apply(QueryBuilder 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 implements QueryCondition { + private String alias; + + public void setAlias(String alias) { + this.alias = alias; + } + + public String getAlias() { + return alias; + } + @Override public QueryCondition and(QueryCondition queryCondition) { return new AndCondition<>(this, (QueryConditionImpl) queryCondition); 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 List 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 List 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 { - List 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 List createList() { - return new ArrayList<>(); - } } - class CopyOnWriteArrayListFactory implements ListFactory { - private static final long serialVersionUID = 1888039726372206411L; - - @Override - public List createList() { - return new CopyOnWriteArrayList<>(); - } + List 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;