admin/info: Add HTTPStats value as part of serverInfo() struct. (#4049)
Remove our counter implementation instead use atomic external package which supports more types and methods.master
parent
1d99a560e3
commit
27749c2124
@ -0,0 +1,188 @@ |
||||
/* |
||||
* Minio Cloud Storage, (C) 2017 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package cmd |
||||
|
||||
import ( |
||||
"fmt" |
||||
"net/http" |
||||
"time" |
||||
|
||||
"go.uber.org/atomic" |
||||
) |
||||
|
||||
// ConnStats - Network statistics
|
||||
// Count total input/output transferred bytes during
|
||||
// the server's life.
|
||||
type ConnStats struct { |
||||
totalInputBytes atomic.Uint64 |
||||
totalOutputBytes atomic.Uint64 |
||||
} |
||||
|
||||
// Increase total input bytes
|
||||
func (s *ConnStats) incInputBytes(n int) { |
||||
s.totalInputBytes.Add(uint64(n)) |
||||
} |
||||
|
||||
// Increase total output bytes
|
||||
func (s *ConnStats) incOutputBytes(n int) { |
||||
s.totalOutputBytes.Add(uint64(n)) |
||||
} |
||||
|
||||
// Return total input bytes
|
||||
func (s *ConnStats) getTotalInputBytes() uint64 { |
||||
return s.totalInputBytes.Load() |
||||
} |
||||
|
||||
// Return total output bytes
|
||||
func (s *ConnStats) getTotalOutputBytes() uint64 { |
||||
return s.totalOutputBytes.Load() |
||||
} |
||||
|
||||
// Prepare new ConnStats structure
|
||||
func newConnStats() *ConnStats { |
||||
return &ConnStats{} |
||||
} |
||||
|
||||
// HTTPMethodStats holds statistics information about
|
||||
// a given HTTP method made by all clients
|
||||
type HTTPMethodStats struct { |
||||
Counter atomic.Uint64 |
||||
Duration atomic.Float64 |
||||
} |
||||
|
||||
// HTTPStats holds statistics information about
|
||||
// HTTP requests made by all clients
|
||||
type HTTPStats struct { |
||||
// HEAD request stats.
|
||||
totalHEADs HTTPMethodStats |
||||
successHEADs HTTPMethodStats |
||||
|
||||
// GET request stats.
|
||||
totalGETs HTTPMethodStats |
||||
successGETs HTTPMethodStats |
||||
|
||||
// PUT request stats.
|
||||
totalPUTs HTTPMethodStats |
||||
successPUTs HTTPMethodStats |
||||
|
||||
// POST request stats.
|
||||
totalPOSTs HTTPMethodStats |
||||
successPOSTs HTTPMethodStats |
||||
|
||||
// DELETE request stats.
|
||||
totalDELETEs HTTPMethodStats |
||||
successDELETEs HTTPMethodStats |
||||
} |
||||
|
||||
func durationStr(totalDuration, totalCount float64) string { |
||||
return fmt.Sprint(time.Duration(totalDuration/totalCount) * time.Second) |
||||
} |
||||
|
||||
// Converts http stats into struct to be sent back to the client.
|
||||
func (st HTTPStats) toServerHTTPStats() ServerHTTPStats { |
||||
serverStats := ServerHTTPStats{} |
||||
serverStats.TotalHEADStats = ServerHTTPMethodStats{ |
||||
Count: st.totalHEADs.Counter.Load(), |
||||
AvgDuration: durationStr(st.totalHEADs.Duration.Load(), float64(st.totalHEADs.Counter.Load())), |
||||
} |
||||
serverStats.SuccessHEADStats = ServerHTTPMethodStats{ |
||||
Count: st.successHEADs.Counter.Load(), |
||||
AvgDuration: durationStr(st.successHEADs.Duration.Load(), float64(st.successHEADs.Counter.Load())), |
||||
} |
||||
serverStats.TotalGETStats = ServerHTTPMethodStats{ |
||||
Count: st.totalGETs.Counter.Load(), |
||||
AvgDuration: durationStr(st.totalGETs.Duration.Load(), float64(st.totalGETs.Counter.Load())), |
||||
} |
||||
serverStats.SuccessGETStats = ServerHTTPMethodStats{ |
||||
Count: st.successGETs.Counter.Load(), |
||||
AvgDuration: durationStr(st.successGETs.Duration.Load(), float64(st.successGETs.Counter.Load())), |
||||
} |
||||
serverStats.TotalPUTStats = ServerHTTPMethodStats{ |
||||
Count: st.totalPUTs.Counter.Load(), |
||||
AvgDuration: durationStr(st.totalPUTs.Duration.Load(), float64(st.totalPUTs.Counter.Load())), |
||||
} |
||||
serverStats.SuccessPUTStats = ServerHTTPMethodStats{ |
||||
Count: st.successPUTs.Counter.Load(), |
||||
AvgDuration: durationStr(st.successPUTs.Duration.Load(), float64(st.successPUTs.Counter.Load())), |
||||
} |
||||
serverStats.TotalPOSTStats = ServerHTTPMethodStats{ |
||||
Count: st.totalPOSTs.Counter.Load(), |
||||
AvgDuration: durationStr(st.totalPOSTs.Duration.Load(), float64(st.totalPOSTs.Counter.Load())), |
||||
} |
||||
serverStats.SuccessPOSTStats = ServerHTTPMethodStats{ |
||||
Count: st.successPOSTs.Counter.Load(), |
||||
AvgDuration: durationStr(st.successPOSTs.Duration.Load(), float64(st.successPOSTs.Counter.Load())), |
||||
} |
||||
serverStats.TotalDELETEStats = ServerHTTPMethodStats{ |
||||
Count: st.totalDELETEs.Counter.Load(), |
||||
AvgDuration: durationStr(st.totalDELETEs.Duration.Load(), float64(st.totalDELETEs.Counter.Load())), |
||||
} |
||||
serverStats.SuccessDELETEStats = ServerHTTPMethodStats{ |
||||
Count: st.successDELETEs.Counter.Load(), |
||||
AvgDuration: durationStr(st.successDELETEs.Duration.Load(), float64(st.successDELETEs.Counter.Load())), |
||||
} |
||||
return serverStats |
||||
} |
||||
|
||||
// Update statistics from http request and response data
|
||||
func (st *HTTPStats) updateStats(r *http.Request, w *httpResponseRecorder, durationSecs float64) { |
||||
// A successful request has a 2xx response code
|
||||
successReq := (w.respStatusCode >= 200 && w.respStatusCode < 300) |
||||
// Update stats according to method verb
|
||||
switch r.Method { |
||||
case "HEAD": |
||||
st.totalHEADs.Counter.Inc() |
||||
st.totalHEADs.Duration.Add(durationSecs) |
||||
if successReq { |
||||
st.successHEADs.Counter.Inc() |
||||
st.successHEADs.Duration.Add(durationSecs) |
||||
} |
||||
case "GET": |
||||
st.totalGETs.Counter.Inc() |
||||
st.totalGETs.Duration.Add(durationSecs) |
||||
if successReq { |
||||
st.successGETs.Counter.Inc() |
||||
st.successGETs.Duration.Add(durationSecs) |
||||
} |
||||
case "PUT": |
||||
st.totalPUTs.Counter.Inc() |
||||
st.totalPUTs.Duration.Add(durationSecs) |
||||
if successReq { |
||||
st.successPUTs.Counter.Inc() |
||||
st.totalPUTs.Duration.Add(durationSecs) |
||||
} |
||||
case "POST": |
||||
st.totalPOSTs.Counter.Inc() |
||||
st.totalPOSTs.Duration.Add(durationSecs) |
||||
if successReq { |
||||
st.successPOSTs.Counter.Inc() |
||||
st.totalPOSTs.Duration.Add(durationSecs) |
||||
} |
||||
case "DELETE": |
||||
st.totalDELETEs.Counter.Inc() |
||||
st.totalDELETEs.Duration.Add(durationSecs) |
||||
if successReq { |
||||
st.successDELETEs.Counter.Inc() |
||||
st.successDELETEs.Duration.Add(durationSecs) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Prepare new HTTPStats structure
|
||||
func newHTTPStats() *HTTPStats { |
||||
return &HTTPStats{} |
||||
} |
@ -1,129 +0,0 @@ |
||||
/* |
||||
* Minio Cloud Storage, (C) 2017 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package cmd |
||||
|
||||
import ( |
||||
"net/http" |
||||
"sync/atomic" |
||||
) |
||||
|
||||
// counter - simplify atomic counting
|
||||
type counter struct { |
||||
val uint64 |
||||
} |
||||
|
||||
// Inc increases counter atomically
|
||||
func (c *counter) Inc(n uint64) { |
||||
atomic.AddUint64(&c.val, n) |
||||
} |
||||
|
||||
// Value fetches counter's value atomically
|
||||
func (c *counter) Value() uint64 { |
||||
return atomic.LoadUint64(&c.val) |
||||
} |
||||
|
||||
// ConnStats - Network statistics
|
||||
// Count total input/output transferred bytes during
|
||||
// the server's life.
|
||||
type ConnStats struct { |
||||
totalInputBytes counter |
||||
totalOutputBytes counter |
||||
} |
||||
|
||||
// Increase total input bytes
|
||||
func (s *ConnStats) incInputBytes(n int) { |
||||
s.totalInputBytes.Inc(uint64(n)) |
||||
} |
||||
|
||||
// Increase total output bytes
|
||||
func (s *ConnStats) incOutputBytes(n int) { |
||||
s.totalOutputBytes.Inc(uint64(n)) |
||||
} |
||||
|
||||
// Return total input bytes
|
||||
func (s *ConnStats) getTotalInputBytes() uint64 { |
||||
return s.totalInputBytes.Value() |
||||
} |
||||
|
||||
// Return total output bytes
|
||||
func (s *ConnStats) getTotalOutputBytes() uint64 { |
||||
return s.totalOutputBytes.Value() |
||||
} |
||||
|
||||
// Prepare new ConnStats structure
|
||||
func newConnStats() *ConnStats { |
||||
return &ConnStats{} |
||||
} |
||||
|
||||
// httpStats holds statistics information about
|
||||
// HTTP requests made by all clients
|
||||
type httpStats struct { |
||||
// HEAD request stats
|
||||
totalHEADs counter |
||||
successHEADs counter |
||||
// GET request stats
|
||||
totalGETs counter |
||||
successGETs counter |
||||
// PUT request
|
||||
totalPUTs counter |
||||
successPUTs counter |
||||
// POST request
|
||||
totalPOSTs counter |
||||
successPOSTs counter |
||||
// DELETE request
|
||||
totalDELETEs counter |
||||
successDELETEs counter |
||||
} |
||||
|
||||
// Update statistics from http request and response data
|
||||
func (st *httpStats) updateStats(r *http.Request, w *httpResponseRecorder) { |
||||
// A successful request has a 2xx response code
|
||||
successReq := (w.respStatusCode >= 200 && w.respStatusCode < 300) |
||||
// Update stats according to method verb
|
||||
switch r.Method { |
||||
case "HEAD": |
||||
st.totalHEADs.Inc(1) |
||||
if successReq { |
||||
st.successHEADs.Inc(1) |
||||
} |
||||
case "GET": |
||||
st.totalGETs.Inc(1) |
||||
if successReq { |
||||
st.successGETs.Inc(1) |
||||
} |
||||
case "PUT": |
||||
st.totalPUTs.Inc(1) |
||||
if successReq { |
||||
st.successPUTs.Inc(1) |
||||
} |
||||
case "POST": |
||||
st.totalPOSTs.Inc(1) |
||||
if successReq { |
||||
st.successPOSTs.Inc(1) |
||||
} |
||||
case "DELETE": |
||||
st.totalDELETEs.Inc(1) |
||||
if successReq { |
||||
st.successDELETEs.Inc(1) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Prepare new HttpStats structure
|
||||
func newHTTPStats() *httpStats { |
||||
return &httpStats{} |
||||
} |
@ -0,0 +1,19 @@ |
||||
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. |
@ -0,0 +1,64 @@ |
||||
PACKAGES := $(shell glide nv)
|
||||
# Many Go tools take file globs or directories as arguments instead of packages.
|
||||
PACKAGE_FILES ?= *.go
|
||||
|
||||
|
||||
# The linting tools evolve with each Go version, so run them only on the latest
|
||||
# stable release.
|
||||
GO_VERSION := $(shell go version | cut -d " " -f 3)
|
||||
GO_MINOR_VERSION := $(word 2,$(subst ., ,$(GO_VERSION)))
|
||||
LINTABLE_MINOR_VERSIONS := 6
|
||||
ifneq ($(filter $(LINTABLE_MINOR_VERSIONS),$(GO_MINOR_VERSION)),) |
||||
SHOULD_LINT := true
|
||||
endif |
||||
|
||||
|
||||
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
|
||||
ifdef SHOULD_LINT |
||||
go get github.com/golang/lint/golint
|
||||
endif |
||||
|
||||
.PHONY: lint |
||||
lint: |
||||
ifdef SHOULD_LINT |
||||
@rm -rf lint.log
|
||||
@echo "Checking formatting..."
|
||||
@gofmt -d -s $(PACKAGE_FILES) 2>&1 | tee lint.log
|
||||
@echo "Checking vet..."
|
||||
@$(foreach dir,$(PACKAGE_FILES),go tool vet $(dir) 2>&1 | tee -a lint.log;)
|
||||
@echo "Checking lint..."
|
||||
@$(foreach dir,$(PKGS),golint $(dir) 2>&1 | tee -a lint.log;)
|
||||
@echo "Checking for unresolved FIXMEs..."
|
||||
@git grep -i fixme | grep -v -e vendor -e Makefile | tee -a lint.log
|
||||
@[ ! -s lint.log ]
|
||||
else |
||||
@echo "Skipping linters on" $(GO_VERSION)
|
||||
endif |
||||
|
||||
|
||||
.PHONY: test_ci |
||||
test_ci: install_ci build |
||||
./scripts/cover.sh $(shell go list $(PACKAGES))
|
@ -0,0 +1,34 @@ |
||||
# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] |
||||
|
||||
Simple wrappers for primitive types to enforce atomic access. |
||||
|
||||
## Installation |
||||
`go get -u go.uber.org/atomic` |
||||
|
||||
## Usage |
||||
The standard library's `sync/atomic` is powerful, but it's easy to forget which |
||||
variables must be accessed atomically. `go.uber.org/atomic` preserves all the |
||||
functionality of the standard library, but wraps the primitive types to |
||||
provide a safer, more convenient API. |
||||
|
||||
```go |
||||
var atom atomic.Uint32 |
||||
atom.Store(42) |
||||
atom.Sub(2) |
||||
atom.CAS(40, 11) |
||||
``` |
||||
|
||||
See the [documentation][doc] for a complete API specification. |
||||
|
||||
## Development Status |
||||
Stable. |
||||
|
||||
<hr> |
||||
Released under the [MIT License](LICENSE.txt). |
||||
|
||||
[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg |
||||
[doc]: https://godoc.org/go.uber.org/atomic |
||||
[ci-img]: https://travis-ci.org/uber-go/atomic.svg?branch=master |
||||
[ci]: https://travis-ci.org/uber-go/atomic |
||||
[cov-img]: https://coveralls.io/repos/github/uber-go/atomic/badge.svg?branch=master |
||||
[cov]: https://coveralls.io/github/uber-go/atomic?branch=master |
@ -0,0 +1,300 @@ |
||||
// 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 ( |
||||
"math" |
||||
"sync/atomic" |
||||
) |
||||
|
||||
// Int32 is an atomic wrapper around an int32.
|
||||
type Int32 struct{ v 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.v) |
||||
} |
||||
|
||||
// Add atomically adds to the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Add(n int32) int32 { |
||||
return atomic.AddInt32(&i.v, n) |
||||
} |
||||
|
||||
// Sub atomically subtracts from the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Sub(n int32) int32 { |
||||
return atomic.AddInt32(&i.v, -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.Sub(1) |
||||
} |
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Int32) CAS(old, new int32) bool { |
||||
return atomic.CompareAndSwapInt32(&i.v, old, new) |
||||
} |
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Int32) Store(n int32) { |
||||
atomic.StoreInt32(&i.v, n) |
||||
} |
||||
|
||||
// Swap atomically swaps the wrapped int32 and returns the old value.
|
||||
func (i *Int32) Swap(n int32) int32 { |
||||
return atomic.SwapInt32(&i.v, n) |
||||
} |
||||
|
||||
// Int64 is an atomic wrapper around an int64.
|
||||
type Int64 struct{ v 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.v) |
||||
} |
||||
|
||||
// Add atomically adds to the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Add(n int64) int64 { |
||||
return atomic.AddInt64(&i.v, n) |
||||
} |
||||
|
||||
// Sub atomically subtracts from the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Sub(n int64) int64 { |
||||
return atomic.AddInt64(&i.v, -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.Sub(1) |
||||
} |
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Int64) CAS(old, new int64) bool { |
||||
return atomic.CompareAndSwapInt64(&i.v, old, new) |
||||
} |
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Int64) Store(n int64) { |
||||
atomic.StoreInt64(&i.v, n) |
||||
} |
||||
|
||||
// Swap atomically swaps the wrapped int64 and returns the old value.
|
||||
func (i *Int64) Swap(n int64) int64 { |
||||
return atomic.SwapInt64(&i.v, n) |
||||
} |
||||
|
||||
// Uint32 is an atomic wrapper around an uint32.
|
||||
type Uint32 struct{ v 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.v) |
||||
} |
||||
|
||||
// Add atomically adds to the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Add(n uint32) uint32 { |
||||
return atomic.AddUint32(&i.v, n) |
||||
} |
||||
|
||||
// Sub atomically subtracts from the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Sub(n uint32) uint32 { |
||||
return atomic.AddUint32(&i.v, ^(n - 1)) |
||||
} |
||||
|
||||
// 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.Sub(1) |
||||
} |
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Uint32) CAS(old, new uint32) bool { |
||||
return atomic.CompareAndSwapUint32(&i.v, old, new) |
||||
} |
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Uint32) Store(n uint32) { |
||||
atomic.StoreUint32(&i.v, n) |
||||
} |
||||
|
||||
// Swap atomically swaps the wrapped uint32 and returns the old value.
|
||||
func (i *Uint32) Swap(n uint32) uint32 { |
||||
return atomic.SwapUint32(&i.v, n) |
||||
} |
||||
|
||||
// Uint64 is an atomic wrapper around a uint64.
|
||||
type Uint64 struct{ v 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.v) |
||||
} |
||||
|
||||
// Add atomically adds to the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Add(n uint64) uint64 { |
||||
return atomic.AddUint64(&i.v, n) |
||||
} |
||||
|
||||
// Sub atomically subtracts from the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Sub(n uint64) uint64 { |
||||
return atomic.AddUint64(&i.v, ^(n - 1)) |
||||
} |
||||
|
||||
// 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.Sub(1) |
||||
} |
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Uint64) CAS(old, new uint64) bool { |
||||
return atomic.CompareAndSwapUint64(&i.v, old, new) |
||||
} |
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Uint64) Store(n uint64) { |
||||
atomic.StoreUint64(&i.v, n) |
||||
} |
||||
|
||||
// Swap atomically swaps the wrapped uint64 and returns the old value.
|
||||
func (i *Uint64) Swap(n uint64) uint64 { |
||||
return atomic.SwapUint64(&i.v, n) |
||||
} |
||||
|
||||
// Bool is an atomic Boolean.
|
||||
type Bool struct{ v uint32 } |
||||
|
||||
// NewBool creates a Bool.
|
||||
func NewBool(initial bool) *Bool { |
||||
return &Bool{boolToInt(initial)} |
||||
} |
||||
|
||||
// Load atomically loads the Boolean.
|
||||
func (b *Bool) Load() bool { |
||||
return truthy(atomic.LoadUint32(&b.v)) |
||||
} |
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (b *Bool) Store(new bool) { |
||||
atomic.StoreUint32(&b.v, boolToInt(new)) |
||||
} |
||||
|
||||
// Swap sets the given value and returns the previous value.
|
||||
func (b *Bool) Swap(new bool) bool { |
||||
return truthy(atomic.SwapUint32(&b.v, boolToInt(new))) |
||||
} |
||||
|
||||
// Toggle atomically negates the Boolean and returns the previous value.
|
||||
func (b *Bool) Toggle() bool { |
||||
return truthy(atomic.AddUint32(&b.v, 1) - 1) |
||||
} |
||||
|
||||
func truthy(n uint32) bool { |
||||
return n&1 == 1 |
||||
} |
||||
|
||||
func boolToInt(b bool) uint32 { |
||||
if b { |
||||
return 1 |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
// Float64 is an atomic wrapper around float64.
|
||||
type Float64 struct { |
||||
v uint64 |
||||
} |
||||
|
||||
// NewFloat64 creates a Float64.
|
||||
func NewFloat64(f float64) *Float64 { |
||||
return &Float64{math.Float64bits(f)} |
||||
} |
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (f *Float64) Load() float64 { |
||||
return math.Float64frombits(atomic.LoadUint64(&f.v)) |
||||
} |
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (f *Float64) Store(s float64) { |
||||
atomic.StoreUint64(&f.v, math.Float64bits(s)) |
||||
} |
||||
|
||||
// Add atomically adds to the wrapped float64 and returns the new value.
|
||||
func (f *Float64) Add(s float64) float64 { |
||||
for { |
||||
old := f.Load() |
||||
new := old + s |
||||
if f.CAS(old, new) { |
||||
return new |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Sub atomically subtracts from the wrapped float64 and returns the new value.
|
||||
func (f *Float64) Sub(s float64) float64 { |
||||
return f.Add(-s) |
||||
} |
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (f *Float64) CAS(old, new float64) bool { |
||||
return atomic.CompareAndSwapUint64(&f.v, math.Float64bits(old), math.Float64bits(new)) |
||||
} |
@ -0,0 +1,17 @@ |
||||
hash: f14d51408e3e0e4f73b34e4039484c78059cd7fc5f4996fdd73db20dc8d24f53 |
||||
updated: 2016-10-27T00:10:51.16960137-07:00 |
||||
imports: [] |
||||
testImports: |
||||
- name: github.com/davecgh/go-spew |
||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d |
||||
subpackages: |
||||
- spew |
||||
- name: github.com/pmezard/go-difflib |
||||
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d |
||||
subpackages: |
||||
- difflib |
||||
- name: github.com/stretchr/testify |
||||
version: d77da356e56a7428ad25149ca77381849a6a5232 |
||||
subpackages: |
||||
- assert |
||||
- require |
@ -0,0 +1,6 @@ |
||||
package: go.uber.org/atomic |
||||
testImport: |
||||
- package: github.com/stretchr/testify |
||||
subpackages: |
||||
- assert |
||||
- require |
@ -0,0 +1,51 @@ |
||||
// 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 "sync/atomic" |
||||
|
||||
// String is an atomic type-safe wrapper around atomic.Value for strings.
|
||||
type String struct{ v atomic.Value } |
||||
|
||||
// NewString creates a String.
|
||||
func NewString(str string) *String { |
||||
s := &String{} |
||||
if str != "" { |
||||
s.Store(str) |
||||
} |
||||
return s |
||||
} |
||||
|
||||
// Load atomically loads the wrapped string.
|
||||
func (s *String) Load() string { |
||||
v := s.v.Load() |
||||
if v == nil { |
||||
return "" |
||||
} |
||||
return v.(string) |
||||
} |
||||
|
||||
// Store atomically stores the passed string.
|
||||
// Note: Converting the string to an interface{} to store in the atomic.Value
|
||||
// requires an allocation.
|
||||
func (s *String) Store(str string) { |
||||
s.v.Store(str) |
||||
} |
Loading…
Reference in new issue