From 8df6829fe951804d69aef2243ee3eb3084b9eeaf Mon Sep 17 00:00:00 2001 From: Adam Magued Date: Fri, 2 Oct 2026 21:24:57 +0000 Subject: [PATCH] gen-atomicint: wrap sync/atomic types to ensure 64-bit alignment on 32-bit platforms On 32-bit architectures (ARM, 386, MIPS), 64-bit atomic operations require 8-byte memory alignment. In uber-go/atomic, Int64 and Duration embedded raw int64 values which have 4-byte alignment on 32-bit architectures, causing unaligned 64-bit atomic panics when embedded in structs. Update internal/gen-atomicint/wrapper.tmpl and generated types to wrap standard library sync/atomic typed atomic types (sync/atomic.Int64, sync/atomic.Uint64, etc.), which embed _ align64 to guarantee 8-byte alignment across 32-bit architectures. Fixes #105 --- bool.go | 2 +- duration.go | 2 +- duration_test.go | 11 +++++++++++ error.go | 2 +- float32.go | 2 +- float64.go | 2 +- float64_test.go | 11 +++++++++++ int32.go | 22 +++++++++++++--------- int64.go | 22 +++++++++++++--------- int64_test.go | 11 +++++++++++ internal/gen-atomicint/wrapper.tmpl | 20 ++++++++++++-------- string.go | 2 +- time.go | 2 +- uint32.go | 22 +++++++++++++--------- uint64.go | 22 +++++++++++++--------- uint64_test.go | 11 +++++++++++ uintptr.go | 22 +++++++++++++--------- 17 files changed, 128 insertions(+), 60 deletions(-) diff --git a/bool.go b/bool.go index f0a2ddd..67a8cd7 100644 --- a/bool.go +++ b/bool.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/duration.go b/duration.go index 7c23868..62d44fd 100644 --- a/duration.go +++ b/duration.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/duration_test.go b/duration_test.go index bc5cea5..2b35d8e 100644 --- a/duration_test.go +++ b/duration_test.go @@ -69,4 +69,15 @@ func TestDuration(t *testing.T) { assert.Equal(t, "42s", NewDuration(42*time.Second).String(), "String() returned an unexpected value.") }) + + t.Run("Alignment", func(t *testing.T) { + type wrapper struct { + _ byte + v Duration + } + s := &wrapper{} + assert.Equal(t, time.Duration(0), s.v.Load()) + s.v.Store(42 * time.Second) + assert.Equal(t, 42*time.Second, s.v.Load()) + }) } diff --git a/error.go b/error.go index b7e3f12..b66c733 100644 --- a/error.go +++ b/error.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/float32.go b/float32.go index 62c3633..8bb71c8 100644 --- a/float32.go +++ b/float32.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/float64.go b/float64.go index 5bc11ca..d55e979 100644 --- a/float64.go +++ b/float64.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/float64_test.go b/float64_test.go index d334de9..44c1d6b 100644 --- a/float64_test.go +++ b/float64_test.go @@ -69,4 +69,15 @@ func TestFloat64(t *testing.T) { assert.Equal(t, "42.5", NewFloat64(42.5).String(), "String() returned an unexpected value.") }) + + t.Run("Alignment", func(t *testing.T) { + type wrapper struct { + _ byte + v Float64 + } + s := &wrapper{} + assert.Equal(t, float64(0), s.v.Load()) + s.v.Store(42.5) + assert.Equal(t, 42.5, s.v.Load()) + }) } diff --git a/int32.go b/int32.go index 5320eac..2f15f1b 100644 --- a/int32.go +++ b/int32.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicint. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,27 +32,31 @@ import ( type Int32 struct { _ nocmp // disallow non-atomic comparison - v int32 + v atomic.Int32 } // NewInt32 creates a new Int32. func NewInt32(val int32) *Int32 { - return &Int32{v: val} + x := &Int32{} + if val != 0 { + x.Store(val) + } + return x } // Load atomically loads the wrapped value. func (i *Int32) Load() int32 { - return atomic.LoadInt32(&i.v) + return i.v.Load() } // Add atomically adds to the wrapped int32 and returns the new value. func (i *Int32) Add(delta int32) int32 { - return atomic.AddInt32(&i.v, delta) + return i.v.Add(delta) } // Sub atomically subtracts from the wrapped int32 and returns the new value. func (i *Int32) Sub(delta int32) int32 { - return atomic.AddInt32(&i.v, -delta) + return i.v.Add(-delta) } // Inc atomically increments the wrapped int32 and returns the new value. @@ -74,17 +78,17 @@ func (i *Int32) CAS(old, new int32) (swapped bool) { // CompareAndSwap is an atomic compare-and-swap. func (i *Int32) CompareAndSwap(old, new int32) (swapped bool) { - return atomic.CompareAndSwapInt32(&i.v, old, new) + return i.v.CompareAndSwap(old, new) } // Store atomically stores the passed value. func (i *Int32) Store(val int32) { - atomic.StoreInt32(&i.v, val) + i.v.Store(val) } // Swap atomically swaps the wrapped int32 and returns the old value. func (i *Int32) Swap(val int32) (old int32) { - return atomic.SwapInt32(&i.v, val) + return i.v.Swap(val) } // MarshalJSON encodes the wrapped int32 into JSON. diff --git a/int64.go b/int64.go index 460821d..d568fc5 100644 --- a/int64.go +++ b/int64.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicint. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,27 +32,31 @@ import ( type Int64 struct { _ nocmp // disallow non-atomic comparison - v int64 + v atomic.Int64 } // NewInt64 creates a new Int64. func NewInt64(val int64) *Int64 { - return &Int64{v: val} + x := &Int64{} + if val != 0 { + x.Store(val) + } + return x } // Load atomically loads the wrapped value. func (i *Int64) Load() int64 { - return atomic.LoadInt64(&i.v) + return i.v.Load() } // Add atomically adds to the wrapped int64 and returns the new value. func (i *Int64) Add(delta int64) int64 { - return atomic.AddInt64(&i.v, delta) + return i.v.Add(delta) } // Sub atomically subtracts from the wrapped int64 and returns the new value. func (i *Int64) Sub(delta int64) int64 { - return atomic.AddInt64(&i.v, -delta) + return i.v.Add(-delta) } // Inc atomically increments the wrapped int64 and returns the new value. @@ -74,17 +78,17 @@ func (i *Int64) CAS(old, new int64) (swapped bool) { // CompareAndSwap is an atomic compare-and-swap. func (i *Int64) CompareAndSwap(old, new int64) (swapped bool) { - return atomic.CompareAndSwapInt64(&i.v, old, new) + return i.v.CompareAndSwap(old, new) } // Store atomically stores the passed value. func (i *Int64) Store(val int64) { - atomic.StoreInt64(&i.v, val) + i.v.Store(val) } // Swap atomically swaps the wrapped int64 and returns the old value. func (i *Int64) Swap(val int64) (old int64) { - return atomic.SwapInt64(&i.v, val) + return i.v.Swap(val) } // MarshalJSON encodes the wrapped int64 into JSON. diff --git a/int64_test.go b/int64_test.go index ed5a104..3843002 100644 --- a/int64_test.go +++ b/int64_test.go @@ -79,4 +79,15 @@ func TestInt64(t *testing.T) { "String() returned an unexpected value.") }) }) + + t.Run("Alignment", func(t *testing.T) { + type wrapper struct { + _ byte + v Int64 + } + s := &wrapper{} + assert.Equal(t, int64(0), s.v.Load()) + s.v.Store(42) + assert.Equal(t, int64(42), s.v.Load()) + }) } diff --git a/internal/gen-atomicint/wrapper.tmpl b/internal/gen-atomicint/wrapper.tmpl index 51a0e3b..5c59c54 100644 --- a/internal/gen-atomicint/wrapper.tmpl +++ b/internal/gen-atomicint/wrapper.tmpl @@ -30,27 +30,31 @@ import ( type {{ .Name }} struct { _ nocmp // disallow non-atomic comparison - v {{ .Wrapped }} + v atomic.{{ .Name }} } // New{{ .Name }} creates a new {{ .Name }}. func New{{ .Name }}(val {{ .Wrapped }}) *{{ .Name }} { - return &{{ .Name }}{v: val} + x := &{{ .Name }}{} + if val != 0 { + x.Store(val) + } + return x } // Load atomically loads the wrapped value. func (i *{{ .Name }}) Load() {{ .Wrapped }} { - return atomic.Load{{ .Name }}(&i.v) + return i.v.Load() } // Add atomically adds to the wrapped {{ .Wrapped }} and returns the new value. func (i *{{ .Name }}) Add(delta {{ .Wrapped }}) {{ .Wrapped }} { - return atomic.Add{{ .Name }}(&i.v, delta) + return i.v.Add(delta) } // Sub atomically subtracts from the wrapped {{ .Wrapped }} and returns the new value. func (i *{{ .Name }}) Sub(delta {{ .Wrapped }}) {{ .Wrapped }} { - return atomic.Add{{ .Name }}(&i.v, + return i.v.Add( {{- if .Unsigned -}} ^(delta - 1) {{- else -}} @@ -78,17 +82,17 @@ func (i *{{ .Name }}) CAS(old, new {{ .Wrapped }}) (swapped bool) { // CompareAndSwap is an atomic compare-and-swap. func (i *{{ .Name }}) CompareAndSwap(old, new {{ .Wrapped }}) (swapped bool) { - return atomic.CompareAndSwap{{ .Name }}(&i.v, old, new) + return i.v.CompareAndSwap(old, new) } // Store atomically stores the passed value. func (i *{{ .Name }}) Store(val {{ .Wrapped }}) { - atomic.Store{{ .Name }}(&i.v, val) + i.v.Store(val) } // Swap atomically swaps the wrapped {{ .Wrapped }} and returns the old value. func (i *{{ .Name }}) Swap(val {{ .Wrapped }}) (old {{ .Wrapped }}) { - return atomic.Swap{{ .Name }}(&i.v, val) + return i.v.Swap(val) } // MarshalJSON encodes the wrapped {{ .Wrapped }} into JSON. diff --git a/string.go b/string.go index 061466c..3604657 100644 --- a/string.go +++ b/string.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/time.go b/time.go index cc2a230..2760373 100644 --- a/time.go +++ b/time.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicwrapper. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/uint32.go b/uint32.go index 4adc294..28d7caa 100644 --- a/uint32.go +++ b/uint32.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicint. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,27 +32,31 @@ import ( type Uint32 struct { _ nocmp // disallow non-atomic comparison - v uint32 + v atomic.Uint32 } // NewUint32 creates a new Uint32. func NewUint32(val uint32) *Uint32 { - return &Uint32{v: val} + x := &Uint32{} + if val != 0 { + x.Store(val) + } + return x } // Load atomically loads the wrapped value. func (i *Uint32) Load() uint32 { - return atomic.LoadUint32(&i.v) + return i.v.Load() } // Add atomically adds to the wrapped uint32 and returns the new value. func (i *Uint32) Add(delta uint32) uint32 { - return atomic.AddUint32(&i.v, delta) + return i.v.Add(delta) } // Sub atomically subtracts from the wrapped uint32 and returns the new value. func (i *Uint32) Sub(delta uint32) uint32 { - return atomic.AddUint32(&i.v, ^(delta - 1)) + return i.v.Add(^(delta - 1)) } // Inc atomically increments the wrapped uint32 and returns the new value. @@ -74,17 +78,17 @@ func (i *Uint32) CAS(old, new uint32) (swapped bool) { // CompareAndSwap is an atomic compare-and-swap. func (i *Uint32) CompareAndSwap(old, new uint32) (swapped bool) { - return atomic.CompareAndSwapUint32(&i.v, old, new) + return i.v.CompareAndSwap(old, new) } // Store atomically stores the passed value. func (i *Uint32) Store(val uint32) { - atomic.StoreUint32(&i.v, val) + i.v.Store(val) } // Swap atomically swaps the wrapped uint32 and returns the old value. func (i *Uint32) Swap(val uint32) (old uint32) { - return atomic.SwapUint32(&i.v, val) + return i.v.Swap(val) } // MarshalJSON encodes the wrapped uint32 into JSON. diff --git a/uint64.go b/uint64.go index 0e2eddb..0337666 100644 --- a/uint64.go +++ b/uint64.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicint. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,27 +32,31 @@ import ( type Uint64 struct { _ nocmp // disallow non-atomic comparison - v uint64 + v atomic.Uint64 } // NewUint64 creates a new Uint64. func NewUint64(val uint64) *Uint64 { - return &Uint64{v: val} + x := &Uint64{} + if val != 0 { + x.Store(val) + } + return x } // Load atomically loads the wrapped value. func (i *Uint64) Load() uint64 { - return atomic.LoadUint64(&i.v) + return i.v.Load() } // Add atomically adds to the wrapped uint64 and returns the new value. func (i *Uint64) Add(delta uint64) uint64 { - return atomic.AddUint64(&i.v, delta) + return i.v.Add(delta) } // Sub atomically subtracts from the wrapped uint64 and returns the new value. func (i *Uint64) Sub(delta uint64) uint64 { - return atomic.AddUint64(&i.v, ^(delta - 1)) + return i.v.Add(^(delta - 1)) } // Inc atomically increments the wrapped uint64 and returns the new value. @@ -74,17 +78,17 @@ func (i *Uint64) CAS(old, new uint64) (swapped bool) { // CompareAndSwap is an atomic compare-and-swap. func (i *Uint64) CompareAndSwap(old, new uint64) (swapped bool) { - return atomic.CompareAndSwapUint64(&i.v, old, new) + return i.v.CompareAndSwap(old, new) } // Store atomically stores the passed value. func (i *Uint64) Store(val uint64) { - atomic.StoreUint64(&i.v, val) + i.v.Store(val) } // Swap atomically swaps the wrapped uint64 and returns the old value. func (i *Uint64) Swap(val uint64) (old uint64) { - return atomic.SwapUint64(&i.v, val) + return i.v.Swap(val) } // MarshalJSON encodes the wrapped uint64 into JSON. diff --git a/uint64_test.go b/uint64_test.go index 9b8f399..1d9c202 100644 --- a/uint64_test.go +++ b/uint64_test.go @@ -73,4 +73,15 @@ func TestUint64(t *testing.T) { assert.Equal(t, "18446744073709551615", atom.String(), "String() returned an unexpected value.") }) + + t.Run("Alignment", func(t *testing.T) { + type wrapper struct { + _ byte + v Uint64 + } + s := &wrapper{} + assert.Equal(t, uint64(0), s.v.Load()) + s.v.Store(42) + assert.Equal(t, uint64(42), s.v.Load()) + }) } diff --git a/uintptr.go b/uintptr.go index 7d5b000..a9d06d1 100644 --- a/uintptr.go +++ b/uintptr.go @@ -1,6 +1,6 @@ // @generated Code generated by gen-atomicint. -// Copyright (c) 2020-2023 Uber Technologies, Inc. +// Copyright (c) 2020-2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,27 +32,31 @@ import ( type Uintptr struct { _ nocmp // disallow non-atomic comparison - v uintptr + v atomic.Uintptr } // NewUintptr creates a new Uintptr. func NewUintptr(val uintptr) *Uintptr { - return &Uintptr{v: val} + x := &Uintptr{} + if val != 0 { + x.Store(val) + } + return x } // Load atomically loads the wrapped value. func (i *Uintptr) Load() uintptr { - return atomic.LoadUintptr(&i.v) + return i.v.Load() } // Add atomically adds to the wrapped uintptr and returns the new value. func (i *Uintptr) Add(delta uintptr) uintptr { - return atomic.AddUintptr(&i.v, delta) + return i.v.Add(delta) } // Sub atomically subtracts from the wrapped uintptr and returns the new value. func (i *Uintptr) Sub(delta uintptr) uintptr { - return atomic.AddUintptr(&i.v, ^(delta - 1)) + return i.v.Add(^(delta - 1)) } // Inc atomically increments the wrapped uintptr and returns the new value. @@ -74,17 +78,17 @@ func (i *Uintptr) CAS(old, new uintptr) (swapped bool) { // CompareAndSwap is an atomic compare-and-swap. func (i *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) { - return atomic.CompareAndSwapUintptr(&i.v, old, new) + return i.v.CompareAndSwap(old, new) } // Store atomically stores the passed value. func (i *Uintptr) Store(val uintptr) { - atomic.StoreUintptr(&i.v, val) + i.v.Store(val) } // Swap atomically swaps the wrapped uintptr and returns the old value. func (i *Uintptr) Swap(val uintptr) (old uintptr) { - return atomic.SwapUintptr(&i.v, val) + return i.v.Swap(val) } // MarshalJSON encodes the wrapped uintptr into JSON.