1 /*
2 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 use asset_common::{CallingInfo, Counter, Group, OwnerType, TaskManager, GROUP_SEPARATOR};
17 use asset_db_operator::{
18 database::{get_path, Database},
19 database_file_upgrade::construct_splited_db_name,
20 types::{column, DbMap, QueryOptions},
21 };
22 use asset_file_operator::de_operator::create_user_de_dir;
23 use asset_log::{loge, logi};
24 use asset_sdk::{
25 log_throw_error,
26 plugin_interface::{ExtDbMap, IAssetPlugin, IAssetPluginCtx, RETURN_LIMIT, RETURN_OFFSET},
27 AssetError, ErrCode, Extension, Result, SyncStatus, Value,
28 };
29 use asset_utils::time;
30 use ylong_runtime::task::JoinHandle;
31 use std::{
32 cell::RefCell,
33 sync::{Arc, Mutex},
34 };
35
36 /// The asset_ext plugin.
37 #[derive(Default)]
38 pub struct AssetPlugin {
39 lib: RefCell<Option<libloading::Library>>,
40 }
41
42 static ASSET_PLUGIN_LOCK: Mutex<()> = Mutex::new(());
43
44 unsafe impl Sync for AssetPlugin {}
45
46 impl AssetPlugin {
new() -> Self47 fn new() -> Self {
48 Self { lib: RefCell::new(None) }
49 }
50
51 /// Get the instance of AssetPlugin.
get_instance() -> Arc<AssetPlugin>52 pub fn get_instance() -> Arc<AssetPlugin> {
53 static mut INSTANCE: Option<Arc<AssetPlugin>> = None;
54 let _guard = ASSET_PLUGIN_LOCK.lock().unwrap();
55 unsafe { INSTANCE.get_or_insert_with(|| Arc::new(AssetPlugin::new())).clone() }
56 }
57
58 /// Load the plugin.
load_plugin(&self) -> Result<Box<dyn IAssetPlugin>>59 pub fn load_plugin(&self) -> Result<Box<dyn IAssetPlugin>> {
60 unsafe {
61 let _guard = ASSET_PLUGIN_LOCK.lock().unwrap();
62 if self.lib.borrow().is_none() {
63 logi!("start to load asset_ext plugin.");
64 match libloading::Library::new("libasset_ext_ffi.z.so") {
65 Ok(lib) => *self.lib.borrow_mut() = Some(lib),
66 Err(err) => {
67 loge!("dlopen libasset_ext_ffi.z.so failed, err: {}", err);
68 return log_throw_error!(ErrCode::InvalidArgument, "dlopen failed {}", err);
69 },
70 };
71 }
72
73 let Some(ref lib) = *self.lib.borrow() else {
74 return log_throw_error!(ErrCode::InvalidArgument, "unexpect error");
75 };
76
77 let func = match lib
78 .get::<libloading::Symbol<unsafe extern "C" fn() -> *mut dyn IAssetPlugin>>(b"_create_plugin")
79 {
80 Ok(func) => func,
81 Err(err) => {
82 loge!("dlsym _create_plugin failed, err: {}", err);
83 return log_throw_error!(ErrCode::InvalidArgument, "dlsym failed {}", err);
84 },
85 };
86
87 let plugin_ptr = func();
88 if plugin_ptr.is_null() {
89 loge!("_create_plugin return null.");
90 return log_throw_error!(ErrCode::InvalidArgument, "_create_plugin return null.");
91 }
92
93 Ok(Box::from_raw(plugin_ptr))
94 }
95 }
96
97 /// Unload plugin.
unload_plugin(&self)98 pub fn unload_plugin(&self) {
99 let _guard = ASSET_PLUGIN_LOCK.lock().unwrap();
100 if self.lib.borrow().is_some() {
101 *self.lib.borrow_mut() = None;
102 }
103 }
104 }
105
106 /// The asset_ext plugin context.
107 #[repr(C)]
108 pub struct AssetContext {
109 /// The asset database's user id.
110 pub user_id: i32,
111 }
112
113
convert_db_map(attributes: &ExtDbMap) -> Result<DbMap>114 fn convert_db_map(attributes: &ExtDbMap) -> Result<DbMap> {
115 let owner_info = attributes.get_bytes_attr(&column::OWNER)?;
116 let owner_type = attributes.get_enum_attr::<OwnerType>(&column::OWNER_TYPE)?;
117 let mut db_map = DbMap::new();
118 db_map.insert_attr(column::OWNER, owner_info.clone());
119 db_map.insert_attr(column::OWNER_TYPE, owner_type);
120 Ok(db_map)
121 }
122
get_db_name(user_id: i32, attributes: &ExtDbMap, is_ce: bool) -> std::result::Result<String, AssetError>123 fn get_db_name(user_id: i32, attributes: &ExtDbMap, is_ce: bool) -> std::result::Result<String, AssetError> {
124 let owner_info = attributes.get_bytes_attr(&column::OWNER)?;
125 let owner_type = attributes.get_enum_attr::<OwnerType>(&column::OWNER_TYPE)?;
126 let calling_info = match attributes.get(&column::GROUP_ID) {
127 Some(Value::Bytes(group)) => {
128 let mut parts = group.split(|&byte| byte == GROUP_SEPARATOR as u8);
129 let developer_id: Vec<u8> = parts.next().unwrap().to_vec();
130 let group_id: Vec<u8> = parts.next().unwrap().to_vec();
131 CallingInfo::new(user_id, owner_type, owner_info.to_vec(), Some(Group { developer_id, group_id }))
132 },
133 _ => CallingInfo::new(user_id, owner_type, owner_info.to_vec(), None),
134 };
135 construct_splited_db_name(&calling_info, is_ce)
136 }
137
get_query_options(attrs: &ExtDbMap) -> QueryOptions138 fn get_query_options(attrs: &ExtDbMap) -> QueryOptions {
139 QueryOptions {
140 offset: match attrs.get(RETURN_OFFSET) {
141 Some(Value::Number(offset)) => Some(*offset),
142 _ => None,
143 },
144 limit: match attrs.get(RETURN_LIMIT) {
145 Some(Value::Number(limit)) => Some(*limit),
146 _ => None,
147 },
148 order_by: None,
149 order: None,
150 amend: None,
151 }
152 }
153
154 #[allow(dead_code)]
155 impl IAssetPluginCtx for AssetContext {
156 /// Initializes the plugin before usage.
init(&mut self, user_id: i32) -> std::result::Result<(), u32>157 fn init(&mut self, user_id: i32) -> std::result::Result<(), u32> {
158 create_user_de_dir(user_id).map_err(|e| e.code as u32)?;
159 self.user_id = user_id;
160 Ok(())
161 }
162
163 /// Create adapt cloud table for certain asset db.
create_adapt_cloud_table_for_specific_db( &self, db_info: &ExtDbMap, is_ce: bool, ) -> std::result::Result<(), u32>164 fn create_adapt_cloud_table_for_specific_db(
165 &self,
166 db_info: &ExtDbMap,
167 is_ce: bool,
168 ) -> std::result::Result<(), u32> {
169 let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
170 let mut db = Database::build_with_file_name(self.user_id, &db_name, is_ce).map_err(|e| e.code as u32)?;
171 db.create_adapt_cloud_table().map_err(|e| e.code as u32)
172 }
173
174 /// Adds an asset to de db.
add(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32>175 fn add(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32> {
176 let db_name = get_db_name(self.user_id, attributes, false).map_err(|e| e.code as u32)?;
177 let mut db = Database::build_with_file_name(self.user_id, &db_name, false).map_err(|e| e.code as u32)?;
178 db.insert_datas(attributes).map_err(|e| e.code as u32)
179 }
180
181 /// Adds an asset to ce db.
ce_add(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32>182 fn ce_add(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32> {
183 let db_name = get_db_name(self.user_id, attributes, true).map_err(|e| e.code as u32)?;
184 let mut db = Database::build_with_file_name(self.user_id, &db_name, true).map_err(|e| e.code as u32)?;
185 db.insert_datas(attributes).map_err(|e| e.code as u32)
186 }
187
188 /// Adds an asset to db in asset and adapt table.
add_cloud_adapt_data( &self, attributes: &ExtDbMap, adapt_attributes: &ExtDbMap, is_ce: bool, need_lock: bool, ) -> std::result::Result<i32, u32>189 fn add_cloud_adapt_data(
190 &self, attributes: &ExtDbMap, adapt_attributes: &ExtDbMap, is_ce: bool, need_lock: bool,
191 ) -> std::result::Result<i32, u32> {
192 let db_name = get_db_name(self.user_id, attributes, is_ce).map_err(|e| e.code as u32)?;
193 if need_lock {
194 let mut db = Database::build_with_file_name(self.user_id, &db_name, is_ce).map_err(|e| e.code as u32)?;
195 db.insert_cloud_adapt_data(attributes, adapt_attributes).map_err(|e| e.code as u32)
196 } else {
197 let mut db = Database::build_with_file_name_without_lock(self.user_id, &db_name, is_ce).map_err(|e| e.code as u32)?;
198 db.insert_cloud_adapt_data_without_lock(attributes, adapt_attributes).map_err(|e| e.code as u32)
199 }
200 }
201
202 /// Adds an asset with replace to de db.
replace(&self, condition: &ExtDbMap, attributes: &ExtDbMap) -> std::result::Result<(), u32>203 fn replace(&self, condition: &ExtDbMap, attributes: &ExtDbMap) -> std::result::Result<(), u32> {
204 let db_name = get_db_name(self.user_id, attributes, false).map_err(|e| e.code as u32)?;
205 let mut db = Database::build_with_file_name(self.user_id, &db_name, false).map_err(|e| e.code as u32)?;
206 db.replace_datas(condition, false, attributes).map_err(|e| e.code as u32)
207 }
208
209 /// Adds an asset with replace to ce db.
ce_replace(&self, condition: &ExtDbMap, attributes: &ExtDbMap) -> std::result::Result<(), u32>210 fn ce_replace(&self, condition: &ExtDbMap, attributes: &ExtDbMap) -> std::result::Result<(), u32> {
211 let db_name = get_db_name(self.user_id, attributes, true).map_err(|e| e.code as u32)?;
212 let mut db = Database::build_with_file_name(self.user_id, &db_name, true).map_err(|e| e.code as u32)?;
213 db.replace_datas(condition, false, attributes).map_err(|e| e.code as u32)
214 }
215
216 /// Queries de db.
query(&self, attributes: &ExtDbMap) -> std::result::Result<Vec<ExtDbMap>, u32>217 fn query(&self, attributes: &ExtDbMap) -> std::result::Result<Vec<ExtDbMap>, u32> {
218 let de_dbs = asset_file_operator::de_operator::get_de_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
219 let mut query_data = vec![];
220 for db_name in de_dbs {
221 let mut db = Database::build_with_file_name(self.user_id, &db_name, false).map_err(|e| e.code as u32)?;
222 query_data.extend(db.query_datas(&vec![], attributes, None, false).map_err(|e| e.code as u32)?);
223 }
224 Ok(query_data)
225 }
226
227 /// Queries ce db.
ce_query(&self, attributes: &ExtDbMap) -> std::result::Result<Vec<ExtDbMap>, u32>228 fn ce_query(&self, attributes: &ExtDbMap) -> std::result::Result<Vec<ExtDbMap>, u32> {
229 let ce_dbs = asset_file_operator::ce_operator::get_ce_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
230 let mut query_data = vec![];
231 for db_name in ce_dbs {
232 let mut db = Database::build_with_file_name(self.user_id, &db_name, true).map_err(|e| e.code as u32)?;
233 query_data.extend(db.query_datas(&vec![], attributes, None, false).map_err(|e| e.code as u32)?);
234 }
235 Ok(query_data)
236 }
237
query_target_data( &self, db_name: &str, columns: &[&'static str], sql_where: &str, limit: u32, offset: u32, is_ce: bool, ) -> std::result::Result<Vec<ExtDbMap>, u32>238 fn query_target_data(
239 &self,
240 db_name: &str,
241 columns: &[&'static str],
242 sql_where: &str,
243 limit: u32,
244 offset: u32,
245 is_ce: bool,
246 ) -> std::result::Result<Vec<ExtDbMap>, u32> {
247 let mut db = Database::build_with_file_name(self.user_id, db_name, is_ce).map_err(|e| e.code as u32)?;
248 let condition = ExtDbMap::new();
249 let query_options = QueryOptions {
250 offset: Some(offset),
251 limit: Some(limit),
252 order: None,
253 order_by: None,
254 amend: Some(sql_where.to_string()),
255 };
256 let query_data =
257 db.query_datas(&columns.to_vec(), &condition, Some(&query_options), false).map_err(|e| e.code as u32)?;
258 Ok(query_data)
259 }
260
261 /// Query db with attributes to a certain db. Normal, Group, CE.
query_certain_db( &self, db_info: &ExtDbMap, attributes: &ExtDbMap, query_options: &ExtDbMap, is_ce: bool, is_filter_sync: bool, ) -> std::result::Result<Vec<ExtDbMap>, u32>262 fn query_certain_db(
263 &self,
264 db_info: &ExtDbMap,
265 attributes: &ExtDbMap,
266 query_options: &ExtDbMap,
267 is_ce: bool,
268 is_filter_sync: bool,
269 ) -> std::result::Result<Vec<ExtDbMap>, u32> {
270 let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
271 let mut db = Database::build_with_file_name(self.user_id, &db_name, is_ce).map_err(|e| e.code as u32)?;
272 db.query_datas(&vec![], attributes, Some(&get_query_options(query_options)), is_filter_sync).map_err(|e| e.code as u32)
273 }
274
275 /// Query db with attributes to a certain db. Normal, Group, CE.
query_certain_db_with_connect_table( &self, db_info: &ExtDbMap, attributes: &ExtDbMap, is_ce: bool, need_lock: bool, ) -> std::result::Result<Vec<ExtDbMap>, u32>276 fn query_certain_db_with_connect_table(
277 &self,
278 db_info: &ExtDbMap,
279 attributes: &ExtDbMap,
280 is_ce: bool,
281 need_lock: bool,
282 ) -> std::result::Result<Vec<ExtDbMap>, u32> {
283 let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
284
285 if need_lock {
286 let mut db = Database::build_with_file_name(self.user_id, &db_name, is_ce).map_err(|e| e.code as u32)?;
287 db.query_datas_with_connect_table(&vec![], attributes, None, false).map_err(|e| e.code as u32)
288 } else {
289 let mut db = Database::build_with_file_name_without_lock(self.user_id, &db_name, is_ce).map_err(|e| e.code as u32)?;
290 db.query_datas_with_connect_table_without_lock(&vec![], attributes, None, false).map_err(|e| e.code as u32)
291 }
292 }
293
294 /// Removes an asset from de db.
remove(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32>295 fn remove(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32> {
296 let de_dbs = asset_file_operator::de_operator::get_de_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
297 let mut total_remove_count = 0;
298 for db_name in de_dbs {
299 let mut db = Database::build_with_file_name(self.user_id, &db_name, false).map_err(|e| e.code as u32)?;
300 total_remove_count += db.delete_datas(attributes, None, false).map_err(|e| e.code as u32)?;
301 }
302 Ok(total_remove_count)
303 }
304
305 /// Removes an asset from ce db.
ce_remove(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32>306 fn ce_remove(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32> {
307 let ce_dbs = asset_file_operator::ce_operator::get_ce_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
308 let mut total_remove_count = 0;
309 for db_name in ce_dbs {
310 let mut db = Database::build_with_file_name(self.user_id, &db_name, true).map_err(|e| e.code as u32)?;
311 total_remove_count += db.delete_datas(attributes, None, false).map_err(|e| e.code as u32)?;
312 }
313 Ok(total_remove_count)
314 }
315
316 /// Removes assets with aliases.
batch_remove(&self, attributes: &ExtDbMap, aliases: &[Vec<u8>], require_attr_encrypted: bool) -> Result<()>317 fn batch_remove(&self, attributes: &ExtDbMap, aliases: &[Vec<u8>], require_attr_encrypted: bool) -> Result<()> {
318 let db_name = get_db_name(self.user_id, attributes, require_attr_encrypted)?;
319 let mut db = Database::build_with_file_name(self.user_id, &db_name, require_attr_encrypted)?;
320 let condition = convert_db_map(attributes)?;
321 let mut update_datas = DbMap::new();
322 let time = time::system_time_in_millis()?;
323 update_datas.insert(column::UPDATE_TIME, Value::Bytes(time));
324 update_datas.insert(column::SYNC_STATUS, Value::Number(SyncStatus::SyncDel as u32));
325 let total_removed_count = db.delete_batch_datas(&condition, &update_datas, aliases)?;
326 logi!("total removed count = {}", total_removed_count);
327 Ok(())
328 }
329
330 /// Removes an asset from a certain db. Normal, Group, CE.
remove_certain_db( &self, db_info: &ExtDbMap, attributes: &ExtDbMap, is_ce: bool, ) -> std::result::Result<i32, u32>331 fn remove_certain_db(
332 &self,
333 db_info: &ExtDbMap,
334 attributes: &ExtDbMap,
335 is_ce: bool,
336 ) -> std::result::Result<i32, u32> {
337 let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
338 let mut db = Database::build_with_file_name(self.user_id, &db_name, is_ce).map_err(|e| e.code as u32)?;
339 db.delete_datas(attributes, None, false).map_err(|e| e.code as u32)
340 }
341
342 /// Removes assets from de db with sepcific condition.
remove_with_specific_cond( &self, specific_cond: &str, condition_value: &[Value], ) -> std::result::Result<i32, u32>343 fn remove_with_specific_cond(
344 &self,
345 specific_cond: &str,
346 condition_value: &[Value],
347 ) -> std::result::Result<i32, u32> {
348 let de_dbs = asset_file_operator::de_operator::get_de_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
349 let mut total_remove_count = 0;
350 for db_name in de_dbs {
351 let mut db = Database::build_with_file_name(self.user_id, &db_name, false).map_err(|e| e.code as u32)?;
352 total_remove_count +=
353 db.delete_specific_condition_datas(specific_cond, condition_value).map_err(|e| e.code as u32)?;
354 }
355 Ok(total_remove_count)
356 }
357
358 /// Removes assets from ce db with sepcific condition.
ce_remove_with_specific_cond( &self, specific_cond: &str, condition_value: &[Value], ) -> std::result::Result<i32, u32>359 fn ce_remove_with_specific_cond(
360 &self,
361 specific_cond: &str,
362 condition_value: &[Value],
363 ) -> std::result::Result<i32, u32> {
364 let ce_dbs = asset_file_operator::ce_operator::get_ce_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
365 let mut total_remove_count = 0;
366 for db_name in ce_dbs {
367 let mut db = Database::build_with_file_name(self.user_id, &db_name, true).map_err(|e| e.code as u32)?;
368 total_remove_count +=
369 db.delete_specific_condition_datas(specific_cond, condition_value).map_err(|e| e.code as u32)?;
370 }
371 Ok(total_remove_count)
372 }
373
374 /// Remove an asset to db in asset and adapt table.
remove_cloud_adapt_data( &self, db_info: &ExtDbMap, attributes: Option<&ExtDbMap>, adapt_attributes: Option<&ExtDbMap>, is_ce: bool, need_lock: bool, ) -> std::result::Result<i32, u32>375 fn remove_cloud_adapt_data(
376 &self,
377 db_info: &ExtDbMap,
378 attributes: Option<&ExtDbMap>,
379 adapt_attributes: Option<&ExtDbMap>,
380 is_ce: bool,
381 need_lock: bool,
382 ) -> std::result::Result<i32, u32> {
383 let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
384 if need_lock {
385 let mut db = Database::build_with_file_name(self.user_id, &db_name, is_ce).map_err(|e| e.code as u32)?;
386 db.delete_adapt_data(attributes, adapt_attributes).map_err(|e| e.code as u32)
387 } else {
388 let mut db = Database::build_with_file_name_without_lock(self.user_id, &db_name, is_ce).map_err(|e| e.code as u32)?;
389 db.delete_adapt_data_without_lock(attributes, adapt_attributes).map_err(|e| e.code as u32)
390 }
391 }
392
393 /// Updates the attributes of an asset in de db.
update(&self, attributes: &ExtDbMap, attrs_to_update: &ExtDbMap) -> std::result::Result<i32, u32>394 fn update(&self, attributes: &ExtDbMap, attrs_to_update: &ExtDbMap) -> std::result::Result<i32, u32> {
395 let de_dbs = asset_file_operator::de_operator::get_de_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
396 let mut total_update_count = 0;
397 for db_name in de_dbs {
398 let mut db = Database::build_with_file_name(self.user_id, &db_name, false).map_err(|e| e.code as u32)?;
399 total_update_count += db.update_datas(attributes, false, attrs_to_update).map_err(|e| e.code as u32)?;
400 }
401 Ok(total_update_count)
402 }
403
404 /// Updates the attributes of an asset in ce db.
ce_update(&self, attributes: &ExtDbMap, attrs_to_update: &ExtDbMap) -> std::result::Result<i32, u32>405 fn ce_update(&self, attributes: &ExtDbMap, attrs_to_update: &ExtDbMap) -> std::result::Result<i32, u32> {
406 let ce_dbs = asset_file_operator::ce_operator::get_ce_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
407 let mut total_update_count = 0;
408 for db_name in ce_dbs {
409 let mut db = Database::build_with_file_name(self.user_id, &db_name, true).map_err(|e| e.code as u32)?;
410 total_update_count += db.update_datas(attributes, false, attrs_to_update).map_err(|e| e.code as u32)?;
411 }
412 Ok(total_update_count)
413 }
414
get_certain_db_lock( &self, db_info: &ExtDbMap, is_ce: bool, ) -> std::result::Result<Arc<Mutex<i32>>, u32>415 fn get_certain_db_lock(
416 &self,
417 db_info: &ExtDbMap,
418 is_ce: bool,
419 ) -> std::result::Result<Arc<Mutex<i32>>, u32> {
420 let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
421 let db = Database::build_with_file_name(self.user_id, &db_name, is_ce).map_err(|e| e.code as u32)?;
422 let lock = db.get_db_lock().map_err(|e| e.code as u32)?;
423 Ok(lock)
424 }
425
426 /// Returns the storage path for de db.
get_storage_path(&self) -> String427 fn get_storage_path(&self) -> String {
428 get_path()
429 }
430
431 /// Increase count
increase_count(&self)432 fn increase_count(&self) {
433 let counter = Counter::get_instance();
434 counter.lock().unwrap().increase_count();
435 }
436
437 /// Decrease count
decrease_count(&self)438 fn decrease_count(&self) {
439 let counter = Counter::get_instance();
440 counter.lock().unwrap().decrease_count();
441 }
442
443 /// Add task
add_task(&self, handle: JoinHandle<()>)444 fn add_task(&self, handle: JoinHandle<()>) {
445 let task_manager = TaskManager::get_instance();
446 task_manager.lock().unwrap().push_task(handle);
447 }
448 }
449