From 27749c212482f22f408efc4576e8410c6d00c7f1 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Thu, 6 Apr 2017 23:08:33 -0700 Subject: [PATCH] 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. --- cmd/admin-handlers.go | 28 ++- cmd/admin-handlers_test.go | 46 ++++ cmd/generic-handlers.go | 14 +- cmd/http-stats.go | 188 ++++++++++++++++ cmd/stats.go | 129 ----------- vendor/go.uber.org/atomic/LICENSE.txt | 19 ++ vendor/go.uber.org/atomic/Makefile | 64 ++++++ vendor/go.uber.org/atomic/README.md | 34 +++ vendor/go.uber.org/atomic/atomic.go | 300 ++++++++++++++++++++++++++ vendor/go.uber.org/atomic/glide.lock | 17 ++ vendor/go.uber.org/atomic/glide.yaml | 6 + vendor/go.uber.org/atomic/string.go | 51 +++++ vendor/vendor.json | 6 + 13 files changed, 771 insertions(+), 131 deletions(-) create mode 100644 cmd/http-stats.go delete mode 100644 cmd/stats.go create mode 100644 vendor/go.uber.org/atomic/LICENSE.txt create mode 100644 vendor/go.uber.org/atomic/Makefile create mode 100644 vendor/go.uber.org/atomic/README.md create mode 100644 vendor/go.uber.org/atomic/atomic.go create mode 100644 vendor/go.uber.org/atomic/glide.lock create mode 100644 vendor/go.uber.org/atomic/glide.yaml create mode 100644 vendor/go.uber.org/atomic/string.go diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index c66662ffd..3ad163db6 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -207,14 +207,37 @@ type ServerConnStats struct { Throughput uint64 `json:"throughput,omitempty"` } +// ServerHTTPMethodStats holds total number of HTTP operations from/to the server, +// including the average duration the call was spent. +type ServerHTTPMethodStats struct { + Count uint64 `json:"count"` + AvgDuration string `json:"avgDuration"` +} + +// ServerHTTPStats holds all type of http operations performed to/from the server +// including their average execution time. +type ServerHTTPStats struct { + TotalHEADStats ServerHTTPMethodStats `json:"totalHEADs"` + SuccessHEADStats ServerHTTPMethodStats `json:"successHEADs"` + TotalGETStats ServerHTTPMethodStats `json:"totalGETs"` + SuccessGETStats ServerHTTPMethodStats `json:"successGETs"` + TotalPUTStats ServerHTTPMethodStats `json:"totalPUTs"` + SuccessPUTStats ServerHTTPMethodStats `json:"successPUTs"` + TotalPOSTStats ServerHTTPMethodStats `json:"totalPOSTs"` + SuccessPOSTStats ServerHTTPMethodStats `json:"successPOSTs"` + TotalDELETEStats ServerHTTPMethodStats `json:"totalDELETEs"` + SuccessDELETEStats ServerHTTPMethodStats `json:"successDELETEs"` +} + // ServerInfo holds the information that will be returned by ServerInfo API type ServerInfo struct { StorageInfo StorageInfo `json:"storage"` ConnStats ServerConnStats `json:"network"` + HTTPStats ServerHTTPStats `json:"http"` Properties ServerProperties `json:"server"` } -// ServerInfoHandler - GET /?server-info +// ServerInfoHandler - GET /?info // ---------- // Get server information func (adminAPI adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Request) { @@ -262,11 +285,13 @@ func (adminAPI adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *htt TotalInputBytes: globalConnStats.getTotalInputBytes(), TotalOutputBytes: globalConnStats.getTotalOutputBytes(), } + httpStats := globalHTTPStats.toServerHTTPStats() // Build the whole returned information info := ServerInfo{ StorageInfo: storage, ConnStats: connStats, + HTTPStats: httpStats, Properties: properties, } @@ -277,6 +302,7 @@ func (adminAPI adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *htt errorIf(err, "Failed to marshal storage info into json.") return } + // Reply with storage information (across nodes in a // distributed setup) as json. writeSuccessResponseJSON(w, jsonBytes) diff --git a/cmd/admin-handlers_test.go b/cmd/admin-handlers_test.go index 6473cba63..2c9a1ab99 100644 --- a/cmd/admin-handlers_test.go +++ b/cmd/admin-handlers_test.go @@ -1313,6 +1313,52 @@ func TestSetConfigHandler(t *testing.T) { } } +func TestAdminServerInfo(t *testing.T) { + adminTestBed, err := prepareAdminXLTestBed() + if err != nil { + t.Fatal("Failed to initialize a single node XL backend for admin handler tests.") + } + defer adminTestBed.TearDown() + + // Initialize admin peers to make admin RPC calls. + eps, err := parseStorageEndpoints([]string{"http://127.0.0.1"}) + if err != nil { + t.Fatalf("Failed to parse storage end point - %v", err) + } + + // Set globalMinioAddr to be able to distinguish local endpoints from remote. + globalMinioAddr = eps[0].Host + initGlobalAdminPeers(eps) + + // Prepare query params for set-config mgmt REST API. + queryVal := url.Values{} + queryVal.Set("info", "") + + req, err := buildAdminRequest(queryVal, "", http.MethodGet, 0, nil) + if err != nil { + t.Fatalf("Failed to construct get-config object request - %v", err) + } + + rec := httptest.NewRecorder() + adminTestBed.mux.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Errorf("Expected to succeed but failed with %d", rec.Code) + } + + result := ServerInfo{} + err = json.NewDecoder(rec.Body).Decode(&result) + if err != nil { + t.Fatalf("Failed to decode set config result json %v", err) + } + + if result.StorageInfo.Free == 0 { + t.Error("Expected StorageInfo.Free to be non empty") + } + if result.Properties.Region != globalMinioDefaultRegion { + t.Errorf("Expected %s, got %s", globalMinioDefaultRegion, result.Properties.Region) + } +} + // TestToAdminAPIErr - test for toAdminAPIErr helper function. func TestToAdminAPIErr(t *testing.T) { testCases := []struct { diff --git a/cmd/generic-handlers.go b/cmd/generic-handlers.go index 83cf0313c..facef3fc7 100644 --- a/cmd/generic-handlers.go +++ b/cmd/generic-handlers.go @@ -401,9 +401,21 @@ func (h httpStatsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Wraps w to record http response information ww := &httpResponseRecorder{ResponseWriter: w} + // Time start before the call is about to start. + tBefore := UTCNow() + // Execute the request h.handler.ServeHTTP(ww, r) + // Time after call has completed. + tAfter := UTCNow() + + // Time duration in secs since the call started. + // + // We don't need to do nanosecond precision in this + // simply for the fact that it is not human readable. + durationSecs := tAfter.Sub(tBefore).Seconds() + // Update http statistics - globalHTTPStats.updateStats(r, ww) + globalHTTPStats.updateStats(r, ww, durationSecs) } diff --git a/cmd/http-stats.go b/cmd/http-stats.go new file mode 100644 index 000000000..ff917131d --- /dev/null +++ b/cmd/http-stats.go @@ -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{} +} diff --git a/cmd/stats.go b/cmd/stats.go deleted file mode 100644 index 9dc691839..000000000 --- a/cmd/stats.go +++ /dev/null @@ -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{} -} diff --git a/vendor/go.uber.org/atomic/LICENSE.txt b/vendor/go.uber.org/atomic/LICENSE.txt new file mode 100644 index 000000000..8765c9fbc --- /dev/null +++ b/vendor/go.uber.org/atomic/LICENSE.txt @@ -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. diff --git a/vendor/go.uber.org/atomic/Makefile b/vendor/go.uber.org/atomic/Makefile new file mode 100644 index 000000000..4bc9a07f5 --- /dev/null +++ b/vendor/go.uber.org/atomic/Makefile @@ -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)) diff --git a/vendor/go.uber.org/atomic/README.md b/vendor/go.uber.org/atomic/README.md new file mode 100644 index 000000000..bb4a12c18 --- /dev/null +++ b/vendor/go.uber.org/atomic/README.md @@ -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. + +
+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 diff --git a/vendor/go.uber.org/atomic/atomic.go b/vendor/go.uber.org/atomic/atomic.go new file mode 100644 index 000000000..cd4aaa204 --- /dev/null +++ b/vendor/go.uber.org/atomic/atomic.go @@ -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)) +} diff --git a/vendor/go.uber.org/atomic/glide.lock b/vendor/go.uber.org/atomic/glide.lock new file mode 100644 index 000000000..3c72c5997 --- /dev/null +++ b/vendor/go.uber.org/atomic/glide.lock @@ -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 diff --git a/vendor/go.uber.org/atomic/glide.yaml b/vendor/go.uber.org/atomic/glide.yaml new file mode 100644 index 000000000..4cf608ec0 --- /dev/null +++ b/vendor/go.uber.org/atomic/glide.yaml @@ -0,0 +1,6 @@ +package: go.uber.org/atomic +testImport: +- package: github.com/stretchr/testify + subpackages: + - assert + - require diff --git a/vendor/go.uber.org/atomic/string.go b/vendor/go.uber.org/atomic/string.go new file mode 100644 index 000000000..acf0f5bb1 --- /dev/null +++ b/vendor/go.uber.org/atomic/string.go @@ -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) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 0908fad83..6529493b2 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -325,6 +325,12 @@ "revision": "173748da739a410c5b0b813b956f89ff94730b4c", "revisionTime": "2016-08-30T17:39:30Z" }, + { + "checksumSHA1": "6NS7FWJl1FobB+Xfe4SzBGD+75g=", + "path": "go.uber.org/atomic", + "revision": "3b8db5e93c4c02efbc313e17b2e796b0914a01fb", + "revisionTime": "2016-12-15T19:56:52Z" + }, { "path": "golang.org/x/crypto/bcrypt", "revision": "7b85b097bf7527677d54d3220065e966a0e3b613",