Merge "Reduce locking during IO heavy operations." into udc-dev

This commit is contained in:
Treehugger Robot
2023-04-30 05:18:12 +00:00
committed by Android (Google) Code Review
2 changed files with 118 additions and 101 deletions

View File

@@ -93,7 +93,6 @@ import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
@@ -149,6 +148,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -321,8 +321,7 @@ public class ShortcutService extends IShortcutService.Stub {
private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks =
new ArrayList<>(1);
@GuardedBy("mLock")
private long mRawLastResetTime;
private final AtomicLong mRawLastResetTime = new AtomicLong(0);
/**
* User ID -> UserShortcuts
@@ -756,10 +755,15 @@ public class ShortcutService extends IShortcutService.Stub {
}
/** Return the base state file name */
private AtomicFile getBaseStateFile() {
final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
path.mkdirs();
return new AtomicFile(path);
final ResilientAtomicFile getBaseStateFile() {
File mainFile = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
File temporaryBackup = new File(injectSystemDataPath(),
FILENAME_BASE_STATE + ".backup");
File reserveCopy = new File(injectSystemDataPath(),
FILENAME_BASE_STATE + ".reservecopy");
int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
return new ResilientAtomicFile(mainFile, temporaryBackup, reserveCopy, fileMode,
"base shortcut", null);
}
/**
@@ -976,80 +980,91 @@ public class ShortcutService extends IShortcutService.Stub {
writeAttr(out, name, intent.toUri(/* flags =*/ 0));
}
@GuardedBy("mLock")
@VisibleForTesting
void saveBaseStateLocked() {
final AtomicFile file = getBaseStateFile();
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Saving to " + file.getBaseFile());
}
void saveBaseState() {
try (ResilientAtomicFile file = getBaseStateFile()) {
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Saving to " + file.getBaseFile());
}
FileOutputStream outs = null;
try {
outs = file.startWrite();
FileOutputStream outs = null;
try {
synchronized (mLock) {
outs = file.startWrite();
}
// Write to XML
TypedXmlSerializer out = Xml.resolveSerializer(outs);
out.startDocument(null, true);
out.startTag(null, TAG_ROOT);
// Write to XML
TypedXmlSerializer out = Xml.resolveSerializer(outs);
out.startDocument(null, true);
out.startTag(null, TAG_ROOT);
// Body.
writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
// Body.
// No locking required. Ok to add lock later if we save more data.
writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime.get());
// Epilogue.
out.endTag(null, TAG_ROOT);
out.endDocument();
// Epilogue.
out.endTag(null, TAG_ROOT);
out.endDocument();
// Close.
file.finishWrite(outs);
} catch (IOException e) {
Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
file.failWrite(outs);
// Close.
file.finishWrite(outs);
} catch (IOException e) {
Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
file.failWrite(outs);
}
}
}
@GuardedBy("mLock")
private void loadBaseStateLocked() {
mRawLastResetTime = 0;
mRawLastResetTime.set(0);
final AtomicFile file = getBaseStateFile();
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Loading from " + file.getBaseFile());
}
try (FileInputStream in = file.openRead()) {
TypedXmlPullParser parser = Xml.resolvePullParser(in);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final int depth = parser.getDepth();
// Check the root tag
final String tag = parser.getName();
if (depth == 1) {
if (!TAG_ROOT.equals(tag)) {
Slog.e(TAG, "Invalid root tag: " + tag);
return;
}
continue;
}
// Assume depth == 2
switch (tag) {
case TAG_LAST_RESET_TIME:
mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
break;
default:
Slog.e(TAG, "Invalid tag: " + tag);
break;
}
try (ResilientAtomicFile file = getBaseStateFile()) {
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Loading from " + file.getBaseFile());
}
} catch (FileNotFoundException e) {
// Use the default
} catch (IOException | XmlPullParserException e) {
Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
FileInputStream in = null;
try {
in = file.openRead();
if (in == null) {
throw new FileNotFoundException(file.getBaseFile().getAbsolutePath());
}
mRawLastResetTime = 0;
TypedXmlPullParser parser = Xml.resolvePullParser(in);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final int depth = parser.getDepth();
// Check the root tag
final String tag = parser.getName();
if (depth == 1) {
if (!TAG_ROOT.equals(tag)) {
Slog.e(TAG, "Invalid root tag: " + tag);
return;
}
continue;
}
// Assume depth == 2
switch (tag) {
case TAG_LAST_RESET_TIME:
mRawLastResetTime.set(parseLongAttribute(parser, ATTR_VALUE));
break;
default:
Slog.e(TAG, "Invalid tag: " + tag);
break;
}
}
} catch (FileNotFoundException e) {
// Use the default
} catch (IOException | XmlPullParserException e) {
// Remove corrupted file and retry.
file.failRead(in, e);
loadBaseStateLocked();
return;
}
}
// Adjust the last reset time.
getLastResetTimeLocked();
@@ -1067,8 +1082,7 @@ public class ShortcutService extends IShortcutService.Stub {
"user shortcut", null);
}
@GuardedBy("mLock")
private void saveUserLocked(@UserIdInt int userId) {
private void saveUser(@UserIdInt int userId) {
try (ResilientAtomicFile file = getUserFile(userId)) {
FileOutputStream os = null;
try {
@@ -1076,9 +1090,10 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.d(TAG, "Saving to " + file);
}
os = file.startWrite();
saveUserInternalLocked(userId, os, /* forBackup= */ false);
synchronized (mLock) {
os = file.startWrite();
saveUserInternalLocked(userId, os, /* forBackup= */ false);
}
file.finishWrite(os);
@@ -1215,16 +1230,19 @@ public class ShortcutService extends IShortcutService.Stub {
}
try {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutSaveDirtyInfo");
List<Integer> dirtyUserIds = new ArrayList<>();
synchronized (mLock) {
for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
final int userId = mDirtyUserIds.get(i);
if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
saveBaseStateLocked();
} else {
saveUserLocked(userId);
}
List<Integer> tmp = mDirtyUserIds;
mDirtyUserIds = dirtyUserIds;
dirtyUserIds = tmp;
}
for (int i = dirtyUserIds.size() - 1; i >= 0; i--) {
final int userId = dirtyUserIds.get(i);
if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
saveBaseState();
} else {
saveUser(userId);
}
mDirtyUserIds.clear();
}
} catch (Exception e) {
wtf("Exception in saveDirtyInfo", e);
@@ -1237,14 +1255,14 @@ public class ShortcutService extends IShortcutService.Stub {
@GuardedBy("mLock")
long getLastResetTimeLocked() {
updateTimesLocked();
return mRawLastResetTime;
return mRawLastResetTime.get();
}
/** Return the next reset time. */
@GuardedBy("mLock")
long getNextResetTimeLocked() {
updateTimesLocked();
return mRawLastResetTime + mResetInterval;
return mRawLastResetTime.get() + mResetInterval;
}
static boolean isClockValid(long time) {
@@ -1259,25 +1277,26 @@ public class ShortcutService extends IShortcutService.Stub {
final long now = injectCurrentTimeMillis();
final long prevLastResetTime = mRawLastResetTime;
final long prevLastResetTime = mRawLastResetTime.get();
long newLastResetTime = prevLastResetTime;
if (mRawLastResetTime == 0) { // first launch.
if (newLastResetTime == 0) { // first launch.
// TODO Randomize??
mRawLastResetTime = now;
} else if (now < mRawLastResetTime) {
newLastResetTime = now;
} else if (now < newLastResetTime) {
// Clock rewound.
if (isClockValid(now)) {
Slog.w(TAG, "Clock rewound");
// TODO Randomize??
mRawLastResetTime = now;
}
} else {
if ((mRawLastResetTime + mResetInterval) <= now) {
final long offset = mRawLastResetTime % mResetInterval;
mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset;
newLastResetTime = now;
}
} else if ((newLastResetTime + mResetInterval) <= now) {
final long offset = newLastResetTime % mResetInterval;
newLastResetTime = ((now / mResetInterval) * mResetInterval) + offset;
}
if (prevLastResetTime != mRawLastResetTime) {
mRawLastResetTime.set(newLastResetTime);
if (prevLastResetTime != newLastResetTime) {
scheduleSaveBaseState();
}
}
@@ -2705,9 +2724,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
void resetAllThrottlingInner() {
synchronized (mLock) {
mRawLastResetTime = injectCurrentTimeMillis();
}
mRawLastResetTime.set(injectCurrentTimeMillis());
scheduleSaveBaseState();
Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
}
@@ -2725,8 +2742,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
getPackageShortcutsLocked(packageName, userId)
.resetRateLimitingForCommandLineNoSaving();
saveUserLocked(userId);
}
saveUser(userId);
}
// We override this method in unit tests to do a simpler check.
@@ -4505,8 +4522,8 @@ public class ShortcutService extends IShortcutService.Stub {
dumpCurrentTime(pw);
pw.println();
});
saveUserLocked(userId);
}
saveUser(userId);
}
// === Dump ===
@@ -4717,9 +4734,9 @@ public class ShortcutService extends IShortcutService.Stub {
pw.print(formatTime(now));
pw.print(" Raw last reset: [");
pw.print(mRawLastResetTime);
pw.print(mRawLastResetTime.get());
pw.print("] ");
pw.print(formatTime(mRawLastResetTime));
pw.print(formatTime(mRawLastResetTime.get()));
final long last = getLastResetTimeLocked();
pw.print(" Last reset: [");

View File

@@ -201,7 +201,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
mService.saveBaseStateLocked();
mService.saveBaseState();
dumpBaseStateFile();