Comparar commits

...

2 Commits

Autor SHA1 Mensagem Data
Dave Bort 0f4eeea3cb WIP(mg-isolate): ChannelDispatcher works with stubs
Change-Id: Id25aea21a44222b4369501224a2b4982d4565560
2017-08-08 13:30:40 -07:00
Dave Bort d8a86a5a0e WIP(mg-isolate): creating EventDispatcher, Handle, StateTracker
Change-Id: Ied32934cca385e6514bc5fc40676ec81cc9a01d8
2017-08-08 11:50:28 -07:00
13 arquivos alterados com 320 adições e 48 exclusões
+32 -18
Ver Arquivo
@@ -10,16 +10,18 @@
#include <assert.h>
#include <err.h>
#include <trace.h>
//#include <trace.h>
#include <kernel/event.h>
#include <platform.h>
//#include <platform.h>
#include <magenta/handle.h>
#include <magenta/message_packet.h>
#ifdef _KERNEL
#include <magenta/process_dispatcher.h>
#include <magenta/rights.h>
#include <magenta/thread_dispatcher.h>
#endif
#include <magenta/rights.h>
#include <mxtl/alloc_checker.h>
#include <mxtl/auto_lock.h>
@@ -29,8 +31,14 @@ using mxtl::AutoLock;
#define LOCAL_TRACE 0
#ifndef _KERNEL
#define thread_reschedule(args...) ((void)0)
#undef TA_NO_THREAD_SAFETY_ANALYSIS
#define TA_NO_THREAD_SAFETY_ANALYSIS /**/
#endif
// static
status_t ChannelDispatcher::Create(uint32_t flags,
mx_status_t ChannelDispatcher::Create(uint32_t flags,
mxtl::RefPtr<Dispatcher>* dispatcher0,
mxtl::RefPtr<Dispatcher>* dispatcher1,
mx_rights_t* rights) {
@@ -54,7 +62,7 @@ status_t ChannelDispatcher::Create(uint32_t flags,
ChannelDispatcher::ChannelDispatcher(uint32_t flags)
: state_tracker_(MX_CHANNEL_WRITABLE) {
DEBUG_ASSERT(flags == 0);
MX_DEBUG_ASSERT(flags == 0);
}
// This is called before either ChannelDispatcher is accessible from threads other than the one
@@ -132,7 +140,7 @@ void ChannelDispatcher::OnPeerZeroHandles() {
}
}
status_t ChannelDispatcher::Read(uint32_t* msg_size,
mx_status_t ChannelDispatcher::Read(uint32_t* msg_size,
uint32_t* msg_handle_count,
mxtl::unique_ptr<MessagePacket>* msg,
bool may_discard) {
@@ -148,7 +156,7 @@ status_t ChannelDispatcher::Read(uint32_t* msg_size,
*msg_size = messages_.front().data_size();
*msg_handle_count = messages_.front().num_handles();
status_t rv = MX_OK;
mx_status_t rv = MX_OK;
if (*msg_size > max_size || *msg_handle_count > max_handle_count) {
if (!may_discard)
return MX_ERR_BUFFER_TOO_SMALL;
@@ -163,7 +171,7 @@ status_t ChannelDispatcher::Read(uint32_t* msg_size,
return rv;
}
status_t ChannelDispatcher::Write(mxtl::unique_ptr<MessagePacket> msg) {
mx_status_t ChannelDispatcher::Write(mxtl::unique_ptr<MessagePacket> msg) {
canary_.Assert();
mxtl::RefPtr<ChannelDispatcher> other;
@@ -184,17 +192,23 @@ status_t ChannelDispatcher::Write(mxtl::unique_ptr<MessagePacket> msg) {
return MX_OK;
}
status_t ChannelDispatcher::Call(mxtl::unique_ptr<MessagePacket> msg,
mx_status_t ChannelDispatcher::Call(mxtl::unique_ptr<MessagePacket> msg,
mx_time_t deadline, bool* return_handles,
mxtl::unique_ptr<MessagePacket>* reply) {
canary_.Assert();
#ifdef _KERNEL
auto waiter = ThreadDispatcher::GetCurrent()->GetMessageWaiter();
#else
MessageWaiter *waiter = nullptr;
#endif
if (unlikely(waiter->BeginWait(mxtl::WrapRefPtr(this), msg->get_txid()) != MX_OK)) {
#ifdef _KERNEL
// If a thread tries BeginWait'ing twice, the VDSO contract around retrying
// channel calls has been violated. Shoot the misbehaving process.
ProcessDispatcher::GetCurrent()->Kill();
#endif
return MX_ERR_BAD_STATE;
}
@@ -224,7 +238,7 @@ status_t ChannelDispatcher::Call(mxtl::unique_ptr<MessagePacket> msg,
return ResumeInterruptedCall(waiter, deadline, reply);
}
status_t ChannelDispatcher::ResumeInterruptedCall(MessageWaiter* waiter,
mx_status_t ChannelDispatcher::ResumeInterruptedCall(MessageWaiter* waiter,
mx_time_t deadline,
mxtl::unique_ptr<MessagePacket>* reply) {
canary_.Assert();
@@ -285,7 +299,7 @@ int ChannelDispatcher::WriteSelf(mxtl::unique_ptr<MessagePacket> msg) {
return 0;
}
status_t ChannelDispatcher::user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer) {
mx_status_t ChannelDispatcher::user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer) {
canary_.Assert();
if ((set_mask & ~MX_USER_SIGNAL_ALL) || (clear_mask & ~MX_USER_SIGNAL_ALL))
@@ -307,7 +321,7 @@ status_t ChannelDispatcher::user_signal(uint32_t clear_mask, uint32_t set_mask,
return other->UserSignalSelf(clear_mask, set_mask);
}
status_t ChannelDispatcher::UserSignalSelf(uint32_t clear_mask, uint32_t set_mask) {
mx_status_t ChannelDispatcher::UserSignalSelf(uint32_t clear_mask, uint32_t set_mask) {
canary_.Assert();
state_tracker_.UpdateState(clear_mask, set_mask);
return MX_OK;
@@ -317,7 +331,7 @@ ChannelDispatcher::MessageWaiter::~MessageWaiter() {
if (unlikely(channel_)) {
channel_->RemoveWaiter(this);
}
DEBUG_ASSERT(!InContainer());
MX_DEBUG_ASSERT(!InContainer());
}
mx_status_t ChannelDispatcher::MessageWaiter::BeginWait(mxtl::RefPtr<ChannelDispatcher> channel,
@@ -325,7 +339,7 @@ mx_status_t ChannelDispatcher::MessageWaiter::BeginWait(mxtl::RefPtr<ChannelDisp
if (unlikely(channel_)) {
return MX_ERR_BAD_STATE;
}
DEBUG_ASSERT(!InContainer());
MX_DEBUG_ASSERT(!InContainer());
txid_ = txid;
status_ = MX_ERR_TIMED_OUT;
@@ -335,16 +349,16 @@ mx_status_t ChannelDispatcher::MessageWaiter::BeginWait(mxtl::RefPtr<ChannelDisp
}
int ChannelDispatcher::MessageWaiter::Deliver(mxtl::unique_ptr<MessagePacket> msg) {
DEBUG_ASSERT(channel_);
MX_DEBUG_ASSERT(channel_);
msg_ = mxtl::move(msg);
status_ = MX_OK;
return event_.Signal(MX_OK);
}
int ChannelDispatcher::MessageWaiter::Cancel(status_t status) {
DEBUG_ASSERT(!InContainer());
DEBUG_ASSERT(channel_);
int ChannelDispatcher::MessageWaiter::Cancel(mx_status_t status) {
MX_DEBUG_ASSERT(!InContainer());
MX_DEBUG_ASSERT(channel_);
status_ = status;
return event_.Signal(status);
}
+5 -4
Ver Arquivo
@@ -7,8 +7,10 @@
#include <magenta/dispatcher.h>
#include <magenta/state_tracker.h>
#include <arch/ops.h>
//#include <arch/ops.h>
#if WITH_LIB_KTRACE
#include <lib/ktrace.h>
#endif
#include <mxtl/atomic.h>
// The first 1K koids are reserved.
@@ -29,7 +31,7 @@ Dispatcher::~Dispatcher() {
#endif
}
status_t Dispatcher::add_observer(StateObserver* observer) {
mx_status_t Dispatcher::add_observer(StateObserver* observer) {
auto state_tracker = get_state_tracker();
if (!state_tracker)
return MX_ERR_NOT_SUPPORTED;
@@ -37,7 +39,7 @@ status_t Dispatcher::add_observer(StateObserver* observer) {
return MX_OK;
}
status_t Dispatcher::user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer) {
mx_status_t Dispatcher::user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer) {
if (peer)
return MX_ERR_NOT_SUPPORTED;
@@ -53,4 +55,3 @@ status_t Dispatcher::user_signal(uint32_t clear_mask, uint32_t set_mask, bool pe
state_tracker->UpdateState(clear_mask, set_mask);
return MX_OK;
}
+2 -2
Ver Arquivo
@@ -14,7 +14,7 @@
constexpr uint32_t kUserSignalMask = MX_EVENT_SIGNALED | MX_USER_SIGNAL_ALL;
status_t EventDispatcher::Create(uint32_t options, mxtl::RefPtr<Dispatcher>* dispatcher,
mx_status_t EventDispatcher::Create(uint32_t options, mxtl::RefPtr<Dispatcher>* dispatcher,
mx_rights_t* rights) {
mxtl::AllocChecker ac;
auto disp = new (&ac) EventDispatcher(options);
@@ -31,7 +31,7 @@ EventDispatcher::EventDispatcher(uint32_t options)
EventDispatcher::~EventDispatcher() {}
status_t EventDispatcher::user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer) {
mx_status_t EventDispatcher::user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer) {
canary_.Assert();
if (peer)
@@ -25,15 +25,15 @@ class ChannelDispatcher final : public Dispatcher {
public:
class MessageWaiter;
static status_t Create(uint32_t flags, mxtl::RefPtr<Dispatcher>* dispatcher0,
mxtl::RefPtr<Dispatcher>* dispatcher1, mx_rights_t* rights);
static mx_status_t Create(uint32_t flags, mxtl::RefPtr<Dispatcher>* dispatcher0,
mxtl::RefPtr<Dispatcher>* dispatcher1, mx_rights_t* rights);
~ChannelDispatcher() final;
mx_obj_type_t get_type() const final { return MX_OBJ_TYPE_CHANNEL; }
StateTracker* get_state_tracker() final { return &state_tracker_; }
mx_status_t add_observer(StateObserver* observer) final;
mx_koid_t get_related_koid() const final TA_REQ(lock_) { return other_koid_; }
status_t user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer) final;
mx_status_t user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer) final;
void on_zero_handles() final;
@@ -42,21 +42,21 @@ public:
// size and handle count, respectively. On MX_OK or MX_ERR_BUFFER_TOO_SMALL, they specify the
// actual size and handle count of the next message. The next message is returned in |*msg| on
// MX_OK and also on MX_ERR_BUFFER_TOO_SMALL when |may_discard| is set.
status_t Read(uint32_t* msg_size,
uint32_t* msg_handle_count,
mxtl::unique_ptr<MessagePacket>* msg,
bool may_disard);
mx_status_t Read(uint32_t* msg_size,
uint32_t* msg_handle_count,
mxtl::unique_ptr<MessagePacket>* msg,
bool may_disard);
// Write to the opposing endpoint's message queue.
status_t Write(mxtl::unique_ptr<MessagePacket> msg);
status_t Call(mxtl::unique_ptr<MessagePacket> msg,
mx_time_t deadline, bool* return_handles,
mxtl::unique_ptr<MessagePacket>* reply);
mx_status_t Write(mxtl::unique_ptr<MessagePacket> msg);
mx_status_t Call(mxtl::unique_ptr<MessagePacket> msg,
mx_time_t deadline, bool* return_handles,
mxtl::unique_ptr<MessagePacket>* reply);
// Performs the wait-then-read half of Call. This is meant for retrying
// after an interruption caused by suspending.
status_t ResumeInterruptedCall(MessageWaiter* waiter, mx_time_t deadline,
mxtl::unique_ptr<MessagePacket>* reply);
mx_status_t ResumeInterruptedCall(MessageWaiter* waiter, mx_time_t deadline,
mxtl::unique_ptr<MessagePacket>* reply);
// MessageWaiter's state is guarded by the lock of the
// owning ChannelDispatcher, and Deliver(), Signal(), Cancel(),
@@ -70,14 +70,15 @@ public:
// See also: comments in ChannelDispatcher::Call()
class MessageWaiter : public mxtl::DoublyLinkedListable<MessageWaiter*> {
public:
MessageWaiter() : txid_(0), status_(MX_ERR_BAD_STATE) {
MessageWaiter()
: txid_(0), status_(MX_ERR_BAD_STATE) {
}
~MessageWaiter();
mx_status_t BeginWait(mxtl::RefPtr<ChannelDispatcher> channel, mx_txid_t txid);
int Deliver(mxtl::unique_ptr<MessagePacket> msg);
int Cancel(status_t status);
int Cancel(mx_status_t status);
mxtl::RefPtr<ChannelDispatcher> get_channel() { return channel_; }
mx_txid_t get_txid() const { return txid_; }
mx_status_t Wait(lk_time_t deadline);
@@ -103,7 +104,7 @@ private:
ChannelDispatcher(uint32_t flags);
void Init(mxtl::RefPtr<ChannelDispatcher> other);
int WriteSelf(mxtl::unique_ptr<MessagePacket> msg);
status_t UserSignalSelf(uint32_t clear_mask, uint32_t set_mask);
mx_status_t UserSignalSelf(uint32_t clear_mask, uint32_t set_mask);
void OnPeerZeroHandles();
mxtl::Canary<mxtl::magic("CHAN")> canary_;
+3 -3
Ver Arquivo
@@ -67,9 +67,9 @@ public:
virtual StateTracker* get_state_tracker() { return nullptr; }
virtual status_t add_observer(StateObserver* observer);
virtual mx_status_t add_observer(StateObserver* observer);
virtual status_t user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer);
virtual mx_status_t user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer);
virtual void on_zero_handles() { }
@@ -81,7 +81,7 @@ public:
// set_name() will truncate to MX_MAX_NAME_LEN - 1 and ensure there is a
// terminating null
virtual status_t set_name(const char* name, size_t len) { return MX_ERR_NOT_SUPPORTED; }
virtual mx_status_t set_name(const char* name, size_t len) { return MX_ERR_NOT_SUPPORTED; }
// Dispatchers that support get/set cookie must provide
// a CookieJar for those cookies to be stored in.
@@ -6,22 +6,21 @@
#pragma once
#include <magenta/types.h>
#include <magenta/dispatcher.h>
#include <magenta/state_tracker.h>
#include <mxtl/canary.h>
#include <sys/types.h>
class EventDispatcher final : public Dispatcher {
public:
static status_t Create(uint32_t options, mxtl::RefPtr<Dispatcher>* dispatcher,
static mx_status_t Create(uint32_t options, mxtl::RefPtr<Dispatcher>* dispatcher,
mx_rights_t* rights);
~EventDispatcher() final;
mx_obj_type_t get_type() const final { return MX_OBJ_TYPE_EVENT; }
StateTracker* get_state_tracker() final { return &state_tracker_; }
CookieJar* get_cookie_jar() final { return &cookie_jar_; }
status_t user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer) final;
mx_status_t user_signal(uint32_t clear_mask, uint32_t set_mask, bool peer) final;
private:
explicit EventDispatcher(uint32_t options);
@@ -8,13 +8,17 @@
#include <stdint.h>
#include <kernel/spinlock.h>
#include <magenta/state_observer.h>
#include <magenta/types.h>
#include <mxtl/canary.h>
#include <mxtl/intrusive_double_list.h>
#include <mxtl/mutex.h>
#undef TA_GUARDED
#undef TA_REQ
#define TA_GUARDED(args...) /**/
#define TA_REQ(args...) /**/
class Handle;
class CookieJar {
+6
Ver Arquivo
@@ -9,6 +9,12 @@
#include <mxtl/auto_lock.h>
#include <mxtl/mutex.h>
#ifndef _KERNEL
#include <magenta/assert.h>
#define DEBUG_ASSERT(args...) MX_DEBUG_ASSERT(args)
#define thread_reschedule() ((void)0)
#endif
using mxtl::AutoLock;
namespace {
+63
Ver Arquivo
@@ -0,0 +1,63 @@
#include <inttypes.h>
#include <mxcpp/new.h>
// kernel/lib/magenta
#include <magenta/channel_dispatcher.h>
#include <magenta/dispatcher.h>
#include <magenta/event_dispatcher.h>
#include <magenta/handle.h>
#include <magenta/handle_reaper.h>
#include <magenta/state_tracker.h>
// system/public/magenta
#include <magenta/types.h>
Handle* MakeHandle(mxtl::RefPtr<Dispatcher> dispatcher, mx_rights_t rights) {
return new Handle(dispatcher, rights, /*base_value=*/0x55555555);
}
namespace internal {
void TearDownHandle(Handle* handle) {
delete handle;
}
} // namespace internal
void DeleteHandle(Handle* handle) {
mxtl::RefPtr<Dispatcher> dispatcher(handle->dispatcher());
auto state_tracker = dispatcher->get_state_tracker();
if (state_tracker) {
state_tracker->Cancel(handle);
}
internal::TearDownHandle(handle);
}
void ReapHandles(Handle** handles, uint32_t num_handles) {
while (num_handles > 0) {
DeleteHandle(*handles++);
}
}
int main(int argc, char** argv) {
Handle* h = MakeHandle(nullptr, MX_RIGHT_READ);
StateTracker st(0x5);
printf("signals 0x%x\n", st.GetSignalsState());
printf("rights 0x%x\n", h->rights());
mxtl::RefPtr<Dispatcher> ev;
mx_rights_t evr;
EventDispatcher::Create(0u, &ev, &evr);
printf("ev koid %" PRIu64 "\n", ev->get_koid());
mxtl::RefPtr<Dispatcher> ch0;
mxtl::RefPtr<Dispatcher> ch1;
mx_rights_t chr;
ChannelDispatcher::Create(0u, &ch0, &ch1, &chr);
printf("ch0 koid %" PRIu64 ", related %" PRIu64 "\n",
ch0->get_koid(), ch0->get_related_koid());
printf("ch1 koid %" PRIu64 ", related %" PRIu64 "\n",
ch1->get_koid(), ch1->get_related_koid());
return 0;
}
+37
Ver Arquivo
@@ -0,0 +1,37 @@
# Copyright 2016 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_TYPE := usertest
MODULE_NAME := magenta-isolate-test
MODULE_SRCS += \
$(LOCAL_DIR)/main.cpp \
kernel/lib/magenta/dispatcher.cpp \
kernel/lib/magenta/channel_dispatcher.cpp \
kernel/lib/magenta/event_dispatcher.cpp \
kernel/lib/magenta/message_packet.cpp \
kernel/lib/magenta/handle.cpp \
kernel/lib/magenta/state_tracker.cpp
MODULE_LIBS := \
system/ulib/unittest \
system/ulib/mxio \
system/ulib/magenta \
system/ulib/c
MODULE_STATIC_LIBS := \
system/ulib/mxcpp \
system/ulib/mxtl
MODULE_COMPILEFLAGS := \
-Ikernel/lib/magenta/include \
-Isystem/ulib/mxtl/include \
-I$(LOCAL_DIR)/stub-include
include make/module.mk
@@ -0,0 +1,39 @@
// Copyright 2017 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
#pragma once
#include <magenta/types.h>
typedef uint64_t lk_time_t;
class Event {
public:
Event(uint32_t opts = 0) {
}
~Event() {
}
Event(const Event&) = delete;
Event& operator=(const Event&) = delete;
// Returns:
// MX_OK - signaled
// MX_ERR_TIMED_OUT - time out expired
// MX_ERR_INTERNAL_INTR_KILLED - thread killed
// Or the |status| which the caller specified in Event::Signal(status)
mx_status_t Wait(lk_time_t deadline) {
return MX_OK;
}
// returns number of ready threads
int Signal(mx_status_t status = MX_OK) {
return MX_OK;
}
mx_status_t Unsignal() {
return MX_OK;
}
};
@@ -0,0 +1,108 @@
// Copyright 2017 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
#pragma once
#include <string.h>
#include <mxtl/type_support.h>
#include <magenta/types.h>
namespace internal {
template <typename T> inline constexpr size_t type_size() { return sizeof(T); }
template <> inline constexpr size_t type_size<void>() { return 1u; }
template <> inline constexpr size_t type_size<const void>() { return 1u; }
template <> inline constexpr size_t type_size<volatile void>() { return 1u; }
template <> inline constexpr size_t type_size<const volatile void>() { return 1u; }
}
template <typename T>
class user_ptr {
private:
static mx_status_t copy_to_user_unsafe(void *dst, const void* src, size_t size) {
memcpy(dst, src, size);
return MX_OK;
}
static mx_status_t copy_from_user_unsafe(void *dst, const void* src, size_t size) {
memcpy(dst, src, size);
return MX_OK;
}
public:
explicit user_ptr(T* p) : ptr_(p) {}
T* get() const { return ptr_; }
template <typename C>
user_ptr<C> reinterpret() const { return user_ptr<C>(reinterpret_cast<C*>(ptr_)); }
// special operator to return the nullness of the pointer
explicit operator bool() const { return ptr_ != nullptr; }
// Returns a user_ptr pointing to the |index|-th element from this one, or a null user_ptr if
// this pointer is null. Note: This does no other validation, and the behavior is undefined on
// overflow. (Using this will fail to compile if T is |void|.)
user_ptr element_offset(size_t index) const {
return ptr_ ? user_ptr(ptr_ + index) : user_ptr(nullptr);
}
// Returns a user_ptr offset by |offset| bytes from this one.
user_ptr byte_offset(size_t offset) const {
return ptr_ ? user_ptr(reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr_) + offset))
: user_ptr(nullptr);
}
// Copies a single T to user memory. (Using this will fail to compile if T is |void|.)
// Note: The templatization is simply to allow the class to compile if T is |void|.
template <typename S = T>
mx_status_t copy_to_user(const S& src) const {
static_assert(mxtl::is_same<S, T>::value, "Do not use the template parameter.");
return copy_to_user_unsafe(ptr_, &src, sizeof(S));
}
// Copies an array of T to user memory. Note: This takes a count not a size, unless T is |void|.
// WARNING: This does not check that |count| is reasonable (i.e., that multiplication won't
// overflow).
mx_status_t copy_array_to_user(const T* src, size_t count) const {
return copy_to_user_unsafe(ptr_, src, count * internal::type_size<T>());
}
// Copies an array of T to user memory. Note: This takes a count not a size, unless T is |void|.
// WARNING: This does not check that |count| is reasonable (i.e., that multiplication won't
// overflow).
mx_status_t copy_array_to_user(const T* src, size_t count, size_t offset) const {
return copy_to_user_unsafe(ptr_ + offset, src, count * internal::type_size<T>());
}
// Copies a single T from user memory. (Using this will fail to compile if T is |void|.)
mx_status_t copy_from_user(typename mxtl::remove_const<T>::type* dst) const {
// Intentionally use sizeof(T) here, so *using* this method won't compile if T is |void|.
return copy_from_user_unsafe(dst, ptr_, sizeof(T));
}
// Copies an array of T from user memory. Note: This takes a count not a size, unless T is
// |void|.
// WARNING: This does not check that |count| is reasonable (i.e., that multiplication won't
// overflow).
mx_status_t copy_array_from_user(typename mxtl::remove_const<T>::type* dst, size_t count) const {
return copy_from_user_unsafe(dst, ptr_, count * internal::type_size<T>());
}
// Copies a sub-array of T from user memory. Note: This takes a count not a size, unless T is
// |void|.
// WARNING: This does not check that |count| is reasonable (i.e., that multiplication won't
// overflow).
mx_status_t copy_array_from_user(typename mxtl::remove_const<T>::type* dst, size_t count, size_t offset) const {
return copy_from_user_unsafe(dst, ptr_ + offset, count * internal::type_size<T>());
}
private:
// It is very important that this class only wrap the pointer type itself
// and not include any other members so as not to break the ABI between
// the kernel and user space.
T* const ptr_;
};
template <typename T>
user_ptr<T> make_user_ptr(T* p) { return user_ptr<T>(p); }