[release] Snap to b74de2162b
Change-Id: I61a7e4ffffcc372c201e59f8f307fffc345f4688
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
+}