mirror of
https://github.com/golang/go
synced 2024-11-18 13:44:48 -07:00
runtime/internal/atomic: add wasm And/Or operators
In the WebAssembly version of these operators we avoid using
a CAS loop since the Go wasm implementation is single-threaded.
A new test file has been added that has build tags in order to
only test this feature on implemented architectures.
This is part of a series of CLs aimed to add the primitives
for And/Or atomic operations that will be used by the public
sync/atomic apis.
For #61395
Change-Id: Ic67ffefc9cfb626915ea86b6b21b500117710327
GitHub-Last-Rev: bbec3a5f35
GitHub-Pull-Request: golang/go#62517
Reviewed-on: https://go-review.googlesource.com/c/go/+/526656
Run-TryBot: Mauri de Souza Meneguzzo <mauri870@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Auto-Submit: Keith Randall <khr@golang.org>
This commit is contained in:
parent
bfa8a8586f
commit
6ecd5f7504
169
src/runtime/internal/atomic/atomic_andor_test.go
Normal file
169
src/runtime/internal/atomic/atomic_andor_test.go
Normal file
@ -0,0 +1,169 @@
|
||||
// +build wasm
|
||||
//
|
||||
// Copyright 2023 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.
|
||||
|
||||
// TODO(61395): move these tests to atomic_test.go once And/Or have
|
||||
// implementations for all architectures.
|
||||
package atomic_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"runtime/internal/atomic"
|
||||
)
|
||||
|
||||
func TestAnd32(t *testing.T) {
|
||||
// Basic sanity check.
|
||||
x := uint32(0xffffffff)
|
||||
for i := uint32(0); i < 32; i++ {
|
||||
old := x
|
||||
v := atomic.And32(&x, ^(1 << i))
|
||||
if r := uint32(0xffffffff) << (i + 1); x != r || v != old {
|
||||
t.Fatalf("clearing bit %#x: want %#x, got new %#x and old %#v", uint32(1<<i), r, x, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Set every bit in array to 1.
|
||||
a := make([]uint32, 1<<12)
|
||||
for i := range a {
|
||||
a[i] = 0xffffffff
|
||||
}
|
||||
|
||||
// Clear array bit-by-bit in different goroutines.
|
||||
done := make(chan bool)
|
||||
for i := 0; i < 32; i++ {
|
||||
m := ^uint32(1 << i)
|
||||
go func() {
|
||||
for i := range a {
|
||||
atomic.And(&a[i], m)
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
}
|
||||
for i := 0; i < 32; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
// Check that the array has been totally cleared.
|
||||
for i, v := range a {
|
||||
if v != 0 {
|
||||
t.Fatalf("a[%v] not cleared: want %#x, got %#x", i, uint32(0), v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnd64(t *testing.T) {
|
||||
// Basic sanity check.
|
||||
x := uint64(0xffffffffffffffff)
|
||||
for i := uint64(0); i < 64; i++ {
|
||||
old := x
|
||||
v := atomic.And64(&x, ^(1 << i))
|
||||
if r := uint64(0xffffffffffffffff) << (i + 1); x != r || v != old {
|
||||
t.Fatalf("clearing bit %#x: want %#x, got new %#x and old %#v", uint64(1<<i), r, x, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Set every bit in array to 1.
|
||||
a := make([]uint64, 1<<12)
|
||||
for i := range a {
|
||||
a[i] = 0xffffffffffffffff
|
||||
}
|
||||
|
||||
// Clear array bit-by-bit in different goroutines.
|
||||
done := make(chan bool)
|
||||
for i := 0; i < 64; i++ {
|
||||
m := ^uint64(1 << i)
|
||||
go func() {
|
||||
for i := range a {
|
||||
atomic.And64(&a[i], m)
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
}
|
||||
for i := 0; i < 64; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
// Check that the array has been totally cleared.
|
||||
for i, v := range a {
|
||||
if v != 0 {
|
||||
t.Fatalf("a[%v] not cleared: want %#x, got %#x", i, uint64(0), v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOr32(t *testing.T) {
|
||||
// Basic sanity check.
|
||||
x := uint32(0)
|
||||
for i := uint32(0); i < 32; i++ {
|
||||
old := x
|
||||
v := atomic.Or32(&x, 1<<i)
|
||||
if r := (uint32(1) << (i + 1)) - 1; x != r || v != old {
|
||||
t.Fatalf("setting bit %#x: want %#x, got new %#x and old %#v", uint32(1<<i), r, x, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Start with every bit in array set to 0.
|
||||
a := make([]uint32, 1<<12)
|
||||
|
||||
// Set every bit in array bit-by-bit in different goroutines.
|
||||
done := make(chan bool)
|
||||
for i := 0; i < 32; i++ {
|
||||
m := uint32(1 << i)
|
||||
go func() {
|
||||
for i := range a {
|
||||
atomic.Or32(&a[i], m)
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
}
|
||||
for i := 0; i < 32; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
// Check that the array has been totally set.
|
||||
for i, v := range a {
|
||||
if v != 0xffffffff {
|
||||
t.Fatalf("a[%v] not fully set: want %#x, got %#x", i, uint32(0xffffffff), v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOr64(t *testing.T) {
|
||||
// Basic sanity check.
|
||||
x := uint64(0)
|
||||
for i := uint64(0); i < 64; i++ {
|
||||
old := x
|
||||
v := atomic.Or64(&x, 1<<i)
|
||||
if r := (uint64(1) << (i + 1)) - 1; x != r || v != old {
|
||||
t.Fatalf("setting bit %#x: want %#x, got new %#x and old %#v", uint64(1<<i), r, x, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Start with every bit in array set to 0.
|
||||
a := make([]uint64, 1<<12)
|
||||
|
||||
// Set every bit in array bit-by-bit in different goroutines.
|
||||
done := make(chan bool)
|
||||
for i := 0; i < 64; i++ {
|
||||
m := uint64(1 << i)
|
||||
go func() {
|
||||
for i := range a {
|
||||
atomic.Or64(&a[i], m)
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
}
|
||||
for i := 0; i < 64; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
// Check that the array has been totally set.
|
||||
for i, v := range a {
|
||||
if v != 0xffffffffffffffff {
|
||||
t.Fatalf("a[%v] not fully set: want %#x, got %#x", i, uint64(0xffffffffffffffff), v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -339,3 +339,51 @@ func Xaddint64(ptr *int64, delta int64) int64 {
|
||||
*ptr = new
|
||||
return new
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:noinline
|
||||
func And32(ptr *uint32, val uint32) uint32 {
|
||||
old := *ptr
|
||||
*ptr = old & val
|
||||
return old
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:noinline
|
||||
func And64(ptr *uint64, val uint64) uint64 {
|
||||
old := *ptr
|
||||
*ptr = old & val
|
||||
return old
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:noinline
|
||||
func Anduintptr(ptr *uintptr, val uintptr) uintptr {
|
||||
old := *ptr
|
||||
*ptr = old & val
|
||||
return old
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:noinline
|
||||
func Or32(ptr *uint32, val uint32) uint32 {
|
||||
old := *ptr
|
||||
*ptr = old | val
|
||||
return old
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:noinline
|
||||
func Or64(ptr *uint64, val uint64) uint64 {
|
||||
old := *ptr
|
||||
*ptr = old | val
|
||||
return old
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:noinline
|
||||
func Oruintptr(ptr *uintptr, val uintptr) uintptr {
|
||||
old := *ptr
|
||||
*ptr = old | val
|
||||
return old
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user