163 lines
5.2 KiB
Java
163 lines
5.2 KiB
Java
|
/*
|
||
|
* Copyright (c) 2015-present, Parse, LLC.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* This source code is licensed under the BSD-style license found in the
|
||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||
|
*/
|
||
|
package com.parse;
|
||
|
|
||
|
import bolts.Continuation;
|
||
|
import bolts.Task;
|
||
|
|
||
|
/** package */ class CachedCurrentInstallationController
|
||
|
implements ParseCurrentInstallationController {
|
||
|
|
||
|
/* package */ static final String TAG = "com.parse.CachedCurrentInstallationController";
|
||
|
|
||
|
/*
|
||
|
* Note about lock ordering:
|
||
|
*
|
||
|
* You must NOT acquire the ParseInstallation instance mutex (the "mutex" field in ParseObject)
|
||
|
* while holding this current installation lock. (We used to use the ParseInstallation.class lock,
|
||
|
* but moved on to an explicit lock object since anyone could acquire the ParseInstallation.class
|
||
|
* lock as ParseInstallation is a public class.) Acquiring the instance mutex while holding this
|
||
|
* current installation lock will lead to a deadlock. Here is an example:
|
||
|
* https://phabricator.fb.com/P3251091
|
||
|
*/
|
||
|
private final Object mutex = new Object();
|
||
|
|
||
|
private final TaskQueue taskQueue = new TaskQueue();
|
||
|
|
||
|
private final ParseObjectStore<ParseInstallation> store;
|
||
|
private final InstallationId installationId;
|
||
|
|
||
|
// The "current installation" is the installation for this device. Protected by
|
||
|
// mutex.
|
||
|
/* package for test */ ParseInstallation currentInstallation;
|
||
|
|
||
|
public CachedCurrentInstallationController(
|
||
|
ParseObjectStore<ParseInstallation> store, InstallationId installationId) {
|
||
|
this.store = store;
|
||
|
this.installationId = installationId;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public Task<Void> setAsync(final ParseInstallation installation) {
|
||
|
if (!isCurrent(installation)) {
|
||
|
return Task.forResult(null);
|
||
|
}
|
||
|
|
||
|
return taskQueue.enqueue(new Continuation<Void, Task<Void>>() {
|
||
|
@Override
|
||
|
public Task<Void> then(Task<Void> toAwait) throws Exception {
|
||
|
return toAwait.continueWithTask(new Continuation<Void, Task<Void>>() {
|
||
|
@Override
|
||
|
public Task<Void> then(Task<Void> task) throws Exception {
|
||
|
return store.setAsync(installation);
|
||
|
}
|
||
|
}).continueWithTask(new Continuation<Void, Task<Void>>() {
|
||
|
@Override
|
||
|
public Task<Void> then(Task<Void> task) throws Exception {
|
||
|
installationId.set(installation.getInstallationId());
|
||
|
return task;
|
||
|
}
|
||
|
}, ParseExecutors.io());
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public Task<ParseInstallation> getAsync() {
|
||
|
synchronized (mutex) {
|
||
|
if (currentInstallation != null) {
|
||
|
return Task.forResult(currentInstallation);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return taskQueue.enqueue(new Continuation<Void, Task<ParseInstallation>>() {
|
||
|
@Override
|
||
|
public Task<ParseInstallation> then(Task<Void> toAwait) throws Exception {
|
||
|
return toAwait.continueWithTask(new Continuation<Void, Task<ParseInstallation>>() {
|
||
|
@Override
|
||
|
public Task<ParseInstallation> then(Task<Void> task) throws Exception {
|
||
|
synchronized (mutex) {
|
||
|
if (currentInstallation != null) {
|
||
|
return Task.forResult(currentInstallation);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return store.getAsync().continueWith(new Continuation<ParseInstallation, ParseInstallation>() {
|
||
|
@Override
|
||
|
public ParseInstallation then(Task<ParseInstallation> task) throws Exception {
|
||
|
ParseInstallation current = task.getResult();
|
||
|
if (current == null) {
|
||
|
current = ParseObject.create(ParseInstallation.class);
|
||
|
current.updateDeviceInfo(installationId);
|
||
|
} else {
|
||
|
installationId.set(current.getInstallationId());
|
||
|
PLog.v(TAG, "Successfully deserialized Installation object");
|
||
|
}
|
||
|
|
||
|
synchronized (mutex) {
|
||
|
currentInstallation = current;
|
||
|
}
|
||
|
return current;
|
||
|
}
|
||
|
}, ParseExecutors.io());
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public Task<Boolean> existsAsync() {
|
||
|
synchronized (mutex) {
|
||
|
if (currentInstallation != null) {
|
||
|
return Task.forResult(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return taskQueue.enqueue(new Continuation<Void, Task<Boolean>>() {
|
||
|
@Override
|
||
|
public Task<Boolean> then(Task<Void> toAwait) throws Exception {
|
||
|
return toAwait.continueWithTask(new Continuation<Void, Task<Boolean>>() {
|
||
|
@Override
|
||
|
public Task<Boolean> then(Task<Void> task) throws Exception {
|
||
|
return store.existsAsync();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void clearFromMemory() {
|
||
|
synchronized (mutex) {
|
||
|
currentInstallation = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void clearFromDisk() {
|
||
|
synchronized (mutex) {
|
||
|
currentInstallation = null;
|
||
|
}
|
||
|
try {
|
||
|
installationId.clear();
|
||
|
ParseTaskUtils.wait(store.deleteAsync());
|
||
|
} catch (ParseException e) {
|
||
|
// ignored
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean isCurrent(ParseInstallation installation) {
|
||
|
synchronized (mutex) {
|
||
|
return currentInstallation == installation;
|
||
|
}
|
||
|
}
|
||
|
}
|