Merge "Usage Stats: don't block writing stats to disk." into gingerbread

This commit is contained in:
Brad Fitzpatrick
2010-08-03 16:57:47 -07:00
committed by Android (Google) Code Review

View File

@ -44,6 +44,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/** /**
* This service collects the statistics associated with usage * This service collects the statistics associated with usage
@ -88,11 +91,13 @@ public final class UsageStatsService extends IUsageStats.Stub {
private boolean mIsResumed; private boolean mIsResumed;
private File mFile; private File mFile;
private String mFileLeaf; private String mFileLeaf;
//private File mBackupFile;
private long mLastWriteElapsedTime;
private File mDir; private File mDir;
private Calendar mCal;
private int mLastWriteDay; private Calendar mCal; // guarded by itself
private final AtomicInteger mLastWriteDay = new AtomicInteger(-1);
private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0);
private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false);
static class TimeStats { static class TimeStats {
int count; int count;
@ -241,17 +246,18 @@ public final class UsageStatsService extends IUsageStats.Stub {
mFileLeaf = getCurrentDateStr(FILE_PREFIX); mFileLeaf = getCurrentDateStr(FILE_PREFIX);
mFile = new File(mDir, mFileLeaf); mFile = new File(mDir, mFileLeaf);
readStatsFromFile(); readStatsFromFile();
mLastWriteElapsedTime = SystemClock.elapsedRealtime(); mLastWriteElapsedTime.set(SystemClock.elapsedRealtime());
// mCal was set by getCurrentDateStr(), want to use that same time. // mCal was set by getCurrentDateStr(), want to use that same time.
mLastWriteDay = mCal.get(Calendar.DAY_OF_YEAR); mLastWriteDay.set(mCal.get(Calendar.DAY_OF_YEAR));
} }
/* /*
* Utility method to convert date into string. * Utility method to convert date into string.
*/ */
private String getCurrentDateStr(String prefix) { private String getCurrentDateStr(String prefix) {
mCal.setTimeInMillis(System.currentTimeMillis());
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
synchronized (mCal) {
mCal.setTimeInMillis(System.currentTimeMillis());
if (prefix != null) { if (prefix != null) {
sb.append(prefix); sb.append(prefix);
} }
@ -266,6 +272,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
sb.append("0"); sb.append("0");
} }
sb.append(dd); sb.append(dd);
}
return sb.toString(); return sb.toString();
} }
@ -361,22 +368,55 @@ public final class UsageStatsService extends IUsageStats.Stub {
} }
} }
private void writeStatsToFile(boolean force) { /**
synchronized (mFileLock) { * Conditionally start up a disk write if it's been awhile, or the
* day has rolled over.
*
* This is called indirectly from user-facing actions (when
* 'force' is false) so it tries to be quick, without writing to
* disk directly or acquiring heavy locks.
*
* @params force do an unconditional, synchronous stats flush
* to disk on the current thread.
*/
private void writeStatsToFile(final boolean force) {
int curDay;
synchronized (mCal) {
mCal.setTimeInMillis(System.currentTimeMillis()); mCal.setTimeInMillis(System.currentTimeMillis());
final int curDay = mCal.get(Calendar.DAY_OF_YEAR); curDay = mCal.get(Calendar.DAY_OF_YEAR);
}
final boolean dayChanged = curDay != mLastWriteDay.get();
// Determine if the day changed... note that this will be wrong // Determine if the day changed... note that this will be wrong
// if the year has changed but we are in the same day of year... // if the year has changed but we are in the same day of year...
// we can probably live with this. // we can probably live with this.
final boolean dayChanged = curDay != mLastWriteDay; final long currElapsedTime = SystemClock.elapsedRealtime();
long currElapsedTime = SystemClock.elapsedRealtime();
// Fast common path, without taking the often-contentious
// mFileLock.
if (!force) { if (!force) {
if (((currElapsedTime-mLastWriteElapsedTime) < FILE_WRITE_INTERVAL) && if (!dayChanged &&
(!dayChanged)) { (currElapsedTime - mLastWriteElapsedTime.get()) < FILE_WRITE_INTERVAL) {
// wait till the next update // wait till the next update
return; return;
} }
if (mUnforcedDiskWriteRunning.compareAndSet(false, true)) {
new Thread("UsageStatsService_DiskWriter") {
public void run() {
try {
Slog.d(TAG, "Disk writer thread starting.");
writeStatsToFile(true);
} finally {
mUnforcedDiskWriteRunning.set(false);
Slog.d(TAG, "Disk writer thread ending.");
} }
}
}.start();
}
return;
}
synchronized (mFileLock) {
// Get the most recent file // Get the most recent file
mFileLeaf = getCurrentDateStr(FILE_PREFIX); mFileLeaf = getCurrentDateStr(FILE_PREFIX);
// Copy current file to back up // Copy current file to back up
@ -395,10 +435,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
try { try {
// Write mStats to file // Write mStats to file
writeStatsFLOCK(); writeStatsFLOCK(mFile);
mLastWriteElapsedTime = currElapsedTime; mLastWriteElapsedTime.set(currElapsedTime);
if (dayChanged) { if (dayChanged) {
mLastWriteDay = curDay; mLastWriteDay.set(curDay);
// clear stats // clear stats
synchronized (mStats) { synchronized (mStats) {
mStats.clear(); mStats.clear();
@ -418,10 +458,11 @@ public final class UsageStatsService extends IUsageStats.Stub {
} }
} }
} }
Slog.d(TAG, "Dumped usage stats.");
} }
private void writeStatsFLOCK() throws IOException { private void writeStatsFLOCK(File file) throws IOException {
FileOutputStream stream = new FileOutputStream(mFile); FileOutputStream stream = new FileOutputStream(file);
try { try {
Parcel out = Parcel.obtain(); Parcel out = Parcel.obtain();
writeStatsToParcelFLOCK(out); writeStatsToParcelFLOCK(out);