google.golang.org/grpc@v1.62.1/xds/internal/xdsclient/singleton.go (about) 1 /* 2 * 3 * Copyright 2020 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package xdsclient 20 21 import ( 22 "fmt" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "google.golang.org/grpc/internal/envconfig" 28 "google.golang.org/grpc/internal/grpcsync" 29 "google.golang.org/grpc/xds/internal/xdsclient/bootstrap" 30 ) 31 32 const ( 33 defaultWatchExpiryTimeout = 15 * time.Second 34 defaultIdleAuthorityDeleteTimeout = 5 * time.Minute 35 ) 36 37 var ( 38 // This is the client returned by New(). It contains one client implementation, 39 // and maintains the refcount. 40 singletonMu sync.Mutex 41 singletonClient *clientRefCounted 42 43 // The following functions are no-ops in the actual code, but can be 44 // overridden in tests to give them visibility into certain events. 45 singletonClientImplCreateHook = func() {} 46 singletonClientImplCloseHook = func() {} 47 ) 48 49 // To override in tests. 50 var bootstrapNewConfig = bootstrap.NewConfig 51 52 func clientRefCountedClose() { 53 singletonMu.Lock() 54 defer singletonMu.Unlock() 55 56 if singletonClient.decrRef() != 0 { 57 return 58 } 59 singletonClient.clientImpl.close() 60 singletonClientImplCloseHook() 61 singletonClient = nil 62 } 63 64 func newRefCountedWithConfig(fallbackConfig *bootstrap.Config) (XDSClient, func(), error) { 65 singletonMu.Lock() 66 defer singletonMu.Unlock() 67 68 if singletonClient != nil { 69 singletonClient.incrRef() 70 return singletonClient, grpcsync.OnceFunc(clientRefCountedClose), nil 71 72 } 73 74 // Use fallbackConfig only if bootstrap env vars are unspecified. 75 var config *bootstrap.Config 76 if envconfig.XDSBootstrapFileName == "" && envconfig.XDSBootstrapFileContent == "" { 77 if fallbackConfig == nil { 78 return nil, nil, fmt.Errorf("xds: bootstrap env vars are unspecified and provided fallback config is nil") 79 } 80 config = fallbackConfig 81 } else { 82 var err error 83 config, err = bootstrapNewConfig() 84 if err != nil { 85 return nil, nil, fmt.Errorf("xds: failed to read bootstrap file: %v", err) 86 } 87 } 88 89 // Create the new client implementation. 90 c, err := newWithConfig(config, defaultWatchExpiryTimeout, defaultIdleAuthorityDeleteTimeout) 91 if err != nil { 92 return nil, nil, err 93 } 94 singletonClient = &clientRefCounted{clientImpl: c, refCount: 1} 95 singletonClientImplCreateHook() 96 97 logger.Infof("xDS node ID: %s", config.NodeProto.GetId()) 98 return singletonClient, grpcsync.OnceFunc(clientRefCountedClose), nil 99 } 100 101 // clientRefCounted is ref-counted, and to be shared by the xds resolver and 102 // balancer implementations, across multiple ClientConns and Servers. 103 type clientRefCounted struct { 104 *clientImpl 105 106 refCount int32 // accessed atomically 107 } 108 109 func (c *clientRefCounted) incrRef() int32 { 110 return atomic.AddInt32(&c.refCount, 1) 111 } 112 113 func (c *clientRefCounted) decrRef() int32 { 114 return atomic.AddInt32(&c.refCount, -1) 115 }