forked from rcore-os/zCore
refactor exception handling
- remove exit field from Syscall - rustify method names
This commit is contained in:
parent
4c7a3b802a
commit
a66c9f37ca
|
@ -176,37 +176,21 @@ fn spawn(thread: Arc<Thread>) {
|
|||
let vmtoken = thread.proc().vmar().table_phys();
|
||||
let future = async move {
|
||||
kernel_hal::Thread::set_tid(thread.id(), thread.proc().id());
|
||||
let mut exit = false;
|
||||
if thread.get_first_thread() {
|
||||
let proc_start_exception =
|
||||
Exception::create(thread.clone(), ExceptionType::ProcessStarting, None);
|
||||
if !proc_start_exception
|
||||
if thread.is_first_thread() {
|
||||
Exception::create(thread.clone(), ExceptionType::ProcessStarting, None)
|
||||
.handle_with_exceptionates(
|
||||
false,
|
||||
JobDebuggerIterator::new(thread.proc().job()),
|
||||
true,
|
||||
)
|
||||
.await
|
||||
{
|
||||
exit = true;
|
||||
}
|
||||
.await;
|
||||
};
|
||||
let start_exception =
|
||||
Exception::create(thread.clone(), ExceptionType::ThreadStarting, None);
|
||||
if !start_exception
|
||||
Exception::create(thread.clone(), ExceptionType::ThreadStarting, None)
|
||||
.handle_with_exceptionates(false, Some(thread.proc().get_debug_exceptionate()), false)
|
||||
.await
|
||||
{
|
||||
exit = true;
|
||||
}
|
||||
while !exit {
|
||||
.await;
|
||||
loop {
|
||||
let mut cx = thread.wait_for_run().await;
|
||||
if thread.state() == ThreadState::Dying {
|
||||
info!(
|
||||
"proc={:?} thread={:?} was killed",
|
||||
thread.proc().name(),
|
||||
thread.name()
|
||||
);
|
||||
break;
|
||||
}
|
||||
trace!("go to user: {:#x?}", cx);
|
||||
|
@ -228,12 +212,12 @@ fn spawn(thread: Arc<Thread>) {
|
|||
EXCEPTIONS_USER.add(1);
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
match cx.trap_num {
|
||||
0 => exit = handle_syscall(&thread, &mut cx.general).await,
|
||||
0 => handle_syscall(&thread, &mut cx.general).await,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
match cx.trap_num {
|
||||
0x100 => exit = handle_syscall(&thread, &mut cx.general).await,
|
||||
0x100 => handle_syscall(&thread, &mut cx.general).await,
|
||||
0x20..=0x3f => {
|
||||
kernel_hal::InterruptManager::handle(cx.trap_num as u8);
|
||||
if cx.trap_num == 0x20 {
|
||||
|
@ -252,34 +236,17 @@ fn spawn(thread: Arc<Thread>) {
|
|||
// FIXME:
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let flags = MMUFlags::WRITE;
|
||||
error!(
|
||||
"page fualt from user mode {:#x} {:#x?}",
|
||||
kernel_hal::fetch_fault_vaddr(),
|
||||
flags
|
||||
);
|
||||
match thread
|
||||
.proc()
|
||||
.vmar()
|
||||
.handle_page_fault(kernel_hal::fetch_fault_vaddr(), flags)
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"proc={:?} thread={:?} err={:?}",
|
||||
thread.proc().name(),
|
||||
thread.name(),
|
||||
e
|
||||
);
|
||||
error!("Page Fault from user mode {:#x?}", cx);
|
||||
let exception = Exception::create(
|
||||
thread.clone(),
|
||||
ExceptionType::FatalPageFault,
|
||||
Some(&cx),
|
||||
);
|
||||
if !exception.handle(true).await {
|
||||
exit = true;
|
||||
}
|
||||
}
|
||||
let fault_vaddr = kernel_hal::fetch_fault_vaddr();
|
||||
error!("page fault from user mode {:#x} {:#x?}", fault_vaddr, flags);
|
||||
let vmar = thread.proc().vmar();
|
||||
if vmar.handle_page_fault(fault_vaddr, flags).is_err() {
|
||||
error!("Page Fault from user mode: {:#x?}", cx);
|
||||
let exception = Exception::create(
|
||||
thread.clone(),
|
||||
ExceptionType::FatalPageFault,
|
||||
Some(&cx),
|
||||
);
|
||||
exception.handle(true).await;
|
||||
}
|
||||
}
|
||||
0x8 => {
|
||||
|
@ -293,22 +260,12 @@ fn spawn(thread: Arc<Thread>) {
|
|||
0x17 => ExceptionType::UnalignedAccess,
|
||||
_ => ExceptionType::General,
|
||||
};
|
||||
error!("User mode exception:{:?} {:#x?}", type_, cx);
|
||||
error!("User mode exception: {:?} {:#x?}", type_, cx);
|
||||
let exception = Exception::create(thread.clone(), type_, Some(&cx));
|
||||
if !exception.handle(true).await {
|
||||
exit = true;
|
||||
}
|
||||
exception.handle(true).await;
|
||||
}
|
||||
}
|
||||
thread.end_running(cx);
|
||||
if exit {
|
||||
info!(
|
||||
"proc={:?} thread={:?} exited",
|
||||
thread.proc().name(),
|
||||
thread.name()
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let end_exception = Exception::create(thread.clone(), ExceptionType::ThreadExiting, None);
|
||||
let handled = thread
|
||||
|
@ -317,15 +274,13 @@ fn spawn(thread: Arc<Thread>) {
|
|||
.send_exception(&end_exception);
|
||||
if let Ok(future) = handled {
|
||||
thread.dying_run(future).await.ok();
|
||||
} else {
|
||||
handled.ok();
|
||||
}
|
||||
thread.terminate();
|
||||
};
|
||||
kernel_hal::Thread::spawn(Box::pin(future), vmtoken);
|
||||
}
|
||||
|
||||
async fn handle_syscall(thread: &Arc<Thread>, regs: &mut GeneralRegs) -> bool {
|
||||
async fn handle_syscall(thread: &Arc<Thread>, regs: &mut GeneralRegs) {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let num = regs.rax as u32;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
|
@ -355,7 +310,6 @@ async fn handle_syscall(thread: &Arc<Thread>, regs: &mut GeneralRegs) -> bool {
|
|||
regs,
|
||||
thread: thread.clone(),
|
||||
spawn_fn: spawn,
|
||||
exit: false,
|
||||
};
|
||||
let ret = syscall.syscall(num, args).await as usize;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
@ -366,5 +320,4 @@ async fn handle_syscall(thread: &Arc<Thread>, regs: &mut GeneralRegs) -> bool {
|
|||
{
|
||||
syscall.regs.x0 = ret;
|
||||
}
|
||||
syscall.exit
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
super::*, crate::ipc::*, crate::object::*, alloc::sync::Arc, alloc::vec, alloc::vec::Vec,
|
||||
core::mem::size_of, core::time::Duration, futures::channel::oneshot, futures::pin_mut,
|
||||
core::mem::size_of, core::ops::Deref, futures::channel::oneshot, futures::pin_mut,
|
||||
kernel_hal::UserContext, spin::Mutex,
|
||||
};
|
||||
|
||||
|
@ -87,8 +87,7 @@ impl Exceptionate {
|
|||
type_: exception.type_,
|
||||
padding: Default::default(),
|
||||
};
|
||||
let (sender, receiver) = oneshot::channel::<()>();
|
||||
let object = ExceptionObject::create(exception.clone(), sender);
|
||||
let (object, closed) = ExceptionObject::create(exception.clone());
|
||||
let handle = Handle::new(object, Rights::DEFAULT_EXCEPTION);
|
||||
let msg = MessagePacket {
|
||||
data: info.pack(),
|
||||
|
@ -102,7 +101,7 @@ impl Exceptionate {
|
|||
}
|
||||
err
|
||||
})?;
|
||||
Ok(receiver)
|
||||
Ok(closed)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,25 +240,31 @@ pub struct ExceptionObject {
|
|||
impl_kobject!(ExceptionObject);
|
||||
|
||||
impl ExceptionObject {
|
||||
/// Create an `ExceptionObject`.
|
||||
fn create(exception: Arc<Exception>, close_signal: oneshot::Sender<()>) -> Arc<Self> {
|
||||
Arc::new(ExceptionObject {
|
||||
/// Create an kernel object of `Exception`.
|
||||
///
|
||||
/// Return the object and a `Receiver` of the object dropped event.
|
||||
fn create(exception: Arc<Exception>) -> (Arc<Self>, oneshot::Receiver<()>) {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
let object = Arc::new(ExceptionObject {
|
||||
base: KObjectBase::new(),
|
||||
exception,
|
||||
close_signal: Some(close_signal),
|
||||
})
|
||||
close_signal: Some(sender),
|
||||
});
|
||||
(object, receiver)
|
||||
}
|
||||
/// Get the exception.
|
||||
pub fn get_exception(&self) -> &Arc<Exception> {
|
||||
}
|
||||
|
||||
impl Deref for ExceptionObject {
|
||||
type Target = Arc<Exception>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.exception
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ExceptionObject {
|
||||
fn drop(&mut self) {
|
||||
self.close_signal
|
||||
.take()
|
||||
.and_then(|signal| signal.send(()).ok());
|
||||
self.close_signal.take().unwrap().send(()).ok();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,15 +305,18 @@ impl Exception {
|
|||
}),
|
||||
})
|
||||
}
|
||||
/// Handle the exception. The return value indicate if the thread is exited after this.
|
||||
/// Note that it's possible that this may returns before exception was send to any exception channel
|
||||
/// This happens only when the thread is killed before we send the exception
|
||||
pub async fn handle(self: &Arc<Self>, fatal: bool) -> bool {
|
||||
|
||||
/// Handle the exception.
|
||||
///
|
||||
/// Note that it's possible that this may returns before exception was send to any exception channel.
|
||||
/// This happens only when the thread is killed before we send the exception.
|
||||
pub async fn handle(self: &Arc<Self>, fatal: bool) {
|
||||
self.handle_with_exceptionates(fatal, ExceptionateIterator::new(self), false)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Same as handle, but use a customed iterator
|
||||
/// Same as handle, but use a customed iterator.
|
||||
///
|
||||
/// If `first_only` is true, this will only send exception to the first one that received the exception
|
||||
/// even when the exception is not handled
|
||||
pub async fn handle_with_exceptionates(
|
||||
|
@ -316,30 +324,14 @@ impl Exception {
|
|||
fatal: bool,
|
||||
exceptionates: impl IntoIterator<Item = Arc<Exceptionate>>,
|
||||
first_only: bool,
|
||||
) -> bool {
|
||||
self.thread.set_exception(Some(self.clone()));
|
||||
) {
|
||||
let future = self.handle_internal(exceptionates, first_only);
|
||||
pin_mut!(future);
|
||||
let result: ZxResult = self
|
||||
.thread
|
||||
.blocking_run(
|
||||
future,
|
||||
ThreadState::BlockedException,
|
||||
Duration::from_nanos(u64::max_value()),
|
||||
)
|
||||
.await;
|
||||
self.thread.set_exception(None);
|
||||
if let Err(err) = result {
|
||||
if err == ZxError::STOP {
|
||||
// We are killed
|
||||
return false;
|
||||
} else if err == ZxError::NEXT && fatal {
|
||||
// Nobody handled the exception, kill myself
|
||||
self.thread.proc().exit(TASK_RETCODE_SYSCALL_KILL);
|
||||
return false;
|
||||
}
|
||||
let result = self.thread.wait_for_exception(future, self.clone()).await;
|
||||
if result == Err(ZxError::NEXT) && fatal {
|
||||
// Nobody handled the exception, kill myself
|
||||
self.thread.proc().exit(TASK_RETCODE_SYSCALL_KILL);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
async fn handle_internal(
|
||||
|
@ -349,10 +341,9 @@ impl Exception {
|
|||
) -> ZxResult {
|
||||
for exceptionate in exceptionates.into_iter() {
|
||||
let closed = match exceptionate.send_exception(self) {
|
||||
Ok(receiver) => receiver,
|
||||
// This channel is not available now!
|
||||
Err(ZxError::NEXT) => continue,
|
||||
Err(err) => return Err(err),
|
||||
res => res?,
|
||||
};
|
||||
self.inner.lock().current_channel_type = exceptionate.type_;
|
||||
// If this error, the sender is dropped, and the handle should also be closed.
|
||||
|
@ -380,18 +371,18 @@ impl Exception {
|
|||
}
|
||||
|
||||
/// Get the exception's channel type.
|
||||
pub fn get_current_channel_type(&self) -> ExceptionChannelType {
|
||||
pub fn current_channel_type(&self) -> ExceptionChannelType {
|
||||
self.inner.lock().current_channel_type
|
||||
}
|
||||
|
||||
/// Get a report of the exception.
|
||||
pub fn get_report(&self) -> ExceptionReport {
|
||||
pub fn report(&self) -> ExceptionReport {
|
||||
self.report.clone()
|
||||
}
|
||||
|
||||
/// Get whether closing the exception handle will
|
||||
/// finish exception processing and resume the underlying thread.
|
||||
pub fn get_state(&self) -> u32 {
|
||||
pub fn state(&self) -> u32 {
|
||||
self.inner.lock().handled as u32
|
||||
}
|
||||
|
||||
|
@ -407,7 +398,7 @@ impl Exception {
|
|||
|
||||
/// Get whether the debugger gets a 'second chance' at handling the exception
|
||||
/// if the process-level handler fails to do so.
|
||||
pub fn get_strategy(&self) -> u32 {
|
||||
pub fn strategy(&self) -> u32 {
|
||||
self.inner.lock().second_chance as u32
|
||||
}
|
||||
|
||||
|
@ -640,7 +631,7 @@ mod tests {
|
|||
.downcast_arc::<ExceptionObject>()
|
||||
.unwrap();
|
||||
if should_handle {
|
||||
exception.get_exception().set_state(1).unwrap();
|
||||
exception.set_state(1).unwrap();
|
||||
}
|
||||
// record the order of the handler used
|
||||
handled_order.lock().push(order);
|
||||
|
|
|
@ -159,7 +159,7 @@ impl Process {
|
|||
inner.status = Status::Running;
|
||||
handle_value = arg1.map_or(INVALID_HANDLE, |handle| inner.add_handle(handle));
|
||||
}
|
||||
thread.set_first_thread(true);
|
||||
thread.set_first_thread();
|
||||
match thread.start(entry, stack, handle_value as usize, arg2, spawn_fn) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
|
|
|
@ -368,7 +368,7 @@ impl Thread {
|
|||
wait_exception_channel_type: inner
|
||||
.exception
|
||||
.as_ref()
|
||||
.map_or(0, |exception| exception.get_current_channel_type() as u32),
|
||||
.map_or(0, |exception| exception.current_channel_type() as u32),
|
||||
cpu_affinity_mask: [0u64; 8],
|
||||
}
|
||||
}
|
||||
|
@ -379,11 +379,8 @@ impl Thread {
|
|||
if inner.get_state() != ThreadState::BlockedException {
|
||||
return Err(ZxError::BAD_STATE);
|
||||
}
|
||||
inner
|
||||
.exception
|
||||
.as_ref()
|
||||
.ok_or(ZxError::BAD_STATE)
|
||||
.map(|exception| exception.get_report())
|
||||
let report = inner.exception.as_ref().ok_or(ZxError::BAD_STATE)?.report();
|
||||
Ok(report)
|
||||
}
|
||||
|
||||
/// Run async future and change state while blocking.
|
||||
|
@ -464,8 +461,9 @@ impl Thread {
|
|||
ret
|
||||
}
|
||||
|
||||
/// Run a blocking task when the thread is exited itself and dying
|
||||
/// The task will stop running if and once the thread is killed
|
||||
/// Run a blocking task when the thread is exited itself and dying.
|
||||
///
|
||||
/// The task will stop running if and once the thread is killed.
|
||||
pub async fn dying_run<F, T, FT>(&self, future: F) -> ZxResult<T>
|
||||
where
|
||||
F: Future<Output = FT> + Unpin,
|
||||
|
@ -486,6 +484,23 @@ impl Thread {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) async fn wait_for_exception<T>(
|
||||
&self,
|
||||
future: impl Future<Output = ZxResult<T>> + Unpin,
|
||||
exception: Arc<Exception>,
|
||||
) -> ZxResult<T> {
|
||||
self.inner.lock().exception = Some(exception);
|
||||
let ret = self
|
||||
.blocking_run(
|
||||
future,
|
||||
ThreadState::BlockedException,
|
||||
Duration::from_nanos(u64::max_value()),
|
||||
)
|
||||
.await;
|
||||
self.inner.lock().exception = None;
|
||||
ret
|
||||
}
|
||||
|
||||
/// Get the thread state.
|
||||
pub fn state(&self) -> ThreadState {
|
||||
self.inner.lock().get_state()
|
||||
|
@ -506,18 +521,13 @@ impl Thread {
|
|||
self.inner.lock().time as u64
|
||||
}
|
||||
|
||||
/// Set the currently processing exception.
|
||||
pub fn set_exception(&self, exception: Option<Arc<Exception>>) {
|
||||
self.inner.lock().exception = exception;
|
||||
}
|
||||
|
||||
/// Set this thread as the first thread of a process.
|
||||
pub fn set_first_thread(&self, first_thread: bool) {
|
||||
self.inner.lock().first_thread = first_thread;
|
||||
pub(super) fn set_first_thread(&self) {
|
||||
self.inner.lock().first_thread = true;
|
||||
}
|
||||
|
||||
/// Get whether this thread is the first thread of a process.
|
||||
pub fn get_first_thread(&self) -> bool {
|
||||
/// Whether this thread is the first thread of a process.
|
||||
pub fn is_first_thread(&self) -> bool {
|
||||
self.inner.lock().first_thread
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ impl Syscall<'_> {
|
|||
let proc = self.thread.proc();
|
||||
let exception =
|
||||
proc.get_object_with_rights::<ExceptionObject>(exception, Rights::default())?;
|
||||
let (object, right) = exception.get_exception().get_thread_and_rights();
|
||||
let (object, right) = exception.get_thread_and_rights();
|
||||
let handle = proc.add_handle(Handle::new(object, right));
|
||||
out.write(handle)?;
|
||||
Ok(())
|
||||
|
@ -87,10 +87,10 @@ impl Syscall<'_> {
|
|||
let proc = self.thread.proc();
|
||||
let exception =
|
||||
proc.get_object_with_rights::<ExceptionObject>(exception, Rights::default())?;
|
||||
if exception.get_exception().get_current_channel_type() == ExceptionChannelType::Thread {
|
||||
if exception.current_channel_type() == ExceptionChannelType::Thread {
|
||||
return Err(ZxError::ACCESS_DENIED);
|
||||
}
|
||||
let (object, right) = exception.get_exception().get_process_and_rights();
|
||||
let (object, right) = exception.get_process_and_rights();
|
||||
let handle = proc.add_handle(Handle::new(object, right));
|
||||
out.write(handle)?;
|
||||
Ok(())
|
||||
|
|
|
@ -53,7 +53,6 @@ pub struct Syscall<'a> {
|
|||
pub regs: &'a mut GeneralRegs,
|
||||
pub thread: Arc<Thread>,
|
||||
pub spawn_fn: fn(thread: Arc<Thread>),
|
||||
pub exit: bool,
|
||||
}
|
||||
|
||||
impl Syscall<'_> {
|
||||
|
@ -379,11 +378,6 @@ impl Syscall<'_> {
|
|||
}
|
||||
};
|
||||
info!("{}|{} {:?} <= {:?}", proc_name, thread_name, sys_type, ret);
|
||||
if ret == Err(ZxError::STOP) && !self.exit {
|
||||
// This is an error that only happens when the thread was killed during a blocking syscall
|
||||
info!("{}|{} KILLED WHEN BLOCKING", proc_name, thread_name);
|
||||
self.exit = true
|
||||
}
|
||||
match ret {
|
||||
Ok(_) => 0,
|
||||
Err(err) => err as isize,
|
||||
|
|
|
@ -88,18 +88,16 @@ impl Syscall<'_> {
|
|||
let mut info_ptr = UserOutPtr::<u32>::from_addr_size(buffer, buffer_size)?;
|
||||
let state = proc
|
||||
.get_object_with_rights::<ExceptionObject>(handle_value, Rights::GET_PROPERTY)?
|
||||
.get_exception()
|
||||
.get_state();
|
||||
.state();
|
||||
info_ptr.write(state)?;
|
||||
Ok(())
|
||||
}
|
||||
Property::ExceptionStrategy => {
|
||||
let mut info_ptr = UserOutPtr::<u32>::from_addr_size(buffer, buffer_size)?;
|
||||
let state = proc
|
||||
let strategy = proc
|
||||
.get_object_with_rights::<ExceptionObject>(handle_value, Rights::GET_PROPERTY)?
|
||||
.get_exception()
|
||||
.get_strategy();
|
||||
info_ptr.write(state)?;
|
||||
.strategy();
|
||||
info_ptr.write(strategy)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
|
@ -170,15 +168,13 @@ impl Syscall<'_> {
|
|||
Property::ExceptionState => {
|
||||
let state = UserInPtr::<u32>::from_addr_size(buffer, buffer_size)?.read()?;
|
||||
proc.get_object_with_rights::<ExceptionObject>(handle_value, Rights::SET_PROPERTY)?
|
||||
.get_exception()
|
||||
.set_state(state)?;
|
||||
Ok(())
|
||||
}
|
||||
Property::ExceptionStrategy => {
|
||||
let state = UserInPtr::<u32>::from_addr_size(buffer, buffer_size)?.read()?;
|
||||
let strategy = UserInPtr::<u32>::from_addr_size(buffer, buffer_size)?.read()?;
|
||||
proc.get_object_with_rights::<ExceptionObject>(handle_value, Rights::SET_PROPERTY)?
|
||||
.get_exception()
|
||||
.set_strategy(state)?;
|
||||
.set_strategy(strategy)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
|
|
|
@ -40,7 +40,6 @@ impl Syscall<'_> {
|
|||
info!("proc.exit: code={:?}", code);
|
||||
let proc = self.thread.proc();
|
||||
proc.exit(code);
|
||||
self.exit = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -180,7 +179,6 @@ impl Syscall<'_> {
|
|||
pub fn sys_thread_exit(&mut self) -> ZxResult {
|
||||
info!("thread.exit:");
|
||||
self.thread.exit();
|
||||
self.exit = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -222,21 +220,8 @@ impl Syscall<'_> {
|
|||
job.kill();
|
||||
} else if let Ok(process) = proc.get_object_with_rights::<Process>(handle, Rights::DESTROY)
|
||||
{
|
||||
if Arc::ptr_eq(&process, &proc) {
|
||||
//self kill, exit
|
||||
self.exit = true;
|
||||
}
|
||||
process.kill();
|
||||
} else if let Ok(thread) = proc.get_object_with_rights::<Thread>(handle, Rights::DESTROY) {
|
||||
info!(
|
||||
"killing thread: proc={:?} thread={:?}",
|
||||
thread.proc().name(),
|
||||
thread.name()
|
||||
);
|
||||
if Arc::ptr_eq(&thread, &self.thread) {
|
||||
//self kill, exit
|
||||
self.exit = true;
|
||||
}
|
||||
thread.kill();
|
||||
} else {
|
||||
return Err(ZxError::WRONG_TYPE);
|
||||
|
|
Loading…
Reference in New Issue