• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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