From 928a9d776b88c38e7f9972817ffcbd147b4d2753 Mon Sep 17 00:00:00 2001 From: Prashant Varanasi Date: Wed, 25 May 2016 10:51:13 -0700 Subject: [PATCH] Import atomic package The code is the same as github.com/uber/tchannel-go/atomic --- .gitignore | 11 +++ .travis.yml | 18 ++++ LICENSE.txt => LICENSE | 0 Makefile | 32 +++++++ atomic.go | 197 +++++++++++++++++++++++++++++++++++++++++ atomic_test.go | 99 +++++++++++++++++++++ glide.lock | 4 + glide.yaml | 7 ++ scripts/cover.sh | 40 +++++++++ stress_test.go | 101 +++++++++++++++++++++ 10 files changed, 509 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml rename LICENSE.txt => LICENSE (100%) create mode 100644 Makefile create mode 100644 atomic.go create mode 100644 atomic_test.go create mode 100644 glide.lock create mode 100644 glide.yaml create mode 100755 scripts/cover.sh create mode 100644 stress_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a2b5e5d --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.DS_Store +/vendor +/cover +cover.out + + +# Binaries +*.test + +# Profiling output +*.prof diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..acf2bdb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +sudo: false +language: go + +go: + - 1.5 + - 1.6 + - tip + +cache: + directories: + - vendor + +install: + - make install_ci + +script: + - make test_ci + - travis_retry goveralls -coverprofile=cover.out -service=travis-ci diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f3e1b3e --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +PACKAGES := $(shell glide novendor) + +export GO15VENDOREXPERIMENT=1 + + +.PHONY: build +build: + go build -i $(PACKAGES) + + +.PHONY: install +install: + glide --version || go get github.com/Masterminds/glide + glide install + + +.PHONY: test +test: + go test -cover -race $(PACKAGES) + + +.PHONY: install_ci +install_ci: install + go get github.com/wadey/gocovmerge + go get github.com/mattn/goveralls + go get golang.org/x/tools/cmd/cover + + +.PHONY: test_ci +test_ci: install_ci build + ./scripts/cover.sh $(shell go list $(PACKAGES)) + diff --git a/atomic.go b/atomic.go new file mode 100644 index 0000000..618576a --- /dev/null +++ b/atomic.go @@ -0,0 +1,197 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package atomic provides simple wrappers around numerics to enforce atomic +// access. +package atomic + +import "sync/atomic" + +// Int32 is an atomic wrapper around an int32. +type Int32 struct{ int32 } + +// NewInt32 creates an Int32. +func NewInt32(i int32) *Int32 { + return &Int32{i} +} + +// Load atomically loads the wrapped value. +func (i *Int32) Load() int32 { + return atomic.LoadInt32(&i.int32) +} + +// Add atomically adds to the wrapped int32 and returns the new value. +func (i *Int32) Add(n int32) int32 { + return atomic.AddInt32(&i.int32, n) +} + +// Inc atomically increments the wrapped int32 and returns the new value. +func (i *Int32) Inc() int32 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int32 and returns the new value. +func (i *Int32) Dec() int32 { + return i.Add(-1) +} + +// CAS is an atomic compare-and-swap. +func (i *Int32) CAS(old, new int32) bool { + return atomic.CompareAndSwapInt32(&i.int32, old, new) +} + +// Store atomically stores the passed value. +func (i *Int32) Store(n int32) { + atomic.StoreInt32(&i.int32, n) +} + +// Swap atomically swaps the wrapped int32 and returns the old value. +func (i *Int32) Swap(n int32) int32 { + return atomic.SwapInt32(&i.int32, n) +} + +// Int64 is an atomic wrapper around an int64. +type Int64 struct{ int64 } + +// NewInt64 creates an Int64. +func NewInt64(i int64) *Int64 { + return &Int64{i} +} + +// Load atomically loads the wrapped value. +func (i *Int64) Load() int64 { + return atomic.LoadInt64(&i.int64) +} + +// Add atomically adds to the wrapped int64 and returns the new value. +func (i *Int64) Add(n int64) int64 { + return atomic.AddInt64(&i.int64, n) +} + +// Inc atomically increments the wrapped int64 and returns the new value. +func (i *Int64) Inc() int64 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int64 and returns the new value. +func (i *Int64) Dec() int64 { + return i.Add(-1) +} + +// CAS is an atomic compare-and-swap. +func (i *Int64) CAS(old, new int64) bool { + return atomic.CompareAndSwapInt64(&i.int64, old, new) +} + +// Store atomically stores the passed value. +func (i *Int64) Store(n int64) { + atomic.StoreInt64(&i.int64, n) +} + +// Swap atomically swaps the wrapped int64 and returns the old value. +func (i *Int64) Swap(n int64) int64 { + return atomic.SwapInt64(&i.int64, n) +} + +// Uint32 is an atomic wrapper around an uint32. +type Uint32 struct{ uint32 } + +// NewUint32 creates a Uint32. +func NewUint32(i uint32) *Uint32 { + return &Uint32{i} +} + +// Load atomically loads the wrapped value. +func (i *Uint32) Load() uint32 { + return atomic.LoadUint32(&i.uint32) +} + +// Add atomically adds to the wrapped uint32 and returns the new value. +func (i *Uint32) Add(n uint32) uint32 { + return atomic.AddUint32(&i.uint32, n) +} + +// Inc atomically increments the wrapped uint32 and returns the new value. +func (i *Uint32) Inc() uint32 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int32 and returns the new value. +func (i *Uint32) Dec() uint32 { + return i.Add(^uint32(0)) +} + +// CAS is an atomic compare-and-swap. +func (i *Uint32) CAS(old, new uint32) bool { + return atomic.CompareAndSwapUint32(&i.uint32, old, new) +} + +// Store atomically stores the passed value. +func (i *Uint32) Store(n uint32) { + atomic.StoreUint32(&i.uint32, n) +} + +// Swap atomically swaps the wrapped uint32 and returns the old value. +func (i *Uint32) Swap(n uint32) uint32 { + return atomic.SwapUint32(&i.uint32, n) +} + +// Uint64 is an atomic wrapper around a uint64. +type Uint64 struct{ uint64 } + +// NewUint64 creates a Uint64. +func NewUint64(i uint64) *Uint64 { + return &Uint64{i} +} + +// Load atomically loads the wrapped value. +func (i *Uint64) Load() uint64 { + return atomic.LoadUint64(&i.uint64) +} + +// Add atomically adds to the wrapped uint64 and returns the new value. +func (i *Uint64) Add(n uint64) uint64 { + return atomic.AddUint64(&i.uint64, n) +} + +// Inc atomically increments the wrapped uint64 and returns the new value. +func (i *Uint64) Inc() uint64 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped uint64 and returns the new value. +func (i *Uint64) Dec() uint64 { + return i.Add(^uint64(0)) +} + +// CAS is an atomic compare-and-swap. +func (i *Uint64) CAS(old, new uint64) bool { + return atomic.CompareAndSwapUint64(&i.uint64, old, new) +} + +// Store atomically stores the passed value. +func (i *Uint64) Store(n uint64) { + atomic.StoreUint64(&i.uint64, n) +} + +// Swap atomically swaps the wrapped uint64 and returns the old value. +func (i *Uint64) Swap(n uint64) uint64 { + return atomic.SwapUint64(&i.uint64, n) +} diff --git a/atomic_test.go b/atomic_test.go new file mode 100644 index 0000000..f3bc17a --- /dev/null +++ b/atomic_test.go @@ -0,0 +1,99 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestInt32(t *testing.T) { + atom := NewInt32(42) + + require.Equal(t, int32(42), atom.Load(), "Load didn't work.") + require.Equal(t, int32(44), atom.Add(2), "Add didn't work.") + require.Equal(t, int32(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, int32(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, int32(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, int32(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, int32(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, int32(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestInt64(t *testing.T) { + atom := NewInt64(42) + + require.Equal(t, int64(42), atom.Load(), "Load didn't work.") + require.Equal(t, int64(44), atom.Add(2), "Add didn't work.") + require.Equal(t, int64(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, int64(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, int64(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, int64(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, int64(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, int64(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestUint32(t *testing.T) { + atom := NewUint32(42) + + require.Equal(t, uint32(42), atom.Load(), "Load didn't work.") + require.Equal(t, uint32(44), atom.Add(2), "Add didn't work.") + require.Equal(t, uint32(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, uint32(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, uint32(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, uint32(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, uint32(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, uint32(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestUint64(t *testing.T) { + atom := NewUint64(42) + + require.Equal(t, uint64(42), atom.Load(), "Load didn't work.") + require.Equal(t, uint64(44), atom.Add(2), "Add didn't work.") + require.Equal(t, uint64(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, uint64(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, uint64(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, uint64(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, uint64(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, uint64(42), atom.Load(), "Store didn't set the correct value.") +} diff --git a/glide.lock b/glide.lock new file mode 100644 index 0000000..914db23 --- /dev/null +++ b/glide.lock @@ -0,0 +1,4 @@ +hash: 8413cf7f8e981fc97e0ce202471bf1cab8b935615ae831b98ebc499655acd513 +updated: 2016-05-25T10:54:24.639721239-07:00 +imports: [] +devImports: [] diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..07a23a6 --- /dev/null +++ b/glide.yaml @@ -0,0 +1,7 @@ +package: github.com/uber-go/atomic +imports: +# Test dependencies +- package: github.com/stretchr/testify + subpackages: + - assert + - require diff --git a/scripts/cover.sh b/scripts/cover.sh new file mode 100755 index 0000000..3d6171b --- /dev/null +++ b/scripts/cover.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +set -e + +COVER=cover +ROOT_PKG=github.com/uber-go/atomic + +if [[ -d "$COVER" ]]; then + rm -rf "$COVER" +fi +mkdir -p "$COVER" + +i=0 +for pkg in "$@"; do + i=$((i + 1)) + + extracoverpkg="" + if [[ -f "$GOPATH/src/$pkg/.extra-coverpkg" ]]; then + extracoverpkg=$( \ + sed -e "s|^|$pkg/|g" < "$GOPATH/src/$pkg/.extra-coverpkg" \ + | tr '\n' ',') + fi + + coverpkg=$(go list -json "$pkg" | jq -r ' + .Deps + | map(select(startswith("'"$ROOT_PKG"'"))) + | map(select(contains("/vendor/") | not)) + | . + ["'"$pkg"'"] + | join(",") + ') + if [[ -n "$extracoverpkg" ]]; then + coverpkg="$extracoverpkg$coverpkg" + fi + + go test \ + -coverprofile "$COVER/cover.${i}.out" -coverpkg "$coverpkg" \ + -v "$pkg" +done + +gocovmerge "$COVER"/*.out > cover.out diff --git a/stress_test.go b/stress_test.go new file mode 100644 index 0000000..b41e949 --- /dev/null +++ b/stress_test.go @@ -0,0 +1,101 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "runtime" + "testing" +) + +const _parallelism = 4 +const _iterations = 1000 + +func TestStressInt32(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism)) + atom := &Int32{0} + for i := 0; i < _parallelism; i++ { + go func() { + for j := 0; j < _iterations; j++ { + atom.Load() + atom.Add(1) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) + } + }() + } +} + +func TestStressInt64(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism)) + atom := &Int64{0} + for i := 0; i < _parallelism; i++ { + go func() { + for j := 0; j < _iterations; j++ { + atom.Load() + atom.Add(1) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) + } + }() + } +} + +func TestStressUint32(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism)) + atom := &Uint32{0} + for i := 0; i < _parallelism; i++ { + go func() { + for j := 0; j < _iterations; j++ { + atom.Load() + atom.Add(1) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) + } + }() + } +} + +func TestStressUint64(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism)) + atom := &Uint64{0} + for i := 0; i < _parallelism; i++ { + go func() { + for j := 0; j < _iterations; j++ { + atom.Load() + atom.Add(1) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) + } + }() + } +}