blob: d09e2f5709d0783296141475900ce6f0d1db72e7 [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use crate::kernel::deadline::Deadline;
use crate::kernel::event::Event;
use crate::kernel::thread::Interruptible;
use crate::object::{
AutoBlocked, Blocked, HandleTableReadGuard, HandleValue, ProcessDispatcher, WaitSignalObserver,
};
use crate::platform_rs::timer::InstantUnknown;
use crate::user_copy::{UserInOutPtr, UserOutPtr};
use debug::ltracef;
use fbl::InlineArray;
use syscalls_macro::syscall;
use zx_status::Status;
use zx_types::{
ZX_RIGHT_WAIT, ZX_SIGNAL_HANDLE_CLOSED, ZX_WAIT_MANY_MAX_ITEMS, zx_instant_mono_t,
zx_signals_t, zx_wait_item_t,
};
const LOCAL_TRACE: u32 = 0;
zr::static_assert!(ZX_WAIT_MANY_MAX_ITEMS <= zx_types::ZX_CHANNEL_MAX_MSG_HANDLES as usize);
#[syscall]
pub fn sys_object_wait_one(
handle_value: HandleValue,
signals: zx_signals_t,
deadline: zx_instant_mono_t,
observed: UserOutPtr<zx_signals_t>,
) -> Result<(), Status> {
ltracef!("handle {:?}\n", handle_value);
pin_init::stack_pin_init!(let event = Event::init_unsignaled());
let mut wait_signal_observer = WaitSignalObserver::new();
let slack_deadline = ProcessDispatcher::with_current(|up| {
pin_init::stack_pin_init!(let guard = HandleTableReadGuard::new(up));
let handle = guard.get_handle(handle_value).ok_or(Status::BAD_HANDLE)?;
if !handle.has_rights(ZX_RIGHT_WAIT) {
return Err(Status::ACCESS_DENIED);
}
wait_signal_observer.begin(&guard, event.as_ref().get_ref(), &handle, signals)?;
let slack = up.get_timer_slack_policy();
Ok(Deadline::new(InstantUnknown(deadline), slack))
})?;
// Event::Wait() will return ZX_OK if already signaled,
// even if the deadline has passed. It will return ZX_ERR_TIMED_OUT
// after the deadline passes if the event has not been
// signaled.
let wait_result = {
let _blocked = AutoBlocked::new(Blocked::WAIT_ONE);
event.wait(&slack_deadline)
};
// Regardless of wait outcome, we must call End().
let signals_state = wait_signal_observer.end();
if !observed.is_null() {
observed.write(signals_state)?;
}
if (signals_state & ZX_SIGNAL_HANDLE_CLOSED) != 0 {
return Err(Status::CANCELED);
}
wait_result
}
#[syscall]
pub fn sys_object_wait_many(
user_items: UserInOutPtr<zx_wait_item_t>,
count: usize,
deadline: zx_instant_mono_t,
) -> Result<(), Status> {
ltracef!("count {}\n", count);
let slack = ProcessDispatcher::with_current(|up| up.get_timer_slack_policy());
let slack_deadline = Deadline::new(InstantUnknown(deadline), slack);
if count == 0 {
let now = crate::platform_rs::timer::current_mono_time();
{
let _blocked = AutoBlocked::new(Blocked::WAIT_MANY);
crate::kernel::thread::sleep_etc(&slack_deadline, Interruptible::YES, now.0)?;
}
return Err(Status::TIMED_OUT);
}
if count > ZX_WAIT_MANY_MAX_ITEMS {
return Err(Status::OUT_OF_RANGE);
}
let mut uninit_items =
[const { core::mem::MaybeUninit::<zx_wait_item_t>::uninit() }; ZX_WAIT_MANY_MAX_ITEMS];
let items = user_items.copy_slice_from_user(&mut uninit_items[..count])?;
const MAX_INLINE_OBSERVERS: usize = 8;
// WaitSignalObserver is heavier than it looks so make sure we know how
// much stack InlineArray is going to use, given limited kernel stack size.
zr::static_assert!(core::mem::size_of::<WaitSignalObserver>() * MAX_INLINE_OBSERVERS < 640);
let mut observers = InlineArray::<WaitSignalObserver, MAX_INLINE_OBSERVERS>::try_new(count)
.map_err(|_| Status::NO_MEMORY)?;
pin_init::stack_pin_init!(let event = Event::init_unsignaled());
// We may need to unwind (which can be done outside the lock).
let mut num_added = 0;
let begin_result = ProcessDispatcher::with_current(|up| {
pin_init::stack_pin_init!(let guard = HandleTableReadGuard::new(up));
for (ix, item) in items[..count].iter().enumerate() {
let handle =
guard.get_handle(HandleValue::new(item.handle)).ok_or(Status::BAD_HANDLE)?;
if !handle.has_rights(ZX_RIGHT_WAIT) {
return Err(Status::ACCESS_DENIED);
}
observers[ix].begin(&guard, event.as_ref().get_ref(), &handle, item.waitfor)?;
num_added += 1;
}
Ok(())
});
if let Err(err) = begin_result {
for obs in &mut observers[..num_added] {
obs.end();
}
return Err(err);
}
// Event::Wait() will return ZX_OK if already signaled,
// even if deadline has passed. It will return ZX_ERR_TIMED_OUT
// after the deadline passes if the event has not been
// signaled.
let wait_result = {
let _blocked = AutoBlocked::new(Blocked::WAIT_MANY);
event.wait(&slack_deadline)
};
// Regardless of wait outcome, we must call End().
let mut combined = 0;
for (ix, obs) in observers.iter_mut().enumerate() {
let pending = obs.end();
items[ix].pending = pending;
combined |= pending;
}
user_items.copy_slice_to_user(items)?;
if (combined & ZX_SIGNAL_HANDLE_CLOSED) != 0 {
return Err(Status::CANCELED);
}
wait_result
}