kopia lustrzana https://github.com/ryukoposting/Signal-Android
Narrow locking in LiveRecipientCache.
This should make it so that we never hold a lock while accessing the database.fork-5.53.8
rodzic
c0eac5564c
commit
5dd5a024c9
|
@ -25,6 +25,8 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
public final class LiveRecipientCache {
|
public final class LiveRecipientCache {
|
||||||
|
|
||||||
|
@ -40,42 +42,51 @@ public final class LiveRecipientCache {
|
||||||
private final Executor executor;
|
private final Executor executor;
|
||||||
private final SQLiteDatabase db;
|
private final SQLiteDatabase db;
|
||||||
|
|
||||||
private volatile RecipientId localRecipientId;
|
private final AtomicReference<RecipientId> localRecipientId;
|
||||||
|
private final AtomicBoolean warmedUp;
|
||||||
private boolean warmedUp;
|
|
||||||
|
|
||||||
@SuppressLint("UseSparseArrays")
|
@SuppressLint("UseSparseArrays")
|
||||||
public LiveRecipientCache(@NonNull Context context) {
|
public LiveRecipientCache(@NonNull Context context) {
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
this.recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||||
this.recipients = new LRUCache<>(CACHE_MAX);
|
this.recipients = new LRUCache<>(CACHE_MAX);
|
||||||
|
this.warmedUp = new AtomicBoolean(false);
|
||||||
|
this.localRecipientId = new AtomicReference<>(null);
|
||||||
this.unknown = new LiveRecipient(context, Recipient.UNKNOWN);
|
this.unknown = new LiveRecipient(context, Recipient.UNKNOWN);
|
||||||
this.db = DatabaseFactory.getInstance(context).getRawDatabase();
|
this.db = DatabaseFactory.getInstance(context).getRawDatabase();
|
||||||
this.executor = new FilteredExecutor(SignalExecutors.BOUNDED, () -> !db.isDbLockedByCurrentThread());
|
this.executor = new FilteredExecutor(SignalExecutors.BOUNDED, () -> !db.isDbLockedByCurrentThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
synchronized @NonNull LiveRecipient getLive(@NonNull RecipientId id) {
|
@NonNull LiveRecipient getLive(@NonNull RecipientId id) {
|
||||||
if (id.isUnknown()) return unknown;
|
if (id.isUnknown()) return unknown;
|
||||||
|
|
||||||
LiveRecipient live = recipients.get(id);
|
LiveRecipient live;
|
||||||
|
boolean needsResolve;
|
||||||
|
|
||||||
if (live == null) {
|
synchronized (recipients) {
|
||||||
final LiveRecipient newLive = new LiveRecipient(context, new Recipient(id));
|
live = recipients.get(id);
|
||||||
|
|
||||||
recipients.put(id, newLive);
|
if (live == null) {
|
||||||
|
live = new LiveRecipient(context, new Recipient(id));
|
||||||
|
recipients.put(id, live);
|
||||||
|
needsResolve = true;
|
||||||
|
} else {
|
||||||
|
needsResolve = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MissingRecipientException prettyStackTraceError = new MissingRecipientException(newLive.getId());
|
if (needsResolve) {
|
||||||
|
final LiveRecipient toResolve = live;
|
||||||
|
|
||||||
|
MissingRecipientException prettyStackTraceError = new MissingRecipientException(toResolve.getId());
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
newLive.resolve();
|
toResolve.resolve();
|
||||||
} catch (MissingRecipientException e) {
|
} catch (MissingRecipientException e) {
|
||||||
throw prettyStackTraceError;
|
throw prettyStackTraceError;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
live = newLive;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return live;
|
return live;
|
||||||
|
@ -88,25 +99,33 @@ public final class LiveRecipientCache {
|
||||||
* If the recipient you add is unresolved, this will enqueue a resolve on a background thread.
|
* If the recipient you add is unresolved, this will enqueue a resolve on a background thread.
|
||||||
*/
|
*/
|
||||||
@AnyThread
|
@AnyThread
|
||||||
public synchronized void addToCache(@NonNull Collection<Recipient> newRecipients) {
|
public void addToCache(@NonNull Collection<Recipient> newRecipients) {
|
||||||
for (Recipient recipient : newRecipients) {
|
for (Recipient recipient : newRecipients) {
|
||||||
LiveRecipient live = recipients.get(recipient.getId());
|
LiveRecipient live;
|
||||||
boolean needsResolve = false;
|
boolean needsResolve;
|
||||||
|
|
||||||
if (live == null) {
|
synchronized (recipients) {
|
||||||
live = new LiveRecipient(context, recipient);
|
live = recipients.get(recipient.getId());
|
||||||
recipients.put(recipient.getId(), live);
|
|
||||||
needsResolve = recipient.isResolving();
|
if (live == null) {
|
||||||
} else if (live.get().isResolving() || !recipient.isResolving()) {
|
live = new LiveRecipient(context, recipient);
|
||||||
live.set(recipient);
|
recipients.put(recipient.getId(), live);
|
||||||
needsResolve = recipient.isResolving();
|
needsResolve = recipient.isResolving();
|
||||||
|
} else if (live.get().isResolving() || !recipient.isResolving()) {
|
||||||
|
live.set(recipient);
|
||||||
|
needsResolve = recipient.isResolving();
|
||||||
|
} else {
|
||||||
|
needsResolve = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsResolve) {
|
if (needsResolve) {
|
||||||
MissingRecipientException prettyStackTraceError = new MissingRecipientException(recipient.getId());
|
LiveRecipient toResolve = live;
|
||||||
|
|
||||||
|
MissingRecipientException prettyStackTraceError = new MissingRecipientException(toResolve.getId());
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
recipient.resolve();
|
toResolve.resolve();
|
||||||
} catch (MissingRecipientException e) {
|
} catch (MissingRecipientException e) {
|
||||||
throw prettyStackTraceError;
|
throw prettyStackTraceError;
|
||||||
}
|
}
|
||||||
|
@ -116,32 +135,42 @@ public final class LiveRecipientCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull Recipient getSelf() {
|
@NonNull Recipient getSelf() {
|
||||||
if (localRecipientId == null) {
|
RecipientId selfId;
|
||||||
|
|
||||||
|
synchronized (localRecipientId) {
|
||||||
|
selfId = localRecipientId.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selfId == null) {
|
||||||
UUID localUuid = TextSecurePreferences.getLocalUuid(context);
|
UUID localUuid = TextSecurePreferences.getLocalUuid(context);
|
||||||
String localE164 = TextSecurePreferences.getLocalNumber(context);
|
String localE164 = TextSecurePreferences.getLocalNumber(context);
|
||||||
|
|
||||||
if (localUuid != null) {
|
if (localUuid != null) {
|
||||||
localRecipientId = recipientDatabase.getByUuid(localUuid).or(recipientDatabase.getByE164(localE164)).orNull();
|
selfId = recipientDatabase.getByUuid(localUuid).or(recipientDatabase.getByE164(localE164)).orNull();
|
||||||
} else if (localE164 != null) {
|
} else if (localE164 != null) {
|
||||||
localRecipientId = recipientDatabase.getByE164(localE164).orNull();
|
selfId = recipientDatabase.getByE164(localE164).orNull();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Tried to call getSelf() before local data was set!");
|
throw new IllegalStateException("Tried to call getSelf() before local data was set!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localRecipientId == null) {
|
if (selfId == null) {
|
||||||
throw new MissingRecipientException(null);
|
throw new MissingRecipientException(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized (localRecipientId) {
|
||||||
|
if (localRecipientId.get() == null) {
|
||||||
|
localRecipientId.set(selfId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getLive(localRecipientId).resolve();
|
return getLive(selfId).resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
public synchronized void warmUp() {
|
public void warmUp() {
|
||||||
if (warmedUp) {
|
if (warmedUp.getAndSet(true)) {
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
warmedUp = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
|
@ -164,12 +193,16 @@ public final class LiveRecipientCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
public synchronized void clearSelf() {
|
public void clearSelf() {
|
||||||
localRecipientId = null;
|
synchronized (localRecipientId) {
|
||||||
|
localRecipientId.set(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
public synchronized void clear() {
|
public void clear() {
|
||||||
recipients.clear();
|
synchronized (recipients) {
|
||||||
|
recipients.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue