diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8f8a70711..09a980a16 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -94,7 +94,7 @@ jobs: go-version: 1.15 - name: Install proto-gen-go run: go get -u github.com/golang/protobuf/protoc-gen-go@v1.4.2 - - name: Install proto-gen-go + - name: Install go-syncmap run: go get github.com/searKing/golang/tools/cmd/go-syncmap - name: Generate files run: go generate ./... diff --git a/pkg/networkservice/chains/endpoint/server.go b/pkg/networkservice/chains/endpoint/server.go index 5ef2567d9..3949e3a12 100644 --- a/pkg/networkservice/chains/endpoint/server.go +++ b/pkg/networkservice/chains/endpoint/server.go @@ -61,13 +61,15 @@ type endpoint struct { // - additionalFunctionality - any additional NetworkServiceServer chain elements to be included in the chain func NewServer(ctx context.Context, name string, authzServer networkservice.NetworkServiceServer, tokenGenerator token.GeneratorFunc, additionalFunctionality ...networkservice.NetworkServiceServer) Endpoint { rv := &endpoint{} - var ns networkservice.NetworkServiceServer = rv rv.NetworkServiceServer = chain.NewNetworkServiceServer( append([]networkservice.NetworkServiceServer{ authzServer, updatepath.NewServer(name), + // `timeout` uses ctx as a context for the timeout Close and it closes only the subsequent chain, so + // chain elements before the `timeout` in chain shouldn't make any updates to the Close context and + // shouldn't be closed on Connection Close. + timeout.NewServer(ctx), monitor.NewServer(ctx, &rv.MonitorConnectionServer), - timeout.NewServer(&ns), updatetoken.NewServer(tokenGenerator), }, additionalFunctionality...)...) return rv diff --git a/pkg/networkservice/common/externalips/string_map.gen.go b/pkg/networkservice/common/externalips/string_map.gen.go index 6dda70aaf..c56180678 100644 --- a/pkg/networkservice/common/externalips/string_map.gen.go +++ b/pkg/networkservice/common/externalips/string_map.gen.go @@ -1,9 +1,11 @@ -// Code generated by "go-syncmap -output string_map.gen.go -type stringMap"; DO NOT EDIT. - +// Code generated by "-output string_map.gen.go -type stringMap -output string_map.gen.go -type stringMap"; DO NOT EDIT. package externalips -import "sync" +import ( + "sync" // Used by sync.Map. +) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert stringMap literal (type stringMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. @@ -12,10 +14,25 @@ func _() { var _nil_stringMap_string_value = func() (val string) { return }() +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *stringMap) Load(key string) (string, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_stringMap_string_value, ok + } + return value.(string), ok +} + +// Store sets the value for a key. func (m *stringMap) Store(key string, value string) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *stringMap) LoadOrStore(key string, value string) (string, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { @@ -24,18 +41,31 @@ func (m *stringMap) LoadOrStore(key string, value string) (string, bool) { return actual.(string), loaded } -func (m *stringMap) Load(key string) (string, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return _nil_stringMap_string_value, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *stringMap) LoadAndDelete(key string) (value string, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_stringMap_string_value, loaded } - return value.(string), ok + return actual.(string), loaded } +// Delete deletes the value for a key. func (m *stringMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *stringMap) Range(f func(key string, value string) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(string)) diff --git a/pkg/networkservice/common/interpose/connection_info_map.gen.go b/pkg/networkservice/common/interpose/connection_info_map.gen.go index 0fecd3532..1909e42cb 100644 --- a/pkg/networkservice/common/interpose/connection_info_map.gen.go +++ b/pkg/networkservice/common/interpose/connection_info_map.gen.go @@ -1,9 +1,11 @@ -// Code generated by "go-syncmap -output connection_info_map.gen.go -type connectionInfoMap"; DO NOT EDIT. - +// Code generated by "-output connection_info_map.gen.go -type connectionInfoMap -output connection_info_map.gen.go -type connectionInfoMap"; DO NOT EDIT. package interpose -import "sync" +import ( + "sync" // Used by sync.Map. +) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert connectionInfoMap literal (type connectionInfoMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. @@ -12,10 +14,25 @@ func _() { var _nil_connectionInfoMap_connectionInfo_value = func() (val connectionInfo) { return }() +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *connectionInfoMap) Load(key string) (connectionInfo, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_connectionInfoMap_connectionInfo_value, ok + } + return value.(connectionInfo), ok +} + +// Store sets the value for a key. func (m *connectionInfoMap) Store(key string, value connectionInfo) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *connectionInfoMap) LoadOrStore(key string, value connectionInfo) (connectionInfo, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { @@ -24,18 +41,31 @@ func (m *connectionInfoMap) LoadOrStore(key string, value connectionInfo) (conne return actual.(connectionInfo), loaded } -func (m *connectionInfoMap) Load(key string) (connectionInfo, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return _nil_connectionInfoMap_connectionInfo_value, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *connectionInfoMap) LoadAndDelete(key string) (value connectionInfo, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_connectionInfoMap_connectionInfo_value, loaded } - return value.(connectionInfo), ok + return actual.(connectionInfo), loaded } +// Delete deletes the value for a key. func (m *connectionInfoMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *connectionInfoMap) Range(f func(key string, value connectionInfo) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(connectionInfo)) diff --git a/pkg/networkservice/common/mechanisms/recvfd/per_connection_file_map.gen.go b/pkg/networkservice/common/mechanisms/recvfd/per_connection_file_map.gen.go index d9a0fcf3b..b43f44eea 100644 --- a/pkg/networkservice/common/mechanisms/recvfd/per_connection_file_map.gen.go +++ b/pkg/networkservice/common/mechanisms/recvfd/per_connection_file_map.gen.go @@ -1,38 +1,71 @@ -// Code generated by "go-syncmap -output per_connection_file_map.gen.go -type perConnectionFileMapMap"; DO NOT EDIT. - +// Code generated by "-output per_connection_file_map.gen.go -type perConnectionFileMapMap -output per_connection_file_map.gen.go -type perConnectionFileMapMap"; DO NOT EDIT. package recvfd -import "sync" +import ( + "sync" // Used by sync.Map. +) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert perConnectionFileMapMap literal (type perConnectionFileMapMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. _ = (sync.Map)(perConnectionFileMapMap{}) } + +var _nil_perConnectionFileMapMap_perConnectionFileMap_value = func() (val *perConnectionFileMap) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *perConnectionFileMapMap) Load(key string) (*perConnectionFileMap, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_perConnectionFileMapMap_perConnectionFileMap_value, ok + } + return value.(*perConnectionFileMap), ok +} + +// Store sets the value for a key. func (m *perConnectionFileMapMap) Store(key string, value *perConnectionFileMap) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *perConnectionFileMapMap) LoadOrStore(key string, value *perConnectionFileMap) (*perConnectionFileMap, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { - return nil, loaded + return _nil_perConnectionFileMapMap_perConnectionFileMap_value, loaded } return actual.(*perConnectionFileMap), loaded } -func (m *perConnectionFileMapMap) Load(key string) (*perConnectionFileMap, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return nil, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *perConnectionFileMapMap) LoadAndDelete(key string) (value *perConnectionFileMap, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_perConnectionFileMapMap_perConnectionFileMap_value, loaded } - return value.(*perConnectionFileMap), ok + return actual.(*perConnectionFileMap), loaded } +// Delete deletes the value for a key. func (m *perConnectionFileMapMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *perConnectionFileMapMap) Range(f func(key string, value *perConnectionFileMap) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(*perConnectionFileMap)) diff --git a/pkg/networkservice/common/timeout/README.md b/pkg/networkservice/common/timeout/README.md new file mode 100644 index 000000000..3cfd6b4e1 --- /dev/null +++ b/pkg/networkservice/common/timeout/README.md @@ -0,0 +1,47 @@ +# Functional requirements + +If authentication token of the previous connection path element expires, it does mean that we lost connection with the +previous NSM-based application. For such case connection should be closed. + +# Implementation + +## timeoutServer + +timeoutServer keeps timers [timeout.timerMap](https://github.com/networkservicemesh/sdk/blob/master/pkg/networkservice/common/timeout/gen.go#L26) +mapping incoming request Connection.ID to a timeout timer firing Close on the subsequent chain after the connection previous +path element expires. To prevent simultaneous execution of multiple Request, Close event for the same Connection.ID in parallel +it also keeps executors [timeout.executorMap](https://github.com/networkservicemesh/sdk/blob/master/pkg/networkservice/common/timeout/gen.go#L27) +mapping request Connection.ID to an executor for serializing all Request, Close event for the mapped Connection.ID. + +timeoutServer closes only subsequent chain elements and uses base context for the Close. So all the chain elements in +the chain before the timeoutServer shouldn't be closed with Close and shouldn't set any required data to the Close context. + +Example of correct timeoutServer usage in [endpoint.NewServer](https://github.com/networkservicemesh/sdk/blob/master/pkg/networkservice/chains/endpoint/server.go#L62): +```go +rv.NetworkServiceServer = chain.NewNetworkServiceServer( + append([]networkservice.NetworkServiceServer{ + authzServer, // <-- shouldn't be closed, don't set anything to the context + updatepath.NewServer(name), // <-- same + timeout.NewServer(ctx), // <-- timeoutServer + monitor.NewServer(ctx, &rv.MonitorConnectionServer), // <-- should be closed + updatetoken.NewServer(tokenGenerator), // <-- should be closed + }, additionalFunctionality...)...) // <-- should be closed, sets data to context +``` + +## Comments on concurrency characteristics + +Concurrency is managed through type specific wrappers of [sync.Map](https://golang.org/pkg/sync/#Map) and with +per-connection [serialize.Executor](https://github.com/edwarnicke/serialize/blob/master/serialize.go) which are created on +Request and deleted on Close. + +Since we are deleting the per-connection executor on connection Close, there possibly can be a race condition: +``` +1. -> timeout close : locking executor +2. -> request-1 : waiting on executor +3. timeout close -> : unlocking executor, removing it from executors +4. -> request-2 : creating exec, storing into executors, locking exec +5. -request-1-> : locking executor, trying to store it into executors +``` +at 5. we get request-1 locking executor, request-2 locking exec and only exec stored in executors. It means that +request-2 and all subsequent events will be executed in parallel with request-1. +So we check for this [here](https://github.com/networkservicemesh/sdk/blob/master/pkg/networkservice/common/timeout/server.go#L68). \ No newline at end of file diff --git a/pkg/networkservice/common/timeout/executor_map.gen.go b/pkg/networkservice/common/timeout/executor_map.gen.go new file mode 100644 index 000000000..ddd3c86a1 --- /dev/null +++ b/pkg/networkservice/common/timeout/executor_map.gen.go @@ -0,0 +1,75 @@ +// Code generated by "-output executor_map.gen.go -type executorMap -output executor_map.gen.go -type executorMap"; DO NOT EDIT. +package timeout + +import ( + "sync" // Used by sync.Map. + + "github.com/edwarnicke/serialize" +) + +// Generate code that will fail if the constants change value. +func _() { + // An "cannot convert executorMap literal (type executorMap) to type sync.Map" compiler error signifies that the base type have changed. + // Re-run the go-syncmap command to generate them again. + _ = (sync.Map)(executorMap{}) +} + +var _nil_executorMap_serialize_Executor_value = func() (val *serialize.Executor) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *executorMap) Load(key string) (*serialize.Executor, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_executorMap_serialize_Executor_value, ok + } + return value.(*serialize.Executor), ok +} + +// Store sets the value for a key. +func (m *executorMap) Store(key string, value *serialize.Executor) { + (*sync.Map)(m).Store(key, value) +} + +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. +func (m *executorMap) LoadOrStore(key string, value *serialize.Executor) (*serialize.Executor, bool) { + actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) + if actual == nil { + return _nil_executorMap_serialize_Executor_value, loaded + } + return actual.(*serialize.Executor), loaded +} + +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *executorMap) LoadAndDelete(key string) (value *serialize.Executor, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_executorMap_serialize_Executor_value, loaded + } + return actual.(*serialize.Executor), loaded +} + +// Delete deletes the value for a key. +func (m *executorMap) Delete(key string) { + (*sync.Map)(m).Delete(key) +} + +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. +func (m *executorMap) Range(f func(key string, value *serialize.Executor) bool) { + (*sync.Map)(m).Range(func(key, value interface{}) bool { + return f(key.(string), value.(*serialize.Executor)) + }) +} diff --git a/pkg/networkservice/common/timeout/gen.go b/pkg/networkservice/common/timeout/gen.go new file mode 100644 index 000000000..b8573f453 --- /dev/null +++ b/pkg/networkservice/common/timeout/gen.go @@ -0,0 +1,27 @@ +// Copyright (c) 2020 Doc.ai and/or its affiliates. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 timeout + +import ( + "sync" +) + +//go:generate go-syncmap -output timer_map.gen.go -type timerMap +//go:generate go-syncmap -output executor_map.gen.go -type executorMap + +type timerMap sync.Map +type executorMap sync.Map diff --git a/pkg/networkservice/common/timeout/server.go b/pkg/networkservice/common/timeout/server.go index b366d137f..d430a0a9b 100644 --- a/pkg/networkservice/common/timeout/server.go +++ b/pkg/networkservice/common/timeout/server.go @@ -1,3 +1,5 @@ +// Copyright (c) 2020 Doc.ai and/or its affiliates. +// // Copyright (c) 2020 Cisco Systems, Inc. // // SPDX-License-Identifier: Apache-2.0 @@ -21,80 +23,170 @@ import ( "context" "time" + "github.com/edwarnicke/serialize" "github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes/empty" - "github.com/networkservicemesh/api/pkg/api/networkservice" + "github.com/pkg/errors" - "github.com/edwarnicke/serialize" + "github.com/networkservicemesh/api/pkg/api/networkservice" "github.com/networkservicemesh/sdk/pkg/networkservice/core/next" - "github.com/networkservicemesh/sdk/pkg/networkservice/core/trace" - "github.com/networkservicemesh/sdk/pkg/tools/extend" + "github.com/networkservicemesh/sdk/pkg/tools/log" ) type timeoutServer struct { - onTimeout *networkservice.NetworkServiceServer - connections map[string]*time.Timer - executor serialize.Executor + ctx context.Context + timers timerMap + executors executorMap } // NewServer - creates a new NetworkServiceServer chain element that implements timeout of expired connections -// - onTimeout - *networkservice.NetworkServiceServer. Since networkservice.NetworkServiceServer is an interface -// (and thus a pointer) *networkservice.NetworkServiceServer is a double pointer. Meaning it -// points to a place that points to a place that implements networkservice.NetworkServiceServer -// This is done because when we use timeout.NewServer as part of a chain, we may not *have* -// a pointer to this server used 'onTimeout'. If we detect we need to heal, onHeal.Request is used to heal. -// If onTimeout is nil, then we simply set onTimeout to this server chain element -// If we are part of a larger chain, we should pass the resulting chain into -// this constructor before we actually have a pointer to it. -func NewServer(onTimout *networkservice.NetworkServiceServer) networkservice.NetworkServiceServer { - rv := &timeoutServer{ - connections: make(map[string]*time.Timer), - onTimeout: onTimout, - } - if rv.onTimeout == nil { - var actualOnTimeout networkservice.NetworkServiceServer = rv - rv.onTimeout = &actualOnTimeout +// for the subsequent chain elements. +// WARNING: `timeout` uses ctx as a context for the Close, so if there are any chain elements setting some data +// in context in chain before the `timeout`, these changes won't appear in the Close context. +func NewServer(ctx context.Context) networkservice.NetworkServiceServer { + return &timeoutServer{ + ctx: ctx, } - return rv } -func (t *timeoutServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) { - ct, err := t.createTimer(ctx, request) - if err != nil { - return nil, err - } +func (t *timeoutServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (conn *networkservice.Connection, err error) { connID := request.GetConnection().GetId() - t.executor.AsyncExec(func() { - if timer, ok := t.connections[connID]; !ok || timer.Stop() { - t.connections[connID] = ct + + newExecutor := new(serialize.Executor) + // If we create an executor, we should be the first one who uses it, or else we can get a double Close issue: + // 1. timeout close -> : closing the Connection + // 2. request -> : creating `newExecutor`, storing into `executors` + // 3. close -> : locking `newExecutor`, checking `newExecutor == executors[connID]` - looks like + // a retry Close case (but it isn't), double closing the Connection + <-newExecutor.AsyncExec(func() { + executor, loaded := t.executors.LoadOrStore(connID, newExecutor) + if loaded { + <-executor.AsyncExec(func() { + // Executor has been possibly removed by `t.close()` at this moment, we need to store it back. + exec, _ := t.executors.LoadOrStore(connID, executor) + if exec != executor { + // It can happen in such situation: + // 1. -> timeout close : locking `executor` + // 2. -> request-1 : waiting on `executor` + // 3. timeout close -> : unlocking `executor`, removing it from `executors` + // 4. -> request-2 : creating `exec`, storing into `executors`, locking `exec` + // 5. -request-1-> : locking `executor`, trying to store it into `executors` + // at 5. we get `request-1` locking `executor`, `request-2` locking `exec` and only `exec` stored + // in `executors`. It means that `request-2` and all subsequent events will be executed in parallel + // with `request-1`. + err = errors.Errorf("race condition, parallel request execution: %v", connID) + return + } + conn, err = t.request(ctx, request, executor) + }) + } else { + conn, err = t.request(ctx, request, executor) } }) - return next.Server(ctx).Request(ctx, request) + + return conn, err } -func (t *timeoutServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) { - t.executor.AsyncExec(func() { - if timer, ok := t.connections[conn.GetId()]; ok { - timer.Stop() - delete(t.connections, conn.GetId()) +func (t *timeoutServer) request(ctx context.Context, request *networkservice.NetworkServiceRequest, executor *serialize.Executor) (*networkservice.Connection, error) { + logEntry := log.Entry(ctx).WithField("timeoutServer", "request") + + connID := request.GetConnection().GetId() + + if timer, ok := t.timers.LoadAndDelete(connID); ok { + if !timer.Stop() { + // Even if we failed to stop the timer, we should execute. It does mean that the timeout action + // is waiting on `executor.AsyncExec()` until we will finish. + // Since timer is being deleted under the `executor.AsyncExec()` this can't be a situation when + // the Request is executing after the timeout Close. Such case cannot be distinguished with the + // first-request case. + logEntry.Warnf("connection has been timed out, re requesting: %v", connID) } - }) - return next.Server(ctx).Close(ctx, conn) -} + } -func (t *timeoutServer) createTimer(ctx context.Context, request *networkservice.NetworkServiceRequest) (*time.Timer, error) { - request = request.Clone() - expireTime, err := ptypes.Timestamp(request.GetConnection().GetPath().GetPathSegments()[request.GetConnection().GetPath().GetIndex()-1].GetExpires()) + conn, err := next.Server(ctx).Request(ctx, request) if err != nil { return nil, err } - duration := time.Until(expireTime) - conn := request.GetConnection().Clone() - return time.AfterFunc(duration, func() { - newCtx := extend.WithValuesFromContext(context.Background(), ctx) - if _, err := (*t.onTimeout).Close(newCtx, conn); err != nil { - trace.Log(newCtx).Errorf("Error attempting to close timed out connection: %s: %+v", request.GetConnection().GetId(), err) + + timer, err := t.createTimer(ctx, conn, executor) + if err != nil { + if _, closeErr := next.Server(ctx).Close(ctx, conn); closeErr != nil { + err = errors.Wrapf(err, "error attempting to close failed connection %v: %+v", connID, closeErr) } - }), nil + return nil, err + } + + t.timers.Store(connID, timer) + + return conn, nil +} + +func (t *timeoutServer) createTimer(ctx context.Context, conn *networkservice.Connection, executor *serialize.Executor) (*time.Timer, error) { + logEntry := log.Entry(ctx).WithField("timeoutServer", "createTimer") + + expireTime, err := ptypes.Timestamp(conn.GetPath().GetPathSegments()[conn.GetPath().GetIndex()-1].GetExpires()) + if err != nil { + return nil, err + } + + conn = conn.Clone() + + timerPtr := new(*time.Timer) + *timerPtr = time.AfterFunc(time.Until(expireTime), func() { + executor.AsyncExec(func() { + if timer, ok := t.timers.Load(conn.GetId()); !ok || timer != *timerPtr { + logEntry.Warnf("timer has been already stopped: %v", conn.GetId()) + return + } + if err := t.close(t.ctx, conn, next.Server(ctx)); err != nil { + logEntry.Errorf("failed to close timed out connection: %v %+v", conn.GetId(), err) + } + }) + }) + + return *timerPtr, nil +} + +func (t *timeoutServer) Close(ctx context.Context, conn *networkservice.Connection) (_ *empty.Empty, err error) { + logEntry := log.Entry(ctx).WithField("timeoutServer", "Close") + + executor, ok := t.executors.Load(conn.GetId()) + if ok { + <-executor.AsyncExec(func() { + var exec *serialize.Executor + if exec, ok = t.executors.Load(conn.GetId()); ok && exec == executor { + err = t.close(ctx, conn, next.Server(ctx)) + } else { + ok = false + } + }) + } + if !ok { + logEntry.Warnf("connection has been already closed: %v", conn.GetId()) + return &empty.Empty{}, nil + } + + return &empty.Empty{}, err +} + +func (t *timeoutServer) close(ctx context.Context, conn *networkservice.Connection, nextServer networkservice.NetworkServiceServer) error { + logEntry := log.Entry(ctx).WithField("timeoutServer", "close") + + timer, ok := t.timers.LoadAndDelete(conn.GetId()) + if ok { + timer.Stop() + } else { + // Last time we failed to close the Connection, let's do it again. + logEntry.Warnf("retrying to close the connection: %v", conn.GetId()) + } + + _, err := nextServer.Close(ctx, conn) + if err == nil { + // If `nextServer.Close()` returns an error, the Connection is not truly closed, so we don't want to delete + // the related executor. + t.executors.Delete(conn.GetId()) + } + + return err } diff --git a/pkg/networkservice/common/timeout/server_test.go b/pkg/networkservice/common/timeout/server_test.go new file mode 100644 index 000000000..4ff7d042c --- /dev/null +++ b/pkg/networkservice/common/timeout/server_test.go @@ -0,0 +1,271 @@ +// Copyright (c) 2020 Doc.ai and/or its affiliates. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 timeout_test + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/golang/protobuf/ptypes/empty" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/credentials" + + "github.com/networkservicemesh/api/pkg/api/networkservice" + kernelmech "github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/kernel" + + "github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanisms" + "github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanisms/kernel" + "github.com/networkservicemesh/sdk/pkg/networkservice/common/timeout" + "github.com/networkservicemesh/sdk/pkg/networkservice/common/updatepath" + "github.com/networkservicemesh/sdk/pkg/networkservice/common/updatetoken" + "github.com/networkservicemesh/sdk/pkg/networkservice/core/adapters" + "github.com/networkservicemesh/sdk/pkg/networkservice/core/chain" + "github.com/networkservicemesh/sdk/pkg/networkservice/core/next" +) + +const ( + clientName = "client" + serverName = "server" + tokenTimeout = 100 * time.Millisecond + waitFor = 10 * tokenTimeout + tick = 10 * time.Millisecond + serverID = "server-id" + parallelCount = 1000 +) + +func testClient(ctx context.Context, server networkservice.NetworkServiceServer, duration time.Duration) networkservice.NetworkServiceClient { + return chain.NewNetworkServiceClient( + updatepath.NewClient(clientName), + updatetoken.NewClient(func(_ credentials.AuthInfo) (string, time.Time, error) { + return "token", time.Now().Add(duration), nil + }), + kernel.NewClient(), + adapters.NewServerToClient( + chain.NewNetworkServiceServer( + updatepath.NewServer(serverName), + timeout.NewServer(ctx), + mechanisms.NewServer(map[string]networkservice.NetworkServiceServer{ + kernelmech.MECHANISM: server, + }), + ), + ), + ) +} + +func TestTimeoutServer_Request(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + connServer := newConnectionsServer(t) + + _, err := testClient(ctx, connServer, tokenTimeout).Request(ctx, &networkservice.NetworkServiceRequest{}) + require.NoError(t, err) + require.Condition(t, connServer.validator(1, 0)) + + require.Eventually(t, connServer.validator(0, 1), waitFor, tick) +} + +func TestTimeoutServer_Close_BeforeTimeout(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + connServer := newConnectionsServer(t) + + client := testClient(ctx, connServer, tokenTimeout) + + conn, err := client.Request(ctx, &networkservice.NetworkServiceRequest{}) + require.NoError(t, err) + require.Condition(t, connServer.validator(1, 0)) + + _, err = client.Close(ctx, conn) + require.NoError(t, err) + require.Condition(t, connServer.validator(0, 1)) + + // ensure there will be no double Close + <-time.After(waitFor) +} + +func TestTimeoutServer_Close_AfterTimeout(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + connServer := newConnectionsServer(t) + + client := testClient(ctx, connServer, tokenTimeout) + + conn, err := client.Request(ctx, &networkservice.NetworkServiceRequest{}) + require.NoError(t, err) + require.Condition(t, connServer.validator(1, 0)) + + require.Eventually(t, connServer.validator(0, 1), waitFor, tick) + + _, err = client.Close(ctx, conn) + require.NoError(t, err) + require.Condition(t, connServer.validator(0, 1)) +} + +func stressTestRequest() *networkservice.NetworkServiceRequest { + return &networkservice.NetworkServiceRequest{ + Connection: &networkservice.Connection{ + Path: &networkservice.Path{ + PathSegments: []*networkservice.PathSegment{ + { + Name: clientName, + Id: "client-id", + }, + { + Name: serverName, + Id: serverID, + }, + }, + }, + }, + } +} + +func TestTimeoutServer_StressTest_DoubleClose(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + logLevel := logrus.GetLevel() + logrus.SetLevel(logrus.WarnLevel) + defer logrus.SetLevel(logLevel) + + connServer := newConnectionsServer(t) + + client := testClient(ctx, connServer, 0) + + wg := new(sync.WaitGroup) + wg.Add(parallelCount) + for i := 0; i < parallelCount; i++ { + go func() { + defer wg.Done() + conn, err := client.Request(ctx, stressTestRequest()) + if err != nil { + assert.EqualError(t, err, "race condition, parallel request execution: server-id") + return + } + _, err = client.Close(ctx, conn) + assert.NoError(t, err) + }() + } + wg.Wait() +} + +type connectionsServer struct { + t *testing.T + lock sync.Mutex + connections map[string]*connectionInfo +} + +type connectionInfo struct { + state bool + closeCount int +} + +func newConnectionsServer(t *testing.T) *connectionsServer { + return &connectionsServer{ + t: t, + connections: map[string]*connectionInfo{}, + } +} + +func (s *connectionsServer) validator(open, closed int) func() bool { + return func() bool { + s.lock.Lock() + defer s.lock.Unlock() + + var connsOpen, connsClosed int + for _, connInfo := range s.connections { + if connInfo.state { + connsOpen++ + } else { + connsClosed++ + } + } + + if connsOpen != open { + logrus.Warnf("open count is not equal: expected %v != actual %v", open, connsOpen) + return false + } + if connsClosed != closed { + logrus.Warnf("closed count is not equal: expected %v != actual %v", closed, connsClosed) + return false + } + + return true + } +} + +func (s *connectionsServer) connValidator(connID string, state bool, closeCount int) func() bool { + return func() bool { + s.lock.Lock() + defer s.lock.Unlock() + + connInfo, ok := s.connections[connID] + if !ok { + logrus.Warnf("connection doesn't exist: %v", connID) + return false + } + if connInfo.state != state || connInfo.closeCount != closeCount { + logrus.Warnf("expected connectionInfo = { state: %v, closeCount: %v }, got: { state: %v, closeCount: %v }", + state, closeCount, connInfo.state, connInfo.closeCount) + return false + } + + return true + } +} + +func (s *connectionsServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) { + s.lock.Lock() + + connID := request.GetConnection().GetId() + + connInfo, ok := s.connections[connID] + if !ok { + connInfo = new(connectionInfo) + s.connections[connID] = connInfo + } + connInfo.state = true + + s.lock.Unlock() + + return next.Server(ctx).Request(ctx, request) +} + +func (s *connectionsServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) { + s.lock.Lock() + + connID := conn.GetId() + + connInfo, ok := s.connections[connID] + if !ok || !connInfo.state { + assert.Fail(s.t, "closing not opened connection: %v", connID) + } else { + connInfo.state = false + connInfo.closeCount++ + } + + s.lock.Unlock() + + return next.Server(ctx).Close(ctx, conn) +} diff --git a/pkg/networkservice/common/timeout/timer_map.gen.go b/pkg/networkservice/common/timeout/timer_map.gen.go new file mode 100644 index 000000000..6763ec3d4 --- /dev/null +++ b/pkg/networkservice/common/timeout/timer_map.gen.go @@ -0,0 +1,74 @@ +// Code generated by "-output timer_map.gen.go -type timerMap -output timer_map.gen.go -type timerMap"; DO NOT EDIT. +package timeout + +import ( + "sync" // Used by sync.Map. + "time" +) + +// Generate code that will fail if the constants change value. +func _() { + // An "cannot convert timerMap literal (type timerMap) to type sync.Map" compiler error signifies that the base type have changed. + // Re-run the go-syncmap command to generate them again. + _ = (sync.Map)(timerMap{}) +} + +var _nil_timerMap_time_Timer_value = func() (val *time.Timer) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *timerMap) Load(key string) (*time.Timer, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_timerMap_time_Timer_value, ok + } + return value.(*time.Timer), ok +} + +// Store sets the value for a key. +func (m *timerMap) Store(key string, value *time.Timer) { + (*sync.Map)(m).Store(key, value) +} + +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. +func (m *timerMap) LoadOrStore(key string, value *time.Timer) (*time.Timer, bool) { + actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) + if actual == nil { + return _nil_timerMap_time_Timer_value, loaded + } + return actual.(*time.Timer), loaded +} + +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *timerMap) LoadAndDelete(key string) (value *time.Timer, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_timerMap_time_Timer_value, loaded + } + return actual.(*time.Timer), loaded +} + +// Delete deletes the value for a key. +func (m *timerMap) Delete(key string) { + (*sync.Map)(m).Delete(key) +} + +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. +func (m *timerMap) Range(f func(key string, value *time.Timer) bool) { + (*sync.Map)(m).Range(func(key, value interface{}) bool { + return f(key.(string), value.(*time.Timer)) + }) +} diff --git a/pkg/registry/common/connect/ns_sync_map.gen.go b/pkg/registry/common/connect/ns_sync_map.gen.go index aa9da4753..59de75553 100644 --- a/pkg/registry/common/connect/ns_sync_map.gen.go +++ b/pkg/registry/common/connect/ns_sync_map.gen.go @@ -1,38 +1,71 @@ -// Code generated by "go-syncmap -output ns_sync_map.gen.go -type nsClientMap"; DO NOT EDIT. - +// Code generated by "-output ns_sync_map.gen.go -type nsClientMap -output ns_sync_map.gen.go -type nsClientMap"; DO NOT EDIT. package connect -import "sync" +import ( + "sync" // Used by sync.Map. +) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert nsClientMap literal (type nsClientMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. _ = (sync.Map)(nsClientMap{}) } + +var _nil_nsClientMap_nsCacheEntry_value = func() (val *nsCacheEntry) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *nsClientMap) Load(key string) (*nsCacheEntry, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_nsClientMap_nsCacheEntry_value, ok + } + return value.(*nsCacheEntry), ok +} + +// Store sets the value for a key. func (m *nsClientMap) Store(key string, value *nsCacheEntry) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *nsClientMap) LoadOrStore(key string, value *nsCacheEntry) (*nsCacheEntry, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { - return nil, loaded + return _nil_nsClientMap_nsCacheEntry_value, loaded } return actual.(*nsCacheEntry), loaded } -func (m *nsClientMap) Load(key string) (*nsCacheEntry, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return nil, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *nsClientMap) LoadAndDelete(key string) (value *nsCacheEntry, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_nsClientMap_nsCacheEntry_value, loaded } - return value.(*nsCacheEntry), ok + return actual.(*nsCacheEntry), loaded } +// Delete deletes the value for a key. func (m *nsClientMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *nsClientMap) Range(f func(key string, value *nsCacheEntry) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(*nsCacheEntry)) diff --git a/pkg/registry/common/connect/nse_sync_map.gen.go b/pkg/registry/common/connect/nse_sync_map.gen.go index 648f2c3dc..b84f2d106 100644 --- a/pkg/registry/common/connect/nse_sync_map.gen.go +++ b/pkg/registry/common/connect/nse_sync_map.gen.go @@ -1,38 +1,71 @@ -// Code generated by "go-syncmap -output nse_sync_map.gen.go -type nseClientMap"; DO NOT EDIT. - +// Code generated by "-output nse_sync_map.gen.go -type nseClientMap -output nse_sync_map.gen.go -type nseClientMap"; DO NOT EDIT. package connect -import "sync" +import ( + "sync" // Used by sync.Map. +) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert nseClientMap literal (type nseClientMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. _ = (sync.Map)(nseClientMap{}) } + +var _nil_nseClientMap_nseCacheEntry_value = func() (val *nseCacheEntry) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *nseClientMap) Load(key string) (*nseCacheEntry, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_nseClientMap_nseCacheEntry_value, ok + } + return value.(*nseCacheEntry), ok +} + +// Store sets the value for a key. func (m *nseClientMap) Store(key string, value *nseCacheEntry) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *nseClientMap) LoadOrStore(key string, value *nseCacheEntry) (*nseCacheEntry, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { - return nil, loaded + return _nil_nseClientMap_nseCacheEntry_value, loaded } return actual.(*nseCacheEntry), loaded } -func (m *nseClientMap) Load(key string) (*nseCacheEntry, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return nil, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *nseClientMap) LoadAndDelete(key string) (value *nseCacheEntry, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_nseClientMap_nseCacheEntry_value, loaded } - return value.(*nseCacheEntry), ok + return actual.(*nseCacheEntry), loaded } +// Delete deletes the value for a key. func (m *nseClientMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *nseClientMap) Range(f func(key string, value *nseCacheEntry) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(*nseCacheEntry)) diff --git a/pkg/registry/common/endpointurls/sync_set.gen.go b/pkg/registry/common/endpointurls/sync_set.gen.go index 15efb4669..feb7791e0 100644 --- a/pkg/registry/common/endpointurls/sync_set.gen.go +++ b/pkg/registry/common/endpointurls/sync_set.gen.go @@ -1,12 +1,12 @@ -// Code generated by "go-syncmap -output sync_set.gen.go -type Set"; DO NOT EDIT. - +// Code generated by "-output sync_set.gen.go -type Set -output sync_set.gen.go -type Set"; DO NOT EDIT. package endpointurls import ( "net/url" - "sync" + "sync" // Used by sync.Map. ) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert Set literal (type Set) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. @@ -15,10 +15,25 @@ func _() { var _nil_Set_struct___value = func() (val struct{}) { return }() +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *Set) Load(key url.URL) (struct{}, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_Set_struct___value, ok + } + return value.(struct{}), ok +} + +// Store sets the value for a key. func (m *Set) Store(key url.URL, value struct{}) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *Set) LoadOrStore(key url.URL, value struct{}) (struct{}, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { @@ -27,18 +42,31 @@ func (m *Set) LoadOrStore(key url.URL, value struct{}) (struct{}, bool) { return actual.(struct{}), loaded } -func (m *Set) Load(key url.URL) (struct{}, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return _nil_Set_struct___value, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *Set) LoadAndDelete(key url.URL) (value struct{}, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_Set_struct___value, loaded } - return value.(struct{}), ok + return actual.(struct{}), loaded } +// Delete deletes the value for a key. func (m *Set) Delete(key url.URL) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *Set) Range(f func(key url.URL, value struct{}) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(url.URL), value.(struct{})) diff --git a/pkg/registry/common/expire/context_sync_map.gen.go b/pkg/registry/common/expire/context_sync_map.gen.go index 711bd5f8e..0b133c47d 100644 --- a/pkg/registry/common/expire/context_sync_map.gen.go +++ b/pkg/registry/common/expire/context_sync_map.gen.go @@ -1,12 +1,12 @@ -// Code generated by "go-syncmap -output context_sync_map.gen.go -type contextMap"; DO NOT EDIT. - +// Code generated by "-output context_sync_map.gen.go -type contextMap -output context_sync_map.gen.go -type contextMap"; DO NOT EDIT. package expire import ( "context" - "sync" + "sync" // Used by sync.Map. ) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert contextMap literal (type contextMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. @@ -15,10 +15,25 @@ func _() { var _nil_contextMap_context_Context_value = func() (val context.Context) { return }() +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *contextMap) Load(key string) (context.Context, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_contextMap_context_Context_value, ok + } + return value.(context.Context), ok +} + +// Store sets the value for a key. func (m *contextMap) Store(key string, value context.Context) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *contextMap) LoadOrStore(key string, value context.Context) (context.Context, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { @@ -27,18 +42,31 @@ func (m *contextMap) LoadOrStore(key string, value context.Context) (context.Con return actual.(context.Context), loaded } -func (m *contextMap) Load(key string) (context.Context, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return _nil_contextMap_context_Context_value, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *contextMap) LoadAndDelete(key string) (value context.Context, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_contextMap_context_Context_value, loaded } - return value.(context.Context), ok + return actual.(context.Context), loaded } +// Delete deletes the value for a key. func (m *contextMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *contextMap) Range(f func(key string, value context.Context) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(context.Context)) diff --git a/pkg/registry/common/expire/int_sync_map.gen.go b/pkg/registry/common/expire/int_sync_map.gen.go index bed5429f3..b02d749c8 100644 --- a/pkg/registry/common/expire/int_sync_map.gen.go +++ b/pkg/registry/common/expire/int_sync_map.gen.go @@ -1,38 +1,71 @@ -// Code generated by "go-syncmap -output int_sync_map.gen.go -type intMap"; DO NOT EDIT. - +// Code generated by "-output int_sync_map.gen.go -type intMap -output int_sync_map.gen.go -type intMap"; DO NOT EDIT. package expire -import "sync" +import ( + "sync" // Used by sync.Map. +) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert intMap literal (type intMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. _ = (sync.Map)(intMap{}) } + +var _nil_intMap_int32_value = func() (val *int32) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *intMap) Load(key string) (*int32, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_intMap_int32_value, ok + } + return value.(*int32), ok +} + +// Store sets the value for a key. func (m *intMap) Store(key string, value *int32) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *intMap) LoadOrStore(key string, value *int32) (*int32, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { - return nil, loaded + return _nil_intMap_int32_value, loaded } return actual.(*int32), loaded } -func (m *intMap) Load(key string) (*int32, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return nil, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *intMap) LoadAndDelete(key string) (value *int32, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_intMap_int32_value, loaded } - return value.(*int32), ok + return actual.(*int32), loaded } +// Delete deletes the value for a key. func (m *intMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *intMap) Range(f func(key string, value *int32) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(*int32)) diff --git a/pkg/registry/common/expire/timer_sync_map.gen.go b/pkg/registry/common/expire/timer_sync_map.gen.go index d7fe61299..4f3ace474 100644 --- a/pkg/registry/common/expire/timer_sync_map.gen.go +++ b/pkg/registry/common/expire/timer_sync_map.gen.go @@ -1,41 +1,72 @@ -// Code generated by "go-syncmap -output timer_sync_map.gen.go -type timerMap"; DO NOT EDIT. - +// Code generated by "-output timer_sync_map.gen.go -type timerMap -output timer_sync_map.gen.go -type timerMap"; DO NOT EDIT. package expire import ( - "sync" + "sync" // Used by sync.Map. "time" ) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert timerMap literal (type timerMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. _ = (sync.Map)(timerMap{}) } + +var _nil_timerMap_time_Timer_value = func() (val *time.Timer) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *timerMap) Load(key string) (*time.Timer, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_timerMap_time_Timer_value, ok + } + return value.(*time.Timer), ok +} + +// Store sets the value for a key. func (m *timerMap) Store(key string, value *time.Timer) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *timerMap) LoadOrStore(key string, value *time.Timer) (*time.Timer, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { - return nil, loaded + return _nil_timerMap_time_Timer_value, loaded } return actual.(*time.Timer), loaded } -func (m *timerMap) Load(key string) (*time.Timer, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return nil, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *timerMap) LoadAndDelete(key string) (value *time.Timer, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_timerMap_time_Timer_value, loaded } - return value.(*time.Timer), ok + return actual.(*time.Timer), loaded } +// Delete deletes the value for a key. func (m *timerMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *timerMap) Range(f func(key string, value *time.Timer) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(*time.Timer)) diff --git a/pkg/registry/common/recvfd/per_endpoint_file_map.gen.go b/pkg/registry/common/recvfd/per_endpoint_file_map.gen.go index c0af9894e..517157b87 100644 --- a/pkg/registry/common/recvfd/per_endpoint_file_map.gen.go +++ b/pkg/registry/common/recvfd/per_endpoint_file_map.gen.go @@ -1,38 +1,71 @@ -// Code generated by "go-syncmap -output per_endpoint_file_map.gen.go -type perEndpointFileMapMap"; DO NOT EDIT. - +// Code generated by "-output per_endpoint_file_map.gen.go -type perEndpointFileMapMap -output per_endpoint_file_map.gen.go -type perEndpointFileMapMap"; DO NOT EDIT. package recvfd -import "sync" +import ( + "sync" // Used by sync.Map. +) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert perEndpointFileMapMap literal (type perEndpointFileMapMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. _ = (sync.Map)(perEndpointFileMapMap{}) } + +var _nil_perEndpointFileMapMap_perEndpointFileMap_value = func() (val *perEndpointFileMap) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *perEndpointFileMapMap) Load(key string) (*perEndpointFileMap, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_perEndpointFileMapMap_perEndpointFileMap_value, ok + } + return value.(*perEndpointFileMap), ok +} + +// Store sets the value for a key. func (m *perEndpointFileMapMap) Store(key string, value *perEndpointFileMap) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *perEndpointFileMapMap) LoadOrStore(key string, value *perEndpointFileMap) (*perEndpointFileMap, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { - return nil, loaded + return _nil_perEndpointFileMapMap_perEndpointFileMap_value, loaded } return actual.(*perEndpointFileMap), loaded } -func (m *perEndpointFileMapMap) Load(key string) (*perEndpointFileMap, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return nil, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *perEndpointFileMapMap) LoadAndDelete(key string) (value *perEndpointFileMap, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_perEndpointFileMapMap_perEndpointFileMap_value, loaded } - return value.(*perEndpointFileMap), ok + return actual.(*perEndpointFileMap), loaded } +// Delete deletes the value for a key. func (m *perEndpointFileMapMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *perEndpointFileMapMap) Range(f func(key string, value *perEndpointFileMap) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(*perEndpointFileMap)) diff --git a/pkg/registry/common/refresh/sync_map.gen.go b/pkg/registry/common/refresh/sync_map.gen.go index 75e6e7c83..151c5b79d 100644 --- a/pkg/registry/common/refresh/sync_map.gen.go +++ b/pkg/registry/common/refresh/sync_map.gen.go @@ -1,12 +1,12 @@ -// Code generated by "go-syncmap -output sync_map.gen.go -type cancelsMap"; DO NOT EDIT. - +// Code generated by "-output sync_map.gen.go -type cancelsMap -output sync_map.gen.go -type cancelsMap"; DO NOT EDIT. package refresh import ( "context" - "sync" + "sync" // Used by sync.Map. ) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert cancelsMap literal (type cancelsMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. @@ -15,10 +15,25 @@ func _() { var _nil_cancelsMap_context_CancelFunc_value = func() (val context.CancelFunc) { return }() +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *cancelsMap) Load(key string) (context.CancelFunc, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_cancelsMap_context_CancelFunc_value, ok + } + return value.(context.CancelFunc), ok +} + +// Store sets the value for a key. func (m *cancelsMap) Store(key string, value context.CancelFunc) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *cancelsMap) LoadOrStore(key string, value context.CancelFunc) (context.CancelFunc, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { @@ -27,18 +42,31 @@ func (m *cancelsMap) LoadOrStore(key string, value context.CancelFunc) (context. return actual.(context.CancelFunc), loaded } -func (m *cancelsMap) Load(key string) (context.CancelFunc, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return _nil_cancelsMap_context_CancelFunc_value, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *cancelsMap) LoadAndDelete(key string) (value context.CancelFunc, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_cancelsMap_context_CancelFunc_value, loaded } - return value.(context.CancelFunc), ok + return actual.(context.CancelFunc), loaded } +// Delete deletes the value for a key. func (m *cancelsMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *cancelsMap) Range(f func(key string, value context.CancelFunc) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(context.CancelFunc)) diff --git a/pkg/registry/memory/ns_sync_map.gen.go b/pkg/registry/memory/ns_sync_map.gen.go index d5be7acba..a03873281 100644 --- a/pkg/registry/memory/ns_sync_map.gen.go +++ b/pkg/registry/memory/ns_sync_map.gen.go @@ -1,42 +1,73 @@ -// Code generated by "go-syncmap -output ns_sync_map.gen.go -type NetworkServiceSyncMap"; DO NOT EDIT. - +// Code generated by "-output ns_sync_map.gen.go -type NetworkServiceSyncMap -output ns_sync_map.gen.go -type NetworkServiceSyncMap"; DO NOT EDIT. package memory import ( - "sync" + "sync" // Used by sync.Map. "github.com/networkservicemesh/api/pkg/api/registry" ) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert NetworkServiceSyncMap literal (type NetworkServiceSyncMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. _ = (sync.Map)(NetworkServiceSyncMap{}) } + +var _nil_NetworkServiceSyncMap_registry_NetworkService_value = func() (val *registry.NetworkService) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *NetworkServiceSyncMap) Load(key string) (*registry.NetworkService, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_NetworkServiceSyncMap_registry_NetworkService_value, ok + } + return value.(*registry.NetworkService), ok +} + +// Store sets the value for a key. func (m *NetworkServiceSyncMap) Store(key string, value *registry.NetworkService) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *NetworkServiceSyncMap) LoadOrStore(key string, value *registry.NetworkService) (*registry.NetworkService, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { - return nil, loaded + return _nil_NetworkServiceSyncMap_registry_NetworkService_value, loaded } return actual.(*registry.NetworkService), loaded } -func (m *NetworkServiceSyncMap) Load(key string) (*registry.NetworkService, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return nil, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *NetworkServiceSyncMap) LoadAndDelete(key string) (value *registry.NetworkService, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_NetworkServiceSyncMap_registry_NetworkService_value, loaded } - return value.(*registry.NetworkService), ok + return actual.(*registry.NetworkService), loaded } +// Delete deletes the value for a key. func (m *NetworkServiceSyncMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *NetworkServiceSyncMap) Range(f func(key string, value *registry.NetworkService) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(*registry.NetworkService)) diff --git a/pkg/registry/memory/nse_sync_map.gen.go b/pkg/registry/memory/nse_sync_map.gen.go index 2e3d3ab29..4b1d66428 100644 --- a/pkg/registry/memory/nse_sync_map.gen.go +++ b/pkg/registry/memory/nse_sync_map.gen.go @@ -1,42 +1,73 @@ -// Code generated by "go-syncmap -output nse_sync_map.gen.go -type NetworkServiceEndpointSyncMap"; DO NOT EDIT. - +// Code generated by "-output nse_sync_map.gen.go -type NetworkServiceEndpointSyncMap -output nse_sync_map.gen.go -type NetworkServiceEndpointSyncMap"; DO NOT EDIT. package memory import ( - "sync" + "sync" // Used by sync.Map. "github.com/networkservicemesh/api/pkg/api/registry" ) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert NetworkServiceEndpointSyncMap literal (type NetworkServiceEndpointSyncMap) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. _ = (sync.Map)(NetworkServiceEndpointSyncMap{}) } + +var _nil_NetworkServiceEndpointSyncMap_registry_NetworkServiceEndpoint_value = func() (val *registry.NetworkServiceEndpoint) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *NetworkServiceEndpointSyncMap) Load(key string) (*registry.NetworkServiceEndpoint, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_NetworkServiceEndpointSyncMap_registry_NetworkServiceEndpoint_value, ok + } + return value.(*registry.NetworkServiceEndpoint), ok +} + +// Store sets the value for a key. func (m *NetworkServiceEndpointSyncMap) Store(key string, value *registry.NetworkServiceEndpoint) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *NetworkServiceEndpointSyncMap) LoadOrStore(key string, value *registry.NetworkServiceEndpoint) (*registry.NetworkServiceEndpoint, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { - return nil, loaded + return _nil_NetworkServiceEndpointSyncMap_registry_NetworkServiceEndpoint_value, loaded } return actual.(*registry.NetworkServiceEndpoint), loaded } -func (m *NetworkServiceEndpointSyncMap) Load(key string) (*registry.NetworkServiceEndpoint, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return nil, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *NetworkServiceEndpointSyncMap) LoadAndDelete(key string) (value *registry.NetworkServiceEndpoint, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_NetworkServiceEndpointSyncMap_registry_NetworkServiceEndpoint_value, loaded } - return value.(*registry.NetworkServiceEndpoint), ok + return actual.(*registry.NetworkServiceEndpoint), loaded } +// Delete deletes the value for a key. func (m *NetworkServiceEndpointSyncMap) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *NetworkServiceEndpointSyncMap) Range(f func(key string, value *registry.NetworkServiceEndpoint) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(*registry.NetworkServiceEndpoint)) diff --git a/pkg/tools/clientmap/clientmap.gen.go b/pkg/tools/clientmap/clientmap.gen.go index 850565bd2..0c1603171 100644 --- a/pkg/tools/clientmap/clientmap.gen.go +++ b/pkg/tools/clientmap/clientmap.gen.go @@ -1,13 +1,13 @@ -// Code generated by "go-syncmap -output clientmap.gen.go -type Map"; DO NOT EDIT. - +// Code generated by "-output clientmap.gen.go -type Map -output clientmap.gen.go -type Map"; DO NOT EDIT. package clientmap import ( - "sync" + "sync" // Used by sync.Map. "github.com/networkservicemesh/api/pkg/api/networkservice" ) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert Map literal (type Map) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. @@ -16,10 +16,25 @@ func _() { var _nil_Map_networkservice_NetworkServiceClient_value = func() (val networkservice.NetworkServiceClient) { return }() +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *Map) Load(key string) (networkservice.NetworkServiceClient, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_Map_networkservice_NetworkServiceClient_value, ok + } + return value.(networkservice.NetworkServiceClient), ok +} + +// Store sets the value for a key. func (m *Map) Store(key string, value networkservice.NetworkServiceClient) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *Map) LoadOrStore(key string, value networkservice.NetworkServiceClient) (networkservice.NetworkServiceClient, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { @@ -28,18 +43,31 @@ func (m *Map) LoadOrStore(key string, value networkservice.NetworkServiceClient) return actual.(networkservice.NetworkServiceClient), loaded } -func (m *Map) Load(key string) (networkservice.NetworkServiceClient, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return _nil_Map_networkservice_NetworkServiceClient_value, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *Map) LoadAndDelete(key string) (value networkservice.NetworkServiceClient, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_Map_networkservice_NetworkServiceClient_value, loaded } - return value.(networkservice.NetworkServiceClient), ok + return actual.(networkservice.NetworkServiceClient), loaded } +// Delete deletes the value for a key. func (m *Map) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *Map) Range(f func(key string, value networkservice.NetworkServiceClient) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(networkservice.NetworkServiceClient)) diff --git a/pkg/tools/stringurl/sync_map.gen.go b/pkg/tools/stringurl/sync_map.gen.go index efd2c0b11..f1ce2ae89 100644 --- a/pkg/tools/stringurl/sync_map.gen.go +++ b/pkg/tools/stringurl/sync_map.gen.go @@ -1,41 +1,72 @@ -// Code generated by "go-syncmap -output sync_map.gen.go -type Map"; DO NOT EDIT. - +// Code generated by "-output sync_map.gen.go -type Map -output sync_map.gen.go -type Map"; DO NOT EDIT. package stringurl import ( "net/url" - "sync" + "sync" // Used by sync.Map. ) +// Generate code that will fail if the constants change value. func _() { // An "cannot convert Map literal (type Map) to type sync.Map" compiler error signifies that the base type have changed. // Re-run the go-syncmap command to generate them again. _ = (sync.Map)(Map{}) } + +var _nil_Map_url_URL_value = func() (val *url.URL) { return }() + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *Map) Load(key string) (*url.URL, bool) { + value, ok := (*sync.Map)(m).Load(key) + if value == nil { + return _nil_Map_url_URL_value, ok + } + return value.(*url.URL), ok +} + +// Store sets the value for a key. func (m *Map) Store(key string, value *url.URL) { (*sync.Map)(m).Store(key, value) } +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. func (m *Map) LoadOrStore(key string, value *url.URL) (*url.URL, bool) { actual, loaded := (*sync.Map)(m).LoadOrStore(key, value) if actual == nil { - return nil, loaded + return _nil_Map_url_URL_value, loaded } return actual.(*url.URL), loaded } -func (m *Map) Load(key string) (*url.URL, bool) { - value, ok := (*sync.Map)(m).Load(key) - if value == nil { - return nil, ok +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *Map) LoadAndDelete(key string) (value *url.URL, loaded bool) { + actual, loaded := (*sync.Map)(m).LoadAndDelete(key) + if actual == nil { + return _nil_Map_url_URL_value, loaded } - return value.(*url.URL), ok + return actual.(*url.URL), loaded } +// Delete deletes the value for a key. func (m *Map) Delete(key string) { (*sync.Map)(m).Delete(key) } +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. func (m *Map) Range(f func(key string, value *url.URL) bool) { (*sync.Map)(m).Range(func(key, value interface{}) bool { return f(key.(string), value.(*url.URL))