am 6091dcbb: Merge "[ActivityManager] Improve multi-thread access the same provider"

* commit '6091dcbb80d3b1b885b6e02386ccb3139e5e43ae':
  [ActivityManager] Improve multi-thread access the same provider
This commit is contained in:
Olawale Ogunwale
2015-04-01 15:18:45 +00:00
committed by Android Git Automerger

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. // The lock of mProviderMap protects the following variables.
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<>();
= new ArrayMap<ProviderKey, ProviderClientRecord>(); final ArrayMap<ProviderKey, AcquiringProviderRecord> mAcquiringProviderMap = new ArrayMap<>();
final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap = new ArrayMap<>();
= new ArrayMap<IBinder, ProviderRefCount>(); final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders = new ArrayMap<>();
final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName = new ArrayMap<>();
= new ArrayMap<IBinder, ProviderClientRecord>();
final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
= new ArrayMap<ComponentName, ProviderClientRecord>();
final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
= new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>(); = new ArrayMap<>();
final GcIdler mGcIdler = new GcIdler(); final GcIdler mGcIdler = new GcIdler();
boolean mGcIdlerScheduled = false; boolean mGcIdlerScheduled = false;
@@ -345,7 +348,7 @@ public final class ActivityThread {
} }
} }
final class ProviderClientRecord { static final class ProviderClientRecord {
final String[] mNames; final String[] mNames;
final IContentProvider mProvider; final IContentProvider mProvider;
final ContentProvider mLocalProvider; final ContentProvider mLocalProvider;
@@ -4645,23 +4648,58 @@ public final class ActivityThread {
public final IContentProvider acquireProvider( public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) { 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) { if (provider != null) {
return provider; 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 IActivityManager.ContentProviderHolder holder = null;
// the same provider at the same time. When this happens, we want to ensure if (first) {
// that the first one wins. // 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 // 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 // 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. // be re-entrant in the case where the provider is in the same process.
IActivityManager.ContentProviderHolder holder = null;
try { try {
holder = ActivityManagerNative.getDefault().getContentProvider( holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable); getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) { } 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) { if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth); Slog.e(TAG, "Failed to find provider info for " + auth);
return null; return null;
@@ -4744,8 +4782,12 @@ public final class ActivityThread {
public final IContentProvider acquireExistingProvider( public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) { 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) { synchronized (mProviderMap) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key); final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) { if (pr == null) {
return null; return null;
@@ -4756,7 +4798,7 @@ public final class ActivityThread {
if (!jBinder.isBinderAlive()) { if (!jBinder.isBinderAlive()) {
// The hosting process of the provider has died; we can't // The hosting process of the provider has died; we can't
// use this one. // 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"); + ": existing object's process dead");
handleUnstableProviderDiedLocked(jBinder, true); handleUnstableProviderDiedLocked(jBinder, true);
return null; return null;
@@ -5078,18 +5120,12 @@ public final class ActivityThread {
if (DEBUG_PROVIDER) { if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, updating ref count"); Slog.v(TAG, "installProvider: lost the race, updating ref count");
} }
// We need to transfer our new reference to the existing // The provider has already been installed, so we need
// ref count, releasing the old one... but only if // to increase reference count to the existing one, but
// release is needed (that is, it is not running in the // only if release is needed (that is, it is not running
// system process). // in the system process or local to the process).
if (!noReleaseNeeded) { if (!noReleaseNeeded) {
incProviderRefLocked(prc, stable); incProviderRefLocked(prc, stable);
try {
ActivityManagerNative.getDefault().removeContentProvider(
holder.connection, stable);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
} }
} else { } else {
ProviderClientRecord client = installProviderAuthoritiesLocked( ProviderClientRecord client = installProviderAuthoritiesLocked(