Merge changes If07d6938,I32f9b89a,Ife4f8c4e into rvc-dev

* changes:
  RESTRICT AUTOMERGE: Add a facility for time-based cache corking
  RESTRICT AUTOMERGE: Cork permission and package cache around bulk permission update
  RESTRICT AUTOMERGE: Cork package information cache invalidations during boot
This commit is contained in:
TreeHugger Robot
2020-04-17 23:25:02 +00:00
committed by Android (Google) Code Review
4 changed files with 145 additions and 6 deletions

View File

@@ -17,6 +17,10 @@
package android.app; package android.app;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.util.Log; import android.util.Log;
@@ -492,6 +496,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
public static void corkInvalidations(@NonNull String name) { public static void corkInvalidations(@NonNull String name) {
synchronized (sCorkLock) { synchronized (sCorkLock) {
int numberCorks = sCorks.getOrDefault(name, 0); int numberCorks = sCorks.getOrDefault(name, 0);
if (DEBUG) {
Log.d(TAG, String.format("corking %s: numberCorks=%s", name, numberCorks));
}
// If we're the first ones to cork this cache, set the cache to the unset state so // If we're the first ones to cork this cache, set the cache to the unset state so
// existing caches talk directly to their services while we've corked updates. // existing caches talk directly to their services while we've corked updates.
// Make sure we don't clobber a disabled cache value. // Make sure we don't clobber a disabled cache value.
@@ -523,6 +531,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
public static void uncorkInvalidations(@NonNull String name) { public static void uncorkInvalidations(@NonNull String name) {
synchronized (sCorkLock) { synchronized (sCorkLock) {
int numberCorks = sCorks.getOrDefault(name, 0); int numberCorks = sCorks.getOrDefault(name, 0);
if (DEBUG) {
Log.d(TAG, String.format("uncorking %s: numberCorks=%s", name, numberCorks));
}
if (numberCorks < 1) { if (numberCorks < 1) {
throw new AssertionError("cork underflow: " + name); throw new AssertionError("cork underflow: " + name);
} }
@@ -538,6 +550,106 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
} }
} }
/**
* Time-based automatic corking helper. This class allows providers of cached data to
* amortize the cost of cache invalidations by corking the cache immediately after a
* modification (instructing clients to bypass the cache temporarily) and automatically
* uncork after some period of time has elapsed.
*
* It's better to use explicit cork and uncork pairs that tighly surround big batches of
* invalidations, but it's not always practical to tell where these invalidation batches
* might occur. AutoCorker's time-based corking is a decent alternative.
*/
public static final class AutoCorker {
public static final int DEFAULT_AUTO_CORK_DELAY_MS = 2000;
private final String mPropertyName;
private final int mAutoCorkDelayMs;
private final Object mLock = new Object();
@GuardedBy("mLock")
private long mUncorkDeadlineMs = -1; // SystemClock.uptimeMillis()
@GuardedBy("mLock")
private Handler mHandler;
public AutoCorker(@NonNull String propertyName) {
this(propertyName, DEFAULT_AUTO_CORK_DELAY_MS);
}
public AutoCorker(@NonNull String propertyName, int autoCorkDelayMs) {
mPropertyName = propertyName;
mAutoCorkDelayMs = autoCorkDelayMs;
// We can't initialize mHandler here: when we're created, the main loop might not
// be set up yet! Wait until we have a main loop to initialize our
// corking callback.
}
public void autoCork() {
if (Looper.getMainLooper() == null) {
// We're not ready to auto-cork yet, so just invalidate the cache immediately.
if (DEBUG) {
Log.w(TAG, "invalidating instead of autocorking early in init: "
+ mPropertyName);
}
PropertyInvalidatedCache.invalidateCache(mPropertyName);
return;
}
synchronized (mLock) {
boolean alreadyQueued = mUncorkDeadlineMs >= 0;
if (DEBUG) {
Log.w(TAG, String.format(
"autoCork mUncorkDeadlineMs=%s", mUncorkDeadlineMs));
}
mUncorkDeadlineMs = SystemClock.uptimeMillis() + mAutoCorkDelayMs;
if (!alreadyQueued) {
getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs);
PropertyInvalidatedCache.corkInvalidations(mPropertyName);
}
}
}
private void handleMessage(Message msg) {
synchronized (mLock) {
if (DEBUG) {
Log.w(TAG, String.format(
"handleMsesage mUncorkDeadlineMs=%s", mUncorkDeadlineMs));
}
if (mUncorkDeadlineMs < 0) {
return; // ???
}
long nowMs = SystemClock.uptimeMillis();
if (mUncorkDeadlineMs > nowMs) {
mUncorkDeadlineMs = nowMs + mAutoCorkDelayMs;
if (DEBUG) {
Log.w(TAG, String.format(
"scheduling uncork at %s",
mUncorkDeadlineMs));
}
getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs);
return;
}
if (DEBUG) {
Log.w(TAG, "automatic uncorking " + mPropertyName);
}
mUncorkDeadlineMs = -1;
PropertyInvalidatedCache.uncorkInvalidations(mPropertyName);
}
}
@GuardedBy("mLock")
private Handler getHandlerLocked() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
AutoCorker.this.handleMessage(msg);
}
};
}
return mHandler;
}
}
protected Result maybeCheckConsistency(Query query, Result proposedResult) { protected Result maybeCheckConsistency(Query query, Result proposedResult) {
if (VERIFY) { if (VERIFY) {
Result resultToCompare = recompute(query); Result resultToCompare = recompute(query);

View File

@@ -8167,4 +8167,19 @@ public abstract class PackageManager {
sPackageInfoCache.disableLocal(); sPackageInfoCache.disableLocal();
} }
/**
* Inhibit package info cache invalidations when correct.
*
* @hide */
public static void corkPackageInfoCache() {
PropertyInvalidatedCache.corkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
}
/**
* Enable package info cache invalidations.
*
* @hide */
public static void uncorkPackageInfoCache() {
PropertyInvalidatedCache.uncorkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
}
} }

View File

@@ -2817,10 +2817,14 @@ public class PackageManagerService extends IPackageManager.Stub
} }
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) { public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
PackageManager.invalidatePackageInfoCache();
PackageManager.disableApplicationInfoCache(); PackageManager.disableApplicationInfoCache();
PackageManager.disablePackageInfoCache(); PackageManager.disablePackageInfoCache();
// Avoid invalidation-thrashing by preventing cache invalidations from causing property
// writes if the cache isn't enabled yet. We re-enable writes later when we're
// done initializing.
PackageManager.corkPackageInfoCache();
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER); Trace.TRACE_TAG_PACKAGE_MANAGER);
mPendingBroadcasts = new PendingPackageBroadcasts(); mPendingBroadcasts = new PendingPackageBroadcasts();
@@ -3626,6 +3630,9 @@ public class PackageManagerService extends IPackageManager.Stub
mModuleInfoProvider = new ModuleInfoProvider(mContext, this); mModuleInfoProvider = new ModuleInfoProvider(mContext, this);
// Uncork cache invalidations and allow clients to cache package information.
PackageManager.uncorkPackageInfoCache();
// Now after opening every single application zip, make sure they // Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and // are all flushed. Not really needed, but keeps things nice and
// tidy. // tidy.

View File

@@ -3908,11 +3908,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
*/ */
private void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated, private void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated,
@NonNull PermissionCallback callback) { @NonNull PermissionCallback callback) {
final int flags = UPDATE_PERMISSIONS_ALL | PackageManager.corkPackageInfoCache(); // Prevent invalidation storm
(sdkUpdated try {
? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL final int flags = UPDATE_PERMISSIONS_ALL |
: 0); (sdkUpdated
updatePermissions(null, null, volumeUuid, flags, callback); ? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL
: 0);
updatePermissions(null, null, volumeUuid, flags, callback);
} finally {
PackageManager.uncorkPackageInfoCache();
}
} }
/** /**