Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1787ad70 authored by riddle_hsu's avatar riddle_hsu Committed by Steve Kondik
Browse files

[ActivityManager] Improve multi-thread access the same provider

Application may use many threads to load data from provider.
If the target provider needs to start process, each access
will occupy one binder thread of system server until the
provider process started and published.

Sometimes application uses more than 16 threads to access
the same provider, and the provider process needs a little
long time to start, then all binder threads of system server
are waiting. But when the provider is ready, it is unable to
publish to notify those waiting threads because no availabe
binder thread to use. And device will become almost hang.

Improvement:
If there is already a thread acquiring provider, let other threads
(which try to acquire the same provider) wait the result of the
first one. That reduces IPC to save binder thread of system server.

Remove calling removeContentProvider in installProvider because
we have ensured only get one provider holder for the same provider,
the original race that gets a new useless holder will not happen.

Change-Id: I521f2603db8ced56912f5dc54342a70451e68381
parent a734ee48
Loading
Loading
Loading
Loading
+69 −33
Original line number Diff line number Diff line
@@ -261,18 +261,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;
@@ -349,7 +352,7 @@ public final class ActivityThread {
        }
    }

    final class ProviderClientRecord {
    static final class ProviderClientRecord {
        final String[] mNames;
        final IContentProvider mProvider;
        final ContentProvider mLocalProvider;
@@ -4643,23 +4646,58 @@ 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.
        IActivityManager.ContentProviderHolder holder = null;
        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.
        IActivityManager.ContentProviderHolder holder = null;
            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);
            return null;
@@ -4742,8 +4780,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;
@@ -4754,7 +4796,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;
@@ -5076,18 +5118,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(