Merge "[ActivityManager] Improve multi-thread access the same provider"

This commit is contained in:
Olawale Ogunwale
2015-04-01 14:58:20 +00:00
committed by Gerrit Code Review

View File

@@ -257,18 +257,21 @@ public final class ActivityThread {
}
}
static final class AcquiringProviderRecord {
IActivityManager.ContentProviderHolder holder;
boolean acquiring = true;
int requests = 1;
}
// The lock of mProviderMap protects the following variables.
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
= new ArrayMap<ProviderKey, ProviderClientRecord>();
final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
= new ArrayMap<IBinder, ProviderRefCount>();
final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
= new ArrayMap<IBinder, ProviderClientRecord>();
final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
= new ArrayMap<ComponentName, ProviderClientRecord>();
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<>();
final ArrayMap<ProviderKey, AcquiringProviderRecord> mAcquiringProviderMap = new ArrayMap<>();
final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap = new ArrayMap<>();
final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders = new ArrayMap<>();
final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName = new ArrayMap<>();
final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
= new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
= new ArrayMap<>();
final GcIdler mGcIdler = new GcIdler();
boolean mGcIdlerScheduled = false;
@@ -345,7 +348,7 @@ public final class ActivityThread {
}
}
final class ProviderClientRecord {
static final class ProviderClientRecord {
final String[] mNames;
final IContentProvider mProvider;
final ContentProvider mLocalProvider;
@@ -4648,22 +4651,57 @@ public final class ActivityThread {
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
final ProviderKey key = new ProviderKey(auth, userId);
final IContentProvider provider = acquireExistingProvider(c, key, stable);
if (provider != null) {
return provider;
}
AcquiringProviderRecord r;
boolean first = false;
synchronized (mAcquiringProviderMap) {
r = mAcquiringProviderMap.get(key);
if (r == null) {
r = new AcquiringProviderRecord();
mAcquiringProviderMap.put(key, r);
first = true;
} else {
r.requests++;
}
}
// There is a possible race here. Another thread may try to acquire
// the same provider at the same time. When this happens, we want to ensure
// that the first one wins.
// Note that we cannot hold the lock while acquiring and installing the
// provider since it might take a long time to run and it could also potentially
// be re-entrant in the case where the provider is in the same process.
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
if (first) {
// Multiple threads may try to acquire the same provider at the same time.
// When this happens, we only let the first one really gets provider.
// Other threads just wait for its result.
// Note that we cannot hold the lock while acquiring and installing the
// provider since it might take a long time to run and it could also potentially
// be re-entrant in the case where the provider is in the same process.
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
synchronized (r) {
r.holder = holder;
r.acquiring = false;
r.notifyAll();
}
} else {
synchronized (r) {
while (r.acquiring) {
try {
r.wait();
} catch (InterruptedException e) {
}
}
holder = r.holder;
}
}
synchronized (mAcquiringProviderMap) {
if (--r.requests == 0) {
mAcquiringProviderMap.remove(key);
}
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
@@ -4747,8 +4785,12 @@ public final class ActivityThread {
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
return acquireExistingProvider(c, new ProviderKey(auth, userId), stable);
}
final IContentProvider acquireExistingProvider(
Context c, ProviderKey key, boolean stable) {
synchronized (mProviderMap) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
@@ -4759,7 +4801,7 @@ public final class ActivityThread {
if (!jBinder.isBinderAlive()) {
// The hosting process of the provider has died; we can't
// use this one.
Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
Log.i(TAG, "Acquiring provider " + key.authority + " for user " + key.userId
+ ": existing object's process dead");
handleUnstableProviderDiedLocked(jBinder, true);
return null;
@@ -5081,18 +5123,12 @@ public final class ActivityThread {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, updating ref count");
}
// We need to transfer our new reference to the existing
// ref count, releasing the old one... but only if
// release is needed (that is, it is not running in the
// system process).
// The provider has already been installed, so we need
// to increase reference count to the existing one, but
// only if release is needed (that is, it is not running
// in the system process or local to the process).
if (!noReleaseNeeded) {
incProviderRefLocked(prc, stable);
try {
ActivityManagerNative.getDefault().removeContentProvider(
holder.connection, stable);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
ProviderClientRecord client = installProviderAuthoritiesLocked(