1  /*
2   * Copyright (C) 2024 The Android Open Source Project
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *      http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.android.launcher3.util.coroutines
18  
19  import kotlinx.coroutines.CoroutineDispatcher
20  import kotlinx.coroutines.DelicateCoroutinesApi
21  import kotlinx.coroutines.Dispatchers
22  import kotlinx.coroutines.newFixedThreadPoolContext
23  
24  interface DispatcherProvider {
25      val default: CoroutineDispatcher
26      val background: CoroutineDispatcher
27      val main: CoroutineDispatcher
28      val unconfined: CoroutineDispatcher
29  }
30  
31  object ProductionDispatchers : DispatcherProvider {
32      private val bgDispatcher = CoroutinesHelper.bgDispatcher()
33  
34      override val default: CoroutineDispatcher = Dispatchers.Default
35      override val background: CoroutineDispatcher = bgDispatcher
36      override val main: CoroutineDispatcher = Dispatchers.Main
37      override val unconfined: CoroutineDispatcher = Dispatchers.Unconfined
38  }
39  
40  private object CoroutinesHelper {
41      /**
42       * Default Coroutine dispatcher for background operations.
43       *
44       * Note that this is explicitly limiting the number of threads. In the past, we used
45       * [Dispatchers.IO]. This caused >40 threads to be spawned, and a lot of thread list lock
46       * contention between then, eventually causing jank.
47       */
48      @OptIn(DelicateCoroutinesApi::class)
bgDispatchernull49      fun bgDispatcher(): CoroutineDispatcher {
50          // Why a new ThreadPool instead of just using Dispatchers.IO with
51          // CoroutineDispatcher.limitedParallelism? Because, if we were to use Dispatchers.IO, we
52          // would share those threads with other dependencies using Dispatchers.IO.
53          // Using a dedicated thread pool we have guarantees only Launcher is able to schedule
54          // code on those.
55          return newFixedThreadPoolContext(
56              nThreads = Runtime.getRuntime().availableProcessors(),
57              name = "LauncherBg",
58          )
59      }
60  }
61