Skip to content

runtime,reflect: clean duplicate definitions runtimeSelect #65396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/cmd/internal/objabi/pkgspecial.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ var runtimePkgs = []string{
"internal/godebugs",
"internal/goexperiment",
"internal/goos",

"internal/runtime/rchan",
}

// extraNoInstrumentPkgs is the set of packages in addition to runtimePkgs that
Expand Down
5 changes: 3 additions & 2 deletions src/go/build/deps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ var depsRules = `
internal/goarch, unsafe
< internal/abi, internal/chacha8rand;

unsafe < maps;
unsafe < maps, internal/runtime/rchan;

# RUNTIME is the core runtime group of packages, all of them very light-weight.
internal/abi,
Expand All @@ -63,6 +63,7 @@ var depsRules = `
internal/cpu,
internal/goarch,
internal/godebugs,
internal/runtime/rchan,
internal/goexperiment,
internal/goos
< internal/bytealg
Expand Down Expand Up @@ -189,7 +190,7 @@ var depsRules = `

# FMT is OS (which includes string routines) plus reflect and fmt.
# It does not include package log, which should be avoided in core packages.
arena, strconv, unicode
arena, strconv, unicode, internal/runtime/rchan
< reflect;

os, reflect
Expand Down
27 changes: 27 additions & 0 deletions src/internal/runtime/rchan/select.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2024 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 rchan

import (
"unsafe"
)

// A Select is a single case passed to rselect.
type Select struct {
Dir SelectDir
Typ unsafe.Pointer // channel type (not used here)
Ch unsafe.Pointer // channel
Val unsafe.Pointer // ptr to data (SendDir) or ptr to receive buffer (RecvDir)
}

// These values must match ../reflect/value.go:/SelectDir.
type SelectDir int

const (
_ SelectDir = iota
SelectSend // case Chan <- Send
SelectRecv // case <-Chan:
SelectDefault // default
)
42 changes: 17 additions & 25 deletions src/reflect/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"internal/abi"
"internal/goarch"
"internal/itoa"
"internal/runtime/rchan"
"internal/unsafeheader"
"math"
"runtime"
Expand Down Expand Up @@ -3015,15 +3016,6 @@ func Copy(dst, src Value) int {
return typedslicecopy(de.Common(), ds, ss)
}

// A runtimeSelect is a single case passed to rselect.
// This must match ../runtime/select.go:/runtimeSelect
type runtimeSelect struct {
dir SelectDir // SelectSend, SelectRecv or SelectDefault
typ *rtype // channel type
ch unsafe.Pointer // channel
val unsafe.Pointer // ptr to data (SendDir) or ptr to receive buffer (RecvDir)
}

// rselect runs a select. It returns the index of the chosen case.
// If the case was a receive, val is filled in with the received value.
// The conventional OK bool indicates whether the receive corresponds
Expand All @@ -3035,7 +3027,7 @@ type runtimeSelect struct {
// that with a forced escape in function Select.
//
//go:noescape
func rselect([]runtimeSelect) (chosen int, recvOK bool)
func rselect([]rchan.Select) (chosen int, recvOK bool)

// A SelectDir describes the communication direction of a select case.
type SelectDir int
Expand Down Expand Up @@ -3086,19 +3078,19 @@ func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
// NOTE: Do not trust that caller is not modifying cases data underfoot.
// The range is safe because the caller cannot modify our copy of the len
// and each iteration makes its own copy of the value c.
var runcases []runtimeSelect
var runcases []rchan.Select
if len(cases) > 4 {
// Slice is heap allocated due to runtime dependent capacity.
runcases = make([]runtimeSelect, len(cases))
runcases = make([]rchan.Select, len(cases))
} else {
// Slice can be stack allocated due to constant capacity.
runcases = make([]runtimeSelect, len(cases), 4)
runcases = make([]rchan.Select, len(cases), 4)
}

haveDefault := false
for i, c := range cases {
rc := &runcases[i]
rc.dir = c.Dir
rc.Dir = rchan.SelectDir(c.Dir)
switch c.Dir {
default:
panic("reflect.Select: invalid Dir")
Expand Down Expand Up @@ -3126,22 +3118,22 @@ func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
if ChanDir(tt.Dir)&SendDir == 0 {
panic("reflect.Select: SendDir case using recv-only channel")
}
rc.ch = ch.pointer()
rc.typ = toRType(&tt.Type)
rc.Ch = ch.pointer()
rc.Typ = (unsafe.Pointer)(&tt.Type)
v := c.Send
if !v.IsValid() {
panic("reflect.Select: SendDir case missing Send value")
}
v.mustBeExported()
v = v.assignTo("reflect.Select", tt.Elem, nil)
if v.flag&flagIndir != 0 {
rc.val = v.ptr
rc.Val = v.ptr
} else {
rc.val = unsafe.Pointer(&v.ptr)
rc.Val = unsafe.Pointer(&v.ptr)
}
// The value to send needs to escape. See the comment at rselect for
// why we need forced escape.
escapes(rc.val)
escapes(rc.Val)

case SelectRecv:
if c.Send.IsValid() {
Expand All @@ -3157,17 +3149,17 @@ func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
if ChanDir(tt.Dir)&RecvDir == 0 {
panic("reflect.Select: RecvDir case using send-only channel")
}
rc.ch = ch.pointer()
rc.typ = toRType(&tt.Type)
rc.val = unsafe_New(tt.Elem)
rc.Ch = ch.pointer()
rc.Typ = (unsafe.Pointer)(&tt.Type)
rc.Val = unsafe_New(tt.Elem)
}
}

chosen, recvOK = rselect(runcases)
if runcases[chosen].dir == SelectRecv {
tt := (*chanType)(unsafe.Pointer(runcases[chosen].typ))
if runcases[chosen].Dir == rchan.SelectDir(SelectRecv) {
tt := (*chanType)(unsafe.Pointer(runcases[chosen].Typ))
t := tt.Elem
p := runcases[chosen].val
p := runcases[chosen].Val
fl := flag(t.Kind())
if t.IfaceIndir() {
recv = Value{t, p, fl | flagIndir}
Expand Down
32 changes: 7 additions & 25 deletions src/runtime/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package runtime

import (
"internal/abi"
"internal/runtime/rchan"
"unsafe"
)

Expand Down Expand Up @@ -519,27 +520,8 @@ func (c *hchan) sortkey() uintptr {
return uintptr(unsafe.Pointer(c))
}

// A runtimeSelect is a single case passed to rselect.
// This must match ../reflect/value.go:/runtimeSelect
type runtimeSelect struct {
dir selectDir
typ unsafe.Pointer // channel type (not used here)
ch *hchan // channel
val unsafe.Pointer // ptr to data (SendDir) or ptr to receive buffer (RecvDir)
}

// These values must match ../reflect/value.go:/SelectDir.
type selectDir int

const (
_ selectDir = iota
selectSend // case Chan <- Send
selectRecv // case <-Chan:
selectDefault // default
)

//go:linkname reflect_rselect reflect.rselect
func reflect_rselect(cases []runtimeSelect) (int, bool) {
func reflect_rselect(cases []rchan.Select) (int, bool) {
if len(cases) == 0 {
block()
}
Expand All @@ -549,19 +531,19 @@ func reflect_rselect(cases []runtimeSelect) (int, bool) {
dflt := -1
for i, rc := range cases {
var j int
switch rc.dir {
case selectDefault:
switch rc.Dir {
case rchan.SelectDefault:
dflt = i
continue
case selectSend:
case rchan.SelectSend:
j = nsends
nsends++
case selectRecv:
case rchan.SelectRecv:
nrecvs++
j = len(cases) - nrecvs
}

sel[j] = scase{c: rc.ch, elem: rc.val}
sel[j] = scase{c: (*hchan)(rc.Ch), elem: rc.Val}
orig[j] = i
}

Expand Down