diff --git a/.github/workflows/ci-preemptive.sh b/.github/workflows/ci-preemptive.sh index fa2fe6d5..2784991b 100644 --- a/.github/workflows/ci-preemptive.sh +++ b/.github/workflows/ci-preemptive.sh @@ -7,7 +7,7 @@ if [ "${CROSS}" = "1" ]; then export CARGO_NET_RETRY=5 export CARGO_NET_TIMEOUT=10 - cargo install cross --git https://github.com/cross-rs/cross --rev 4090beca3cfffa44371a5bba524de3a578aa46c3 + cargo install cross --git https://github.com/cross-rs/cross --rev c7dee4d008475ce1c140773cbcd6078f4b86c2aa CARGO=cross fi diff --git a/.github/workflows/ci.sh b/.github/workflows/ci.sh index f9e4563e..3ebdfb0b 100644 --- a/.github/workflows/ci.sh +++ b/.github/workflows/ci.sh @@ -7,7 +7,7 @@ if [ "${CROSS}" = "1" ]; then export CARGO_NET_RETRY=5 export CARGO_NET_TIMEOUT=10 - cargo install cross --git https://github.com/cross-rs/cross --rev 4090beca3cfffa44371a5bba524de3a578aa46c3 + cargo install cross --git https://github.com/cross-rs/cross --rev c7dee4d008475ce1c140773cbcd6078f4b86c2aa CARGO=cross fi diff --git a/Cargo.toml b/Cargo.toml index 6ee406e7..0d93b0ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ windows-sys = "0.59" anyhow = "1.0" slab = "0.4" backtrace = "0.3" -minhook = "0.6" +minhook = "0.7" psm = "0.1" once_cell = "1" diff --git a/core/docs/en/coroutine.md b/core/docs/en/coroutine.md index f8a5ab23..faabeca9 100644 --- a/core/docs/en/coroutine.md +++ b/core/docs/en/coroutine.md @@ -54,7 +54,7 @@ The above is excerpted from [corosensei](https://github.com/Amanieu/corosensei). | switch efficiency | ✅ Higher | ❌ High | | memory usage | ✅ Bytes/KB/MB | ❌ KB/MB | | scheduled by OS | ❌ | ✅ | -| stack grow | ✅ | ❌ | +| grow stack | ✅ | ❌ | ## Stackfull VS Stackless diff --git a/core/src/co_pool/mod.rs b/core/src/co_pool/mod.rs index faa75b0b..f9ba8fca 100644 --- a/core/src/co_pool/mod.rs +++ b/core/src/co_pool/mod.rs @@ -221,10 +221,7 @@ impl<'p> CoroutinePool<'p> { match self.state() { PoolState::Running => {} PoolState::Stopping | PoolState::Stopped => { - return Err(Error::new( - ErrorKind::Other, - "The coroutine pool is stopping or stopped !", - )) + return Err(Error::other("The coroutine pool is stopping or stopped !")) } } let name = name.unwrap_or(format!("{}@{}", self.name(), uuid::Uuid::new_v4())); @@ -292,12 +289,11 @@ impl<'p> CoroutinePool<'p> { let (lock, cvar) = &*arc; drop( cvar.wait_timeout_while( - lock.lock() - .map_err(|e| Error::new(ErrorKind::Other, format!("{e}")))?, + lock.lock().map_err(|e| Error::other(format!("{e}")))?, wait_time, |&mut pending| pending, ) - .map_err(|e| Error::new(ErrorKind::Other, format!("{e}")))?, + .map_err(|e| Error::other(format!("{e}")))?, ); if let Some(r) = self.try_take_task_result(key) { self.notify(key); @@ -372,8 +368,7 @@ impl<'p> CoroutinePool<'p> { "The coroutine pool:{} has reached its maximum size !", self.name() ); - return Err(Error::new( - ErrorKind::Other, + return Err(Error::other( "The coroutine pool has reached its maximum size !", )); } @@ -447,12 +442,7 @@ impl<'p> CoroutinePool<'p> { PoolState::Running | PoolState::Stopping => { drop(self.try_grow()); } - PoolState::Stopped => { - return Err(Error::new( - ErrorKind::Other, - "The coroutine pool is stopped !", - )) - } + PoolState::Stopped => return Err(Error::other("The coroutine pool is stopped !")), } Self::init_current(self); let r = self.try_timeout_schedule(timeout_time); diff --git a/core/src/co_pool/state.rs b/core/src/co_pool/state.rs index 6b227cc0..7f6c1ebd 100644 --- a/core/src/co_pool/state.rs +++ b/core/src/co_pool/state.rs @@ -1,6 +1,6 @@ use crate::co_pool::CoroutinePool; use crate::common::constants::PoolState; -use std::io::{Error, ErrorKind}; +use std::io::Error; impl CoroutinePool<'_> { /// running -> stopping @@ -37,10 +37,11 @@ impl CoroutinePool<'_> { assert_eq!(old_state, self.state.replace(new_state)); return Ok(old_state); } - Err(Error::new( - ErrorKind::Other, - format!("{} unexpected {current}->{:?}", self.name(), new_state), - )) + Err(Error::other(format!( + "{} unexpected {current}->{:?}", + self.name(), + new_state + ))) } } diff --git a/core/src/coroutine/korosensei.rs b/core/src/coroutine/korosensei.rs index b6eee7ea..ac1b5a54 100644 --- a/core/src/coroutine/korosensei.rs +++ b/core/src/coroutine/korosensei.rs @@ -11,7 +11,7 @@ use std::cell::{Cell, RefCell, UnsafeCell}; use std::collections::VecDeque; use std::ffi::c_longlong; use std::fmt::Debug; -use std::io::{Error, ErrorKind}; +use std::io::Error; cfg_if::cfg_if! { if #[cfg(unix)] { @@ -452,10 +452,10 @@ where CoroutineState::Syscall(y, syscall, state) => { Ok(CoroutineState::Syscall(y, syscall, state)) } - _ => Err(Error::new( - ErrorKind::Other, - format!("{} unexpected state {current}", self.name()), - )), + _ => Err(Error::other(format!( + "{} unexpected state {current}", + self.name() + ))), } } CoroutineResult::Return(result) => { diff --git a/core/src/coroutine/state.rs b/core/src/coroutine/state.rs index 37c64f77..4ff97827 100644 --- a/core/src/coroutine/state.rs +++ b/core/src/coroutine/state.rs @@ -4,7 +4,7 @@ use crate::coroutine::listener::Listener; use crate::coroutine::Coroutine; use crate::{error, info}; use std::fmt::Debug; -use std::io::{Error, ErrorKind}; +use std::io::Error; impl Coroutine<'_, Param, Yield, Return> where @@ -45,14 +45,11 @@ where } _ => {} } - Err(Error::new( - ErrorKind::Other, - format!( - "{} unexpected {current}->{:?}", - self.name(), - CoroutineState::::Ready - ), - )) + Err(Error::other(format!( + "{} unexpected {current}->{:?}", + self.name(), + CoroutineState::::Ready + ))) } /// ready -> running @@ -87,14 +84,11 @@ where } _ => {} } - Err(Error::new( - ErrorKind::Other, - format!( - "{} unexpected {current}->{:?}", - self.name(), - CoroutineState::::Running - ), - )) + Err(Error::other(format!( + "{} unexpected {current}->{:?}", + self.name(), + CoroutineState::::Running + ))) } /// running -> suspend @@ -109,14 +103,11 @@ where self.on_suspend(self, old_state); return Ok(()); } - Err(Error::new( - ErrorKind::Other, - format!( - "{} unexpected {current}->{:?}", - self.name(), - CoroutineState::::Suspend(val, timestamp) - ), - )) + Err(Error::other(format!( + "{} unexpected {current}->{:?}", + self.name(), + CoroutineState::::Suspend(val, timestamp) + ))) } /// running -> syscall @@ -148,14 +139,11 @@ where } _ => {} } - Err(Error::new( - ErrorKind::Other, - format!( - "{} unexpected {current}->{:?}", - self.name(), - CoroutineState::::Syscall(val, syscall, syscall_state) - ), - )) + Err(Error::other(format!( + "{} unexpected {current}->{:?}", + self.name(), + CoroutineState::::Syscall(val, syscall, syscall_state) + ))) } /// running -> complete @@ -170,14 +158,11 @@ where self.on_complete(self, old_state, val); return Ok(()); } - Err(Error::new( - ErrorKind::Other, - format!( - "{} unexpected {current}->{:?}", - self.name(), - CoroutineState::::Complete(val) - ), - )) + Err(Error::other(format!( + "{} unexpected {current}->{:?}", + self.name(), + CoroutineState::::Complete(val) + ))) } /// running -> error @@ -192,14 +177,11 @@ where self.on_error(self, old_state, msg); return Ok(()); } - Err(Error::new( - ErrorKind::Other, - format!( - "{} unexpected {current}->{:?}", - self.name(), - CoroutineState::::Error(msg) - ), - )) + Err(Error::other(format!( + "{} unexpected {current}->{:?}", + self.name(), + CoroutineState::::Error(msg) + ))) } } diff --git a/core/src/net/operator/windows/mod.rs b/core/src/net/operator/windows/mod.rs index a871d7cc..b68f3f6c 100644 --- a/core/src/net/operator/windows/mod.rs +++ b/core/src/net/operator/windows/mod.rs @@ -239,7 +239,7 @@ impl<'o> Operator<'o> { &mut sock_info_len, ) != 0 { - return Err(Error::new(ErrorKind::Other, "get socket info failed")); + return Err(Error::other("get socket info failed")); } self.add_handle(fd as HANDLE)?; let socket = WSASocketW( @@ -251,10 +251,7 @@ impl<'o> Operator<'o> { WSA_FLAG_OVERLAPPED, ); if INVALID_SOCKET == socket { - return Err(Error::new( - ErrorKind::Other, - format!("add {syscall_name} operation failed"), - )); + return Err(Error::other(format!("add {syscall_name} operation failed"))); } let size = size_of::() .saturating_add(16) @@ -370,10 +367,9 @@ impl<'o> Operator<'o> { { let errno = WSAGetLastError(); if WSA_IO_PENDING != errno { - return Err(Error::new( - ErrorKind::Other, - format!("add {syscall_name} operation failed with {errno}"), - )); + return Err(Error::other(format!( + "add {syscall_name} operation failed with {errno}" + ))); } } eprintln!("add {syscall_name} operation:{overlapped}"); @@ -464,10 +460,9 @@ impl<'o> Operator<'o> { { let errno = WSAGetLastError(); if WSA_IO_PENDING != errno { - return Err(Error::new( - ErrorKind::Other, - format!("add {syscall_name} operation failed with {errno}"), - )); + return Err(Error::other(format!( + "add {syscall_name} operation failed with {errno}" + ))); } } eprintln!("add {syscall_name} operation:{overlapped}"); diff --git a/core/src/scheduler.rs b/core/src/scheduler.rs index a5edc24c..14ac0347 100644 --- a/core/src/scheduler.rs +++ b/core/src/scheduler.rs @@ -9,7 +9,7 @@ use crate::{co, impl_current_for, impl_display_by_debug, impl_for_named}; use dashmap::DashMap; use std::collections::{BinaryHeap, HashMap, VecDeque}; use std::ffi::c_longlong; -use std::io::{Error, ErrorKind}; +use std::io::Error; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::Duration; @@ -318,8 +318,7 @@ impl<'s> Scheduler<'s> { ); } _ => { - return Err(Error::new( - ErrorKind::Other, + return Err(Error::other( "try_timeout_schedule should never execute to here", )); } diff --git a/core/tests/coroutine.rs b/core/tests/coroutine.rs index 11c3eaae..685d01a0 100644 --- a/core/tests/coroutine.rs +++ b/core/tests/coroutine.rs @@ -23,10 +23,7 @@ fn coroutine_panic() -> std::io::Result<()> { })?; match coroutine.resume()? { CoroutineState::Error(_) => Ok(()), - _ => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "The coroutine should panic", - )), + _ => Err(std::io::Error::other("The coroutine should panic")), } } @@ -233,8 +230,7 @@ fn coroutine_preemptive() -> std::io::Result<()> { ) .unwrap(); if result.1.timed_out() { - Err(std::io::Error::new( - std::io::ErrorKind::Other, + Err(std::io::Error::other( "The monitor should send signals to coroutines in running state", )) } else { @@ -279,8 +275,7 @@ fn coroutine_syscall_not_preemptive() -> std::io::Result<()> { if result.1.timed_out() { Ok(()) } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, + Err(std::io::Error::other( "The monitor should not send signals to coroutines in syscall state", )) } diff --git a/hook/docs/cn/hook.md b/hook/docs/cn/hook.md new file mode 100644 index 00000000..dfe9f352 --- /dev/null +++ b/hook/docs/cn/hook.md @@ -0,0 +1,67 @@ +--- +title: Hook总览 +date: 2025-01-20 10:00:00 +author: loongs-zhang +--- + +# Hook总览 + +## 为什么hook? + +在`Coroutine::resume_with`之后,一个协程可能会长时间占用调度线程(例如,陷入重度计算或系统调用),从而拖慢被该线程调度的其他协程。为了解决陷入系统调用的问题,我们引入hook机制,这样当协程进入系统调用时,它会被自动挂起,从而让其他协程执行。 + +这带来了一个新问题,`preemptive`特性会`发送大量信号`,而`信号会中断正在执行的系统调用`。此外,由于大多数用户在代码中未处理信号,因此如果他们直接使用`open-routine-core`并且启用`preemptive`特性将导致`灾难性后果`。 + +## 什么是hook? + +Hook可以通过在运行时插入自定义代码来修改或扩展现有代码的行为,甚至可以监控、拦截、修改和重定向系统调用。现在,让我们用一个[例子](https://github.com/loongs-zhang/link-example)来直观地体验它。 + +假设我们有以下测试代码: + +```rust +use std::time::{Duration, Instant}; + +#[test] +fn test_hook() { + let start = Instant::now(); + std::thread::sleep(Duration::MAX); + let cost = Instant::now().duration_since(start); + println!("cost: {:?}", cost); +} +``` + +如果我们不hook,因为`std::thread::sleep(Duration::MAX)`,这个测试几乎永远不会结束,但有了hook,我们可以在`不更改测试代码`的情况下将`nanosleep`系统调用重定向到[我们的自定义代码](https://github.com/loongs-zhang/link-example/blob/master/dep/src/lib.rs),然后测试就会[很快结束](https://github.com/loongs-zhang/link-example/actions/runs/12862762378/job/35858206179)。 + +
+ +
+ +## 工作原理 + +```mermaid +sequenceDiagram + Actor Your Project + participant open-coroutine + participant open-coroutine-hook + participant open-coroutine-core + + Your Project ->> open-coroutine: depends on + open-coroutine ->> open-coroutine-hook: depends on + alt at compile time + open-coroutine ->> open-coroutine: build open-coroutine-hook into dylib + open-coroutine ->> open-coroutine: link open-coroutine-hook's dylib + else runtime + Your Project -->> Operation System: logic execute syscall + alt what actually happened + Your Project ->> open-coroutine-hook: redirect syscall to open-coroutine-hook's syscall mod + open-coroutine-hook ->> open-coroutine-core: call open-coroutine-core's syscall mod + open-coroutine-core ->> Operation System: execute fast syscall actually + Operation System ->> open-coroutine-core: return syscall result and errno + open-coroutine-core -->> Operation System: maybe execute fast syscall many times + open-coroutine-core -->> open-coroutine-core: maybe modify syscall result or errno + open-coroutine-core ->> open-coroutine-hook: return syscall result and errno + open-coroutine-hook ->> Your Project: return syscall result and errno + end + Operation System ->> Your Project: return syscall result and errno + end +``` diff --git a/hook/docs/en/hook.md b/hook/docs/en/hook.md index 0b4544c6..cdd6e15d 100644 --- a/hook/docs/en/hook.md +++ b/hook/docs/en/hook.md @@ -8,17 +8,14 @@ author: loongs-zhang ## Why hook? -After a `Coroutine::resume_with`, a coroutine may occupy the scheduling thread for a long time, thereby slowing down -other coroutines scheduled by that scheduling thread. To solve this problem, we introduce hook, which automatically -suspends coroutines entering syscall and allow other coroutines to execute. +After a `Coroutine::resume_with`, a coroutine may occupy the scheduling thread for a long time (e.g. getting stuck in +heavy computing or syscall), thereby slowing down other coroutines scheduled by that scheduling thread. To solve the +problem of getting stuck in syscall, we introduce hook, which automatically suspends coroutines that enter syscall and +allow other coroutines to execute. -The coroutine occupies scheduling threads for a long time in two scenarios: getting stuck in heavy computing or syscall. -The following only solves the problem of getting stuck in syscall. - -Another problem is that `signals can interrupt the running syscall`, and the `preemptive` feature mechanism sends a -large -number of signals. In addition, most user code does not handle signals, if they directly use `open-routine-core` and -enabling preemptive will lead to `catastrophic consequences`. +This brings a new problem, the `preemptive` feature will send a large number of signals `which can interrupt the running +syscall`. In addition, most user code does not handle signals, if they directly use `open-routine-core` and enabling the +preemptive feature will lead to `catastrophic consequences`. ## What is hook? @@ -40,8 +37,8 @@ fn test_hook() { } ``` -If we don't hook, this test would almost never end due to `std::thread::sleep(Duration::MAX)`, but with hook, we -redirect the `nanosleep` syscall +If we don't hook, because `std::thread::sleep(Duration::MAX)`, this test almost never ends, but with hook, we redirect +the `nanosleep` syscall to [our custom code](https://github.com/loongs-zhang/link-example/blob/master/dep/src/lib.rs) `without change the test code`, and then the test will [end soon](https://github.com/loongs-zhang/link-example/actions/runs/12862762378/job/35858206179). diff --git a/hook/src/syscall/windows.rs b/hook/src/syscall/windows.rs index 7f154c7c..03264347 100644 --- a/hook/src/syscall/windows.rs +++ b/hook/src/syscall/windows.rs @@ -1,5 +1,5 @@ use std::ffi::{c_int, c_longlong, c_uint, c_void}; -use std::io::{Error, ErrorKind}; +use std::io::Error; use windows_sys::core::{PCSTR, PCWSTR, PSTR}; use windows_sys::Win32::Foundation::{BOOL, HANDLE, TRUE}; use windows_sys::Win32::Networking::WinSock::{ @@ -89,6 +89,5 @@ unsafe fn attach() -> std::io::Result<()> { // impl_hook!("api-ms-win-core-synch-l1-2-0.dll", WAITONADDRESS, WaitOnAddress(address: *const c_void, compareaddress: *const c_void, addresssize: usize, dwmilliseconds: c_uint) -> BOOL); // Enable the hook - minhook::MinHook::enable_all_hooks() - .map_err(|_| Error::new(ErrorKind::Other, "init all hooks failed !")) + minhook::MinHook::enable_all_hooks().map_err(|_| Error::other("init all hooks failed !")) } diff --git a/open-coroutine/examples/file_co.rs b/open-coroutine/examples/file_co.rs index 867a8acb..44a27152 100644 --- a/open-coroutine/examples/file_co.rs +++ b/open-coroutine/examples/file_co.rs @@ -42,8 +42,5 @@ pub fn main() -> Result<()> { if let Some(r) = join_handle.timeout_join(Duration::from_secs(30))? { return r; } - Err(Error::new( - std::io::ErrorKind::Other, - "Failed to join the task", - )) + Err(Error::other("Failed to join the task")) } diff --git a/open-coroutine/examples/preemptive.rs b/open-coroutine/examples/preemptive.rs index 7dba535f..a9421589 100644 --- a/open-coroutine/examples/preemptive.rs +++ b/open-coroutine/examples/preemptive.rs @@ -74,8 +74,7 @@ pub fn main() -> std::io::Result<()> { ) .unwrap(); if result.1.timed_out() { - Err(std::io::Error::new( - std::io::ErrorKind::Other, + Err(std::io::Error::other( "preemptive schedule failed", )) } else { diff --git a/open-coroutine/examples/socket_co.rs b/open-coroutine/examples/socket_co.rs index 0680b85b..8bf0bf06 100644 --- a/open-coroutine/examples/socket_co.rs +++ b/open-coroutine/examples/socket_co.rs @@ -1,5 +1,5 @@ use open_coroutine::task; -use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Read, Write}; +use std::io::{Error, IoSlice, IoSliceMut, Read, Write}; use std::net::{Shutdown, TcpListener, ToSocketAddrs}; #[cfg(unix)] use std::os::fd::AsRawFd; @@ -175,8 +175,7 @@ pub fn main() -> std::io::Result<()> { ) .unwrap(); if result.1.timed_out() { - Err(Error::new( - ErrorKind::Other, + Err(Error::other( "The coroutine server and coroutine client did not completed within the specified time", )) } else { diff --git a/open-coroutine/examples/socket_co_client.rs b/open-coroutine/examples/socket_co_client.rs index 82df216b..3b595d7a 100644 --- a/open-coroutine/examples/socket_co_client.rs +++ b/open-coroutine/examples/socket_co_client.rs @@ -1,5 +1,5 @@ use open_coroutine::task; -use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Read, Write}; +use std::io::{Error, IoSlice, IoSliceMut, Read, Write}; use std::net::{Shutdown, TcpListener, ToSocketAddrs}; #[cfg(unix)] use std::os::fd::AsRawFd; @@ -170,8 +170,7 @@ pub fn main() -> std::io::Result<()> { ) .unwrap(); if result.1.timed_out() { - Err(Error::new( - ErrorKind::Other, + Err(Error::other( "The coroutine client did not completed within the specified time", )) } else { diff --git a/open-coroutine/examples/socket_co_server.rs b/open-coroutine/examples/socket_co_server.rs index c14d7ed3..ed2d5751 100644 --- a/open-coroutine/examples/socket_co_server.rs +++ b/open-coroutine/examples/socket_co_server.rs @@ -1,5 +1,5 @@ use open_coroutine::task; -use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Read, Write}; +use std::io::{Error, IoSlice, IoSliceMut, Read, Write}; use std::net::{Shutdown, TcpListener, ToSocketAddrs}; #[cfg(unix)] use std::os::fd::AsRawFd; @@ -172,8 +172,7 @@ pub fn main() -> std::io::Result<()> { ) .unwrap(); if result.1.timed_out() { - Err(Error::new( - ErrorKind::Other, + Err(Error::other( "The coroutine service did not completed within the specified time", )) } else { diff --git a/open-coroutine/examples/socket_not_co.rs b/open-coroutine/examples/socket_not_co.rs index c162f006..7e4ef3c2 100644 --- a/open-coroutine/examples/socket_not_co.rs +++ b/open-coroutine/examples/socket_not_co.rs @@ -1,4 +1,4 @@ -use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Read, Write}; +use std::io::{Error, IoSlice, IoSliceMut, Read, Write}; use std::net::{Shutdown, TcpListener, ToSocketAddrs}; #[cfg(unix)] use std::os::fd::AsRawFd; @@ -166,8 +166,7 @@ pub fn main() -> std::io::Result<()> { ) .unwrap(); if result.1.timed_out() { - Err(Error::new( - ErrorKind::Other, + Err(Error::other( "The service did not completed within the specified time", )) } else { diff --git a/open-coroutine/src/lib.rs b/open-coroutine/src/lib.rs index b44034e9..cd97324e 100644 --- a/open-coroutine/src/lib.rs +++ b/open-coroutine/src/lib.rs @@ -133,8 +133,7 @@ pub fn crate_task R>( let result: &'static mut std::io::Result = Box::leak(Box::new( std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (data.0)(data.1))) .map_err(|e| { - Error::new( - ErrorKind::Other, + Error::other( e.downcast_ref::<&'static str>() .map_or("task failed without message", |msg| *msg), ) @@ -165,7 +164,7 @@ impl JoinHandle { unsafe { let ptr = task_timeout_join(self, dur.as_nanos().try_into().expect("overflow")); match ptr.cmp(&0) { - Ordering::Less => Err(Error::new(ErrorKind::Other, "timeout join failed")), + Ordering::Less => Err(Error::other("timeout join failed")), Ordering::Equal => Ok(None), Ordering::Greater => Ok(Some((*Box::from_raw(ptr as *mut std::io::Result))?)), } @@ -176,7 +175,7 @@ impl JoinHandle { unsafe { let ptr = task_join(&self); match ptr.cmp(&0) { - Ordering::Less => Err(Error::new(ErrorKind::Other, "join failed")), + Ordering::Less => Err(Error::other("join failed")), Ordering::Equal => Ok(None), Ordering::Greater => Ok(Some((*Box::from_raw(ptr as *mut std::io::Result))?)), } @@ -192,7 +191,7 @@ impl JoinHandle { for handle in slice { let left_time = timeout_time.saturating_sub(open_coroutine_core::common::now()); if 0 == left_time { - return Err(Error::new(ErrorKind::Other, "timeout join failed")); + return Err(Error::other("timeout join failed")); } if let Ok(x) = handle.timeout_join(Duration::from_nanos(left_time).min(SLICE)) { return Ok(x);