[MS04] Add an executor to the memory store.
Test: atest IpMemoryStoreServiceTest
atest IpMemoryStore
atest ParcelableTests
Bug: 116512211
Change-Id: I67797b3ff83fa9de4c813035204d4542d1fcc14e
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
index 98dc988..55a72190 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
@@ -31,6 +31,9 @@
import android.net.ipmemorystore.NetworkAttributesParcelable;
import android.util.Log;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
/**
* Implementation for the IP memory store.
* This component offers specialized services for network components to store and retrieve
@@ -41,11 +44,14 @@
*/
public class IpMemoryStoreService extends IIpMemoryStore.Stub {
private static final String TAG = IpMemoryStoreService.class.getSimpleName();
+ private static final int MAX_CONCURRENT_THREADS = 4;
@NonNull
final Context mContext;
@Nullable
final SQLiteDatabase mDb;
+ @NonNull
+ final ExecutorService mExecutor;
/**
* Construct an IpMemoryStoreService object.
@@ -75,6 +81,37 @@
db = null;
}
mDb = db;
+ // The work-stealing thread pool executor will spawn threads as needed up to
+ // the max only when there is no free thread available. This generally behaves
+ // exactly like one would expect it intuitively :
+ // - When work arrives, it will spawn a new thread iff there are no available threads
+ // - When there is no work to do it will shutdown threads after a while (the while
+ // being equal to 2 seconds (not configurable) when max threads are spun up and
+ // twice as much for every one less thread)
+ // - When all threads are busy the work is enqueued and waits for any worker
+ // to become available.
+ // Because the stealing pool is made for very heavily parallel execution of
+ // small tasks that spawn others, it creates a queue per thread that in this
+ // 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);
+ }
+
+ /**
+ * Shutdown the memory store service, cancelling running tasks and dropping queued tasks.
+ *
+ * This is provided to give a way to clean up, and is meant to be available in case of an
+ * emergency shutdown.
+ */
+ public void shutdown() {
+ // By contrast with ExecutorService#shutdown, ExecutorService#shutdownNow tries
+ // to cancel the existing tasks, and does not wait for completion. It does not
+ // guarantee the threads can be terminated in any given amount of time.
+ mExecutor.shutdownNow();
+ if (mDb != null) mDb.close();
}
/**