Fix the OSDK CI failure

This commit is contained in:
Jianfeng Jiang
2024-06-21 05:51:13 +00:00
committed by Tate, Hongliang Tian
parent fe7251c413
commit 3de8a9330a
40 changed files with 260 additions and 189 deletions

15
Cargo.lock generated
View File

@ -139,7 +139,6 @@ dependencies = [
"bitvec",
"component",
"int-to-c-enum",
"ktest",
"log",
"ostd",
"pod",
@ -180,7 +179,6 @@ dependencies = [
"int-to-c-enum",
"intrusive-collections",
"keyable-arc",
"ktest",
"lazy_static",
"lending-iterator",
"libflate",
@ -240,7 +238,6 @@ dependencies = [
"aster-rights",
"aster-rights-proc",
"inherit-methods-macro",
"ktest",
"ostd",
"pod",
"typeflags-util",
@ -797,20 +794,9 @@ version = "0.1.0"
name = "ktest"
version = "0.1.0"
dependencies = [
"ktest-proc-macro",
"owo-colors",
]
[[package]]
name = "ktest-proc-macro"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"rand",
"syn 2.0.49",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -1131,6 +1117,7 @@ version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"rand",
"syn 2.0.49",
]

View File

@ -9,7 +9,6 @@ members = [
"ostd/libs/linux-bzimage/boot-params",
"ostd/libs/linux-bzimage/setup",
"ostd/libs/ktest",
"ostd/libs/ktest-proc-macro",
"kernel",
"kernel/aster-nix",
"kernel/comps/block",

View File

@ -86,12 +86,11 @@ export
# or tested without OSDK.
NON_OSDK_CRATES := \
ostd/libs/align_ext \
ostd/libs/ostd-macros \
ostd/libs/id-alloc \
ostd/libs/linux-bzimage/builder \
ostd/libs/linux-bzimage/boot-params \
ostd/libs/ktest \
ostd/libs/ktest-proc-macro \
ostd/libs/ostd-macros \
kernel/libs/cpio-decoder \
kernel/libs/int-to-c-enum \
kernel/libs/int-to-c-enum/derive \

View File

@ -84,10 +84,6 @@ The dependency version may change over time.
[dependencies.ostd]
git = "https://github.com/asterinas/asterinas"
branch = "main"
[dependencies.ktest]
git = "https://github.com/asterinas/asterinas"
branch = "main"
```
OSDK will also exclude the directory

View File

@ -42,7 +42,6 @@ smoltcp = { version = "0.9.1", default-features = false, features = [
"socket-raw",
"socket-dhcpv4",
] }
ktest = { path = "../../ostd/libs/ktest" }
tdx-guest = { version = "0.1.0", optional = true }
# parse elf file

View File

@ -21,7 +21,10 @@ mod test {
bio::{BioEnqueueError, BioStatus, BioType, SubmittedBio},
BlockDevice,
};
use ostd::mm::{FrameAllocOptions, Segment, VmIo};
use ostd::{
mm::{FrameAllocOptions, Segment, VmIo},
prelude::*,
};
use rand::{rngs::SmallRng, RngCore, SeedableRng};
use crate::{

View File

@ -541,6 +541,8 @@ impl<T> AsRef<Error> for (Error, T) {
mod test {
use alloc::sync::Arc;
use ostd::prelude::*;
use crate::fs::utils::Channel;
#[ktest]

View File

@ -41,9 +41,6 @@ extern crate alloc;
extern crate lru;
#[macro_use]
extern crate controlled;
#[cfg(ktest)]
#[macro_use]
extern crate ktest;
#[macro_use]
extern crate getset;

View File

@ -652,6 +652,8 @@ pub fn current() -> Arc<Process> {
#[cfg(ktest)]
mod test {
use ostd::prelude::*;
use super::*;
fn new_process(parent: Option<Arc<Process>>) -> Arc<Process> {

View File

@ -264,7 +264,7 @@ impl Condvar {
#[cfg(ktest)]
mod test {
use ostd::sync::Mutex;
use ostd::{prelude::*, sync::Mutex};
use super::*;
use crate::thread::{

View File

@ -189,7 +189,7 @@ fn taskless_softirq_handler(
mod test {
use core::sync::atomic::AtomicUsize;
use ostd::trap::enable_local;
use ostd::{prelude::*, trap::enable_local};
use super::*;

View File

@ -136,7 +136,7 @@ impl<R> VmarChildOptions<R> {
#[cfg(ktest)]
mod test {
use aster_rights::Full;
use ostd::mm::VmIo;
use ostd::{mm::VmIo, prelude::*};
use super::*;
use crate::vm::{

View File

@ -477,7 +477,7 @@ impl VmoChildType for VmoCowChild {}
#[cfg(ktest)]
mod test {
use aster_rights::Full;
use ostd::mm::VmIo;
use ostd::{mm::VmIo, prelude::*};
use super::*;

View File

@ -13,7 +13,6 @@ bitflags = "1.3"
bitvec = { version = "1.0.1", default-features = false, features = ["alloc"]}
component = { path = "../../libs/comp-sys/component" }
int-to-c-enum = { path = "../../libs/int-to-c-enum" }
ktest = { path = "../../../ostd/libs/ktest" }
log = "0.4"
ostd = { path = "../../../ostd" }
pod = { git = "https://github.com/asterinas/pod", rev = "d7dba56" }

View File

@ -9,7 +9,6 @@ use alloc::{
use core::ops::Range;
use bitvec::{array::BitArray, prelude::Lsb0};
use ktest::ktest;
use ostd::{
mm::{
Daddr, DmaDirection, DmaStream, FrameAllocOptions, HasDaddr, VmReader, VmWriter, PAGE_SIZE,
@ -288,6 +287,8 @@ impl Drop for DmaSegment {
mod test {
use alloc::vec::Vec;
use ostd::prelude::*;
use super::*;
#[ktest]

View File

@ -12,6 +12,5 @@ typeflags-util = { path = "../typeflags-util" }
aster-rights-proc = { path = "../aster-rights-proc" }
aster-rights = { path = "../aster-rights" }
inherit-methods-macro = { git = "https://github.com/asterinas/inherit-methods-macro", rev = "98f7e3e" }
ktest = { path = "../../../ostd/libs/ktest" }
[features]

View File

@ -127,9 +127,10 @@ impl Mul<u32> for Coeff {
#[cfg(ktest)]
mod test {
use ktest::ktest;
use ostd::prelude::*;
use super::*;
#[ktest]
fn calculation() {
let coeff = Coeff::new(23456, 56789, 1_000_000_000);

View File

@ -1,12 +1,10 @@
#![no_std]
#![deny(unsafe_code)]
#[cfg_attr(ktest, macro_use)]
extern crate ktest;
extern crate ostd;
#[cfg(ktest)]
mod tests {
use ostd::prelude::*;
#[ktest]
fn it_works() {
let memory_regions = ostd::boot::memory_regions();

View File

@ -42,8 +42,6 @@ fn add_manifest_dependencies(cargo_metadata: &serde_json::Value, crate_name: &st
let ostd_dep = toml::Table::from_str(&aster_crate_dep("ostd")).unwrap();
dependencies.as_table_mut().unwrap().extend(ostd_dep);
let ktest_dep = toml::Table::from_str(&aster_crate_dep("ktest")).unwrap();
dependencies.as_table_mut().unwrap().extend(ktest_dep);
let content = toml::to_string(&manifest).unwrap();
fs::write(mainfest_path, content).unwrap();

View File

@ -18,16 +18,19 @@ fn create_kernel_in_workspace() {
}
create_workspace(WORKSPACE_NAME, &[KERNEL_NAME]);
let kernel_path = PathBuf::from(WORKSPACE_NAME).join(KERNEL_NAME);
let manifest_path = kernel_path.join("Cargo.toml");
let mut cmd = cargo_osdk(["new", "--kernel", KERNEL_NAME]);
cmd.current_dir(WORKSPACE_NAME);
let output = cmd.output().unwrap();
depends_on_local_ostd(&manifest_path);
assert_success(&output);
remove_dir_all(&kernel_path).unwrap();
let mut cmd = cargo_osdk(["new", "-t", "kernel", KERNEL_NAME]);
cmd.current_dir(WORKSPACE_NAME);
let output = cmd.output().unwrap();
depends_on_local_ostd(&manifest_path);
assert_success(&output);
remove_dir_all(&kernel_path).unwrap();

View File

@ -21,6 +21,8 @@ mod workspace {
cargo_osdk_new
.ok()
.expect("Failed to create kernel project");
let manifest_path = os_dir.join("Cargo.toml");
depends_on_local_ostd(manifest_path);
}
fn prepare_workspace(workspace: &str) {

View File

@ -2,7 +2,7 @@
use std::{fs, path::PathBuf};
use crate::util::cargo_osdk;
use crate::util::{cargo_osdk, depends_on_local_ostd};
#[test]
fn create_a_kernel_project() {
@ -23,6 +23,8 @@ fn create_a_kernel_project() {
assert!(kernel_path.join("Cargo.toml").is_file());
assert!(kernel_path.join("rust-toolchain.toml").is_file());
depends_on_local_ostd(kernel_path.join("Cargo.toml"));
fs::remove_dir_all(&kernel_path).unwrap();
}

View File

@ -2,7 +2,7 @@
use std::{fs, path::PathBuf};
use crate::util::cargo_osdk;
use crate::util::{cargo_osdk, depends_on_local_ostd};
#[test]
fn create_and_run_kernel() {
@ -19,6 +19,10 @@ fn create_and_run_kernel() {
command.current_dir(work_dir);
command.ok().unwrap();
// Makes the kernel depend on local OSTD
let manifest_path = os_dir.join("Cargo.toml");
depends_on_local_ostd(&manifest_path);
let mut command = cargo_osdk(&["build"]);
command.current_dir(&os_dir);
command.ok().unwrap();
@ -48,6 +52,9 @@ fn create_and_test_library() {
command.current_dir(work_dir);
command.ok().unwrap();
let manifest_path = module_dir.join("Cargo.toml");
depends_on_local_ostd(manifest_path);
let mut command = cargo_osdk(&["test"]);
command.current_dir(&module_dir);
command.ok().unwrap();

View File

@ -7,7 +7,7 @@ use std::{
path::PathBuf,
};
use crate::util::cargo_osdk;
use crate::util::{cargo_osdk, depends_on_local_ostd};
#[test]
fn work_in_workspace() {
@ -46,9 +46,14 @@ fn work_in_workspace() {
.unwrap();
module_src_file.flush().unwrap();
// Make module depends on local ostd
let module_manifest_path = workspace_dir.join(module).join("Cargo.toml");
depends_on_local_ostd(module_manifest_path);
// Add dependency to myos/Cargo.toml
let kernel_manifest_path = workspace_dir.join(kernel).join("Cargo.toml");
assert!(kernel_manifest_path.is_file());
depends_on_local_ostd(&kernel_manifest_path);
let mut kernel_manifest_file = OpenOptions::new()
.append(true)
.open(&kernel_manifest_path)

View File

@ -10,6 +10,7 @@ use std::{
};
use assert_cmd::Command;
use toml::{Table, Value};
pub fn cargo_osdk<T: AsRef<OsStr>, I: IntoIterator<Item = T>>(args: I) -> Command {
let mut command = Command::cargo_bin("cargo-osdk").unwrap();
@ -85,3 +86,35 @@ pub fn add_member_to_workspace(workspace: impl AsRef<Path>, new_member: &str) {
let new_content = workspace_manifest.to_string();
fs::write(&path, new_content).unwrap();
}
/// Makes crates created by `cargo ostd new` depends on ostd locally,
/// instead of ostd from local branch.
///
/// Each crate created by `cargo ostd new` should add this patch.
pub fn depends_on_local_ostd(manifest_path: impl AsRef<Path>) {
let crate_dir = env!("CARGO_MANIFEST_DIR");
let ostd_dir = PathBuf::from(crate_dir)
.join("..")
.join("ostd")
.canonicalize()
.unwrap()
.to_string_lossy()
.to_string();
// FIXME: It may be more elegant to add `patch` section instead of replacing dependency.
// But adding `patch` section does not work in my local test, which is confusing.
let manifest_content = fs::read_to_string(&manifest_path).unwrap();
let mut manifest: Table = toml::from_str(&manifest_content).unwrap();
let dep = manifest
.get_mut("dependencies")
.map(Value::as_table_mut)
.flatten()
.unwrap();
let mut table = Table::new();
table.insert("path".to_string(), Value::String(ostd_dir));
dep.insert("ostd".to_string(), Value::Table(table));
fs::write(manifest_path, manifest.to_string().as_bytes()).unwrap();
}

View File

@ -1,15 +0,0 @@
[package]
name = "ktest-proc-macro"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.69"
quote = "1.0.33"
rand = "0.8.5"
syn = { version = "2.0.29", features = ["full"] }

View File

@ -1,113 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
#![feature(proc_macro_span)]
extern crate proc_macro2;
use proc_macro::TokenStream;
use quote::quote;
use rand::{distributions::Alphanumeric, Rng};
use syn::{parse_macro_input, Expr, Ident, ItemFn};
/// The test attribute macro to mark a test function.
#[proc_macro_attribute]
pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Assuming that the item has type `fn() -> ()`, otherwise panics.
let input = parse_macro_input!(item as ItemFn);
assert!(
input.sig.inputs.is_empty(),
"ktest function should have no arguments"
);
assert!(
matches!(input.sig.output, syn::ReturnType::Default),
"ktest function should return `()`"
);
// Generate a random identifier to avoid name conflicts.
let fn_id: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(8)
.map(char::from)
.collect();
let fn_name = &input.sig.ident;
let fn_ktest_item_name = Ident::new(
&format!("{}_ktest_item_{}", &input.sig.ident, &fn_id),
proc_macro2::Span::call_site(),
);
let is_should_panic_attr = |attr: &&syn::Attribute| {
attr.path()
.segments
.iter()
.any(|segment| segment.ident == "should_panic")
};
let mut attr_iter = input.attrs.iter();
let should_panic = attr_iter.find(is_should_panic_attr);
let (should_panic, expectation) = match should_panic {
Some(attr) => {
assert!(
!attr_iter.any(|attr: &syn::Attribute| is_should_panic_attr(&attr)),
"multiple `should_panic` attributes"
);
match &attr.meta {
syn::Meta::List(l) => {
let arg_err_message = "`should_panic` attribute should only have zero or one `expected` argument, with the format of `expected = \"<panic message>\"`";
let expected_assign =
syn::parse2::<syn::ExprAssign>(l.tokens.clone()).expect(arg_err_message);
let Expr::Lit(s) = *expected_assign.right else {
panic!("{}", arg_err_message);
};
let syn::Lit::Str(expectation) = s.lit else {
panic!("{}", arg_err_message);
};
(true, Some(expectation))
}
_ => (true, None),
}
}
None => (false, None),
};
let expectation_tokens = if let Some(s) = expectation {
quote! {
Some(#s)
}
} else {
quote! {
None
}
};
let package_name = std::env::var("CARGO_PKG_NAME").unwrap();
let span = proc_macro::Span::call_site();
let source = span.source_file().path();
let source = source.to_str().unwrap();
let line = span.line();
let col = span.column();
let register_ktest_item = quote! {
#[cfg(ktest)]
#[used]
#[link_section = ".ktest_array"]
static #fn_ktest_item_name: ktest::KtestItem = ktest::KtestItem::new(
#fn_name,
(#should_panic, #expectation_tokens),
ktest::KtestItemInfo {
module_path: module_path!(),
fn_name: stringify!(#fn_name),
package: #package_name,
source: #source,
line: #line,
col: #col,
},
);
};
let output = quote! {
#input
#register_ktest_item
};
TokenStream::from(output)
}

View File

@ -7,4 +7,3 @@ edition = "2021"
[dependencies]
owo-colors = "3.5.0"
ktest-proc-macro = { path = "../ktest-proc-macro" }

View File

@ -22,9 +22,10 @@
//! module, e.g.:
//!
//! ```rust
//! use ktest::ktest;
//! #[cfg(ktest)]
//! mod test {
//! use ostd::prelude::*;
//!
//! #[ktest]
//! fn trivial_assertion() {
//! assert_eq!(0, 0);
@ -42,13 +43,12 @@
//! }
//! ```
//!
//! And also, any crates using the ktest framework should be linked with ostd
//! and import the `ktest` crate:
//! Any crates using the ktest framework should be linked with ostd.
//!
//! ```toml
//! # Cargo.toml
//! [dependencies]
//! ktest = { path = "relative/path/to/ktest" }
//! ostd = { path = "relative/path/to/ostd" }
//! ```
//!
//! By the way, `#[ktest]` attribute along also works, but it hinders test control
@ -97,8 +97,6 @@ pub mod tree;
extern crate alloc;
use alloc::{boxed::Box, string::String};
pub use ktest_proc_macro::ktest;
#[derive(Clone, Debug)]
pub struct PanicInfo {
pub message: String,

View File

@ -11,4 +11,5 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0.78"
quote = "1.0.35"
rand = "0.8.5"
syn = { version = "2.0.48", features = ["full"] }

View File

@ -1,8 +1,11 @@
// SPDX-License-Identifier: MPL-2.0
#![feature(proc_macro_span)]
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn};
use rand::{distributions::Alphanumeric, Rng};
use syn::{parse_macro_input, Expr, Ident, ItemFn};
/// This macro is used to mark the kernel entry point.
///
@ -33,3 +36,152 @@ pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
)
.into()
}
/// The test attribute macro to mark a test function.
///
/// # Example
///
/// For crates other than ostd,
/// this macro can be used in the following form.
///
/// ```norun
/// use ostd::prelude::*;
///
/// #[ktest]
/// fn test_fn() {
/// assert_eq!(1 + 1, 2);
/// }
/// ```
///
/// For ostd crate itself,
/// this macro can be used in the form
///
/// ```norun
/// use crate::prelude::*;
///
/// #[ktest]
/// fn test_fn() {
/// assert_eq!(1 + 1, 2);
/// }
/// ```
#[proc_macro_attribute]
pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Assuming that the item has type `fn() -> ()`, otherwise panics.
let input = parse_macro_input!(item as ItemFn);
assert!(
input.sig.inputs.is_empty(),
"ostd::test function should have no arguments"
);
assert!(
matches!(input.sig.output, syn::ReturnType::Default),
"ostd::test function should return `()`"
);
// Generate a random identifier to avoid name conflicts.
let fn_id: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(8)
.map(char::from)
.collect();
let fn_name = &input.sig.ident;
let fn_ktest_item_name = Ident::new(
&format!("{}_ktest_item_{}", &input.sig.ident, &fn_id),
proc_macro2::Span::call_site(),
);
let is_should_panic_attr = |attr: &&syn::Attribute| {
attr.path()
.segments
.iter()
.any(|segment| segment.ident == "should_panic")
};
let mut attr_iter = input.attrs.iter();
let should_panic = attr_iter.find(is_should_panic_attr);
let (should_panic, expectation) = match should_panic {
Some(attr) => {
assert!(
!attr_iter.any(|attr: &syn::Attribute| is_should_panic_attr(&attr)),
"multiple `should_panic` attributes"
);
match &attr.meta {
syn::Meta::List(l) => {
let arg_err_message = "`should_panic` attribute should only have zero or one `expected` argument, with the format of `expected = \"<panic message>\"`";
let expected_assign =
syn::parse2::<syn::ExprAssign>(l.tokens.clone()).expect(arg_err_message);
let Expr::Lit(s) = *expected_assign.right else {
panic!("{}", arg_err_message);
};
let syn::Lit::Str(expectation) = s.lit else {
panic!("{}", arg_err_message);
};
(true, Some(expectation))
}
_ => (true, None),
}
}
None => (false, None),
};
let expectation_tokens = if let Some(s) = expectation {
quote! {
Some(#s)
}
} else {
quote! {
None
}
};
let package_name = std::env::var("CARGO_PKG_NAME").unwrap();
let span = proc_macro::Span::call_site();
let source = span.source_file().path();
let source = source.to_str().unwrap();
let line = span.line();
let col = span.column();
let register_ktest_item = if package_name.as_str() == "ostd" {
quote! {
#[cfg(ktest)]
#[used]
#[link_section = ".ktest_array"]
static #fn_ktest_item_name: ktest::KtestItem = ktest::KtestItem::new(
#fn_name,
(#should_panic, #expectation_tokens),
ktest::KtestItemInfo {
module_path: module_path!(),
fn_name: stringify!(#fn_name),
package: #package_name,
source: #source,
line: #line,
col: #col,
},
);
}
} else {
quote! {
#[cfg(ktest)]
#[used]
#[link_section = ".ktest_array"]
static #fn_ktest_item_name: ostd::ktest::KtestItem = ostd::ktest::KtestItem::new(
#fn_name,
(#should_panic, #expectation_tokens),
ostd::ktest::KtestItemInfo {
module_path: module_path!(),
fn_name: stringify!(#fn_name),
package: #package_name,
source: #source,
line: #line,
col: #col,
},
);
}
};
let output = quote! {
#input
#register_ktest_item
};
TokenStream::from(output)
}

View File

@ -22,9 +22,6 @@
#![warn(missing_docs)]
extern crate alloc;
#[cfg(ktest)]
#[macro_use]
extern crate ktest;
extern crate static_assertions;
pub mod arch;
@ -109,6 +106,8 @@ fn invoke_ffi_init_funcs() {
/// Simple unit tests for the ktest framework.
#[cfg(ktest)]
mod test {
use crate::prelude::*;
#[ktest]
fn trivial_assertion() {
assert_eq!(0, 0);
@ -126,3 +125,9 @@ mod test {
panic!("expected panic message");
}
}
/// The module re-exports everything from the ktest crate
#[cfg(ktest)]
pub mod ktest {
pub use ktest::*;
}

View File

@ -190,7 +190,7 @@ mod test {
use alloc::vec;
use super::*;
use crate::mm::FrameAllocOptions;
use crate::{mm::FrameAllocOptions, prelude::*};
#[ktest]
fn map_with_coherent_device() {

View File

@ -310,7 +310,7 @@ mod test {
use alloc::vec;
use super::*;
use crate::mm::FrameAllocOptions;
use crate::{mm::FrameAllocOptions, prelude::*};
#[ktest]
fn streaming_map() {

View File

@ -161,6 +161,9 @@ impl<E: PageTableEntryTrait, C: PagingConstsTrait> Drop for BootPageTable<E, C>
}
}
#[cfg(ktest)]
use crate::prelude::*;
#[cfg(ktest)]
#[ktest]
fn test_boot_pt_map_protect() {

View File

@ -3,10 +3,13 @@
use core::mem::ManuallyDrop;
use super::*;
use crate::mm::{
use crate::{
mm::{
kspace::LINEAR_MAPPING_BASE_VADDR,
page::{allocator, meta::FrameMeta},
page_prop::{CachePolicy, PageFlags},
},
prelude::*,
};
const PAGE_SIZE: usize = 4096;

View File

@ -10,6 +10,9 @@ pub type Result<T> = core::result::Result<T, crate::error::Error>;
pub(crate) use alloc::{boxed::Box, sync::Arc, vec::Vec};
pub(crate) use core::any::Any;
#[cfg(ktest)]
pub use ostd_macros::ktest;
pub use crate::{
early_print as print, early_println as println,
mm::{Paddr, Vaddr},

View File

@ -293,6 +293,7 @@ impl fmt::Debug for AtomicBits {
#[cfg(ktest)]
mod test {
use super::*;
use crate::prelude::*;
#[ktest]
fn new() {

View File

@ -298,7 +298,7 @@ impl Waker {
#[cfg(ktest)]
mod test {
use super::*;
use crate::task::TaskOptions;
use crate::{prelude::*, task::TaskOptions};
fn queue_wake<F>(wake: F)
where

View File

@ -332,6 +332,8 @@ impl TaskOptions {
#[cfg(ktest)]
mod test {
use crate::prelude::*;
#[ktest]
fn create_task() {
let task = || {