[fuchsia] Expose installed threads
Expose runtime thread handles so applications can use it to change their
thread profile.
Bug: 99550
Change-Id: I106479855a81492b0e41bcb1703d0bc325e0d734
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/go/+/675204
Reviewed-by: Tamir Duberstein <tamird@google.com>
Fuchsia-Auto-Submit: Bruno Dal Bo <brunodalbo@google.com>
Commit-Queue: Bruno Dal Bo <brunodalbo@google.com>
diff --git a/src/runtime/os_fuchsia.go b/src/runtime/os_fuchsia.go
index d0313eb..048f409 100644
--- a/src/runtime/os_fuchsia.go
+++ b/src/runtime/os_fuchsia.go
@@ -71,6 +71,12 @@
//go:linkname zx_set_namespace syscall/zx.set_namespace
func zx_set_namespace(m map[string]uint32)
+//go:linkname zx_notify_new_thread syscall/zx.notify_new_thread
+func zx_notify_new_thread(h uint32)
+
+//go:linkname zx_init_threads_channel syscall/zx.init_threads_channel
+func zx_init_threads_channel()
+
type mOS struct {
thread uint32
threadKoid uint64
@@ -223,6 +229,8 @@
}
}
+var critlock mutex
+
func osinit() {
if _cgo_get_initial_handles != nil {
cgocallm0(_cgo_get_initial_handles, unsafe.Pointer(&fdioHandles))
@@ -231,6 +239,7 @@
}
zx_set_proc_handle(fdioHandles.processSelf)
zx_set_vmar_root(fdioHandles.vmarRootSelf)
+ zx_init_threads_channel()
} else {
println("runtime: fuchsia requires cgo")
exit(2)
@@ -276,6 +285,7 @@
return
}
getg().m.threadKoid = info.koid
+ zx_notify_new_thread(h)
}
//go:nosplit
diff --git a/src/syscall/zx/threads_fuchsia.go b/src/syscall/zx/threads_fuchsia.go
new file mode 100644
index 0000000..471206e
--- /dev/null
+++ b/src/syscall/zx/threads_fuchsia.go
@@ -0,0 +1,51 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package zx
+
+var threadQueue struct {
+ writer, reader Channel
+}
+
+// init_threads_channel initializes a channel that is used as buffer for created
+// threads. It must be called from os_init.
+//go:nosplit
+func init_threads_channel() {
+ if status := Sys_channel_create(0, threadQueue.writer.Handle(), threadQueue.reader.Handle()); status != ErrOk {
+ // NB: Can't allocate here to provide status in panic.
+ panic("failed to create threads channel")
+ }
+}
+
+// notify_new_thread is called by the runtime whenever a new thread is created
+// in minit. It duplicates the handle and stores it in the threadQueue channels
+// for later retrieval by applications. We can't do anything a lot fancier than
+// this because minit is a very restricted environment where we can't allocate
+// or do much else.
+//go:nosplit
+func notify_new_thread(h int32) {
+ var dup Handle
+ // Duplicate the handle with the minimum set of rights to accomplish
+ // exposing the threads to applications and installing thread profiles.
+ if status := Sys_handle_duplicate(Handle(h), RightManageThread|RightTransfer, &dup); status != ErrOk {
+ // NB: Can't allocate here to provide status in panic.
+ panic("failed to duplicate thread handle")
+ }
+ if status := Sys_channel_write(threadQueue.writer.Handle().Load(), 0, nil, 0, &dup, 1); status != ErrOk {
+ // NB: Can't allocate here to provide status in panic.
+ panic("failed to write thread to channel")
+ }
+}
+
+// GetThreadsChannel returns a reference to a channel that will be enqueued with
+// a zero-byte single-handle message for every thread that the runtime creates.
+// Applications may read from this channel to retrieve handles to the runtime
+// threads.
+//
+// Closing this channel may cause the runtime to panic. Writing to this channel
+// has no effect but increasing process memory consumption, there's nothing
+// reading the others side.
+func GetThreadsChannel() *Channel {
+ return &threadQueue.reader
+}