1 // Copyright (c) 2023 Huawei Device Co., Ltd. 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 use std::io; 15 use std::sync::Mutex; 16 17 cfg_ffrt!( 18 use ylong_ffrt::{ffrt_set_cpu_worker_max_num, Qos}; 19 use std::collections::HashMap; 20 use libc::c_uint; 21 ); 22 23 use crate::builder::common_builder::impl_common; 24 use crate::builder::CommonBuilder; 25 #[cfg(feature = "multi_instance_runtime")] 26 use crate::executor::{AsyncHandle, Runtime}; 27 28 pub(crate) static GLOBAL_BUILDER: Mutex<Option<MultiThreadBuilder>> = Mutex::new(None); 29 30 /// Runtime builder that configures a multi-threaded runtime, or the global 31 /// runtime. 32 pub struct MultiThreadBuilder { 33 pub(crate) common: CommonBuilder, 34 35 #[cfg(not(feature = "ffrt"))] 36 /// Maximum thread number for core thread pool 37 pub(crate) core_thread_size: Option<usize>, 38 39 #[cfg(feature = "ffrt")] 40 /// Thread number for each qos 41 pub(crate) thread_num_by_qos: HashMap<Qos, u32>, 42 } 43 44 impl MultiThreadBuilder { new() -> Self45 pub(crate) fn new() -> Self { 46 MultiThreadBuilder { 47 common: CommonBuilder::new(), 48 #[cfg(not(feature = "ffrt"))] 49 core_thread_size: None, 50 #[cfg(feature = "ffrt")] 51 thread_num_by_qos: HashMap::new(), 52 } 53 } 54 55 /// Configures the global runtime. 56 /// 57 /// # Error 58 /// If the global runtime is already running or this method has been called 59 /// before, then it will return an `AlreadyExists` error. build_global(self) -> io::Result<()>60 pub fn build_global(self) -> io::Result<()> { 61 let mut builder = GLOBAL_BUILDER.lock().unwrap(); 62 if builder.is_some() { 63 return Err(io::ErrorKind::AlreadyExists.into()); 64 } 65 66 #[cfg(feature = "ffrt")] 67 { 68 for (qos, worker_num) in self.thread_num_by_qos.iter() { 69 unsafe { 70 ffrt_set_cpu_worker_max_num(*qos, worker_num.clone() as c_uint); 71 } 72 } 73 } 74 75 *builder = Some(self); 76 Ok(()) 77 } 78 } 79 80 #[cfg(feature = "ffrt")] 81 impl MultiThreadBuilder { 82 /// Sets the maximum worker number for a specific qos group. 83 /// 84 /// If a worker number has already been set for a qos, calling the method 85 /// with the same qos will overwrite the old value. 86 /// 87 /// # Error 88 /// The accepted worker number range for each qos is [1, 20]. If 0 is passed 89 /// in, then the maximum worker number will be set to 1. If a number 90 /// greater than 20 is passed in, then the maximum worker number will be 91 /// set to 20. max_worker_num_by_qos(mut self, qos: Qos, num: u32) -> Self92 pub fn max_worker_num_by_qos(mut self, qos: Qos, num: u32) -> Self { 93 let worker = match num { 94 0 => 1, 95 n if n > 20 => 20, 96 n => n, 97 }; 98 self.thread_num_by_qos.insert(qos, worker); 99 self 100 } 101 } 102 103 #[cfg(not(feature = "ffrt"))] 104 impl MultiThreadBuilder { 105 /// Initializes the runtime and returns its instance. 106 #[cfg(feature = "multi_instance_runtime")] build(&mut self) -> io::Result<Runtime>107 pub fn build(&mut self) -> io::Result<Runtime> { 108 use crate::builder::initialize_async_spawner; 109 let async_spawner = initialize_async_spawner(self)?; 110 111 Ok(Runtime { 112 async_spawner: AsyncHandle::MultiThread(async_spawner), 113 }) 114 } 115 116 /// Sets the number of core worker threads. 117 /// 118 /// 119 /// The boundary of thread number is 1-64: 120 /// If sets a number smaller than 1, then thread number would be set to 1. 121 /// If sets a number larger than 64, then thread number would be set to 64. 122 /// The default value is the number of cores of the cpu. 123 /// 124 /// # Examples 125 /// ``` 126 /// use crate::ylong_runtime::builder::RuntimeBuilder; 127 /// 128 /// let runtime = RuntimeBuilder::new_multi_thread().worker_num(8); 129 /// ``` worker_num(mut self, core_pool_size: usize) -> Self130 pub fn worker_num(mut self, core_pool_size: usize) -> Self { 131 if core_pool_size < 1 { 132 self.core_thread_size = Some(1); 133 } else if core_pool_size > 64 { 134 self.core_thread_size = Some(64); 135 } else { 136 self.core_thread_size = Some(core_pool_size); 137 } 138 self 139 } 140 } 141 142 impl_common!(MultiThreadBuilder); 143 144 #[cfg(feature = "full")] 145 #[cfg(test)] 146 mod test { 147 use crate::builder::RuntimeBuilder; 148 use crate::executor::{global_default_async, AsyncHandle}; 149 150 /// UT test cases for blocking on a time sleep without initializing the 151 /// runtime. 152 /// 153 /// # Brief 154 /// 1. Configure the global runtime to make it have six core threads 155 /// 2. Get the global runtime 156 /// 3. Check the core thread number of the runtime 157 /// 4. Call build_global once more 158 /// 5. Check the error 159 #[test] ut_build_global()160 fn ut_build_global() { 161 let ret = RuntimeBuilder::new_multi_thread() 162 .worker_num(6) 163 .max_blocking_pool_size(3) 164 .build_global(); 165 assert!(ret.is_ok()); 166 167 let async_pool = global_default_async(); 168 match &async_pool.async_spawner { 169 AsyncHandle::CurrentThread(_) => unreachable!(), 170 AsyncHandle::MultiThread(x) => { 171 assert_eq!(x.inner.total, 6); 172 } 173 } 174 175 let ret = RuntimeBuilder::new_multi_thread() 176 .worker_num(2) 177 .max_blocking_pool_size(3) 178 .build_global(); 179 assert!(ret.is_err()); 180 } 181 } 182 183 #[cfg(feature = "ffrt")] 184 #[cfg(test)] 185 mod ffrt_test { 186 use ylong_ffrt::Qos::{Default, UserInteractive}; 187 188 use crate::builder::MultiThreadBuilder; 189 190 /// UT test cases for max_worker_num_by_qos 191 /// runtime. 192 /// 193 /// # Brief 194 /// 1. Sets UserInteractive qos group to have 0 maximum worker number. 195 /// 2. Checks if the actual value is 1 196 /// 3. Sets UserInteractive qos group to have 21 maximum worker number. 197 /// 4. Checks if the actual value is 20 198 /// 5. Set Default qos group to have 8 maximum worker number. 199 /// 6. Checks if the actual value is 8. 200 /// 7. Calls build_global on the builder, check if the return value is Ok 201 #[test] ut_set_max_worker()202 fn ut_set_max_worker() { 203 let builder = MultiThreadBuilder::new(); 204 let builder = builder.max_worker_num_by_qos(UserInteractive, 0); 205 let num = builder.thread_num_by_qos.get(&UserInteractive).unwrap(); 206 assert_eq!(*num, 1); 207 208 let builder = builder.max_worker_num_by_qos(UserInteractive, 21); 209 let num = builder.thread_num_by_qos.get(&UserInteractive).unwrap(); 210 assert_eq!(*num, 20); 211 212 let builder = MultiThreadBuilder::new().max_worker_num_by_qos(Default, 8); 213 let num = builder.thread_num_by_qos.get(&Default).unwrap(); 214 assert_eq!(*num, 8); 215 } 216 } 217