• 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, ffrt_set_worker_stack_size, Qos};
19     use std::collections::HashMap;
20     use libc::{c_uint, c_ulong};
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     #[cfg(feature = "ffrt")]
44     /// Thread stack size for each qos
45     pub(crate) stack_size_by_qos: HashMap<Qos, usize>,
46 }
47 
48 impl MultiThreadBuilder {
new() -> Self49     pub(crate) fn new() -> Self {
50         MultiThreadBuilder {
51             common: CommonBuilder::new(),
52             #[cfg(not(feature = "ffrt"))]
53             core_thread_size: None,
54             #[cfg(feature = "ffrt")]
55             thread_num_by_qos: HashMap::new(),
56             #[cfg(feature = "ffrt")]
57             stack_size_by_qos: HashMap::new(),
58         }
59     }
60 
61     /// Configures the global runtime.
62     ///
63     /// # Error
64     /// If the global runtime is already running or this method has been called
65     /// before, then it will return an `AlreadyExists` error.
build_global(self) -> io::Result<()>66     pub fn build_global(self) -> io::Result<()> {
67         let mut builder = GLOBAL_BUILDER.lock().unwrap();
68         if builder.is_some() {
69             return Err(io::ErrorKind::AlreadyExists.into());
70         }
71 
72         #[cfg(feature = "ffrt")]
73         unsafe {
74             for (qos, worker_num) in self.thread_num_by_qos.iter() {
75                 ffrt_set_cpu_worker_max_num(*qos, *worker_num as c_uint);
76             }
77 
78             for (qos, stack_size) in self.thread_num_by_qos.iter() {
79                 ffrt_set_worker_stack_size(*qos, *stack_size as c_ulong);
80             }
81         }
82 
83         *builder = Some(self);
84         Ok(())
85     }
86 }
87 
88 #[cfg(feature = "ffrt")]
89 impl MultiThreadBuilder {
90     /// Sets the maximum worker number for a specific qos group.
91     ///
92     /// If a worker number has already been set for a qos, calling the method
93     /// with the same qos will overwrite the old value.
94     ///
95     /// # Error
96     /// The accepted worker number range for each qos is [1, 20]. If 0 is passed
97     /// in, then the maximum worker number will be set to 1. If a number
98     /// greater than 20 is passed in, then the maximum worker number will be
99     /// set to 20.
max_worker_num_by_qos(mut self, qos: Qos, num: u32) -> Self100     pub fn max_worker_num_by_qos(mut self, qos: Qos, num: u32) -> Self {
101         let worker = match num {
102             0 => 1,
103             n if n > 20 => 20,
104             n => n,
105         };
106         self.thread_num_by_qos.insert(qos, worker);
107         self
108     }
109 
110     /// Sets the thread stack size for a specific qos group.
111     ///
112     /// If a stack size has already been set for a qos, calling the method
113     /// with the same qos will overwrite the old value
114     ///
115     /// # Error
116     /// The lowest accepted stack size is 16k. If a value under 16k is passed
117     /// in, then the stack size will be set to 16k instead.
stack_size_by_qos(mut self, qos: Qos, stack_size: usize) -> Self118     pub fn stack_size_by_qos(mut self, qos: Qos, stack_size: usize) -> Self {
119         const PTHREAD_STACK_MIN: usize = 16 * 1000;
120 
121         let stack_size = match stack_size {
122             n if n < PTHREAD_STACK_MIN => PTHREAD_STACK_MIN,
123             n => n,
124         };
125         self.stack_size_by_qos.insert(qos, stack_size);
126         self
127     }
128 }
129 
130 #[cfg(not(feature = "ffrt"))]
131 impl MultiThreadBuilder {
132     /// Initializes the runtime and returns its instance.
133     #[cfg(feature = "multi_instance_runtime")]
build(&mut self) -> io::Result<Runtime>134     pub fn build(&mut self) -> io::Result<Runtime> {
135         use crate::builder::initialize_async_spawner;
136         let async_spawner = initialize_async_spawner(self)?;
137 
138         Ok(Runtime {
139             async_spawner: AsyncHandle::MultiThread(async_spawner),
140         })
141     }
142 
143     /// Sets the number of core worker threads.
144     ///
145     ///
146     /// The boundary of thread number is 1-64:
147     /// If sets a number smaller than 1, then thread number would be set to 1.
148     /// If sets a number larger than 64, then thread number would be set to 64.
149     /// The default value is the number of cores of the cpu.
150     ///
151     /// # Examples
152     /// ```
153     /// use crate::ylong_runtime::builder::RuntimeBuilder;
154     ///
155     /// let runtime = RuntimeBuilder::new_multi_thread().worker_num(8);
156     /// ```
worker_num(mut self, core_pool_size: usize) -> Self157     pub fn worker_num(mut self, core_pool_size: usize) -> Self {
158         if core_pool_size < 1 {
159             self.core_thread_size = Some(1);
160         } else if core_pool_size > 64 {
161             self.core_thread_size = Some(64);
162         } else {
163             self.core_thread_size = Some(core_pool_size);
164         }
165         self
166     }
167 }
168 
169 impl_common!(MultiThreadBuilder);
170 
171 #[cfg(feature = "full")]
172 #[cfg(test)]
173 mod test {
174     use crate::builder::RuntimeBuilder;
175     use crate::executor::{global_default_async, AsyncHandle};
176 
177     /// UT test cases for blocking on a time sleep without initializing the
178     /// runtime.
179     ///
180     /// # Brief
181     /// 1. Configure the global runtime to make it have six core threads
182     /// 2. Get the global runtime
183     /// 3. Check the core thread number of the runtime
184     /// 4. Call build_global once more
185     /// 5. Check the error
186     #[test]
ut_build_global()187     fn ut_build_global() {
188         let ret = RuntimeBuilder::new_multi_thread()
189             .worker_num(6)
190             .max_blocking_pool_size(3)
191             .build_global();
192         assert!(ret.is_ok());
193 
194         let async_pool = global_default_async();
195         match &async_pool.async_spawner {
196             AsyncHandle::CurrentThread(_) => unreachable!(),
197             AsyncHandle::MultiThread(x) => {
198                 assert_eq!(x.inner.total, 6);
199             }
200         }
201 
202         let ret = RuntimeBuilder::new_multi_thread()
203             .worker_num(2)
204             .max_blocking_pool_size(3)
205             .build_global();
206         assert!(ret.is_err());
207     }
208 }
209 
210 #[cfg(feature = "ffrt")]
211 #[cfg(test)]
212 mod ffrt_test {
213     use ylong_ffrt::Qos::{Default, UserInitiated, UserInteractive};
214 
215     use crate::builder::MultiThreadBuilder;
216 
217     /// UT test cases for max_worker_num_by_qos
218     ///
219     /// # Brief
220     /// 1. Sets UserInteractive qos group to have 0 maximum worker number.
221     /// 2. Checks if the actual value is 1
222     /// 3. Sets UserInteractive qos group to have 21 maximum worker number.
223     /// 4. Checks if the actual value is 20
224     /// 5. Set Default qos group to have 8 maximum worker number.
225     /// 6. Checks if the actual value is 8.
226     /// 7. Calls build_global on the builder, check if the return value is Ok
227     #[test]
ut_set_max_worker()228     fn ut_set_max_worker() {
229         let builder = MultiThreadBuilder::new();
230         let builder = builder.max_worker_num_by_qos(UserInteractive, 0);
231         let num = builder.thread_num_by_qos.get(&UserInteractive).unwrap();
232         assert_eq!(*num, 1);
233 
234         let builder = builder.max_worker_num_by_qos(UserInteractive, 21);
235         let num = builder.thread_num_by_qos.get(&UserInteractive).unwrap();
236         assert_eq!(*num, 20);
237 
238         let builder = MultiThreadBuilder::new().max_worker_num_by_qos(Default, 8);
239         let num = builder.thread_num_by_qos.get(&Default).unwrap();
240         assert_eq!(*num, 8);
241     }
242 
243     /// UT cases for stack_size_by_qos
244     ///
245     /// # Brief
246     /// 1. Sets UserInitiated qos group's stack size to 16k - 1
247     /// 2. Checks if the actual stack size is 16k
248     /// 3. Sets UserInteractive qos group's stack size to 16k
249     /// 4. Checks if the actual stack size is 16k
250     /// 5. Sets Default qos group's stack size to 16M
251     /// 6. Checks if the actual stack size is 16M
252     /// 7. Sets UserInteractive qos group's stack size to 16k + 1
253     /// 8. Checks if the actual stack size is 16k + 1
254     #[test]
ut_set_stack_size()255     fn ut_set_stack_size() {
256         let builder = MultiThreadBuilder::new();
257         let builder = builder.stack_size_by_qos(UserInitiated, 16 * 1000 - 1);
258         let num = builder.stack_size_by_qos.get(&UserInitiated).unwrap();
259         assert_eq!(*num, 16 * 1000);
260 
261         let builder = MultiThreadBuilder::new();
262         let builder = builder.stack_size_by_qos(UserInteractive, 16 * 1000);
263         let num = builder.stack_size_by_qos.get(&UserInteractive).unwrap();
264         assert_eq!(*num, 16 * 1000);
265 
266         let builder = MultiThreadBuilder::new();
267         let builder = builder.stack_size_by_qos(Default, 16 * 1000 * 1000);
268         let num = builder.stack_size_by_qos.get(&Default).unwrap();
269         assert_eq!(*num, 16 * 1000 * 1000);
270 
271         let builder = MultiThreadBuilder::new();
272         let builder = builder.stack_size_by_qos(UserInteractive, 16 * 1000 + 1);
273         let num = builder.stack_size_by_qos.get(&UserInteractive).unwrap();
274         assert_eq!(*num, 16 * 1000 + 1);
275     }
276 }
277