WIP(dispatcher-isolate): Dispatchers in userspace
Would really help with unit testing
Can currently instatiate (with stubs)
- ChannelDispatcher
- EventDispatcher
- Handle
- StateTracker
Change-Id: I478374e16682aaecc7b7750c710b83a686fd067c
diff --git a/kernel/object/channel_dispatcher.cpp b/kernel/object/channel_dispatcher.cpp
index 5d4ec58..61a6836 100644
--- a/kernel/object/channel_dispatcher.cpp
+++ b/kernel/object/channel_dispatcher.cpp
@@ -10,14 +10,16 @@
#include <assert.h>
#include <err.h>
-#include <trace.h>
+//#include <trace.h>
#include <kernel/event.h>
-#include <platform.h>
+//#include <platform.h>
#include <object/handle.h>
#include <object/message_packet.h>
+#ifdef _KERNEL
#include <object/process_dispatcher.h>
#include <object/thread_dispatcher.h>
+#endif
#include <zircon/rights.h>
#include <fbl/alloc_checker.h>
@@ -28,6 +30,12 @@
#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
zx_status_t ChannelDispatcher::Create(fbl::RefPtr<Dispatcher>* dispatcher0,
fbl::RefPtr<Dispatcher>* dispatcher1,
@@ -187,11 +195,17 @@
canary_.Assert();
+#ifdef _KERNEL
auto waiter = ThreadDispatcher::GetCurrent()->GetMessageWaiter();
+#else
+ MessageWaiter *waiter = nullptr;
+#endif
if (unlikely(waiter->BeginWait(fbl::WrapRefPtr(this), msg->get_txid()) != ZX_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 ZX_ERR_BAD_STATE;
}
@@ -314,7 +328,7 @@
if (unlikely(channel_)) {
channel_->RemoveWaiter(this);
}
- DEBUG_ASSERT(!InContainer());
+ ZX_DEBUG_ASSERT(!InContainer());
}
zx_status_t ChannelDispatcher::MessageWaiter::BeginWait(fbl::RefPtr<ChannelDispatcher> channel,
@@ -322,7 +336,7 @@
if (unlikely(channel_)) {
return ZX_ERR_BAD_STATE;
}
- DEBUG_ASSERT(!InContainer());
+ ZX_DEBUG_ASSERT(!InContainer());
txid_ = txid;
status_ = ZX_ERR_TIMED_OUT;
@@ -332,7 +346,7 @@
}
int ChannelDispatcher::MessageWaiter::Deliver(fbl::unique_ptr<MessagePacket> msg) {
- DEBUG_ASSERT(channel_);
+ ZX_DEBUG_ASSERT(channel_);
msg_ = fbl::move(msg);
status_ = ZX_OK;
@@ -340,8 +354,8 @@
}
int ChannelDispatcher::MessageWaiter::Cancel(zx_status_t status) {
- DEBUG_ASSERT(!InContainer());
- DEBUG_ASSERT(channel_);
+ ZX_DEBUG_ASSERT(!InContainer());
+ ZX_DEBUG_ASSERT(channel_);
status_ = status;
return event_.Signal(status);
}
diff --git a/kernel/object/dispatcher.cpp b/kernel/object/dispatcher.cpp
index 58d5f70..172ab3a 100644
--- a/kernel/object/dispatcher.cpp
+++ b/kernel/object/dispatcher.cpp
@@ -6,8 +6,10 @@
#include <object/dispatcher.h>
-#include <arch/ops.h>
+//#include <arch/ops.h>
+#if WITH_LIB_KTRACE
#include <lib/ktrace.h>
+#endif
#include <fbl/atomic.h>
#include <object/state_tracker.h>
diff --git a/kernel/object/include/object/event_dispatcher.h b/kernel/object/include/object/event_dispatcher.h
index 7cbb6b9..1d2fabb 100644
--- a/kernel/object/include/object/event_dispatcher.h
+++ b/kernel/object/include/object/event_dispatcher.h
@@ -11,8 +11,6 @@
#include <object/dispatcher.h>
#include <object/state_tracker.h>
-#include <sys/types.h>
-
class EventDispatcher final : public Dispatcher {
public:
static zx_status_t Create(uint32_t options, fbl::RefPtr<Dispatcher>* dispatcher,
diff --git a/kernel/object/include/object/state_tracker.h b/kernel/object/include/object/state_tracker.h
index a9f1d02..b4ce3e8 100644
--- a/kernel/object/include/object/state_tracker.h
+++ b/kernel/object/include/object/state_tracker.h
@@ -8,13 +8,17 @@
#include <stdint.h>
-#include <kernel/spinlock.h>
#include <zircon/types.h>
#include <fbl/canary.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/mutex.h>
#include <object/state_observer.h>
+#undef TA_GUARDED
+#undef TA_REQ
+#define TA_GUARDED(args...) /**/
+#define TA_REQ(args...) /**/
+
class Handle;
class CookieJar {
diff --git a/kernel/object/state_tracker.cpp b/kernel/object/state_tracker.cpp
index 327bb660..b08a2cc 100644
--- a/kernel/object/state_tracker.cpp
+++ b/kernel/object/state_tracker.cpp
@@ -9,6 +9,12 @@
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
+#ifndef _KERNEL
+#include <zircon/assert.h>
+#define DEBUG_ASSERT(args...) ZX_DEBUG_ASSERT(args)
+#define thread_reschedule() ((void)0)
+#endif
+
using fbl::AutoLock;
namespace {
diff --git a/system/utest/dispatcher-isolate/main.cpp b/system/utest/dispatcher-isolate/main.cpp
new file mode 100644
index 0000000..90f3f74
--- /dev/null
+++ b/system/utest/dispatcher-isolate/main.cpp
@@ -0,0 +1,61 @@
+#include <inttypes.h>
+
+#include <zxcpp/new.h>
+
+#include <object/channel_dispatcher.h>
+#include <object/dispatcher.h>
+#include <object/event_dispatcher.h>
+#include <object/handle.h>
+#include <object/handle_reaper.h>
+#include <object/state_tracker.h>
+
+#include <zircon/types.h>
+
+Handle* MakeHandle(fbl::RefPtr<Dispatcher> dispatcher, zx_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) {
+ fbl::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, ZX_RIGHT_READ);
+ StateTracker st(0x5);
+ printf("signals 0x%x\n", st.GetSignalsState());
+ printf("rights 0x%x\n", h->rights());
+
+ fbl::RefPtr<Dispatcher> ev;
+ zx_rights_t evr;
+ EventDispatcher::Create(0u, &ev, &evr);
+ printf("ev koid %" PRIu64 "\n", ev->get_koid());
+
+ fbl::RefPtr<Dispatcher> ch0;
+ fbl::RefPtr<Dispatcher> ch1;
+ zx_rights_t chr;
+ ChannelDispatcher::Create(&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;
+}
diff --git a/system/utest/dispatcher-isolate/rules.mk b/system/utest/dispatcher-isolate/rules.mk
new file mode 100644
index 0000000..bc8030a
--- /dev/null
+++ b/system/utest/dispatcher-isolate/rules.mk
@@ -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 := dispatcher-isolate-test
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/main.cpp \
+ kernel/object/dispatcher.cpp \
+ kernel/object/channel_dispatcher.cpp \
+ kernel/object/event_dispatcher.cpp \
+ kernel/object/message_packet.cpp \
+ kernel/object/handle.cpp \
+ kernel/object/state_tracker.cpp
+
+MODULE_LIBS := \
+ system/ulib/unittest \
+ system/ulib/fdio \
+ system/ulib/zircon \
+ system/ulib/c
+
+MODULE_STATIC_LIBS := \
+ system/ulib/zxcpp \
+ system/ulib/fbl
+
+MODULE_COMPILEFLAGS := \
+ -Ikernel/object/include \
+ -Isystem/ulib/fbl/include \
+ -I$(LOCAL_DIR)/stub-include
+
+include make/module.mk
diff --git a/system/utest/dispatcher-isolate/stub-include/kernel/event.h b/system/utest/dispatcher-isolate/stub-include/kernel/event.h
new file mode 100644
index 0000000..162e37c
--- /dev/null
+++ b/system/utest/dispatcher-isolate/stub-include/kernel/event.h
@@ -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 <zircon/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:
+ // ZX_OK - signaled
+ // ZX_ERR_TIMED_OUT - time out expired
+ // ZX_ERR_INTERNAL_INTR_KILLED - thread killed
+ // Or the |status| which the caller specified in Event::Signal(status)
+ zx_status_t Wait(lk_time_t deadline) {
+ return ZX_OK;
+ }
+
+ // returns number of ready threads
+ int Signal(zx_status_t status = ZX_OK) {
+ return ZX_OK;
+ }
+
+ zx_status_t Unsignal() {
+ return ZX_OK;
+ }
+};
diff --git a/system/utest/dispatcher-isolate/stub-include/lib/user_copy/user_ptr.h b/system/utest/dispatcher-isolate/stub-include/lib/user_copy/user_ptr.h
new file mode 100644
index 0000000..52716c0
--- /dev/null
+++ b/system/utest/dispatcher-isolate/stub-include/lib/user_copy/user_ptr.h
@@ -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 <fbl/type_support.h>
+#include <zircon/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 zx_status_t copy_to_user_unsafe(void *dst, const void* src, size_t size) {
+ memcpy(dst, src, size);
+ return ZX_OK;
+ }
+ static zx_status_t copy_from_user_unsafe(void *dst, const void* src, size_t size) {
+ memcpy(dst, src, size);
+ return ZX_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>
+ zx_status_t copy_to_user(const S& src) const {
+ static_assert(fbl::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).
+ zx_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).
+ zx_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|.)
+ zx_status_t copy_from_user(typename fbl::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).
+ zx_status_t copy_array_from_user(typename fbl::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).
+ zx_status_t copy_array_from_user(typename fbl::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); }