Make the memory store operations serial.
Bug: 128499160 Test: new test in this patch Change-Id: I10b5c5cd85fcb76924ba96c8c379be677774705d
This commit is contained in:
@@ -60,7 +60,6 @@ import java.util.concurrent.Executors;
|
|||||||
*/
|
*/
|
||||||
public class IpMemoryStoreService extends IIpMemoryStore.Stub {
|
public class IpMemoryStoreService extends IIpMemoryStore.Stub {
|
||||||
private static final String TAG = IpMemoryStoreService.class.getSimpleName();
|
private static final String TAG = IpMemoryStoreService.class.getSimpleName();
|
||||||
private static final int MAX_CONCURRENT_THREADS = 4;
|
|
||||||
private static final int DATABASE_SIZE_THRESHOLD = 10 * 1024 * 1024; //10MB
|
private static final int DATABASE_SIZE_THRESHOLD = 10 * 1024 * 1024; //10MB
|
||||||
private static final int MAX_DROP_RECORD_TIMES = 500;
|
private static final int MAX_DROP_RECORD_TIMES = 500;
|
||||||
private static final int MIN_DELETE_NUM = 5;
|
private static final int MIN_DELETE_NUM = 5;
|
||||||
@@ -107,23 +106,17 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub {
|
|||||||
db = null;
|
db = null;
|
||||||
}
|
}
|
||||||
mDb = db;
|
mDb = db;
|
||||||
// The work-stealing thread pool executor will spawn threads as needed up to
|
// The single thread executor guarantees that all work is executed sequentially on the
|
||||||
// the max only when there is no free thread available. This generally behaves
|
// same thread, and no two tasks can be active at the same time. This is required to
|
||||||
// exactly like one would expect it intuitively :
|
// ensure operations from multiple clients don't interfere with each other (in particular,
|
||||||
// - When work arrives, it will spawn a new thread iff there are no available threads
|
// operations involving a transaction must not run concurrently with other operations
|
||||||
// - When there is no work to do it will shutdown threads after a while (the while
|
// as the other operations might be taken as part of the transaction). By default, the
|
||||||
// being equal to 2 seconds (not configurable) when max threads are spun up and
|
// single thread executor runs off an unbounded queue.
|
||||||
// twice as much for every one less thread)
|
// TODO : investigate replacing this scheme with a scheme where each thread has its own
|
||||||
// - When all threads are busy the work is enqueued and waits for any worker
|
// instance of the database, as it may be faster. It is likely however that IpMemoryStore
|
||||||
// to become available.
|
// operations are mostly IO-bound anyway, and additional contention is unlikely to bring
|
||||||
// Because the stealing pool is made for very heavily parallel execution of
|
// benefits. Alternatively, a read-write lock might increase throughput.
|
||||||
// small tasks that spawn others, it creates a queue per thread that in this
|
mExecutor = Executors.newSingleThreadExecutor();
|
||||||
// case is overhead. However, the three behaviors above make it a superior
|
|
||||||
// choice to cached or fixedThreadPoolExecutor, neither of which can actually
|
|
||||||
// enqueue a task waiting for a thread to be free. This can probably be solved
|
|
||||||
// with judicious subclassing of ThreadPoolExecutor, but that's a lot of dangerous
|
|
||||||
// complexity for little benefit in this case.
|
|
||||||
mExecutor = Executors.newWorkStealingPool(MAX_CONCURRENT_THREADS);
|
|
||||||
RegularMaintenanceJobService.schedule(mContext, this);
|
RegularMaintenanceJobService.schedule(mContext, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -732,4 +732,25 @@ public class IpMemoryStoreServiceTest {
|
|||||||
latch.countDown();
|
latch.countDown();
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testTasksAreSerial() {
|
||||||
|
final long sleepTimeMs = 1000;
|
||||||
|
final long startTime = System.currentTimeMillis();
|
||||||
|
mService.retrieveNetworkAttributes("somekey", onNetworkAttributesRetrieved(
|
||||||
|
(status, key, attr) -> {
|
||||||
|
assertTrue("Unexpected status : " + status.resultCode, status.isSuccess());
|
||||||
|
try {
|
||||||
|
Thread.sleep(sleepTimeMs);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
fail("InterruptedException");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
doLatched("Serial tasks timing out", latch ->
|
||||||
|
mService.retrieveNetworkAttributes("somekey", onNetworkAttributesRetrieved(
|
||||||
|
(status, key, attr) -> {
|
||||||
|
assertTrue("Unexpected status : " + status.resultCode,
|
||||||
|
status.isSuccess());
|
||||||
|
assertTrue(System.currentTimeMillis() >= startTime + sleepTimeMs);
|
||||||
|
})), DEFAULT_TIMEOUT_MS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user