Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor #1159

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions objectbox-java/src/main/java/io/objectbox/Box.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

/**
Expand Down
42 changes: 24 additions & 18 deletions objectbox-java/src/main/java/io/objectbox/BoxStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -342,47 +342,53 @@ 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) {
if (!openFiles.contains(canonicalPath)) return false;
}
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
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
21 changes: 5 additions & 16 deletions objectbox-java/src/main/java/io/objectbox/flatbuffers/Utf8Old.java
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,23 @@
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;
}

@Override
public QueryCondition<T> alias(String name) {
this.alias = name;
setAlias(name);
return this;
}

// Note: Expose for DAOcompat
@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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<>();
}
}
Original file line number Diff line number Diff line change
@@ -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<>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down