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.systemui.util.kotlin
18 
19 import com.android.systemui.dagger.SysUISingleton
20 import com.android.systemui.dagger.qualifiers.Application
21 import com.android.systemui.dagger.qualifiers.Background
22 import com.android.systemui.dagger.qualifiers.Tracing
23 import dagger.Module
24 import dagger.Provides
25 import kotlin.coroutines.CoroutineContext
26 import kotlinx.coroutines.CoroutineDispatcher
27 import kotlinx.coroutines.CoroutineScope
28 import kotlinx.coroutines.DelicateCoroutinesApi
29 import kotlinx.coroutines.Dispatchers
30 import kotlinx.coroutines.newFixedThreadPoolContext
31 import kotlinx.coroutines.plus
32 
33 private const val LIMIT_BACKGROUND_DISPATCHER_THREADS = true
34 
35 /** Providers for various SystemUI-specific coroutines-related constructs. */
36 @Module
37 class SysUICoroutinesModule {
38     @Provides
39     @SysUISingleton
40     @Background
bgApplicationScopenull41     fun bgApplicationScope(
42         @Application applicationScope: CoroutineScope,
43         @Background coroutineContext: CoroutineContext,
44     ): CoroutineScope = applicationScope.plus(coroutineContext)
45 
46     /**
47      * Default Coroutine dispatcher for background operations.
48      *
49      * Note that this is explicitly limiting the number of threads. In the past, we used
50      * [Dispatchers.IO]. This caused >40 threads to be spawned, and a lot of thread list lock
51      * contention between then, eventually causing jank.
52      */
53     @OptIn(DelicateCoroutinesApi::class)
54     @Provides
55     @SysUISingleton
56     @Background
57     @Deprecated(
58         "Use @Background CoroutineContext instead",
59         ReplaceWith("bgCoroutineContext()", "kotlin.coroutines.CoroutineContext")
60     )
61     fun bgDispatcher(): CoroutineDispatcher {
62         return if (LIMIT_BACKGROUND_DISPATCHER_THREADS) {
63             // Why a new ThreadPool instead of just using Dispatchers.IO with
64             // CoroutineDispatcher.limitedParallelism? Because, if we were to use Dispatchers.IO, we
65             // would share those threads with other dependencies using Dispatchers.IO.
66             // Using a dedicated thread pool we have guarantees only SystemUI is able to schedule
67             // code on those.
68             newFixedThreadPoolContext(
69                 nThreads = Runtime.getRuntime().availableProcessors(),
70                 name = "SystemUIBg"
71             )
72         } else {
73             Dispatchers.IO
74         }
75     }
76 
77     @Provides
78     @Background
79     @SysUISingleton
bgCoroutineContextnull80     fun bgCoroutineContext(
81         @Tracing tracingCoroutineContext: CoroutineContext,
82         @Background bgCoroutineDispatcher: CoroutineDispatcher,
83     ): CoroutineContext {
84         return bgCoroutineDispatcher + tracingCoroutineContext
85     }
86 }
87