blob: 688ada60c241e232c472bdac0bde11ecc17b8b94 [file] [log] [blame]
// Copyright 2016 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
#include <err.h>
#include <inttypes.h>
#include <trace.h>
#include <kernel/event.h>
#include <platform.h>
#include <lib/ktrace.h>
#include <lib/user_copy/user_ptr.h>
#include <magenta/magenta.h>
#include <magenta/port_dispatcher.h>
#include <magenta/process_dispatcher.h>
#include <magenta/wait_state_observer.h>
#include <mxtl/auto_lock.h>
#include <mxtl/inline_array.h>
#include <mxtl/ref_ptr.h>
#include "syscalls_priv.h"
using mxtl::AutoLock;
#define LOCAL_TRACE 0
constexpr uint32_t kMaxWaitHandleCount = 1024u;
// Note: This is used for quite a few InlineArrays (simultaneously) in sys_handle_wait_many.
constexpr size_t kWaitManyInlineCount = 8u;
mx_status_t sys_object_wait_one(mx_handle_t handle_value,
mx_signals_t signals,
mx_time_t deadline,
user_ptr<mx_signals_t> _observed) {
LTRACEF("handle %x\n", handle_value);
Event event;
mx_status_t result;
WaitStateObserver wait_state_observer;
auto up = ProcessDispatcher::GetCurrent();
{
AutoLock lock(up->handle_table_lock());
Handle* handle = up->GetHandleLocked(handle_value);
if (!handle)
return MX_ERR_BAD_HANDLE;
if (!magenta_rights_check(handle, MX_RIGHT_READ))
return MX_ERR_ACCESS_DENIED;
result = wait_state_observer.Begin(&event, handle, signals);
if (result != MX_OK)
return result;
}
#if WITH_LIB_KTRACE
auto koid = static_cast<uint32_t>(up->GetKoidForHandle(handle_value));
ktrace(TAG_WAIT_ONE, koid, signals, (uint32_t)deadline, (uint32_t)(deadline >> 32));
#endif
// event_wait() will return MX_OK if already signaled,
// even if the deadline has passed. It will return MX_ERR_TIMED_OUT
// after the deadline passes if the event has not been
// signaled.
result = event.Wait(deadline);
// Regardless of wait outcome, we must call End().
auto signals_state = wait_state_observer.End();
#if WITH_LIB_KTRACE
ktrace(TAG_WAIT_ONE_DONE, koid, signals_state, result, 0);
#endif
if (_observed) {
if (_observed.copy_to_user(signals_state) != MX_OK)
return MX_ERR_INVALID_ARGS;
}
if (signals_state & MX_SIGNAL_HANDLE_CLOSED)
return MX_ERR_CANCELED;
return result;
}
mx_status_t sys_object_wait_many(user_ptr<mx_wait_item_t> _items, uint32_t count, mx_time_t deadline) {
LTRACEF("count %u\n", count);
if (!count) {
mx_status_t result = magenta_sleep(deadline);
if (result != MX_OK)
return result;
return MX_ERR_TIMED_OUT;
}
if (!_items)
return MX_ERR_INVALID_ARGS;
if (count > kMaxWaitHandleCount)
return MX_ERR_INVALID_ARGS;
mxtl::AllocChecker ac;
mxtl::InlineArray<mx_wait_item_t, kWaitManyInlineCount> items(&ac, count);
if (!ac.check())
return MX_ERR_NO_MEMORY;
if (_items.copy_array_from_user(items.get(), count) != MX_OK)
return MX_ERR_INVALID_ARGS;
mxtl::InlineArray<WaitStateObserver, kWaitManyInlineCount> wait_state_observers(&ac, count);
if (!ac.check())
return MX_ERR_NO_MEMORY;
Event event;
// We may need to unwind (which can be done outside the lock).
mx_status_t result = MX_OK;
size_t num_added = 0;
{
auto up = ProcessDispatcher::GetCurrent();
AutoLock lock(up->handle_table_lock());
for (; num_added != count; ++num_added) {
Handle* handle = up->GetHandleLocked(items[num_added].handle);
if (!handle) {
result = MX_ERR_BAD_HANDLE;
break;
}
if (!magenta_rights_check(handle, MX_RIGHT_READ)) {
result = MX_ERR_ACCESS_DENIED;
break;
}
result = wait_state_observers[num_added].Begin(&event, handle, items[num_added].waitfor);
if (result != MX_OK)
break;
}
}
if (result != MX_OK) {
for (size_t ix = 0; ix < num_added; ++ix)
wait_state_observers[ix].End();
return result;
}
// event_wait() will return MX_OK if already signaled,
// even if deadline has passed. It will return MX_ERR_TIMED_OUT
// after the deadline passes if the event has not been
// signaled.
result = event.Wait(deadline);
// Regardless of wait outcome, we must call End().
mx_signals_t combined = 0;
for (size_t ix = 0; ix != count; ++ix) {
combined |= (items[ix].pending = wait_state_observers[ix].End());
}
if (_items.copy_array_to_user(items.get(), count) != MX_OK)
return MX_ERR_INVALID_ARGS;
if (combined & MX_SIGNAL_HANDLE_CLOSED)
return MX_ERR_CANCELED;
return result;
}
mx_status_t sys_object_wait_async(mx_handle_t handle_value, mx_handle_t port_handle,
uint64_t key, mx_signals_t signals, uint32_t options) {
LTRACEF("handle %x\n", handle_value);
auto up = ProcessDispatcher::GetCurrent();
mxtl::RefPtr<PortDispatcher> port;
auto status = up->GetDispatcherWithRights(port_handle, MX_RIGHT_WRITE, &port);
if (status != MX_OK)
return status;
{
AutoLock lock(up->handle_table_lock());
Handle* handle = up->GetHandleLocked(handle_value);
if (!handle)
return MX_ERR_BAD_HANDLE;
if (!magenta_rights_check(handle, MX_RIGHT_READ))
return MX_ERR_ACCESS_DENIED;
return port->MakeObservers(options, handle, key, signals);
}
}