├── .codecov.yml
├── .github
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
└── workflows
│ └── go.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE.txt
├── Makefile
├── README.md
├── assert_test.go
├── bool.go
├── bool_ext.go
├── bool_test.go
├── doc.go
├── duration.go
├── duration_ext.go
├── duration_test.go
├── error.go
├── error_ext.go
├── error_test.go
├── example_test.go
├── float32.go
├── float32_ext.go
├── float32_test.go
├── float64.go
├── float64_ext.go
├── float64_test.go
├── gen.go
├── go.mod
├── go.sum
├── int32.go
├── int32_test.go
├── int64.go
├── int64_test.go
├── internal
├── gen-atomicint
│ ├── main.go
│ └── wrapper.tmpl
└── gen-atomicwrapper
│ ├── main.go
│ └── wrapper.tmpl
├── nocmp.go
├── nocmp_test.go
├── pointer_go118.go
├── pointer_go118_pre119.go
├── pointer_go119.go
├── pointer_test.go
├── stress_test.go
├── string.go
├── string_ext.go
├── string_test.go
├── time.go
├── time_ext.go
├── time_test.go
├── tools
├── go.mod
├── go.sum
└── tools.go
├── uint32.go
├── uint32_test.go
├── uint64.go
├── uint64_test.go
├── uintptr.go
├── uintptr_test.go
├── unsafe_pointer.go
├── unsafe_pointer_test.go
├── value.go
└── value_test.go
/.codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | range: 80..100
3 | round: down
4 | precision: 2
5 |
6 | status:
7 | project: # measuring the overall project coverage
8 | default: # context, you can create multiple ones with custom titles
9 | enabled: yes # must be yes|true to enable this status
10 | target: 100 # specify the target coverage for each commit status
11 | # option: "auto" (must increase from parent commit or pull request base)
12 | # option: "X%" a static target percentage to hit
13 | if_not_found: success # if parent is not found report status as success, error, or failure
14 | if_ci_failed: error # if ci fails report status as success, error, or failure
15 |
16 | # Also update COVER_IGNORE_PKGS in the Makefile.
17 | ignore:
18 | - /internal/gen-atomicint/
19 | - /internal/gen-valuewrapper/
20 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Before opening your pull request, please make sure that you've:
2 |
3 | - [ ] updated the changelog if the change is user-facing;
4 | - [ ] added tests to cover your changes;
5 | - [ ] run the test suite locally (`make test`); and finally,
6 | - [ ] run the linters locally (`make lint`).
7 |
8 | Thanks for your contribution!
9 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 |
8 | # Auto-update tools dependencies, but not library dependencies.
9 | - package-ecosystem: "gomod"
10 | directory: "/tools"
11 | schedule:
12 | interval: "weekly"
13 |
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: Go
2 |
3 | on:
4 | push:
5 | branches: ['*']
6 | tags: ['v*']
7 | pull_request:
8 | branches: ['*']
9 |
10 | permissions:
11 | contents: read
12 |
13 | jobs:
14 |
15 | build:
16 | runs-on: ubuntu-latest
17 | strategy:
18 | matrix:
19 | go: ["1.17.x", "1.18.x", "1.19.x"]
20 | include:
21 | - go: 1.19.x
22 | latest: true
23 |
24 | steps:
25 | - name: Setup Go
26 | uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
27 | with:
28 | go-version: ${{ matrix.go }}
29 |
30 | - name: Checkout code
31 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
32 |
33 | - name: Load cached dependencies
34 | uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v.4.2.0
35 | with:
36 | path: ~/go/pkg/mod
37 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
38 | restore-keys: |
39 | ${{ runner.os }}-go-
40 |
41 | - name: Lint
42 | if: matrix.latest
43 | run: make lint
44 |
45 | - name: Test
46 | run: make cover
47 |
48 | - name: Upload coverage to codecov.io
49 | uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
50 | env:
51 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
52 |
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin
2 | .DS_Store
3 | /vendor
4 | cover.html
5 | cover.out
6 | lint.log
7 |
8 | # Binaries
9 | *.test
10 |
11 | # Profiling output
12 | *.prof
13 |
14 | # Output of fossa analyzer
15 | /fossa
16 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 | ### Added
9 | - Add `MarshalJSON` and `UnmarshalJSON` method to `atomic.Pointer[T]` type
10 | allowing users to use pointer with json.
11 |
12 | ## [1.11.0] - 2023-05-02
13 | ### Fixed
14 | - Fix `Swap` and `CompareAndSwap` for `Value` wrappers without initialization.
15 |
16 | ### Added
17 | - Add `String` method to `atomic.Pointer[T]` type allowing users to safely print
18 | underlying values of pointers.
19 |
20 | [1.11.0]: https://github.com/uber-go/atomic/compare/v1.10.0...v1.11.0
21 |
22 | ## [1.10.0] - 2022-08-11
23 | ### Added
24 | - Add `atomic.Float32` type for atomic operations on `float32`.
25 | - Add `CompareAndSwap` and `Swap` methods to `atomic.String`, `atomic.Error`,
26 | and `atomic.Value`.
27 | - Add generic `atomic.Pointer[T]` type for atomic operations on pointers of any
28 | type. This is present only for Go 1.18 or higher, and is a drop-in for
29 | replacement for the standard library's `sync/atomic.Pointer` type.
30 |
31 | ### Changed
32 | - Deprecate `CAS` methods on all types in favor of corresponding
33 | `CompareAndSwap` methods.
34 |
35 | Thanks to @eNV25 and @icpd for their contributions to this release.
36 |
37 | [1.10.0]: https://github.com/uber-go/atomic/compare/v1.9.0...v1.10.0
38 |
39 | ## [1.9.0] - 2021-07-15
40 | ### Added
41 | - Add `Float64.Swap` to match int atomic operations.
42 | - Add `atomic.Time` type for atomic operations on `time.Time` values.
43 |
44 | [1.9.0]: https://github.com/uber-go/atomic/compare/v1.8.0...v1.9.0
45 |
46 | ## [1.8.0] - 2021-06-09
47 | ### Added
48 | - Add `atomic.Uintptr` type for atomic operations on `uintptr` values.
49 | - Add `atomic.UnsafePointer` type for atomic operations on `unsafe.Pointer` values.
50 |
51 | [1.8.0]: https://github.com/uber-go/atomic/compare/v1.7.0...v1.8.0
52 |
53 | ## [1.7.0] - 2020-09-14
54 | ### Added
55 | - Support JSON serialization and deserialization of primitive atomic types.
56 | - Support Text marshalling and unmarshalling for string atomics.
57 |
58 | ### Changed
59 | - Disallow incorrect comparison of atomic values in a non-atomic way.
60 |
61 | ### Removed
62 | - Remove dependency on `golang.org/x/{lint, tools}`.
63 |
64 | [1.7.0]: https://github.com/uber-go/atomic/compare/v1.6.0...v1.7.0
65 |
66 | ## [1.6.0] - 2020-02-24
67 | ### Changed
68 | - Drop library dependency on `golang.org/x/{lint, tools}`.
69 |
70 | [1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
71 |
72 | ## [1.5.1] - 2019-11-19
73 | - Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together
74 | causing `CAS` to fail even though the old value matches.
75 |
76 | [1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
77 |
78 | ## [1.5.0] - 2019-10-29
79 | ### Changed
80 | - With Go modules, only the `go.uber.org/atomic` import path is supported now.
81 | If you need to use the old import path, please add a `replace` directive to
82 | your `go.mod`.
83 |
84 | [1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
85 |
86 | ## [1.4.0] - 2019-05-01
87 | ### Added
88 | - Add `atomic.Error` type for atomic operations on `error` values.
89 |
90 | [1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
91 |
92 | ## [1.3.2] - 2018-05-02
93 | ### Added
94 | - Add `atomic.Duration` type for atomic operations on `time.Duration` values.
95 |
96 | [1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
97 |
98 | ## [1.3.1] - 2017-11-14
99 | ### Fixed
100 | - Revert optimization for `atomic.String.Store("")` which caused data races.
101 |
102 | [1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
103 |
104 | ## [1.3.0] - 2017-11-13
105 | ### Added
106 | - Add `atomic.Bool.CAS` for compare-and-swap semantics on bools.
107 |
108 | ### Changed
109 | - Optimize `atomic.String.Store("")` by avoiding an allocation.
110 |
111 | [1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0
112 |
113 | ## [1.2.0] - 2017-04-12
114 | ### Added
115 | - Shadow `atomic.Value` from `sync/atomic`.
116 |
117 | [1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0
118 |
119 | ## [1.1.0] - 2017-03-10
120 | ### Added
121 | - Add atomic `Float64` type.
122 |
123 | ### Changed
124 | - Support new `go.uber.org/atomic` import path.
125 |
126 | [1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0
127 |
128 | ## [1.0.0] - 2016-07-18
129 |
130 | - Initial release.
131 |
132 | [1.0.0]: https://github.com/uber-go/atomic/releases/tag/v1.0.0
133 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 Uber Technologies, Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Directory to place `go install`ed binaries into.
2 | export GOBIN ?= $(shell pwd)/bin
3 |
4 | GOLINT = $(GOBIN)/golint
5 | GEN_ATOMICINT = $(GOBIN)/gen-atomicint
6 | GEN_ATOMICWRAPPER = $(GOBIN)/gen-atomicwrapper
7 | STATICCHECK = $(GOBIN)/staticcheck
8 |
9 | GO_FILES ?= $(shell find . '(' -path .git -o -path vendor ')' -prune -o -name '*.go' -print)
10 |
11 | # Also update ignore section in .codecov.yml.
12 | COVER_IGNORE_PKGS = \
13 | go.uber.org/atomic/internal/gen-atomicint \
14 | go.uber.org/atomic/internal/gen-atomicwrapper
15 |
16 | .PHONY: build
17 | build:
18 | go build ./...
19 |
20 | .PHONY: test
21 | test:
22 | go test -race ./...
23 |
24 | .PHONY: gofmt
25 | gofmt:
26 | $(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
27 | gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
28 | @[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" && cat $(FMT_LOG) && false)
29 |
30 | $(GOLINT):
31 | cd tools && go install golang.org/x/lint/golint
32 |
33 | $(STATICCHECK):
34 | cd tools && go install honnef.co/go/tools/cmd/staticcheck
35 |
36 | $(GEN_ATOMICWRAPPER): $(wildcard ./internal/gen-atomicwrapper/*)
37 | go build -o $@ ./internal/gen-atomicwrapper
38 |
39 | $(GEN_ATOMICINT): $(wildcard ./internal/gen-atomicint/*)
40 | go build -o $@ ./internal/gen-atomicint
41 |
42 | .PHONY: golint
43 | golint: $(GOLINT)
44 | $(GOLINT) ./...
45 |
46 | .PHONY: staticcheck
47 | staticcheck: $(STATICCHECK)
48 | $(STATICCHECK) ./...
49 |
50 | .PHONY: lint
51 | lint: gofmt golint staticcheck generatenodirty
52 |
53 | # comma separated list of packages to consider for code coverage.
54 | COVER_PKG = $(shell \
55 | go list -find ./... | \
56 | grep -v $(foreach pkg,$(COVER_IGNORE_PKGS),-e "^$(pkg)$$") | \
57 | paste -sd, -)
58 |
59 | .PHONY: cover
60 | cover:
61 | go test -coverprofile=cover.out -coverpkg $(COVER_PKG) -v ./...
62 | go tool cover -html=cover.out -o cover.html
63 |
64 | .PHONY: generate
65 | generate: $(GEN_ATOMICINT) $(GEN_ATOMICWRAPPER)
66 | go generate ./...
67 |
68 | .PHONY: generatenodirty
69 | generatenodirty:
70 | @[ -z "$$(git status --porcelain)" ] || ( \
71 | echo "Working tree is dirty. Commit your changes first."; \
72 | git status; \
73 | exit 1 )
74 | @make generate
75 | @status=$$(git status --porcelain); \
76 | [ -z "$$status" ] || ( \
77 | echo "Working tree is dirty after `make generate`:"; \
78 | echo "$$status"; \
79 | echo "Please ensure that the generated code is up-to-date." )
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard]
2 |
3 | Simple wrappers for primitive types to enforce atomic access.
4 |
5 | ## Installation
6 |
7 | ```shell
8 | $ go get -u go.uber.org/atomic@v1
9 | ```
10 |
11 | ### Legacy Import Path
12 |
13 | As of v1.5.0, the import path `go.uber.org/atomic` is the only supported way
14 | of using this package. If you are using Go modules, this package will fail to
15 | compile with the legacy import path path `github.com/uber-go/atomic`.
16 |
17 | We recommend migrating your code to the new import path but if you're unable
18 | to do so, or if your dependencies are still using the old import path, you
19 | will have to add a `replace` directive to your `go.mod` file downgrading the
20 | legacy import path to an older version.
21 |
22 | ```
23 | replace github.com/uber-go/atomic => github.com/uber-go/atomic v1.4.0
24 | ```
25 |
26 | You can do so automatically by running the following command.
27 |
28 | ```shell
29 | $ go mod edit -replace github.com/uber-go/atomic=github.com/uber-go/atomic@v1.4.0
30 | ```
31 |
32 | ## Usage
33 |
34 | The standard library's `sync/atomic` is powerful, but it's easy to forget which
35 | variables must be accessed atomically. `go.uber.org/atomic` preserves all the
36 | functionality of the standard library, but wraps the primitive types to
37 | provide a safer, more convenient API.
38 |
39 | ```go
40 | var atom atomic.Uint32
41 | atom.Store(42)
42 | atom.Sub(2)
43 | atom.CompareAndSwap(40, 11)
44 | ```
45 |
46 | See the [documentation][doc] for a complete API specification.
47 |
48 | ## Development Status
49 |
50 | Stable.
51 |
52 | ---
53 |
54 | Released under the [MIT License](LICENSE.txt).
55 |
56 | [doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg
57 | [doc]: https://godoc.org/go.uber.org/atomic
58 | [ci-img]: https://github.com/uber-go/atomic/actions/workflows/go.yml/badge.svg
59 | [ci]: https://github.com/uber-go/atomic/actions/workflows/go.yml
60 | [cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg
61 | [cov]: https://codecov.io/gh/uber-go/atomic
62 | [reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic
63 | [reportcard]: https://goreportcard.com/report/go.uber.org/atomic
64 |
--------------------------------------------------------------------------------
/assert_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "errors"
26 | "testing"
27 |
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | // Marks the test as failed if the error cannot be cast into the provided type
32 | // with errors.As.
33 | //
34 | // assertErrorAsType(t, err, new(ErrFoo))
35 | func assertErrorAsType(t *testing.T, err error, typ interface{}, msgAndArgs ...interface{}) bool {
36 | t.Helper()
37 |
38 | return assert.True(t, errors.As(err, typ), msgAndArgs...)
39 | }
40 |
41 | func assertErrorJSONUnmarshalType(t *testing.T, err error, msgAndArgs ...interface{}) bool {
42 | t.Helper()
43 |
44 | return assertErrorAsType(t, err, new(*json.UnmarshalTypeError), msgAndArgs...)
45 | }
46 |
--------------------------------------------------------------------------------
/bool.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicwrapper.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | import (
26 | "encoding/json"
27 | )
28 |
29 | // Bool is an atomic type-safe wrapper for bool values.
30 | type Bool struct {
31 | _ nocmp // disallow non-atomic comparison
32 |
33 | v Uint32
34 | }
35 |
36 | var _zeroBool bool
37 |
38 | // NewBool creates a new Bool.
39 | func NewBool(val bool) *Bool {
40 | x := &Bool{}
41 | if val != _zeroBool {
42 | x.Store(val)
43 | }
44 | return x
45 | }
46 |
47 | // Load atomically loads the wrapped bool.
48 | func (x *Bool) Load() bool {
49 | return truthy(x.v.Load())
50 | }
51 |
52 | // Store atomically stores the passed bool.
53 | func (x *Bool) Store(val bool) {
54 | x.v.Store(boolToInt(val))
55 | }
56 |
57 | // CAS is an atomic compare-and-swap for bool values.
58 | //
59 | // Deprecated: Use CompareAndSwap.
60 | func (x *Bool) CAS(old, new bool) (swapped bool) {
61 | return x.CompareAndSwap(old, new)
62 | }
63 |
64 | // CompareAndSwap is an atomic compare-and-swap for bool values.
65 | func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) {
66 | return x.v.CompareAndSwap(boolToInt(old), boolToInt(new))
67 | }
68 |
69 | // Swap atomically stores the given bool and returns the old
70 | // value.
71 | func (x *Bool) Swap(val bool) (old bool) {
72 | return truthy(x.v.Swap(boolToInt(val)))
73 | }
74 |
75 | // MarshalJSON encodes the wrapped bool into JSON.
76 | func (x *Bool) MarshalJSON() ([]byte, error) {
77 | return json.Marshal(x.Load())
78 | }
79 |
80 | // UnmarshalJSON decodes a bool from JSON.
81 | func (x *Bool) UnmarshalJSON(b []byte) error {
82 | var v bool
83 | if err := json.Unmarshal(b, &v); err != nil {
84 | return err
85 | }
86 | x.Store(v)
87 | return nil
88 | }
89 |
--------------------------------------------------------------------------------
/bool_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "strconv"
25 | )
26 |
27 | //go:generate bin/gen-atomicwrapper -name=Bool -type=bool -wrapped=Uint32 -pack=boolToInt -unpack=truthy -cas -swap -json -file=bool.go
28 |
29 | func truthy(n uint32) bool {
30 | return n == 1
31 | }
32 |
33 | func boolToInt(b bool) uint32 {
34 | if b {
35 | return 1
36 | }
37 | return 0
38 | }
39 |
40 | // Toggle atomically negates the Boolean and returns the previous value.
41 | func (b *Bool) Toggle() (old bool) {
42 | for {
43 | old := b.Load()
44 | if b.CAS(old, !old) {
45 | return old
46 | }
47 | }
48 | }
49 |
50 | // String encodes the wrapped value as a string.
51 | func (b *Bool) String() string {
52 | return strconv.FormatBool(b.Load())
53 | }
54 |
--------------------------------------------------------------------------------
/bool_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "testing"
26 |
27 | "github.com/stretchr/testify/assert"
28 | "github.com/stretchr/testify/require"
29 | )
30 |
31 | func TestBool(t *testing.T) {
32 | atom := NewBool(false)
33 | require.False(t, atom.Toggle(), "Expected Toggle to return previous value.")
34 | require.True(t, atom.Toggle(), "Expected Toggle to return previous value.")
35 | require.False(t, atom.Toggle(), "Expected Toggle to return previous value.")
36 | require.True(t, atom.Load(), "Unexpected state after swap.")
37 |
38 | require.True(t, atom.CAS(true, true), "CAS should swap when old matches")
39 | require.True(t, atom.Load(), "CAS should have no effect")
40 | require.True(t, atom.CAS(true, false), "CAS should swap when old matches")
41 | require.False(t, atom.Load(), "CAS should have modified the value")
42 | require.False(t, atom.CAS(true, false), "CAS should fail on old mismatch")
43 | require.False(t, atom.Load(), "CAS should not have modified the value")
44 |
45 | atom.Store(false)
46 | require.False(t, atom.Load(), "Unexpected state after store.")
47 |
48 | prev := atom.Swap(false)
49 | require.False(t, prev, "Expected Swap to return previous value.")
50 |
51 | prev = atom.Swap(true)
52 | require.False(t, prev, "Expected Swap to return previous value.")
53 |
54 | t.Run("JSON/Marshal", func(t *testing.T) {
55 | atom.Store(true)
56 | bytes, err := json.Marshal(atom)
57 | require.NoError(t, err, "json.Marshal errored unexpectedly.")
58 | require.Equal(t, []byte("true"), bytes, "json.Marshal encoded the wrong bytes.")
59 | })
60 |
61 | t.Run("JSON/Unmarshal", func(t *testing.T) {
62 | err := json.Unmarshal([]byte("false"), &atom)
63 | require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
64 | require.False(t, atom.Load(), "json.Unmarshal didn't set the correct value.")
65 | })
66 |
67 | t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
68 | err := json.Unmarshal([]byte("42"), &atom)
69 | require.Error(t, err, "json.Unmarshal didn't error as expected.")
70 | assertErrorJSONUnmarshalType(t, err,
71 | "json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
72 | })
73 |
74 | t.Run("String", func(t *testing.T) {
75 | t.Run("true", func(t *testing.T) {
76 | assert.Equal(t, "true", NewBool(true).String(),
77 | "String() returned an unexpected value.")
78 | })
79 |
80 | t.Run("false", func(t *testing.T) {
81 | var b Bool
82 | assert.Equal(t, "false", b.String(),
83 | "String() returned an unexpected value.")
84 | })
85 | })
86 | }
87 |
88 | func TestBool_InitializeDefaults(t *testing.T) {
89 | tests := []struct {
90 | msg string
91 | newBool func() *Bool
92 | }{
93 | {
94 | msg: "Uninitialized",
95 | newBool: func() *Bool {
96 | var b Bool
97 | return &b
98 | },
99 | },
100 | {
101 | msg: "NewBool with default",
102 | newBool: func() *Bool {
103 | return NewBool(false)
104 | },
105 | },
106 | {
107 | msg: "Bool swapped with default",
108 | newBool: func() *Bool {
109 | b := NewBool(true)
110 | b.Swap(false)
111 | return b
112 | },
113 | },
114 | {
115 | msg: "Bool CAS'd with default",
116 | newBool: func() *Bool {
117 | b := NewBool(true)
118 | b.CompareAndSwap(true, false)
119 | return b
120 | },
121 | },
122 | }
123 |
124 | for _, tt := range tests {
125 | t.Run(tt.msg, func(t *testing.T) {
126 | t.Run("MarshalJSON", func(t *testing.T) {
127 | b := tt.newBool()
128 | marshalled, err := b.MarshalJSON()
129 | require.NoError(t, err)
130 | assert.Equal(t, "false", string(marshalled))
131 | })
132 |
133 | t.Run("String", func(t *testing.T) {
134 | b := tt.newBool()
135 | assert.Equal(t, "false", b.String())
136 | })
137 |
138 | t.Run("CompareAndSwap", func(t *testing.T) {
139 | b := tt.newBool()
140 | require.True(t, b.CompareAndSwap(false, true))
141 | assert.Equal(t, true, b.Load())
142 | })
143 |
144 | t.Run("Swap", func(t *testing.T) {
145 | b := tt.newBool()
146 | assert.Equal(t, false, b.Swap(true))
147 | })
148 | })
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | // Package atomic provides simple wrappers around numerics to enforce atomic
22 | // access.
23 | package atomic
24 |
--------------------------------------------------------------------------------
/duration.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicwrapper.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | import (
26 | "encoding/json"
27 | "time"
28 | )
29 |
30 | // Duration is an atomic type-safe wrapper for time.Duration values.
31 | type Duration struct {
32 | _ nocmp // disallow non-atomic comparison
33 |
34 | v Int64
35 | }
36 |
37 | var _zeroDuration time.Duration
38 |
39 | // NewDuration creates a new Duration.
40 | func NewDuration(val time.Duration) *Duration {
41 | x := &Duration{}
42 | if val != _zeroDuration {
43 | x.Store(val)
44 | }
45 | return x
46 | }
47 |
48 | // Load atomically loads the wrapped time.Duration.
49 | func (x *Duration) Load() time.Duration {
50 | return time.Duration(x.v.Load())
51 | }
52 |
53 | // Store atomically stores the passed time.Duration.
54 | func (x *Duration) Store(val time.Duration) {
55 | x.v.Store(int64(val))
56 | }
57 |
58 | // CAS is an atomic compare-and-swap for time.Duration values.
59 | //
60 | // Deprecated: Use CompareAndSwap.
61 | func (x *Duration) CAS(old, new time.Duration) (swapped bool) {
62 | return x.CompareAndSwap(old, new)
63 | }
64 |
65 | // CompareAndSwap is an atomic compare-and-swap for time.Duration values.
66 | func (x *Duration) CompareAndSwap(old, new time.Duration) (swapped bool) {
67 | return x.v.CompareAndSwap(int64(old), int64(new))
68 | }
69 |
70 | // Swap atomically stores the given time.Duration and returns the old
71 | // value.
72 | func (x *Duration) Swap(val time.Duration) (old time.Duration) {
73 | return time.Duration(x.v.Swap(int64(val)))
74 | }
75 |
76 | // MarshalJSON encodes the wrapped time.Duration into JSON.
77 | func (x *Duration) MarshalJSON() ([]byte, error) {
78 | return json.Marshal(x.Load())
79 | }
80 |
81 | // UnmarshalJSON decodes a time.Duration from JSON.
82 | func (x *Duration) UnmarshalJSON(b []byte) error {
83 | var v time.Duration
84 | if err := json.Unmarshal(b, &v); err != nil {
85 | return err
86 | }
87 | x.Store(v)
88 | return nil
89 | }
90 |
--------------------------------------------------------------------------------
/duration_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import "time"
24 |
25 | //go:generate bin/gen-atomicwrapper -name=Duration -type=time.Duration -wrapped=Int64 -pack=int64 -unpack=time.Duration -cas -swap -json -imports time -file=duration.go
26 |
27 | // Add atomically adds to the wrapped time.Duration and returns the new value.
28 | func (d *Duration) Add(delta time.Duration) time.Duration {
29 | return time.Duration(d.v.Add(int64(delta)))
30 | }
31 |
32 | // Sub atomically subtracts from the wrapped time.Duration and returns the new value.
33 | func (d *Duration) Sub(delta time.Duration) time.Duration {
34 | return time.Duration(d.v.Sub(int64(delta)))
35 | }
36 |
37 | // String encodes the wrapped value as a string.
38 | func (d *Duration) String() string {
39 | return d.Load().String()
40 | }
41 |
--------------------------------------------------------------------------------
/duration_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "testing"
26 | "time"
27 |
28 | "github.com/stretchr/testify/assert"
29 | "github.com/stretchr/testify/require"
30 | )
31 |
32 | func TestDuration(t *testing.T) {
33 | atom := NewDuration(5 * time.Minute)
34 |
35 | require.Equal(t, 5*time.Minute, atom.Load(), "Load didn't work.")
36 | require.Equal(t, 6*time.Minute, atom.Add(time.Minute), "Add didn't work.")
37 | require.Equal(t, 4*time.Minute, atom.Sub(2*time.Minute), "Sub didn't work.")
38 |
39 | require.True(t, atom.CAS(4*time.Minute, time.Minute), "CAS didn't report a swap.")
40 | require.Equal(t, time.Minute, atom.Load(), "CAS didn't set the correct value.")
41 |
42 | require.Equal(t, time.Minute, atom.Swap(2*time.Minute), "Swap didn't return the old value.")
43 | require.Equal(t, 2*time.Minute, atom.Load(), "Swap didn't set the correct value.")
44 |
45 | atom.Store(10 * time.Minute)
46 | require.Equal(t, 10*time.Minute, atom.Load(), "Store didn't set the correct value.")
47 |
48 | t.Run("JSON/Marshal", func(t *testing.T) {
49 | atom.Store(time.Second)
50 | bytes, err := json.Marshal(atom)
51 | require.NoError(t, err, "json.Marshal errored unexpectedly.")
52 | require.Equal(t, []byte("1000000000"), bytes, "json.Marshal encoded the wrong bytes.")
53 | })
54 |
55 | t.Run("JSON/Unmarshal", func(t *testing.T) {
56 | err := json.Unmarshal([]byte("1000000000"), &atom)
57 | require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
58 | require.Equal(t, time.Second, atom.Load(), "json.Unmarshal didn't set the correct value.")
59 | })
60 |
61 | t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
62 | err := json.Unmarshal([]byte("\"1000000000\""), &atom)
63 | require.Error(t, err, "json.Unmarshal didn't error as expected.")
64 | assertErrorJSONUnmarshalType(t, err,
65 | "json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
66 | })
67 |
68 | t.Run("String", func(t *testing.T) {
69 | assert.Equal(t, "42s", NewDuration(42*time.Second).String(),
70 | "String() returned an unexpected value.")
71 | })
72 | }
73 |
--------------------------------------------------------------------------------
/error.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicwrapper.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | // Error is an atomic type-safe wrapper for error values.
26 | type Error struct {
27 | _ nocmp // disallow non-atomic comparison
28 |
29 | v Value
30 | }
31 |
32 | var _zeroError error
33 |
34 | // NewError creates a new Error.
35 | func NewError(val error) *Error {
36 | x := &Error{}
37 | if val != _zeroError {
38 | x.Store(val)
39 | }
40 | return x
41 | }
42 |
43 | // Load atomically loads the wrapped error.
44 | func (x *Error) Load() error {
45 | return unpackError(x.v.Load())
46 | }
47 |
48 | // Store atomically stores the passed error.
49 | func (x *Error) Store(val error) {
50 | x.v.Store(packError(val))
51 | }
52 |
53 | // CompareAndSwap is an atomic compare-and-swap for error values.
54 | func (x *Error) CompareAndSwap(old, new error) (swapped bool) {
55 | if x.v.CompareAndSwap(packError(old), packError(new)) {
56 | return true
57 | }
58 |
59 | if old == _zeroError {
60 | // If the old value is the empty value, then it's possible the
61 | // underlying Value hasn't been set and is nil, so retry with nil.
62 | return x.v.CompareAndSwap(nil, packError(new))
63 | }
64 |
65 | return false
66 | }
67 |
68 | // Swap atomically stores the given error and returns the old
69 | // value.
70 | func (x *Error) Swap(val error) (old error) {
71 | return unpackError(x.v.Swap(packError(val)))
72 | }
73 |
--------------------------------------------------------------------------------
/error_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020-2022 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | // atomic.Value panics on nil inputs, or if the underlying type changes.
24 | // Stabilize by always storing a custom struct that we control.
25 |
26 | //go:generate bin/gen-atomicwrapper -name=Error -type=error -wrapped=Value -pack=packError -unpack=unpackError -compareandswap -swap -file=error.go
27 |
28 | type packedError struct{ Value error }
29 |
30 | func packError(v error) interface{} {
31 | return packedError{v}
32 | }
33 |
34 | func unpackError(v interface{}) error {
35 | if err, ok := v.(packedError); ok {
36 | return err.Value
37 | }
38 | return nil
39 | }
40 |
--------------------------------------------------------------------------------
/error_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "errors"
25 | "testing"
26 |
27 | "github.com/stretchr/testify/assert"
28 | "github.com/stretchr/testify/require"
29 | )
30 |
31 | func TestErrorByValue(t *testing.T) {
32 | err := &Error{}
33 | require.Nil(t, err.Load(), "Initial value shall be nil")
34 | }
35 |
36 | func TestNewErrorWithNilArgument(t *testing.T) {
37 | err := NewError(nil)
38 | require.Nil(t, err.Load(), "Initial value shall be nil")
39 | }
40 |
41 | func TestErrorCanStoreNil(t *testing.T) {
42 | err := NewError(errors.New("hello"))
43 | err.Store(nil)
44 | require.Nil(t, err.Load(), "Stored value shall be nil")
45 | }
46 |
47 | func TestNewErrorWithError(t *testing.T) {
48 | err1 := errors.New("hello1")
49 | err2 := errors.New("hello2")
50 |
51 | atom := NewError(err1)
52 | require.Equal(t, err1, atom.Load(), "Expected Load to return initialized value")
53 |
54 | atom.Store(err2)
55 | require.Equal(t, err2, atom.Load(), "Expected Load to return overridden value")
56 | }
57 |
58 | func TestErrorSwap(t *testing.T) {
59 | err1 := errors.New("hello1")
60 | err2 := errors.New("hello2")
61 |
62 | atom := NewError(err1)
63 | require.Equal(t, err1, atom.Load(), "Expected Load to return initialized value")
64 |
65 | old := atom.Swap(err2)
66 | require.Equal(t, err2, atom.Load(), "Expected Load to return overridden value")
67 | require.Equal(t, err1, old, "Expected old to be initial value")
68 | }
69 |
70 | func TestErrorCompareAndSwap(t *testing.T) {
71 | err1 := errors.New("hello1")
72 | err2 := errors.New("hello2")
73 |
74 | atom := NewError(err1)
75 | require.Equal(t, err1, atom.Load(), "Expected Load to return initialized value")
76 |
77 | swapped := atom.CompareAndSwap(err2, err2)
78 | require.False(t, swapped, "Expected swapped to be false")
79 | require.Equal(t, err1, atom.Load(), "Expected Load to return initial value")
80 |
81 | swapped = atom.CompareAndSwap(err1, err2)
82 | require.True(t, swapped, "Expected swapped to be true")
83 | require.Equal(t, err2, atom.Load(), "Expected Load to return overridden value")
84 | }
85 |
86 | func TestError_InitializeDefaults(t *testing.T) {
87 | tests := []struct {
88 | msg string
89 | newError func() *Error
90 | }{
91 | {
92 | msg: "Uninitialized",
93 | newError: func() *Error {
94 | var e Error
95 | return &e
96 | },
97 | },
98 | {
99 | msg: "NewError with default",
100 | newError: func() *Error {
101 | return NewError(nil)
102 | },
103 | },
104 | {
105 | msg: "Error swapped with default",
106 | newError: func() *Error {
107 | e := NewError(assert.AnError)
108 | e.Swap(nil)
109 | return e
110 | },
111 | },
112 | {
113 | msg: "Error CAS'd with default",
114 | newError: func() *Error {
115 | e := NewError(assert.AnError)
116 | e.CompareAndSwap(assert.AnError, nil)
117 | return e
118 | },
119 | },
120 | }
121 |
122 | for _, tt := range tests {
123 | t.Run(tt.msg, func(t *testing.T) {
124 | t.Run("CompareAndSwap", func(t *testing.T) {
125 | e := tt.newError()
126 | require.True(t, e.CompareAndSwap(nil, assert.AnError))
127 | assert.Equal(t, assert.AnError, e.Load())
128 | })
129 |
130 | t.Run("Swap", func(t *testing.T) {
131 | e := tt.newError()
132 | assert.Equal(t, nil, e.Swap(assert.AnError))
133 | })
134 | })
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/example_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic_test
22 |
23 | import (
24 | "fmt"
25 |
26 | "go.uber.org/atomic"
27 | )
28 |
29 | func Example() {
30 | // Uint32 is a thin wrapper around the primitive uint32 type.
31 | var atom atomic.Uint32
32 |
33 | // The wrapper ensures that all operations are atomic.
34 | atom.Store(42)
35 | fmt.Println(atom.Inc())
36 | fmt.Println(atom.CompareAndSwap(43, 0))
37 | fmt.Println(atom.Load())
38 |
39 | // Output:
40 | // 43
41 | // true
42 | // 0
43 | }
44 |
--------------------------------------------------------------------------------
/float32.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicwrapper.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | import (
26 | "encoding/json"
27 | "math"
28 | )
29 |
30 | // Float32 is an atomic type-safe wrapper for float32 values.
31 | type Float32 struct {
32 | _ nocmp // disallow non-atomic comparison
33 |
34 | v Uint32
35 | }
36 |
37 | var _zeroFloat32 float32
38 |
39 | // NewFloat32 creates a new Float32.
40 | func NewFloat32(val float32) *Float32 {
41 | x := &Float32{}
42 | if val != _zeroFloat32 {
43 | x.Store(val)
44 | }
45 | return x
46 | }
47 |
48 | // Load atomically loads the wrapped float32.
49 | func (x *Float32) Load() float32 {
50 | return math.Float32frombits(x.v.Load())
51 | }
52 |
53 | // Store atomically stores the passed float32.
54 | func (x *Float32) Store(val float32) {
55 | x.v.Store(math.Float32bits(val))
56 | }
57 |
58 | // Swap atomically stores the given float32 and returns the old
59 | // value.
60 | func (x *Float32) Swap(val float32) (old float32) {
61 | return math.Float32frombits(x.v.Swap(math.Float32bits(val)))
62 | }
63 |
64 | // MarshalJSON encodes the wrapped float32 into JSON.
65 | func (x *Float32) MarshalJSON() ([]byte, error) {
66 | return json.Marshal(x.Load())
67 | }
68 |
69 | // UnmarshalJSON decodes a float32 from JSON.
70 | func (x *Float32) UnmarshalJSON(b []byte) error {
71 | var v float32
72 | if err := json.Unmarshal(b, &v); err != nil {
73 | return err
74 | }
75 | x.Store(v)
76 | return nil
77 | }
78 |
--------------------------------------------------------------------------------
/float32_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020-2022 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "math"
25 | "strconv"
26 | )
27 |
28 | //go:generate bin/gen-atomicwrapper -name=Float32 -type=float32 -wrapped=Uint32 -pack=math.Float32bits -unpack=math.Float32frombits -swap -json -imports math -file=float32.go
29 |
30 | // Add atomically adds to the wrapped float32 and returns the new value.
31 | func (f *Float32) Add(delta float32) float32 {
32 | for {
33 | old := f.Load()
34 | new := old + delta
35 | if f.CAS(old, new) {
36 | return new
37 | }
38 | }
39 | }
40 |
41 | // Sub atomically subtracts from the wrapped float32 and returns the new value.
42 | func (f *Float32) Sub(delta float32) float32 {
43 | return f.Add(-delta)
44 | }
45 |
46 | // CAS is an atomic compare-and-swap for float32 values.
47 | //
48 | // Deprecated: Use CompareAndSwap
49 | func (f *Float32) CAS(old, new float32) (swapped bool) {
50 | return f.CompareAndSwap(old, new)
51 | }
52 |
53 | // CompareAndSwap is an atomic compare-and-swap for float32 values.
54 | //
55 | // Note: CompareAndSwap handles NaN incorrectly. NaN != NaN using Go's inbuilt operators
56 | // but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN.
57 | // This avoids typical CompareAndSwap loops from blocking forever, e.g.,
58 | //
59 | // for {
60 | // old := atom.Load()
61 | // new = f(old)
62 | // if atom.CompareAndSwap(old, new) {
63 | // break
64 | // }
65 | // }
66 | //
67 | // If CompareAndSwap did not match NaN to match, then the above would loop forever.
68 | func (f *Float32) CompareAndSwap(old, new float32) (swapped bool) {
69 | return f.v.CompareAndSwap(math.Float32bits(old), math.Float32bits(new))
70 | }
71 |
72 | // String encodes the wrapped value as a string.
73 | func (f *Float32) String() string {
74 | // 'g' is the behavior for floats with %v.
75 | return strconv.FormatFloat(float64(f.Load()), 'g', -1, 32)
76 | }
77 |
--------------------------------------------------------------------------------
/float32_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "testing"
26 |
27 | "github.com/stretchr/testify/assert"
28 | "github.com/stretchr/testify/require"
29 | )
30 |
31 | func TestFloat32(t *testing.T) {
32 | atom := NewFloat32(4.2)
33 |
34 | require.Equal(t, float32(4.2), atom.Load(), "Load didn't work.")
35 |
36 | require.True(t, atom.CAS(4.2, 0.5), "CAS didn't report a swap.")
37 | require.Equal(t, float32(0.5), atom.Load(), "CAS didn't set the correct value.")
38 | require.False(t, atom.CAS(0.0, 1.5), "CAS reported a swap.")
39 |
40 | atom.Store(42.0)
41 | require.Equal(t, float32(42.0), atom.Load(), "Store didn't set the correct value.")
42 | require.Equal(t, float32(42.5), atom.Add(0.5), "Add didn't work.")
43 | require.Equal(t, float32(42.0), atom.Sub(0.5), "Sub didn't work.")
44 |
45 | require.Equal(t, float32(42.0), atom.Swap(45.0), "Swap didn't return the old value.")
46 | require.Equal(t, float32(45.0), atom.Load(), "Swap didn't set the correct value.")
47 |
48 | t.Run("JSON/Marshal", func(t *testing.T) {
49 | atom.Store(42.5)
50 | bytes, err := json.Marshal(atom)
51 | require.NoError(t, err, "json.Marshal errored unexpectedly.")
52 | require.Equal(t, []byte("42.5"), bytes, "json.Marshal encoded the wrong bytes.")
53 | })
54 |
55 | t.Run("JSON/Unmarshal", func(t *testing.T) {
56 | err := json.Unmarshal([]byte("40.5"), &atom)
57 | require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
58 | require.Equal(t, float32(40.5), atom.Load(), "json.Unmarshal didn't set the correct value.")
59 | })
60 |
61 | t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
62 | err := json.Unmarshal([]byte("\"40.5\""), &atom)
63 | require.Error(t, err, "json.Unmarshal didn't error as expected.")
64 | assertErrorJSONUnmarshalType(t, err,
65 | "json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
66 | })
67 |
68 | t.Run("String", func(t *testing.T) {
69 | assert.Equal(t, "42.5", NewFloat32(42.5).String(),
70 | "String() returned an unexpected value.")
71 | })
72 | }
73 |
--------------------------------------------------------------------------------
/float64.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicwrapper.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | import (
26 | "encoding/json"
27 | "math"
28 | )
29 |
30 | // Float64 is an atomic type-safe wrapper for float64 values.
31 | type Float64 struct {
32 | _ nocmp // disallow non-atomic comparison
33 |
34 | v Uint64
35 | }
36 |
37 | var _zeroFloat64 float64
38 |
39 | // NewFloat64 creates a new Float64.
40 | func NewFloat64(val float64) *Float64 {
41 | x := &Float64{}
42 | if val != _zeroFloat64 {
43 | x.Store(val)
44 | }
45 | return x
46 | }
47 |
48 | // Load atomically loads the wrapped float64.
49 | func (x *Float64) Load() float64 {
50 | return math.Float64frombits(x.v.Load())
51 | }
52 |
53 | // Store atomically stores the passed float64.
54 | func (x *Float64) Store(val float64) {
55 | x.v.Store(math.Float64bits(val))
56 | }
57 |
58 | // Swap atomically stores the given float64 and returns the old
59 | // value.
60 | func (x *Float64) Swap(val float64) (old float64) {
61 | return math.Float64frombits(x.v.Swap(math.Float64bits(val)))
62 | }
63 |
64 | // MarshalJSON encodes the wrapped float64 into JSON.
65 | func (x *Float64) MarshalJSON() ([]byte, error) {
66 | return json.Marshal(x.Load())
67 | }
68 |
69 | // UnmarshalJSON decodes a float64 from JSON.
70 | func (x *Float64) UnmarshalJSON(b []byte) error {
71 | var v float64
72 | if err := json.Unmarshal(b, &v); err != nil {
73 | return err
74 | }
75 | x.Store(v)
76 | return nil
77 | }
78 |
--------------------------------------------------------------------------------
/float64_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020-2022 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "math"
25 | "strconv"
26 | )
27 |
28 | //go:generate bin/gen-atomicwrapper -name=Float64 -type=float64 -wrapped=Uint64 -pack=math.Float64bits -unpack=math.Float64frombits -swap -json -imports math -file=float64.go
29 |
30 | // Add atomically adds to the wrapped float64 and returns the new value.
31 | func (f *Float64) Add(delta float64) float64 {
32 | for {
33 | old := f.Load()
34 | new := old + delta
35 | if f.CAS(old, new) {
36 | return new
37 | }
38 | }
39 | }
40 |
41 | // Sub atomically subtracts from the wrapped float64 and returns the new value.
42 | func (f *Float64) Sub(delta float64) float64 {
43 | return f.Add(-delta)
44 | }
45 |
46 | // CAS is an atomic compare-and-swap for float64 values.
47 | //
48 | // Deprecated: Use CompareAndSwap
49 | func (f *Float64) CAS(old, new float64) (swapped bool) {
50 | return f.CompareAndSwap(old, new)
51 | }
52 |
53 | // CompareAndSwap is an atomic compare-and-swap for float64 values.
54 | //
55 | // Note: CompareAndSwap handles NaN incorrectly. NaN != NaN using Go's inbuilt operators
56 | // but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN.
57 | // This avoids typical CompareAndSwap loops from blocking forever, e.g.,
58 | //
59 | // for {
60 | // old := atom.Load()
61 | // new = f(old)
62 | // if atom.CompareAndSwap(old, new) {
63 | // break
64 | // }
65 | // }
66 | //
67 | // If CompareAndSwap did not match NaN to match, then the above would loop forever.
68 | func (f *Float64) CompareAndSwap(old, new float64) (swapped bool) {
69 | return f.v.CompareAndSwap(math.Float64bits(old), math.Float64bits(new))
70 | }
71 |
72 | // String encodes the wrapped value as a string.
73 | func (f *Float64) String() string {
74 | // 'g' is the behavior for floats with %v.
75 | return strconv.FormatFloat(f.Load(), 'g', -1, 64)
76 | }
77 |
--------------------------------------------------------------------------------
/float64_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "testing"
26 |
27 | "github.com/stretchr/testify/assert"
28 | "github.com/stretchr/testify/require"
29 | )
30 |
31 | func TestFloat64(t *testing.T) {
32 | atom := NewFloat64(4.2)
33 |
34 | require.Equal(t, float64(4.2), atom.Load(), "Load didn't work.")
35 |
36 | require.True(t, atom.CAS(4.2, 0.5), "CAS didn't report a swap.")
37 | require.Equal(t, float64(0.5), atom.Load(), "CAS didn't set the correct value.")
38 | require.False(t, atom.CAS(0.0, 1.5), "CAS reported a swap.")
39 |
40 | atom.Store(42.0)
41 | require.Equal(t, float64(42.0), atom.Load(), "Store didn't set the correct value.")
42 | require.Equal(t, float64(42.5), atom.Add(0.5), "Add didn't work.")
43 | require.Equal(t, float64(42.0), atom.Sub(0.5), "Sub didn't work.")
44 |
45 | require.Equal(t, float64(42.0), atom.Swap(45.0), "Swap didn't return the old value.")
46 | require.Equal(t, float64(45.0), atom.Load(), "Swap didn't set the correct value.")
47 |
48 | t.Run("JSON/Marshal", func(t *testing.T) {
49 | atom.Store(42.5)
50 | bytes, err := json.Marshal(atom)
51 | require.NoError(t, err, "json.Marshal errored unexpectedly.")
52 | require.Equal(t, []byte("42.5"), bytes, "json.Marshal encoded the wrong bytes.")
53 | })
54 |
55 | t.Run("JSON/Unmarshal", func(t *testing.T) {
56 | err := json.Unmarshal([]byte("40.5"), &atom)
57 | require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
58 | require.Equal(t, float64(40.5), atom.Load(), "json.Unmarshal didn't set the correct value.")
59 | })
60 |
61 | t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
62 | err := json.Unmarshal([]byte("\"40.5\""), &atom)
63 | require.Error(t, err, "json.Unmarshal didn't error as expected.")
64 | assertErrorJSONUnmarshalType(t, err,
65 | "json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
66 | })
67 |
68 | t.Run("String", func(t *testing.T) {
69 | assert.Equal(t, "42.5", NewFloat64(42.5).String(),
70 | "String() returned an unexpected value.")
71 | })
72 | }
73 |
--------------------------------------------------------------------------------
/gen.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | //go:generate bin/gen-atomicint -name=Int32 -wrapped=int32 -file=int32.go
24 | //go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
25 | //go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
26 | //go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go
27 | //go:generate bin/gen-atomicint -name=Uintptr -wrapped=uintptr -unsigned -file=uintptr.go
28 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module go.uber.org/atomic
2 |
3 | require github.com/stretchr/testify v1.3.0
4 |
5 | require (
6 | github.com/davecgh/go-spew v1.1.1 // indirect
7 | github.com/pmezard/go-difflib v1.0.0 // indirect
8 | )
9 |
10 | go 1.18
11 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
7 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
8 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
9 |
--------------------------------------------------------------------------------
/int32.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicint.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | import (
26 | "encoding/json"
27 | "strconv"
28 | "sync/atomic"
29 | )
30 |
31 | // Int32 is an atomic wrapper around int32.
32 | type Int32 struct {
33 | _ nocmp // disallow non-atomic comparison
34 |
35 | v int32
36 | }
37 |
38 | // NewInt32 creates a new Int32.
39 | func NewInt32(val int32) *Int32 {
40 | return &Int32{v: val}
41 | }
42 |
43 | // Load atomically loads the wrapped value.
44 | func (i *Int32) Load() int32 {
45 | return atomic.LoadInt32(&i.v)
46 | }
47 |
48 | // Add atomically adds to the wrapped int32 and returns the new value.
49 | func (i *Int32) Add(delta int32) int32 {
50 | return atomic.AddInt32(&i.v, delta)
51 | }
52 |
53 | // Sub atomically subtracts from the wrapped int32 and returns the new value.
54 | func (i *Int32) Sub(delta int32) int32 {
55 | return atomic.AddInt32(&i.v, -delta)
56 | }
57 |
58 | // Inc atomically increments the wrapped int32 and returns the new value.
59 | func (i *Int32) Inc() int32 {
60 | return i.Add(1)
61 | }
62 |
63 | // Dec atomically decrements the wrapped int32 and returns the new value.
64 | func (i *Int32) Dec() int32 {
65 | return i.Sub(1)
66 | }
67 |
68 | // CAS is an atomic compare-and-swap.
69 | //
70 | // Deprecated: Use CompareAndSwap.
71 | func (i *Int32) CAS(old, new int32) (swapped bool) {
72 | return i.CompareAndSwap(old, new)
73 | }
74 |
75 | // CompareAndSwap is an atomic compare-and-swap.
76 | func (i *Int32) CompareAndSwap(old, new int32) (swapped bool) {
77 | return atomic.CompareAndSwapInt32(&i.v, old, new)
78 | }
79 |
80 | // Store atomically stores the passed value.
81 | func (i *Int32) Store(val int32) {
82 | atomic.StoreInt32(&i.v, val)
83 | }
84 |
85 | // Swap atomically swaps the wrapped int32 and returns the old value.
86 | func (i *Int32) Swap(val int32) (old int32) {
87 | return atomic.SwapInt32(&i.v, val)
88 | }
89 |
90 | // MarshalJSON encodes the wrapped int32 into JSON.
91 | func (i *Int32) MarshalJSON() ([]byte, error) {
92 | return json.Marshal(i.Load())
93 | }
94 |
95 | // UnmarshalJSON decodes JSON into the wrapped int32.
96 | func (i *Int32) UnmarshalJSON(b []byte) error {
97 | var v int32
98 | if err := json.Unmarshal(b, &v); err != nil {
99 | return err
100 | }
101 | i.Store(v)
102 | return nil
103 | }
104 |
105 | // String encodes the wrapped value as a string.
106 | func (i *Int32) String() string {
107 | v := i.Load()
108 | return strconv.FormatInt(int64(v), 10)
109 | }
110 |
--------------------------------------------------------------------------------
/int32_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "math"
26 | "testing"
27 |
28 | "github.com/stretchr/testify/assert"
29 | "github.com/stretchr/testify/require"
30 | )
31 |
32 | func TestInt32(t *testing.T) {
33 | atom := NewInt32(42)
34 |
35 | require.Equal(t, int32(42), atom.Load(), "Load didn't work.")
36 | require.Equal(t, int32(46), atom.Add(4), "Add didn't work.")
37 | require.Equal(t, int32(44), atom.Sub(2), "Sub didn't work.")
38 | require.Equal(t, int32(45), atom.Inc(), "Inc didn't work.")
39 | require.Equal(t, int32(44), atom.Dec(), "Dec didn't work.")
40 |
41 | require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
42 | require.Equal(t, int32(0), atom.Load(), "CAS didn't set the correct value.")
43 |
44 | require.Equal(t, int32(0), atom.Swap(1), "Swap didn't return the old value.")
45 | require.Equal(t, int32(1), atom.Load(), "Swap didn't set the correct value.")
46 |
47 | atom.Store(42)
48 | require.Equal(t, int32(42), atom.Load(), "Store didn't set the correct value.")
49 |
50 | t.Run("JSON/Marshal", func(t *testing.T) {
51 | bytes, err := json.Marshal(atom)
52 | require.NoError(t, err, "json.Marshal errored unexpectedly.")
53 | require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
54 | })
55 |
56 | t.Run("JSON/Unmarshal", func(t *testing.T) {
57 | err := json.Unmarshal([]byte("40"), &atom)
58 | require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
59 | require.Equal(t, int32(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
60 | })
61 |
62 | t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
63 | err := json.Unmarshal([]byte(`"40"`), &atom)
64 | require.Error(t, err, "json.Unmarshal didn't error as expected.")
65 | assertErrorJSONUnmarshalType(t, err,
66 | "json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
67 | })
68 |
69 | t.Run("String", func(t *testing.T) {
70 | t.Run("positive", func(t *testing.T) {
71 | atom := NewInt32(math.MaxInt32)
72 | assert.Equal(t, "2147483647", atom.String(),
73 | "String() returned an unexpected value.")
74 | })
75 |
76 | t.Run("negative", func(t *testing.T) {
77 | atom := NewInt32(math.MinInt32)
78 | assert.Equal(t, "-2147483648", atom.String(),
79 | "String() returned an unexpected value.")
80 | })
81 | })
82 | }
83 |
--------------------------------------------------------------------------------
/int64.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicint.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | import (
26 | "encoding/json"
27 | "strconv"
28 | "sync/atomic"
29 | )
30 |
31 | // Int64 is an atomic wrapper around int64.
32 | type Int64 struct {
33 | _ nocmp // disallow non-atomic comparison
34 |
35 | v int64
36 | }
37 |
38 | // NewInt64 creates a new Int64.
39 | func NewInt64(val int64) *Int64 {
40 | return &Int64{v: val}
41 | }
42 |
43 | // Load atomically loads the wrapped value.
44 | func (i *Int64) Load() int64 {
45 | return atomic.LoadInt64(&i.v)
46 | }
47 |
48 | // Add atomically adds to the wrapped int64 and returns the new value.
49 | func (i *Int64) Add(delta int64) int64 {
50 | return atomic.AddInt64(&i.v, delta)
51 | }
52 |
53 | // Sub atomically subtracts from the wrapped int64 and returns the new value.
54 | func (i *Int64) Sub(delta int64) int64 {
55 | return atomic.AddInt64(&i.v, -delta)
56 | }
57 |
58 | // Inc atomically increments the wrapped int64 and returns the new value.
59 | func (i *Int64) Inc() int64 {
60 | return i.Add(1)
61 | }
62 |
63 | // Dec atomically decrements the wrapped int64 and returns the new value.
64 | func (i *Int64) Dec() int64 {
65 | return i.Sub(1)
66 | }
67 |
68 | // CAS is an atomic compare-and-swap.
69 | //
70 | // Deprecated: Use CompareAndSwap.
71 | func (i *Int64) CAS(old, new int64) (swapped bool) {
72 | return i.CompareAndSwap(old, new)
73 | }
74 |
75 | // CompareAndSwap is an atomic compare-and-swap.
76 | func (i *Int64) CompareAndSwap(old, new int64) (swapped bool) {
77 | return atomic.CompareAndSwapInt64(&i.v, old, new)
78 | }
79 |
80 | // Store atomically stores the passed value.
81 | func (i *Int64) Store(val int64) {
82 | atomic.StoreInt64(&i.v, val)
83 | }
84 |
85 | // Swap atomically swaps the wrapped int64 and returns the old value.
86 | func (i *Int64) Swap(val int64) (old int64) {
87 | return atomic.SwapInt64(&i.v, val)
88 | }
89 |
90 | // MarshalJSON encodes the wrapped int64 into JSON.
91 | func (i *Int64) MarshalJSON() ([]byte, error) {
92 | return json.Marshal(i.Load())
93 | }
94 |
95 | // UnmarshalJSON decodes JSON into the wrapped int64.
96 | func (i *Int64) UnmarshalJSON(b []byte) error {
97 | var v int64
98 | if err := json.Unmarshal(b, &v); err != nil {
99 | return err
100 | }
101 | i.Store(v)
102 | return nil
103 | }
104 |
105 | // String encodes the wrapped value as a string.
106 | func (i *Int64) String() string {
107 | v := i.Load()
108 | return strconv.FormatInt(int64(v), 10)
109 | }
110 |
--------------------------------------------------------------------------------
/int64_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "math"
26 | "testing"
27 |
28 | "github.com/stretchr/testify/assert"
29 | "github.com/stretchr/testify/require"
30 | )
31 |
32 | func TestInt64(t *testing.T) {
33 | atom := NewInt64(42)
34 |
35 | require.Equal(t, int64(42), atom.Load(), "Load didn't work.")
36 | require.Equal(t, int64(46), atom.Add(4), "Add didn't work.")
37 | require.Equal(t, int64(44), atom.Sub(2), "Sub didn't work.")
38 | require.Equal(t, int64(45), atom.Inc(), "Inc didn't work.")
39 | require.Equal(t, int64(44), atom.Dec(), "Dec didn't work.")
40 |
41 | require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
42 | require.Equal(t, int64(0), atom.Load(), "CAS didn't set the correct value.")
43 |
44 | require.Equal(t, int64(0), atom.Swap(1), "Swap didn't return the old value.")
45 | require.Equal(t, int64(1), atom.Load(), "Swap didn't set the correct value.")
46 |
47 | atom.Store(42)
48 | require.Equal(t, int64(42), atom.Load(), "Store didn't set the correct value.")
49 |
50 | t.Run("JSON/Marshal", func(t *testing.T) {
51 | bytes, err := json.Marshal(atom)
52 | require.NoError(t, err, "json.Marshal errored unexpectedly.")
53 | require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
54 | })
55 |
56 | t.Run("JSON/Unmarshal", func(t *testing.T) {
57 | err := json.Unmarshal([]byte("40"), &atom)
58 | require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
59 | require.Equal(t, int64(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
60 | })
61 |
62 | t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
63 | err := json.Unmarshal([]byte(`"40"`), &atom)
64 | require.Error(t, err, "json.Unmarshal didn't error as expected.")
65 | assertErrorJSONUnmarshalType(t, err,
66 | "json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
67 | })
68 |
69 | t.Run("String", func(t *testing.T) {
70 | t.Run("positive", func(t *testing.T) {
71 | atom := NewInt64(math.MaxInt64)
72 | assert.Equal(t, "9223372036854775807", atom.String(),
73 | "String() returned an unexpected value.")
74 | })
75 |
76 | t.Run("negative", func(t *testing.T) {
77 | atom := NewInt64(math.MinInt64)
78 | assert.Equal(t, "-9223372036854775808", atom.String(),
79 | "String() returned an unexpected value.")
80 | })
81 | })
82 | }
83 |
--------------------------------------------------------------------------------
/internal/gen-atomicint/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020-2022 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | // gen-atomicint generates an atomic wrapper around an integer type.
22 | //
23 | // gen-atomicint -name Int32 -wrapped int32 -file out.go
24 | //
25 | // The generated wrapper will use the functions in the sync/atomic package
26 | // named after the generated type.
27 | package main
28 |
29 | import (
30 | "bytes"
31 | "embed"
32 | "errors"
33 | "flag"
34 | "fmt"
35 | "go/format"
36 | "io"
37 | "log"
38 | "os"
39 | "text/template"
40 | "time"
41 | )
42 |
43 | func main() {
44 | log.SetFlags(0)
45 | if err := run(os.Args[1:]); err != nil {
46 | log.Fatalf("%+v", err)
47 | }
48 | }
49 |
50 | func run(args []string) error {
51 | var opts struct {
52 | Name string
53 | Wrapped string
54 | File string
55 | Unsigned bool
56 | }
57 |
58 | flag := flag.NewFlagSet("gen-atomicint", flag.ContinueOnError)
59 |
60 | flag.StringVar(&opts.Name, "name", "", "name of the generated type (e.g. Int32)")
61 | flag.StringVar(&opts.Wrapped, "wrapped", "", "name of the wrapped type (e.g. int32)")
62 | flag.StringVar(&opts.File, "file", "", "output file path (default: stdout)")
63 | flag.BoolVar(&opts.Unsigned, "unsigned", false, "whether the type is unsigned")
64 |
65 | if err := flag.Parse(args); err != nil {
66 | return err
67 | }
68 |
69 | if len(opts.Name) == 0 || len(opts.Wrapped) == 0 {
70 | return errors.New("flags -name and -wrapped are required")
71 | }
72 |
73 | var w io.Writer = os.Stdout
74 | if file := opts.File; len(file) > 0 {
75 | f, err := os.Create(file)
76 | if err != nil {
77 | return fmt.Errorf("create %q: %v", file, err)
78 | }
79 | defer f.Close()
80 |
81 | w = f
82 | }
83 |
84 | data := struct {
85 | Name string
86 | Wrapped string
87 | Unsigned bool
88 | ToYear int
89 | }{
90 | Name: opts.Name,
91 | Wrapped: opts.Wrapped,
92 | Unsigned: opts.Unsigned,
93 | ToYear: time.Now().Year(),
94 | }
95 |
96 | var buff bytes.Buffer
97 | if err := _tmpl.ExecuteTemplate(&buff, "wrapper.tmpl", data); err != nil {
98 | return fmt.Errorf("render template: %v", err)
99 | }
100 |
101 | bs, err := format.Source(buff.Bytes())
102 | if err != nil {
103 | return fmt.Errorf("reformat source: %v", err)
104 | }
105 |
106 | io.WriteString(w, "// @generated Code generated by gen-atomicint.\n\n")
107 | _, err = w.Write(bs)
108 | return err
109 | }
110 |
111 | var (
112 | //go:embed *.tmpl
113 | _tmplFS embed.FS
114 |
115 | _tmpl = template.Must(template.New("atomicint").ParseFS(_tmplFS, "*.tmpl"))
116 | )
117 |
--------------------------------------------------------------------------------
/internal/gen-atomicint/wrapper.tmpl:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020-{{.ToYear}} Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "strconv"
26 | "sync/atomic"
27 | )
28 |
29 | // {{ .Name }} is an atomic wrapper around {{ .Wrapped }}.
30 | type {{ .Name }} struct {
31 | _ nocmp // disallow non-atomic comparison
32 |
33 | v {{ .Wrapped }}
34 | }
35 |
36 | // New{{ .Name }} creates a new {{ .Name }}.
37 | func New{{ .Name }}(val {{ .Wrapped }}) *{{ .Name }} {
38 | return &{{ .Name }}{v: val}
39 | }
40 |
41 | // Load atomically loads the wrapped value.
42 | func (i *{{ .Name }}) Load() {{ .Wrapped }} {
43 | return atomic.Load{{ .Name }}(&i.v)
44 | }
45 |
46 | // Add atomically adds to the wrapped {{ .Wrapped }} and returns the new value.
47 | func (i *{{ .Name }}) Add(delta {{ .Wrapped }}) {{ .Wrapped }} {
48 | return atomic.Add{{ .Name }}(&i.v, delta)
49 | }
50 |
51 | // Sub atomically subtracts from the wrapped {{ .Wrapped }} and returns the new value.
52 | func (i *{{ .Name }}) Sub(delta {{ .Wrapped }}) {{ .Wrapped }} {
53 | return atomic.Add{{ .Name }}(&i.v,
54 | {{- if .Unsigned -}}
55 | ^(delta - 1)
56 | {{- else -}}
57 | -delta
58 | {{- end -}}
59 | )
60 | }
61 |
62 | // Inc atomically increments the wrapped {{ .Wrapped }} and returns the new value.
63 | func (i *{{ .Name }}) Inc() {{ .Wrapped }} {
64 | return i.Add(1)
65 | }
66 |
67 | // Dec atomically decrements the wrapped {{ .Wrapped }} and returns the new value.
68 | func (i *{{ .Name }}) Dec() {{ .Wrapped }} {
69 | return i.Sub(1)
70 | }
71 |
72 | // CAS is an atomic compare-and-swap.
73 | //
74 | // Deprecated: Use CompareAndSwap.
75 | func (i *{{ .Name }}) CAS(old, new {{ .Wrapped }}) (swapped bool) {
76 | return i.CompareAndSwap(old, new)
77 | }
78 |
79 | // CompareAndSwap is an atomic compare-and-swap.
80 | func (i *{{ .Name }}) CompareAndSwap(old, new {{ .Wrapped }}) (swapped bool) {
81 | return atomic.CompareAndSwap{{ .Name }}(&i.v, old, new)
82 | }
83 |
84 | // Store atomically stores the passed value.
85 | func (i *{{ .Name }}) Store(val {{ .Wrapped }}) {
86 | atomic.Store{{ .Name }}(&i.v, val)
87 | }
88 |
89 | // Swap atomically swaps the wrapped {{ .Wrapped }} and returns the old value.
90 | func (i *{{ .Name }}) Swap(val {{ .Wrapped }}) (old {{ .Wrapped }}) {
91 | return atomic.Swap{{ .Name }}(&i.v, val)
92 | }
93 |
94 | // MarshalJSON encodes the wrapped {{ .Wrapped }} into JSON.
95 | func (i *{{ .Name }}) MarshalJSON() ([]byte, error) {
96 | return json.Marshal(i.Load())
97 | }
98 |
99 | // UnmarshalJSON decodes JSON into the wrapped {{ .Wrapped }}.
100 | func (i *{{ .Name }}) UnmarshalJSON(b []byte) error {
101 | var v {{ .Wrapped }}
102 | if err := json.Unmarshal(b, &v); err != nil {
103 | return err
104 | }
105 | i.Store(v)
106 | return nil
107 | }
108 |
109 | // String encodes the wrapped value as a string.
110 | func (i *{{ .Name }}) String() string {
111 | v := i.Load()
112 | {{ if .Unsigned -}}
113 | return strconv.FormatUint(uint64(v), 10)
114 | {{- else -}}
115 | return strconv.FormatInt(int64(v), 10)
116 | {{- end }}
117 | }
118 |
--------------------------------------------------------------------------------
/internal/gen-atomicwrapper/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020-2022 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | // gen-atomicwrapper generates wrapper types around other atomic types.
22 | //
23 | // It supports plugging in functions which convert the value inside the atomic
24 | // type to the user-facing value. For example,
25 | //
26 | // Given, atomic.Value and the functions,
27 | //
28 | // func packString(string) interface{}
29 | // func unpackString(interface{}) string
30 | //
31 | // We can run the following command:
32 | //
33 | // gen-atomicwrapper -name String -wrapped Value \
34 | // -type string -pack fromString -unpack tostring
35 | //
36 | // This will generate approximately,
37 | //
38 | // type String struct{ v Value }
39 | //
40 | // func (s *String) Load() string {
41 | // return unpackString(v.Load())
42 | // }
43 | //
44 | // func (s *String) Store(s string) {
45 | // return s.v.Store(packString(s))
46 | // }
47 | //
48 | // The packing/unpacking logic allows the stored value to be different from
49 | // the user-facing value.
50 | package main
51 |
52 | import (
53 | "bytes"
54 | "embed"
55 | "errors"
56 | "flag"
57 | "fmt"
58 | "go/format"
59 | "io"
60 | "log"
61 | "os"
62 | "sort"
63 | "strings"
64 | "text/template"
65 | "time"
66 | )
67 |
68 | func main() {
69 | log.SetFlags(0)
70 | if err := run(os.Args[1:]); err != nil {
71 | log.Fatalf("%+v", err)
72 | }
73 | }
74 |
75 | type stringList []string
76 |
77 | func (sl *stringList) String() string {
78 | return strings.Join(*sl, ",")
79 | }
80 |
81 | func (sl *stringList) Set(s string) error {
82 | for _, i := range strings.Split(s, ",") {
83 | *sl = append(*sl, strings.TrimSpace(i))
84 | }
85 | return nil
86 | }
87 |
88 | func run(args []string) error {
89 | var opts struct {
90 | Name string
91 | Wrapped string
92 | Type string
93 |
94 | Imports stringList
95 | Pack, Unpack string
96 |
97 | CAS bool
98 | CompareAndSwap bool
99 | Swap bool
100 | JSON bool
101 |
102 | File string
103 | ToYear int
104 | }
105 |
106 | opts.ToYear = time.Now().Year()
107 |
108 | flag := flag.NewFlagSet("gen-atomicwrapper", flag.ContinueOnError)
109 |
110 | // Required flags
111 | flag.StringVar(&opts.Name, "name", "",
112 | "name of the generated type (e.g. Duration)")
113 | flag.StringVar(&opts.Wrapped, "wrapped", "",
114 | "name of the wrapped atomic (e.g. Int64)")
115 | flag.StringVar(&opts.Type, "type", "",
116 | "name of the type exposed by the atomic (e.g. time.Duration)")
117 |
118 | // Optional flags
119 | flag.Var(&opts.Imports, "imports",
120 | "comma separated list of imports to add")
121 | flag.StringVar(&opts.Pack, "pack", "",
122 | "function to transform values with before storage")
123 | flag.StringVar(&opts.Unpack, "unpack", "",
124 | "function to reverse packing on loading")
125 | flag.StringVar(&opts.File, "file", "",
126 | "output file path (default: stdout)")
127 |
128 | // Switches for individual methods. Underlying atomics must support
129 | // these.
130 | flag.BoolVar(&opts.CAS, "cas", false,
131 | "generate a deprecated `CAS(old, new) bool` method; requires -pack")
132 | flag.BoolVar(&opts.CompareAndSwap, "compareandswap", false,
133 | "generate a `CompareAndSwap(old, new) bool` method; requires -pack")
134 | flag.BoolVar(&opts.Swap, "swap", false,
135 | "generate a `Swap(new) old` method; requires -pack and -unpack")
136 | flag.BoolVar(&opts.JSON, "json", false,
137 | "generate `MarshalJSON/UnmarshalJSON` methods")
138 |
139 | if err := flag.Parse(args); err != nil {
140 | return err
141 | }
142 |
143 | if len(opts.Name) == 0 ||
144 | len(opts.Wrapped) == 0 ||
145 | len(opts.Type) == 0 ||
146 | len(opts.Pack) == 0 ||
147 | len(opts.Unpack) == 0 {
148 | return errors.New("flags -name, -wrapped, -pack, -unpack and -type are required")
149 | }
150 |
151 | if opts.CAS {
152 | opts.CompareAndSwap = true
153 | }
154 |
155 | var w io.Writer = os.Stdout
156 | if file := opts.File; len(file) > 0 {
157 | f, err := os.Create(file)
158 | if err != nil {
159 | return fmt.Errorf("create %q: %v", file, err)
160 | }
161 | defer f.Close()
162 |
163 | w = f
164 | }
165 |
166 | // Import encoding/json if needed.
167 | if opts.JSON {
168 | found := false
169 | for _, imp := range opts.Imports {
170 | if imp == "encoding/json" {
171 | found = true
172 | break
173 | }
174 | }
175 |
176 | if !found {
177 | opts.Imports = append(opts.Imports, "encoding/json")
178 | }
179 | }
180 |
181 | sort.Strings([]string(opts.Imports))
182 |
183 | var buff bytes.Buffer
184 | if err := _tmpl.ExecuteTemplate(&buff, "wrapper.tmpl", opts); err != nil {
185 | return fmt.Errorf("render template: %v", err)
186 | }
187 |
188 | bs, err := format.Source(buff.Bytes())
189 | if err != nil {
190 | return fmt.Errorf("reformat source: %v", err)
191 | }
192 |
193 | io.WriteString(w, "// @generated Code generated by gen-atomicwrapper.\n\n")
194 | _, err = w.Write(bs)
195 | return err
196 | }
197 |
198 | var (
199 | //go:embed *.tmpl
200 | _tmplFS embed.FS
201 |
202 | _tmpl = template.Must(template.New("atomicwrapper").ParseFS(_tmplFS, "*.tmpl"))
203 | )
204 |
--------------------------------------------------------------------------------
/internal/gen-atomicwrapper/wrapper.tmpl:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020-{{.ToYear}} Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | {{ with .Imports }}
24 | import (
25 | {{ range . -}}
26 | {{ printf "%q" . }}
27 | {{ end }}
28 | )
29 | {{ end }}
30 |
31 | // {{ .Name }} is an atomic type-safe wrapper for {{ .Type }} values.
32 | type {{ .Name }} struct{
33 | _ nocmp // disallow non-atomic comparison
34 |
35 | v {{ .Wrapped }}
36 | }
37 |
38 | var _zero{{ .Name }} {{ .Type }}
39 |
40 |
41 | // New{{ .Name }} creates a new {{ .Name }}.
42 | func New{{ .Name }}(val {{ .Type }}) *{{ .Name }} {
43 | x := &{{ .Name }}{}
44 | if val != _zero{{ .Name }} {
45 | x.Store(val)
46 | }
47 | return x
48 | }
49 |
50 | // Load atomically loads the wrapped {{ .Type }}.
51 | func (x *{{ .Name }}) Load() {{ .Type }} {
52 | {{ if .Unpack -}}
53 | return {{ .Unpack }}(x.v.Load())
54 | {{- else -}}
55 | if v := x.v.Load(); v != nil {
56 | return v.({{ .Type }})
57 | }
58 | return _zero{{ .Name }}
59 | {{- end }}
60 | }
61 |
62 | // Store atomically stores the passed {{ .Type }}.
63 | func (x *{{ .Name }}) Store(val {{ .Type }}) {
64 | x.v.Store({{ .Pack }}(val))
65 | }
66 |
67 | {{ if .CAS -}}
68 | // CAS is an atomic compare-and-swap for {{ .Type }} values.
69 | //
70 | // Deprecated: Use CompareAndSwap.
71 | func (x *{{ .Name }}) CAS(old, new {{ .Type }}) (swapped bool) {
72 | return x.CompareAndSwap(old, new)
73 | }
74 | {{- end }}
75 |
76 | {{ if .CompareAndSwap -}}
77 | // CompareAndSwap is an atomic compare-and-swap for {{ .Type }} values.
78 | func (x *{{ .Name }}) CompareAndSwap(old, new {{ .Type }}) (swapped bool) {
79 | {{ if eq .Wrapped "Value" -}}
80 | if x.v.CompareAndSwap({{ .Pack }}(old), {{ .Pack }}(new)) {
81 | return true
82 | }
83 |
84 | if old == _zero{{ .Name }} {
85 | // If the old value is the empty value, then it's possible the
86 | // underlying Value hasn't been set and is nil, so retry with nil.
87 | return x.v.CompareAndSwap(nil, {{ .Pack }}(new))
88 | }
89 |
90 | return false
91 | {{- else -}}
92 | return x.v.CompareAndSwap({{ .Pack }}(old), {{ .Pack }}(new))
93 | {{- end }}
94 | }
95 | {{- end }}
96 |
97 | {{ if .Swap -}}
98 | // Swap atomically stores the given {{ .Type }} and returns the old
99 | // value.
100 | func (x *{{ .Name }}) Swap(val {{ .Type }}) (old {{ .Type }}) {
101 | return {{ .Unpack }}(x.v.Swap({{ .Pack }}(val)))
102 | }
103 | {{- end }}
104 |
105 | {{ if .JSON -}}
106 | // MarshalJSON encodes the wrapped {{ .Type }} into JSON.
107 | func (x *{{ .Name }}) MarshalJSON() ([]byte, error) {
108 | return json.Marshal(x.Load())
109 | }
110 |
111 | // UnmarshalJSON decodes a {{ .Type }} from JSON.
112 | func (x *{{ .Name }}) UnmarshalJSON(b []byte) error {
113 | var v {{ .Type }}
114 | if err := json.Unmarshal(b, &v); err != nil {
115 | return err
116 | }
117 | x.Store(v)
118 | return nil
119 | }
120 | {{- end }}
121 |
--------------------------------------------------------------------------------
/nocmp.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | // nocmp is an uncomparable struct. Embed this inside another struct to make
24 | // it uncomparable.
25 | //
26 | // type Foo struct {
27 | // nocmp
28 | // // ...
29 | // }
30 | //
31 | // This DOES NOT:
32 | //
33 | // - Disallow shallow copies of structs
34 | // - Disallow comparison of pointers to uncomparable structs
35 | type nocmp [0]func()
36 |
--------------------------------------------------------------------------------
/nocmp_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "bytes"
25 | "os"
26 | "os/exec"
27 | "path/filepath"
28 | "reflect"
29 | "testing"
30 |
31 | "github.com/stretchr/testify/assert"
32 | "github.com/stretchr/testify/require"
33 | )
34 |
35 | func TestNocmpComparability(t *testing.T) {
36 | tests := []struct {
37 | desc string
38 | give interface{}
39 | comparable bool
40 | }{
41 | {
42 | desc: "nocmp struct",
43 | give: nocmp{},
44 | },
45 | {
46 | desc: "struct with nocmp embedded",
47 | give: struct{ nocmp }{},
48 | },
49 | {
50 | desc: "pointer to struct with nocmp embedded",
51 | give: &struct{ nocmp }{},
52 | comparable: true,
53 | },
54 |
55 | // All exported types must be uncomparable.
56 | {desc: "Bool", give: Bool{}},
57 | {desc: "Duration", give: Duration{}},
58 | {desc: "Error", give: Error{}},
59 | {desc: "Float64", give: Float64{}},
60 | {desc: "Int32", give: Int32{}},
61 | {desc: "Int64", give: Int64{}},
62 | {desc: "String", give: String{}},
63 | {desc: "Uint32", give: Uint32{}},
64 | {desc: "Uint64", give: Uint64{}},
65 | {desc: "Value", give: Value{}},
66 | }
67 |
68 | for _, tt := range tests {
69 | t.Run(tt.desc, func(t *testing.T) {
70 | typ := reflect.TypeOf(tt.give)
71 | assert.Equalf(t, tt.comparable, typ.Comparable(),
72 | "type %v comparability mismatch", typ)
73 | })
74 | }
75 | }
76 |
77 | // nocmp must not add to the size of a struct in-memory.
78 | func TestNocmpSize(t *testing.T) {
79 | type x struct{ _ int }
80 |
81 | before := reflect.TypeOf(x{}).Size()
82 |
83 | type y struct {
84 | _ nocmp
85 | _ x
86 | }
87 |
88 | after := reflect.TypeOf(y{}).Size()
89 |
90 | assert.Equal(t, before, after,
91 | "expected nocmp to have no effect on struct size")
92 | }
93 |
94 | // This test will fail to compile if we disallow copying of nocmp.
95 | //
96 | // We need to allow this so that users can do,
97 | //
98 | // var x atomic.Int32
99 | // x = atomic.NewInt32(1)
100 | func TestNocmpCopy(t *testing.T) {
101 | type foo struct{ _ nocmp }
102 |
103 | t.Run("struct copy", func(t *testing.T) {
104 | a := foo{}
105 | b := a
106 | _ = b // unused
107 | })
108 |
109 | t.Run("pointer copy", func(t *testing.T) {
110 | a := &foo{}
111 | b := *a
112 | _ = b // unused
113 | })
114 | }
115 |
116 | // Fake go.mod with no dependencies.
117 | const _exampleGoMod = `module example.com/nocmp`
118 |
119 | const _badFile = `package atomic
120 |
121 | import "fmt"
122 |
123 | type Int64 struct {
124 | nocmp
125 |
126 | v int64
127 | }
128 |
129 | func shouldNotCompile() {
130 | var x, y Int64
131 | fmt.Println(x == y)
132 | }
133 | `
134 |
135 | func TestNocmpIntegration(t *testing.T) {
136 | tempdir := t.TempDir()
137 |
138 | nocmp, err := os.ReadFile("nocmp.go")
139 | require.NoError(t, err, "unable to read nocmp.go")
140 |
141 | require.NoError(t,
142 | os.WriteFile(filepath.Join(tempdir, "go.mod"), []byte(_exampleGoMod), 0o644),
143 | "unable to write go.mod")
144 |
145 | require.NoError(t,
146 | os.WriteFile(filepath.Join(tempdir, "nocmp.go"), nocmp, 0o644),
147 | "unable to write nocmp.go")
148 |
149 | require.NoError(t,
150 | os.WriteFile(filepath.Join(tempdir, "bad.go"), []byte(_badFile), 0o644),
151 | "unable to write bad.go")
152 |
153 | var stderr bytes.Buffer
154 | cmd := exec.Command("go", "build")
155 | cmd.Dir = tempdir
156 | // Create a minimal build environment with only HOME set so that "go
157 | // build" has somewhere to put the cache and other Go files in.
158 | cmd.Env = []string{"HOME=" + filepath.Join(tempdir, "home")}
159 | cmd.Stderr = &stderr
160 | require.Error(t, cmd.Run(), "bad.go must not compile")
161 |
162 | assert.Contains(t, stderr.String(),
163 | "struct containing nocmp cannot be compared")
164 | }
165 |
--------------------------------------------------------------------------------
/pointer_go118.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2022 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | //go:build go1.18
22 | // +build go1.18
23 |
24 | package atomic
25 |
26 | import (
27 | "encoding/json"
28 | "fmt"
29 | )
30 |
31 | // String returns a human readable representation of a Pointer's underlying value.
32 | func (p *Pointer[T]) String() string {
33 | return fmt.Sprint(p.Load())
34 | }
35 |
36 | // MarshalJSON encodes the wrapped pointer into JSON.
37 | func (p *Pointer[T]) MarshalJSON() ([]byte, error) {
38 | return json.Marshal(p.Load())
39 | }
40 |
41 | // UnmarshalJSON decodes JSON into the wrapped pointer.
42 | func (p *Pointer[T]) UnmarshalJSON(b []byte) error {
43 | var v T
44 | if err := json.Unmarshal(b, &v); err != nil {
45 | return err
46 | }
47 | p.Store(&v)
48 | return nil
49 | }
50 |
--------------------------------------------------------------------------------
/pointer_go118_pre119.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2022 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | //go:build go1.18 && !go1.19
22 | // +build go1.18,!go1.19
23 |
24 | package atomic
25 |
26 | import "unsafe"
27 |
28 | type Pointer[T any] struct {
29 | _ nocmp // disallow non-atomic comparison
30 | p UnsafePointer
31 | }
32 |
33 | // NewPointer creates a new Pointer.
34 | func NewPointer[T any](v *T) *Pointer[T] {
35 | var p Pointer[T]
36 | if v != nil {
37 | p.p.Store(unsafe.Pointer(v))
38 | }
39 | return &p
40 | }
41 |
42 | // Load atomically loads the wrapped value.
43 | func (p *Pointer[T]) Load() *T {
44 | return (*T)(p.p.Load())
45 | }
46 |
47 | // Store atomically stores the passed value.
48 | func (p *Pointer[T]) Store(val *T) {
49 | p.p.Store(unsafe.Pointer(val))
50 | }
51 |
52 | // Swap atomically swaps the wrapped pointer and returns the old value.
53 | func (p *Pointer[T]) Swap(val *T) (old *T) {
54 | return (*T)(p.p.Swap(unsafe.Pointer(val)))
55 | }
56 |
57 | // CompareAndSwap is an atomic compare-and-swap.
58 | func (p *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) {
59 | return p.p.CompareAndSwap(unsafe.Pointer(old), unsafe.Pointer(new))
60 | }
61 |
--------------------------------------------------------------------------------
/pointer_go119.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2022 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | //go:build go1.19
22 | // +build go1.19
23 |
24 | package atomic
25 |
26 | import "sync/atomic"
27 |
28 | // Pointer is an atomic pointer of type *T.
29 | type Pointer[T any] struct {
30 | _ nocmp // disallow non-atomic comparison
31 | p atomic.Pointer[T]
32 | }
33 |
34 | // NewPointer creates a new Pointer.
35 | func NewPointer[T any](v *T) *Pointer[T] {
36 | var p Pointer[T]
37 | if v != nil {
38 | p.p.Store(v)
39 | }
40 | return &p
41 | }
42 |
43 | // Load atomically loads the wrapped value.
44 | func (p *Pointer[T]) Load() *T {
45 | return p.p.Load()
46 | }
47 |
48 | // Store atomically stores the passed value.
49 | func (p *Pointer[T]) Store(val *T) {
50 | p.p.Store(val)
51 | }
52 |
53 | // Swap atomically swaps the wrapped pointer and returns the old value.
54 | func (p *Pointer[T]) Swap(val *T) (old *T) {
55 | return p.p.Swap(val)
56 | }
57 |
58 | // CompareAndSwap is an atomic compare-and-swap.
59 | func (p *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) {
60 | return p.p.CompareAndSwap(old, new)
61 | }
62 |
--------------------------------------------------------------------------------
/pointer_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2022 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | //go:build go1.18
22 | // +build go1.18
23 |
24 | package atomic
25 |
26 | import (
27 | "encoding/json"
28 | "fmt"
29 | "testing"
30 |
31 | "github.com/stretchr/testify/require"
32 | )
33 |
34 | func TestPointer(t *testing.T) {
35 | type foo struct {
36 | V int `json:"v"`
37 | }
38 |
39 | i := foo{42}
40 | j := foo{0}
41 | k := foo{1}
42 |
43 | tests := []struct {
44 | desc string
45 | newAtomic func() *Pointer[foo]
46 | initial *foo
47 | }{
48 | {
49 | desc: "New",
50 | newAtomic: func() *Pointer[foo] {
51 | return NewPointer(&i)
52 | },
53 | initial: &i,
54 | },
55 | {
56 | desc: "New/nil",
57 | newAtomic: func() *Pointer[foo] {
58 | return NewPointer[foo](nil)
59 | },
60 | initial: nil,
61 | },
62 | {
63 | desc: "zero value",
64 | newAtomic: func() *Pointer[foo] {
65 | var p Pointer[foo]
66 | return &p
67 | },
68 | initial: nil,
69 | },
70 | }
71 |
72 | for _, tt := range tests {
73 | t.Run(tt.desc, func(t *testing.T) {
74 | t.Run("Load", func(t *testing.T) {
75 | atom := tt.newAtomic()
76 | require.Equal(t, tt.initial, atom.Load(), "Load should report nil.")
77 | })
78 |
79 | t.Run("Swap", func(t *testing.T) {
80 | atom := tt.newAtomic()
81 | require.Equal(t, tt.initial, atom.Swap(&k), "Swap didn't return the old value.")
82 | require.Equal(t, &k, atom.Load(), "Swap didn't set the correct value.")
83 | })
84 |
85 | t.Run("CAS", func(t *testing.T) {
86 | atom := tt.newAtomic()
87 | require.True(t, atom.CompareAndSwap(tt.initial, &j), "CAS didn't report a swap.")
88 | require.Equal(t, &j, atom.Load(), "CAS didn't set the correct value.")
89 | })
90 |
91 | t.Run("Store", func(t *testing.T) {
92 | atom := tt.newAtomic()
93 | atom.Store(&i)
94 | require.Equal(t, &i, atom.Load(), "Store didn't set the correct value.")
95 | })
96 | t.Run("String", func(t *testing.T) {
97 | atom := tt.newAtomic()
98 | require.Equal(t, fmt.Sprint(tt.initial), atom.String(), "String did not return the correct value.")
99 | })
100 |
101 | t.Run("MarshalJSON", func(t *testing.T) {
102 | atom := tt.newAtomic()
103 | marshaledPointer, err := json.Marshal(atom)
104 | require.NoError(t, err)
105 | marshaledRaw, err := json.Marshal(tt.initial)
106 | require.NoError(t, err)
107 | require.Equal(t, marshaledRaw, marshaledPointer, "MarshalJSON did not return the correct value.")
108 | })
109 | })
110 | }
111 |
112 | t.Run("UnmarshalJSON", func(t *testing.T) {
113 | var p Pointer[foo]
114 |
115 | require.NoError(t, json.Unmarshal([]byte(`{"v":1024}`), &p))
116 | require.Equal(t, 1024, p.Load().V, "UnmarshalJSON should have expected result")
117 | })
118 |
119 | t.Run("UnmarshalJSON error", func(t *testing.T) {
120 | var p Pointer[foo]
121 |
122 | require.Error(t, json.Unmarshal([]byte(`{"v":true}`), &p), "json.Unmarshal should return an error")
123 | })
124 | }
125 |
--------------------------------------------------------------------------------
/stress_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "errors"
25 | "math"
26 | "runtime"
27 | "sync"
28 | "sync/atomic"
29 | "testing"
30 | "time"
31 | )
32 |
33 | const (
34 | _parallelism = 4
35 | _iterations = 1000
36 | )
37 |
38 | var _stressTests = map[string]func() func(){
39 | "i32/std": stressStdInt32,
40 | "i32": stressInt32,
41 | "i64/std": stressStdInt64,
42 | "i64": stressInt64,
43 | "u32/std": stressStdUint32,
44 | "u32": stressUint32,
45 | "u64/std": stressStdUint64,
46 | "u64": stressUint64,
47 | "f64": stressFloat64,
48 | "bool": stressBool,
49 | "string": stressString,
50 | "duration": stressDuration,
51 | "error": stressError,
52 | "time": stressTime,
53 | }
54 |
55 | func TestStress(t *testing.T) {
56 | for name, ff := range _stressTests {
57 | t.Run(name, func(t *testing.T) {
58 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism))
59 |
60 | start := make(chan struct{})
61 | var wg sync.WaitGroup
62 | wg.Add(_parallelism)
63 | f := ff()
64 | for i := 0; i < _parallelism; i++ {
65 | go func() {
66 | defer wg.Done()
67 | <-start
68 | for j := 0; j < _iterations; j++ {
69 | f()
70 | }
71 | }()
72 | }
73 | close(start)
74 | wg.Wait()
75 | })
76 | }
77 | }
78 |
79 | func BenchmarkStress(b *testing.B) {
80 | for name, ff := range _stressTests {
81 | b.Run(name, func(b *testing.B) {
82 | f := ff()
83 |
84 | b.Run("serial", func(b *testing.B) {
85 | for i := 0; i < b.N; i++ {
86 | f()
87 | }
88 | })
89 |
90 | b.Run("parallel", func(b *testing.B) {
91 | b.RunParallel(func(pb *testing.PB) {
92 | for pb.Next() {
93 | f()
94 | }
95 | })
96 | })
97 | })
98 | }
99 | }
100 |
101 | func stressStdInt32() func() {
102 | var atom int32
103 | return func() {
104 | atomic.LoadInt32(&atom)
105 | atomic.AddInt32(&atom, 1)
106 | atomic.AddInt32(&atom, -2)
107 | atomic.AddInt32(&atom, 1)
108 | atomic.AddInt32(&atom, -1)
109 | atomic.CompareAndSwapInt32(&atom, 1, 0)
110 | atomic.SwapInt32(&atom, 5)
111 | atomic.StoreInt32(&atom, 1)
112 | }
113 | }
114 |
115 | func stressInt32() func() {
116 | var atom Int32
117 | return func() {
118 | atom.Load()
119 | atom.Add(1)
120 | atom.Sub(2)
121 | atom.Inc()
122 | atom.Dec()
123 | atom.CAS(1, 0)
124 | atom.Swap(5)
125 | atom.Store(1)
126 | }
127 | }
128 |
129 | func stressStdInt64() func() {
130 | var atom int64
131 | return func() {
132 | atomic.LoadInt64(&atom)
133 | atomic.AddInt64(&atom, 1)
134 | atomic.AddInt64(&atom, -2)
135 | atomic.AddInt64(&atom, 1)
136 | atomic.AddInt64(&atom, -1)
137 | atomic.CompareAndSwapInt64(&atom, 1, 0)
138 | atomic.SwapInt64(&atom, 5)
139 | atomic.StoreInt64(&atom, 1)
140 | }
141 | }
142 |
143 | func stressInt64() func() {
144 | var atom Int64
145 | return func() {
146 | atom.Load()
147 | atom.Add(1)
148 | atom.Sub(2)
149 | atom.Inc()
150 | atom.Dec()
151 | atom.CAS(1, 0)
152 | atom.Swap(5)
153 | atom.Store(1)
154 | }
155 | }
156 |
157 | func stressStdUint32() func() {
158 | var atom uint32
159 | return func() {
160 | atomic.LoadUint32(&atom)
161 | atomic.AddUint32(&atom, 1)
162 | // Adding `MaxUint32` is the same as subtracting 1
163 | atomic.AddUint32(&atom, math.MaxUint32-1)
164 | atomic.AddUint32(&atom, 1)
165 | atomic.AddUint32(&atom, math.MaxUint32)
166 | atomic.CompareAndSwapUint32(&atom, 1, 0)
167 | atomic.SwapUint32(&atom, 5)
168 | atomic.StoreUint32(&atom, 1)
169 | }
170 | }
171 |
172 | func stressUint32() func() {
173 | var atom Uint32
174 | return func() {
175 | atom.Load()
176 | atom.Add(1)
177 | atom.Sub(2)
178 | atom.Inc()
179 | atom.Dec()
180 | atom.CAS(1, 0)
181 | atom.Swap(5)
182 | atom.Store(1)
183 | }
184 | }
185 |
186 | func stressStdUint64() func() {
187 | var atom uint64
188 | return func() {
189 | atomic.LoadUint64(&atom)
190 | atomic.AddUint64(&atom, 1)
191 | // Adding `MaxUint64` is the same as subtracting 1
192 | atomic.AddUint64(&atom, math.MaxUint64-1)
193 | atomic.AddUint64(&atom, 1)
194 | atomic.AddUint64(&atom, math.MaxUint64)
195 | atomic.CompareAndSwapUint64(&atom, 1, 0)
196 | atomic.SwapUint64(&atom, 5)
197 | atomic.StoreUint64(&atom, 1)
198 | }
199 | }
200 |
201 | func stressUint64() func() {
202 | var atom Uint64
203 | return func() {
204 | atom.Load()
205 | atom.Add(1)
206 | atom.Sub(2)
207 | atom.Inc()
208 | atom.Dec()
209 | atom.CAS(1, 0)
210 | atom.Swap(5)
211 | atom.Store(1)
212 | }
213 | }
214 |
215 | func stressFloat64() func() {
216 | var atom Float64
217 | return func() {
218 | atom.Load()
219 | atom.CAS(1.0, 0.1)
220 | atom.Add(1.1)
221 | atom.Sub(0.2)
222 | atom.Store(1.0)
223 | }
224 | }
225 |
226 | func stressBool() func() {
227 | var atom Bool
228 | return func() {
229 | atom.Load()
230 | atom.Store(false)
231 | atom.Swap(true)
232 | atom.CAS(true, false)
233 | atom.CAS(true, false)
234 | atom.Load()
235 | atom.Toggle()
236 | atom.Toggle()
237 | }
238 | }
239 |
240 | func stressString() func() {
241 | var atom String
242 | return func() {
243 | atom.Load()
244 | atom.Store("abc")
245 | atom.Load()
246 | atom.Store("def")
247 | atom.Load()
248 | atom.Store("")
249 | }
250 | }
251 |
252 | func stressDuration() func() {
253 | var atom = NewDuration(0)
254 | return func() {
255 | atom.Load()
256 | atom.Add(1)
257 | atom.Sub(2)
258 | atom.CAS(1, 0)
259 | atom.Swap(5)
260 | atom.Store(1)
261 | }
262 | }
263 |
264 | func stressError() func() {
265 | var atom = NewError(nil)
266 | var err1 = errors.New("err1")
267 | var err2 = errors.New("err2")
268 | return func() {
269 | atom.Load()
270 | atom.Store(err1)
271 | atom.Load()
272 | atom.Store(err2)
273 | atom.Load()
274 | atom.Store(nil)
275 | }
276 | }
277 |
278 | func stressTime() func() {
279 | var atom = NewTime(time.Date(2021, 6, 17, 9, 0, 0, 0, time.UTC))
280 | var dayAgo = time.Date(2021, 6, 16, 9, 0, 0, 0, time.UTC)
281 | var weekAgo = time.Date(2021, 6, 10, 9, 0, 0, 0, time.UTC)
282 | return func() {
283 | atom.Load()
284 | atom.Store(dayAgo)
285 | atom.Load()
286 | atom.Store(weekAgo)
287 | atom.Store(time.Time{})
288 | }
289 | }
290 |
--------------------------------------------------------------------------------
/string.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicwrapper.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | // String is an atomic type-safe wrapper for string values.
26 | type String struct {
27 | _ nocmp // disallow non-atomic comparison
28 |
29 | v Value
30 | }
31 |
32 | var _zeroString string
33 |
34 | // NewString creates a new String.
35 | func NewString(val string) *String {
36 | x := &String{}
37 | if val != _zeroString {
38 | x.Store(val)
39 | }
40 | return x
41 | }
42 |
43 | // Load atomically loads the wrapped string.
44 | func (x *String) Load() string {
45 | return unpackString(x.v.Load())
46 | }
47 |
48 | // Store atomically stores the passed string.
49 | func (x *String) Store(val string) {
50 | x.v.Store(packString(val))
51 | }
52 |
53 | // CompareAndSwap is an atomic compare-and-swap for string values.
54 | func (x *String) CompareAndSwap(old, new string) (swapped bool) {
55 | if x.v.CompareAndSwap(packString(old), packString(new)) {
56 | return true
57 | }
58 |
59 | if old == _zeroString {
60 | // If the old value is the empty value, then it's possible the
61 | // underlying Value hasn't been set and is nil, so retry with nil.
62 | return x.v.CompareAndSwap(nil, packString(new))
63 | }
64 |
65 | return false
66 | }
67 |
68 | // Swap atomically stores the given string and returns the old
69 | // value.
70 | func (x *String) Swap(val string) (old string) {
71 | return unpackString(x.v.Swap(packString(val)))
72 | }
73 |
--------------------------------------------------------------------------------
/string_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | //go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped Value -pack packString -unpack unpackString -compareandswap -swap -file=string.go
24 |
25 | func packString(s string) interface{} {
26 | return s
27 | }
28 |
29 | func unpackString(v interface{}) string {
30 | if s, ok := v.(string); ok {
31 | return s
32 | }
33 | return ""
34 | }
35 |
36 | // String returns the wrapped value.
37 | func (s *String) String() string {
38 | return s.Load()
39 | }
40 |
41 | // MarshalText encodes the wrapped string into a textual form.
42 | //
43 | // This makes it encodable as JSON, YAML, XML, and more.
44 | func (s *String) MarshalText() ([]byte, error) {
45 | return []byte(s.Load()), nil
46 | }
47 |
48 | // UnmarshalText decodes text and replaces the wrapped string with it.
49 | //
50 | // This makes it decodable from JSON, YAML, XML, and more.
51 | func (s *String) UnmarshalText(b []byte) error {
52 | s.Store(string(b))
53 | return nil
54 | }
55 |
--------------------------------------------------------------------------------
/string_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016-2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "encoding/xml"
26 | "testing"
27 |
28 | "github.com/stretchr/testify/assert"
29 | "github.com/stretchr/testify/require"
30 | )
31 |
32 | func TestStringNoInitialValue(t *testing.T) {
33 | atom := &String{}
34 | require.Equal(t, "", atom.Load(), "Initial value should be blank string")
35 | }
36 |
37 | func TestString(t *testing.T) {
38 | atom := NewString("")
39 | require.Equal(t, "", atom.Load(), "Expected Load to return initialized value")
40 |
41 | atom.Store("abc")
42 | require.Equal(t, "abc", atom.Load(), "Unexpected value after Store")
43 |
44 | atom = NewString("bcd")
45 | require.Equal(t, "bcd", atom.Load(), "Expected Load to return initialized value")
46 |
47 | t.Run("JSON/Marshal", func(t *testing.T) {
48 | bytes, err := json.Marshal(atom)
49 | require.NoError(t, err, "json.Marshal errored unexpectedly.")
50 | require.Equal(t, []byte(`"bcd"`), bytes, "json.Marshal encoded the wrong bytes.")
51 | })
52 |
53 | t.Run("JSON/Unmarshal", func(t *testing.T) {
54 | err := json.Unmarshal([]byte(`"abc"`), &atom)
55 | require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
56 | require.Equal(t, "abc", atom.Load(), "json.Unmarshal didn't set the correct value.")
57 | })
58 |
59 | t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
60 | err := json.Unmarshal([]byte("42"), &atom)
61 | require.Error(t, err, "json.Unmarshal didn't error as expected.")
62 | assertErrorJSONUnmarshalType(t, err,
63 | "json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
64 | })
65 |
66 | atom = NewString("foo")
67 |
68 | t.Run("XML/Marshal", func(t *testing.T) {
69 | bytes, err := xml.Marshal(atom)
70 | require.NoError(t, err, "xml.Marshal errored unexpectedly.")
71 | require.Equal(t, []byte("foo"), bytes, "xml.Marshal encoded the wrong bytes.")
72 | })
73 |
74 | t.Run("XML/Unmarshal", func(t *testing.T) {
75 | err := xml.Unmarshal([]byte("bar"), &atom)
76 | require.NoError(t, err, "xml.Unmarshal errored unexpectedly.")
77 | require.Equal(t, "bar", atom.Load(), "xml.Unmarshal didn't set the correct value.")
78 | })
79 |
80 | t.Run("String", func(t *testing.T) {
81 | atom := NewString("foo")
82 | assert.Equal(t, "foo", atom.String(),
83 | "String() returned an unexpected value.")
84 | })
85 |
86 | t.Run("CompareAndSwap", func(t *testing.T) {
87 | atom := NewString("foo")
88 |
89 | swapped := atom.CompareAndSwap("bar", "bar")
90 | require.False(t, swapped, "swapped isn't false")
91 | require.Equal(t, atom.Load(), "foo", "Load returned wrong value")
92 |
93 | swapped = atom.CompareAndSwap("foo", "bar")
94 | require.True(t, swapped, "swapped isn't true")
95 | require.Equal(t, atom.Load(), "bar", "Load returned wrong value")
96 | })
97 |
98 | t.Run("Swap", func(t *testing.T) {
99 | atom := NewString("foo")
100 |
101 | old := atom.Swap("bar")
102 | require.Equal(t, old, "foo", "Swap returned wrong value")
103 | require.Equal(t, atom.Load(), "bar", "Load returned wrong value")
104 | })
105 | }
106 |
107 | func TestString_InitializeDefault(t *testing.T) {
108 | tests := []struct {
109 | msg string
110 | newStr func() *String
111 | }{
112 | {
113 | msg: "Uninitialized",
114 | newStr: func() *String {
115 | var s String
116 | return &s
117 | },
118 | },
119 | {
120 | msg: "NewString with default",
121 | newStr: func() *String {
122 | return NewString("")
123 | },
124 | },
125 | {
126 | msg: "String swapped with default",
127 | newStr: func() *String {
128 | s := NewString("initial")
129 | s.Swap("")
130 | return s
131 | },
132 | },
133 | {
134 | msg: "String CAS'd with default",
135 | newStr: func() *String {
136 | s := NewString("initial")
137 | s.CompareAndSwap("initial", "")
138 | return s
139 | },
140 | },
141 | }
142 |
143 | for _, tt := range tests {
144 | t.Run(tt.msg, func(t *testing.T) {
145 | t.Run("MarshalText", func(t *testing.T) {
146 | str := tt.newStr()
147 | text, err := str.MarshalText()
148 | require.NoError(t, err)
149 | assert.Equal(t, "", string(text), "")
150 | })
151 |
152 | t.Run("String", func(t *testing.T) {
153 | str := tt.newStr()
154 | assert.Equal(t, "", str.String())
155 | })
156 |
157 | t.Run("CompareAndSwap", func(t *testing.T) {
158 | str := tt.newStr()
159 | require.True(t, str.CompareAndSwap("", "new"))
160 | assert.Equal(t, "new", str.Load())
161 | })
162 |
163 | t.Run("Swap", func(t *testing.T) {
164 | str := tt.newStr()
165 | assert.Equal(t, "", str.Swap("new"))
166 | })
167 | })
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/time.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicwrapper.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | import (
26 | "time"
27 | )
28 |
29 | // Time is an atomic type-safe wrapper for time.Time values.
30 | type Time struct {
31 | _ nocmp // disallow non-atomic comparison
32 |
33 | v Value
34 | }
35 |
36 | var _zeroTime time.Time
37 |
38 | // NewTime creates a new Time.
39 | func NewTime(val time.Time) *Time {
40 | x := &Time{}
41 | if val != _zeroTime {
42 | x.Store(val)
43 | }
44 | return x
45 | }
46 |
47 | // Load atomically loads the wrapped time.Time.
48 | func (x *Time) Load() time.Time {
49 | return unpackTime(x.v.Load())
50 | }
51 |
52 | // Store atomically stores the passed time.Time.
53 | func (x *Time) Store(val time.Time) {
54 | x.v.Store(packTime(val))
55 | }
56 |
--------------------------------------------------------------------------------
/time_ext.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import "time"
24 |
25 | //go:generate bin/gen-atomicwrapper -name=Time -type=time.Time -wrapped=Value -pack=packTime -unpack=unpackTime -imports time -file=time.go
26 |
27 | func packTime(t time.Time) interface{} {
28 | return t
29 | }
30 |
31 | func unpackTime(v interface{}) time.Time {
32 | if t, ok := v.(time.Time); ok {
33 | return t
34 | }
35 | return time.Time{}
36 | }
37 |
--------------------------------------------------------------------------------
/time_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "testing"
25 | "time"
26 |
27 | "github.com/stretchr/testify/assert"
28 | "github.com/stretchr/testify/require"
29 | )
30 |
31 | func TestTime(t *testing.T) {
32 | start := time.Date(2021, 6, 17, 9, 10, 0, 0, time.UTC)
33 | atom := NewTime(start)
34 |
35 | require.Equal(t, start, atom.Load(), "Load didn't work")
36 | require.Equal(t, time.Time{}, NewTime(time.Time{}).Load(), "Default time value is wrong")
37 | }
38 |
39 | func TestTimeLocation(t *testing.T) {
40 | // Check TZ data hasn't been lost from load/store.
41 | ny, err := time.LoadLocation("America/New_York")
42 | require.NoError(t, err, "Failed to load location")
43 | nyTime := NewTime(time.Date(2021, 1, 1, 0, 0, 0, 0, ny))
44 |
45 | var atom Time
46 | atom.Store(nyTime.Load())
47 |
48 | assert.Equal(t, ny, atom.Load().Location(), "Location information is wrong")
49 | }
50 |
51 | func TestLargeTime(t *testing.T) {
52 | // Check "large/small" time that are beyond int64 ns
53 | // representation (< year 1678 or > year 2262) can be
54 | // correctly load/store'd.
55 | t.Parallel()
56 |
57 | t.Run("future", func(t *testing.T) {
58 | future := time.Date(2262, 12, 31, 0, 0, 0, 0, time.UTC)
59 | atom := NewTime(future)
60 | dayAfterFuture := atom.Load().AddDate(0, 1, 0)
61 |
62 | atom.Store(dayAfterFuture)
63 | assert.Equal(t, 2263, atom.Load().Year())
64 | })
65 |
66 | t.Run("past", func(t *testing.T) {
67 | past := time.Date(1678, 1, 1, 0, 0, 0, 0, time.UTC)
68 | atom := NewTime(past)
69 | dayBeforePast := atom.Load().AddDate(0, -1, 0)
70 |
71 | atom.Store(dayBeforePast)
72 | assert.Equal(t, 1677, atom.Load().Year())
73 | })
74 | }
75 |
76 | func TestMonotonic(t *testing.T) {
77 | before := NewTime(time.Now())
78 | time.Sleep(15 * time.Millisecond)
79 | after := NewTime(time.Now())
80 |
81 | // try loading/storing before and test monotonic clock value hasn't been lost
82 | bt := before.Load()
83 | before.Store(bt)
84 | d := after.Load().Sub(before.Load())
85 | assert.True(t, 15 <= d.Milliseconds())
86 | }
87 |
--------------------------------------------------------------------------------
/tools/go.mod:
--------------------------------------------------------------------------------
1 | module go.uber.org/atomic/tools
2 |
3 | go 1.18
4 |
5 | require (
6 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616
7 | honnef.co/go/tools v0.4.7
8 | )
9 |
10 | require (
11 | github.com/BurntSushi/toml v1.2.1 // indirect
12 | golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect
13 | golang.org/x/mod v0.12.0 // indirect
14 | golang.org/x/sys v0.11.0 // indirect
15 | golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5 // indirect
16 | )
17 |
--------------------------------------------------------------------------------
/tools/go.sum:
--------------------------------------------------------------------------------
1 | github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
2 | github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
3 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
4 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
5 | golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE=
6 | golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
7 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
8 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
9 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
10 | golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
11 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
12 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
13 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
14 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
15 | golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
16 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
17 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
18 | golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
19 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
20 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
21 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
22 | golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5 h1:Vk4mysSz+GqQK2eqgWbo4zEO89wkeAjJiFIr9bpqa8k=
23 | golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
24 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
25 | honnef.co/go/tools v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs=
26 | honnef.co/go/tools v0.4.7/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0=
27 |
--------------------------------------------------------------------------------
/tools/tools.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | //go:build tools
22 | // +build tools
23 |
24 | package tools
25 |
26 | import (
27 | // Tools used during development.
28 | _ "golang.org/x/lint/golint"
29 | _ "honnef.co/go/tools/cmd/staticcheck"
30 | )
31 |
--------------------------------------------------------------------------------
/uint32.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicint.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | import (
26 | "encoding/json"
27 | "strconv"
28 | "sync/atomic"
29 | )
30 |
31 | // Uint32 is an atomic wrapper around uint32.
32 | type Uint32 struct {
33 | _ nocmp // disallow non-atomic comparison
34 |
35 | v uint32
36 | }
37 |
38 | // NewUint32 creates a new Uint32.
39 | func NewUint32(val uint32) *Uint32 {
40 | return &Uint32{v: val}
41 | }
42 |
43 | // Load atomically loads the wrapped value.
44 | func (i *Uint32) Load() uint32 {
45 | return atomic.LoadUint32(&i.v)
46 | }
47 |
48 | // Add atomically adds to the wrapped uint32 and returns the new value.
49 | func (i *Uint32) Add(delta uint32) uint32 {
50 | return atomic.AddUint32(&i.v, delta)
51 | }
52 |
53 | // Sub atomically subtracts from the wrapped uint32 and returns the new value.
54 | func (i *Uint32) Sub(delta uint32) uint32 {
55 | return atomic.AddUint32(&i.v, ^(delta - 1))
56 | }
57 |
58 | // Inc atomically increments the wrapped uint32 and returns the new value.
59 | func (i *Uint32) Inc() uint32 {
60 | return i.Add(1)
61 | }
62 |
63 | // Dec atomically decrements the wrapped uint32 and returns the new value.
64 | func (i *Uint32) Dec() uint32 {
65 | return i.Sub(1)
66 | }
67 |
68 | // CAS is an atomic compare-and-swap.
69 | //
70 | // Deprecated: Use CompareAndSwap.
71 | func (i *Uint32) CAS(old, new uint32) (swapped bool) {
72 | return i.CompareAndSwap(old, new)
73 | }
74 |
75 | // CompareAndSwap is an atomic compare-and-swap.
76 | func (i *Uint32) CompareAndSwap(old, new uint32) (swapped bool) {
77 | return atomic.CompareAndSwapUint32(&i.v, old, new)
78 | }
79 |
80 | // Store atomically stores the passed value.
81 | func (i *Uint32) Store(val uint32) {
82 | atomic.StoreUint32(&i.v, val)
83 | }
84 |
85 | // Swap atomically swaps the wrapped uint32 and returns the old value.
86 | func (i *Uint32) Swap(val uint32) (old uint32) {
87 | return atomic.SwapUint32(&i.v, val)
88 | }
89 |
90 | // MarshalJSON encodes the wrapped uint32 into JSON.
91 | func (i *Uint32) MarshalJSON() ([]byte, error) {
92 | return json.Marshal(i.Load())
93 | }
94 |
95 | // UnmarshalJSON decodes JSON into the wrapped uint32.
96 | func (i *Uint32) UnmarshalJSON(b []byte) error {
97 | var v uint32
98 | if err := json.Unmarshal(b, &v); err != nil {
99 | return err
100 | }
101 | i.Store(v)
102 | return nil
103 | }
104 |
105 | // String encodes the wrapped value as a string.
106 | func (i *Uint32) String() string {
107 | v := i.Load()
108 | return strconv.FormatUint(uint64(v), 10)
109 | }
110 |
--------------------------------------------------------------------------------
/uint32_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "math"
26 | "testing"
27 |
28 | "github.com/stretchr/testify/assert"
29 | "github.com/stretchr/testify/require"
30 | )
31 |
32 | func TestUint32(t *testing.T) {
33 | atom := NewUint32(42)
34 |
35 | require.Equal(t, uint32(42), atom.Load(), "Load didn't work.")
36 | require.Equal(t, uint32(46), atom.Add(4), "Add didn't work.")
37 | require.Equal(t, uint32(44), atom.Sub(2), "Sub didn't work.")
38 | require.Equal(t, uint32(45), atom.Inc(), "Inc didn't work.")
39 | require.Equal(t, uint32(44), atom.Dec(), "Dec didn't work.")
40 |
41 | require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
42 | require.Equal(t, uint32(0), atom.Load(), "CAS didn't set the correct value.")
43 |
44 | require.Equal(t, uint32(0), atom.Swap(1), "Swap didn't return the old value.")
45 | require.Equal(t, uint32(1), atom.Load(), "Swap didn't set the correct value.")
46 |
47 | atom.Store(42)
48 | require.Equal(t, uint32(42), atom.Load(), "Store didn't set the correct value.")
49 |
50 | t.Run("JSON/Marshal", func(t *testing.T) {
51 | bytes, err := json.Marshal(atom)
52 | require.NoError(t, err, "json.Marshal errored unexpectedly.")
53 | require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
54 | })
55 |
56 | t.Run("JSON/Unmarshal", func(t *testing.T) {
57 | err := json.Unmarshal([]byte("40"), &atom)
58 | require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
59 | require.Equal(t, uint32(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
60 | })
61 |
62 | t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
63 | err := json.Unmarshal([]byte(`"40"`), &atom)
64 | require.Error(t, err, "json.Unmarshal didn't error as expected.")
65 | assertErrorJSONUnmarshalType(t, err,
66 | "json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
67 | })
68 |
69 | t.Run("String", func(t *testing.T) {
70 | // Use an integer with the signed bit set. If we're converting
71 | // incorrectly, we'll get a negative value here.
72 | atom := NewUint32(math.MaxUint32)
73 | assert.Equal(t, "4294967295", atom.String(),
74 | "String() returned an unexpected value.")
75 | })
76 | }
77 |
--------------------------------------------------------------------------------
/uint64.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicint.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | import (
26 | "encoding/json"
27 | "strconv"
28 | "sync/atomic"
29 | )
30 |
31 | // Uint64 is an atomic wrapper around uint64.
32 | type Uint64 struct {
33 | _ nocmp // disallow non-atomic comparison
34 |
35 | v uint64
36 | }
37 |
38 | // NewUint64 creates a new Uint64.
39 | func NewUint64(val uint64) *Uint64 {
40 | return &Uint64{v: val}
41 | }
42 |
43 | // Load atomically loads the wrapped value.
44 | func (i *Uint64) Load() uint64 {
45 | return atomic.LoadUint64(&i.v)
46 | }
47 |
48 | // Add atomically adds to the wrapped uint64 and returns the new value.
49 | func (i *Uint64) Add(delta uint64) uint64 {
50 | return atomic.AddUint64(&i.v, delta)
51 | }
52 |
53 | // Sub atomically subtracts from the wrapped uint64 and returns the new value.
54 | func (i *Uint64) Sub(delta uint64) uint64 {
55 | return atomic.AddUint64(&i.v, ^(delta - 1))
56 | }
57 |
58 | // Inc atomically increments the wrapped uint64 and returns the new value.
59 | func (i *Uint64) Inc() uint64 {
60 | return i.Add(1)
61 | }
62 |
63 | // Dec atomically decrements the wrapped uint64 and returns the new value.
64 | func (i *Uint64) Dec() uint64 {
65 | return i.Sub(1)
66 | }
67 |
68 | // CAS is an atomic compare-and-swap.
69 | //
70 | // Deprecated: Use CompareAndSwap.
71 | func (i *Uint64) CAS(old, new uint64) (swapped bool) {
72 | return i.CompareAndSwap(old, new)
73 | }
74 |
75 | // CompareAndSwap is an atomic compare-and-swap.
76 | func (i *Uint64) CompareAndSwap(old, new uint64) (swapped bool) {
77 | return atomic.CompareAndSwapUint64(&i.v, old, new)
78 | }
79 |
80 | // Store atomically stores the passed value.
81 | func (i *Uint64) Store(val uint64) {
82 | atomic.StoreUint64(&i.v, val)
83 | }
84 |
85 | // Swap atomically swaps the wrapped uint64 and returns the old value.
86 | func (i *Uint64) Swap(val uint64) (old uint64) {
87 | return atomic.SwapUint64(&i.v, val)
88 | }
89 |
90 | // MarshalJSON encodes the wrapped uint64 into JSON.
91 | func (i *Uint64) MarshalJSON() ([]byte, error) {
92 | return json.Marshal(i.Load())
93 | }
94 |
95 | // UnmarshalJSON decodes JSON into the wrapped uint64.
96 | func (i *Uint64) UnmarshalJSON(b []byte) error {
97 | var v uint64
98 | if err := json.Unmarshal(b, &v); err != nil {
99 | return err
100 | }
101 | i.Store(v)
102 | return nil
103 | }
104 |
105 | // String encodes the wrapped value as a string.
106 | func (i *Uint64) String() string {
107 | v := i.Load()
108 | return strconv.FormatUint(uint64(v), 10)
109 | }
110 |
--------------------------------------------------------------------------------
/uint64_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "math"
26 | "testing"
27 |
28 | "github.com/stretchr/testify/assert"
29 | "github.com/stretchr/testify/require"
30 | )
31 |
32 | func TestUint64(t *testing.T) {
33 | atom := NewUint64(42)
34 |
35 | require.Equal(t, uint64(42), atom.Load(), "Load didn't work.")
36 | require.Equal(t, uint64(46), atom.Add(4), "Add didn't work.")
37 | require.Equal(t, uint64(44), atom.Sub(2), "Sub didn't work.")
38 | require.Equal(t, uint64(45), atom.Inc(), "Inc didn't work.")
39 | require.Equal(t, uint64(44), atom.Dec(), "Dec didn't work.")
40 |
41 | require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
42 | require.Equal(t, uint64(0), atom.Load(), "CAS didn't set the correct value.")
43 |
44 | require.Equal(t, uint64(0), atom.Swap(1), "Swap didn't return the old value.")
45 | require.Equal(t, uint64(1), atom.Load(), "Swap didn't set the correct value.")
46 |
47 | atom.Store(42)
48 | require.Equal(t, uint64(42), atom.Load(), "Store didn't set the correct value.")
49 |
50 | t.Run("JSON/Marshal", func(t *testing.T) {
51 | bytes, err := json.Marshal(atom)
52 | require.NoError(t, err, "json.Marshal errored unexpectedly.")
53 | require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
54 | })
55 |
56 | t.Run("JSON/Unmarshal", func(t *testing.T) {
57 | err := json.Unmarshal([]byte("40"), &atom)
58 | require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
59 | require.Equal(t, uint64(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
60 | })
61 |
62 | t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
63 | err := json.Unmarshal([]byte(`"40"`), &atom)
64 | require.Error(t, err, "json.Unmarshal didn't error as expected.")
65 | assertErrorJSONUnmarshalType(t, err,
66 | "json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
67 | })
68 |
69 | t.Run("String", func(t *testing.T) {
70 | // Use an integer with the signed bit set. If we're converting
71 | // incorrectly, we'll get a negative value here.
72 | atom := NewUint64(math.MaxUint64)
73 | assert.Equal(t, "18446744073709551615", atom.String(),
74 | "String() returned an unexpected value.")
75 | })
76 | }
77 |
--------------------------------------------------------------------------------
/uintptr.go:
--------------------------------------------------------------------------------
1 | // @generated Code generated by gen-atomicint.
2 |
3 | // Copyright (c) 2020-2023 Uber Technologies, Inc.
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | package atomic
24 |
25 | import (
26 | "encoding/json"
27 | "strconv"
28 | "sync/atomic"
29 | )
30 |
31 | // Uintptr is an atomic wrapper around uintptr.
32 | type Uintptr struct {
33 | _ nocmp // disallow non-atomic comparison
34 |
35 | v uintptr
36 | }
37 |
38 | // NewUintptr creates a new Uintptr.
39 | func NewUintptr(val uintptr) *Uintptr {
40 | return &Uintptr{v: val}
41 | }
42 |
43 | // Load atomically loads the wrapped value.
44 | func (i *Uintptr) Load() uintptr {
45 | return atomic.LoadUintptr(&i.v)
46 | }
47 |
48 | // Add atomically adds to the wrapped uintptr and returns the new value.
49 | func (i *Uintptr) Add(delta uintptr) uintptr {
50 | return atomic.AddUintptr(&i.v, delta)
51 | }
52 |
53 | // Sub atomically subtracts from the wrapped uintptr and returns the new value.
54 | func (i *Uintptr) Sub(delta uintptr) uintptr {
55 | return atomic.AddUintptr(&i.v, ^(delta - 1))
56 | }
57 |
58 | // Inc atomically increments the wrapped uintptr and returns the new value.
59 | func (i *Uintptr) Inc() uintptr {
60 | return i.Add(1)
61 | }
62 |
63 | // Dec atomically decrements the wrapped uintptr and returns the new value.
64 | func (i *Uintptr) Dec() uintptr {
65 | return i.Sub(1)
66 | }
67 |
68 | // CAS is an atomic compare-and-swap.
69 | //
70 | // Deprecated: Use CompareAndSwap.
71 | func (i *Uintptr) CAS(old, new uintptr) (swapped bool) {
72 | return i.CompareAndSwap(old, new)
73 | }
74 |
75 | // CompareAndSwap is an atomic compare-and-swap.
76 | func (i *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) {
77 | return atomic.CompareAndSwapUintptr(&i.v, old, new)
78 | }
79 |
80 | // Store atomically stores the passed value.
81 | func (i *Uintptr) Store(val uintptr) {
82 | atomic.StoreUintptr(&i.v, val)
83 | }
84 |
85 | // Swap atomically swaps the wrapped uintptr and returns the old value.
86 | func (i *Uintptr) Swap(val uintptr) (old uintptr) {
87 | return atomic.SwapUintptr(&i.v, val)
88 | }
89 |
90 | // MarshalJSON encodes the wrapped uintptr into JSON.
91 | func (i *Uintptr) MarshalJSON() ([]byte, error) {
92 | return json.Marshal(i.Load())
93 | }
94 |
95 | // UnmarshalJSON decodes JSON into the wrapped uintptr.
96 | func (i *Uintptr) UnmarshalJSON(b []byte) error {
97 | var v uintptr
98 | if err := json.Unmarshal(b, &v); err != nil {
99 | return err
100 | }
101 | i.Store(v)
102 | return nil
103 | }
104 |
105 | // String encodes the wrapped value as a string.
106 | func (i *Uintptr) String() string {
107 | v := i.Load()
108 | return strconv.FormatUint(uint64(v), 10)
109 | }
110 |
--------------------------------------------------------------------------------
/uintptr_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "encoding/json"
25 | "fmt"
26 | "testing"
27 |
28 | "github.com/stretchr/testify/assert"
29 | "github.com/stretchr/testify/require"
30 | )
31 |
32 | func TestUintptr(t *testing.T) {
33 | atom := NewUintptr(42)
34 |
35 | require.Equal(t, uintptr(42), atom.Load(), "Load didn't work.")
36 | require.Equal(t, uintptr(46), atom.Add(4), "Add didn't work.")
37 | require.Equal(t, uintptr(44), atom.Sub(2), "Sub didn't work.")
38 | require.Equal(t, uintptr(45), atom.Inc(), "Inc didn't work.")
39 | require.Equal(t, uintptr(44), atom.Dec(), "Dec didn't work.")
40 |
41 | require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.")
42 | require.Equal(t, uintptr(0), atom.Load(), "CAS didn't set the correct value.")
43 |
44 | require.Equal(t, uintptr(0), atom.Swap(1), "Swap didn't return the old value.")
45 | require.Equal(t, uintptr(1), atom.Load(), "Swap didn't set the correct value.")
46 |
47 | atom.Store(42)
48 | require.Equal(t, uintptr(42), atom.Load(), "Store didn't set the correct value.")
49 |
50 | t.Run("JSON/Marshal", func(t *testing.T) {
51 | bytes, err := json.Marshal(atom)
52 | require.NoError(t, err, "json.Marshal errored unexpectedly.")
53 | require.Equal(t, []byte("42"), bytes, "json.Marshal encoded the wrong bytes.")
54 | })
55 |
56 | t.Run("JSON/Unmarshal", func(t *testing.T) {
57 | err := json.Unmarshal([]byte("40"), &atom)
58 | require.NoError(t, err, "json.Unmarshal errored unexpectedly.")
59 | require.Equal(t, uintptr(40), atom.Load(), "json.Unmarshal didn't set the correct value.")
60 | })
61 |
62 | t.Run("JSON/Unmarshal/Error", func(t *testing.T) {
63 | err := json.Unmarshal([]byte(`"40"`), &atom)
64 | require.Error(t, err, "json.Unmarshal didn't error as expected.")
65 | assertErrorJSONUnmarshalType(t, err,
66 | "json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
67 | })
68 |
69 | t.Run("String", func(t *testing.T) {
70 | // Use an integer with the signed bit set. If we're converting
71 | // incorrectly, we'll get a negative value here.
72 | // Use an int variable, as constants cause compile-time overflows.
73 | negative := -1
74 | atom := NewUintptr(uintptr(negative))
75 | want := fmt.Sprint(uintptr(negative))
76 | assert.Equal(t, want, atom.String(),
77 | "String() returned an unexpected value.")
78 | })
79 | }
80 |
--------------------------------------------------------------------------------
/unsafe_pointer.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021-2022 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "sync/atomic"
25 | "unsafe"
26 | )
27 |
28 | // UnsafePointer is an atomic wrapper around unsafe.Pointer.
29 | type UnsafePointer struct {
30 | _ nocmp // disallow non-atomic comparison
31 |
32 | v unsafe.Pointer
33 | }
34 |
35 | // NewUnsafePointer creates a new UnsafePointer.
36 | func NewUnsafePointer(val unsafe.Pointer) *UnsafePointer {
37 | return &UnsafePointer{v: val}
38 | }
39 |
40 | // Load atomically loads the wrapped value.
41 | func (p *UnsafePointer) Load() unsafe.Pointer {
42 | return atomic.LoadPointer(&p.v)
43 | }
44 |
45 | // Store atomically stores the passed value.
46 | func (p *UnsafePointer) Store(val unsafe.Pointer) {
47 | atomic.StorePointer(&p.v, val)
48 | }
49 |
50 | // Swap atomically swaps the wrapped unsafe.Pointer and returns the old value.
51 | func (p *UnsafePointer) Swap(val unsafe.Pointer) (old unsafe.Pointer) {
52 | return atomic.SwapPointer(&p.v, val)
53 | }
54 |
55 | // CAS is an atomic compare-and-swap.
56 | //
57 | // Deprecated: Use CompareAndSwap
58 | func (p *UnsafePointer) CAS(old, new unsafe.Pointer) (swapped bool) {
59 | return p.CompareAndSwap(old, new)
60 | }
61 |
62 | // CompareAndSwap is an atomic compare-and-swap.
63 | func (p *UnsafePointer) CompareAndSwap(old, new unsafe.Pointer) (swapped bool) {
64 | return atomic.CompareAndSwapPointer(&p.v, old, new)
65 | }
66 |
--------------------------------------------------------------------------------
/unsafe_pointer_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "testing"
25 | "unsafe"
26 |
27 | "github.com/stretchr/testify/require"
28 | )
29 |
30 | func TestUnsafePointer(t *testing.T) {
31 | i := int64(42)
32 | j := int64(0)
33 | k := int64(1)
34 |
35 | tests := []struct {
36 | desc string
37 | newAtomic func() *UnsafePointer
38 | initial unsafe.Pointer
39 | }{
40 | {
41 | desc: "non-empty",
42 | newAtomic: func() *UnsafePointer {
43 | return NewUnsafePointer(unsafe.Pointer(&i))
44 | },
45 | initial: unsafe.Pointer(&i),
46 | },
47 | {
48 | desc: "nil",
49 | newAtomic: func() *UnsafePointer {
50 | var p UnsafePointer
51 | return &p
52 | },
53 | initial: unsafe.Pointer(nil),
54 | },
55 | }
56 |
57 | for _, tt := range tests {
58 | t.Run(tt.desc, func(t *testing.T) {
59 | t.Run("Load", func(t *testing.T) {
60 | atom := tt.newAtomic()
61 | require.Equal(t, tt.initial, atom.Load(), "Load should report nil.")
62 | })
63 |
64 | t.Run("Swap", func(t *testing.T) {
65 | atom := tt.newAtomic()
66 | require.Equal(t, tt.initial, atom.Swap(unsafe.Pointer(&k)), "Swap didn't return the old value.")
67 | require.Equal(t, unsafe.Pointer(&k), atom.Load(), "Swap didn't set the correct value.")
68 | })
69 |
70 | t.Run("CAS", func(t *testing.T) {
71 | atom := tt.newAtomic()
72 | require.True(t, atom.CAS(tt.initial, unsafe.Pointer(&j)), "CAS didn't report a swap.")
73 | require.Equal(t, unsafe.Pointer(&j), atom.Load(), "CAS didn't set the correct value.")
74 | })
75 |
76 | t.Run("Store", func(t *testing.T) {
77 | atom := tt.newAtomic()
78 | atom.Store(unsafe.Pointer(&i))
79 | require.Equal(t, unsafe.Pointer(&i), atom.Load(), "Store didn't set the correct value.")
80 | })
81 | })
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/value.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import "sync/atomic"
24 |
25 | // Value shadows the type of the same name from sync/atomic
26 | // https://godoc.org/sync/atomic#Value
27 | type Value struct {
28 | _ nocmp // disallow non-atomic comparison
29 |
30 | atomic.Value
31 | }
32 |
--------------------------------------------------------------------------------
/value_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Uber Technologies, Inc.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package atomic
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | )
28 |
29 | func TestValue(t *testing.T) {
30 | var v Value
31 | assert.Nil(t, v.Load(), "initial Value is not nil")
32 |
33 | v.Store(42)
34 | assert.Equal(t, 42, v.Load())
35 |
36 | v.Store(84)
37 | assert.Equal(t, 84, v.Load())
38 |
39 | assert.Panics(t, func() { v.Store("foo") })
40 | }
41 |
--------------------------------------------------------------------------------