@@ -74,9 +74,11 @@ void processAll(ExecutorService executor) {
7474 }
7575 isProcessing = false ;
7676
77- // After processing all operations, shut down the executor
77+ // After processing all operations, shut down the executor.
78+ // Pass the executor reference explicitly so a concurrent reset()
79+ // cannot swap in a new one that we accidentally shut down.
7880 IterableLogger .d (TAG , "All queued operations processed, shutting down background executor" );
79- shutdownBackgroundExecutorAsync ();
81+ shutdownBackgroundExecutorAsync (backgroundExecutor );
8082 });
8183 }
8284
@@ -334,19 +336,14 @@ static void shutdownBackgroundExecutor() {
334336 }
335337
336338 /**
337- * Shutdown the background executor asynchronously to avoid blocking the executor thread itself
338- * Used internally after initialization completes
339+ * Shutdown the given executor asynchronously to avoid blocking the executor thread itself.
340+ * The caller passes the exact executor instance that should be shut down, so a concurrent
341+ * reset() that swaps in a new executor cannot cause us to shut down the wrong one.
339342 */
340- private static void shutdownBackgroundExecutorAsync () {
341- final ExecutorService executorToShutdown ;
342- synchronized (initLock ) {
343- executorToShutdown = backgroundExecutor ;
344- if (executorToShutdown == null || executorToShutdown .isShutdown ()) {
345- return ;
346- }
343+ private static void shutdownBackgroundExecutorAsync (ExecutorService executorToShutdown ) {
344+ if (executorToShutdown == null || executorToShutdown .isShutdown ()) {
345+ return ;
347346 }
348- // Blocking operations happen outside the lock to avoid deadlock
349- // (this method is called from a task running on the executor itself)
350347 new Thread (() -> {
351348 try {
352349 executorToShutdown .shutdown ();
@@ -417,13 +414,14 @@ static void resetBackgroundInitializationState() {
417414 pendingCallbacks .clear ();
418415 callbackManager .reset ();
419416
420- // Always create a fresh executor. The old one may have a pending
421- // shutdownBackgroundExecutorAsync that hasn't run yet — if we kept it,
422- // the async shutdown would kill the executor under the next test.
423- if (backgroundExecutor != null && !backgroundExecutor .isShutdown ()) {
424- backgroundExecutor .shutdownNow ();
425- }
417+ // Swap in a fresh executor first, then shut down the old one.
418+ // This ensures shutdownBackgroundExecutorAsync (which may still be
419+ // pending from the old executor) cannot kill the new one.
420+ ExecutorService oldExecutor = backgroundExecutor ;
426421 backgroundExecutor = createExecutor ();
422+ if (oldExecutor != null && !oldExecutor .isShutdown ()) {
423+ oldExecutor .shutdownNow ();
424+ }
427425 }
428426 }
429427
0 commit comments