dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/utils/xds_cache/timeoutCache.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. 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 * 20 * Copyright 2019 gRPC authors. 21 * 22 */ 23 24 package xds_cache 25 26 import ( 27 "sync" 28 "time" 29 ) 30 31 type cacheEntry struct { 32 item interface{} 33 // Note that to avoid deadlocks (potentially caused by lock ordering), 34 // callback can only be called without holding cache's mutex. 35 callback func() 36 timer *time.Timer 37 // deleted is set to true in Remove() when the call to timer.Stop() fails. 38 // This can happen when the timer in the cache entry fires around the same 39 // time that timer.stop() is called in Remove(). 40 deleted bool 41 } 42 43 // TimeoutCache is a cache with items to be deleted after a timeout. 44 type TimeoutCache struct { 45 mu sync.Mutex 46 timeout time.Duration 47 cache map[interface{}]*cacheEntry 48 } 49 50 // NewTimeoutCache creates a TimeoutCache with the given timeout. 51 func NewTimeoutCache(timeout time.Duration) *TimeoutCache { 52 return &TimeoutCache{ 53 timeout: timeout, 54 cache: make(map[interface{}]*cacheEntry), 55 } 56 } 57 58 // Add adds an item to the cache, with the specified callback to be called when 59 // the item is removed from the cache upon timeout. If the item is removed from 60 // the cache using a call to Remove before the timeout expires, the callback 61 // will not be called. 62 // 63 // If the Add was successful, it returns (newly added item, true). If there is 64 // an existing entry for the specified key, the cache entry is not be updated 65 // with the specified item and it returns (existing item, false). 66 func (c *TimeoutCache) Add(key, item interface{}, callback func()) (interface{}, bool) { 67 c.mu.Lock() 68 defer c.mu.Unlock() 69 if e, ok := c.cache[key]; ok { 70 return e.item, false 71 } 72 73 entry := &cacheEntry{ 74 item: item, 75 callback: callback, 76 } 77 entry.timer = time.AfterFunc(c.timeout, func() { 78 c.mu.Lock() 79 if entry.deleted { 80 c.mu.Unlock() 81 // Abort the delete since this has been taken care of in Remove(). 82 return 83 } 84 delete(c.cache, key) 85 c.mu.Unlock() 86 entry.callback() 87 }) 88 c.cache[key] = entry 89 return item, true 90 } 91 92 // Remove the item with the key from the cache. 93 // 94 // If the specified key exists in the cache, it returns (item associated with 95 // key, true) and the callback associated with the item is guaranteed to be not 96 // called. If the given key is not found in the cache, it returns (nil, false) 97 func (c *TimeoutCache) Remove(key interface{}) (item interface{}, ok bool) { 98 c.mu.Lock() 99 defer c.mu.Unlock() 100 entry, ok := c.removeInternal(key) 101 if !ok { 102 return nil, false 103 } 104 return entry.item, true 105 } 106 107 // removeInternal removes and returns the item with key. 108 // 109 // caller must hold c.mu. 110 func (c *TimeoutCache) removeInternal(key interface{}) (*cacheEntry, bool) { 111 entry, ok := c.cache[key] 112 if !ok { 113 return nil, false 114 } 115 delete(c.cache, key) 116 if !entry.timer.Stop() { 117 // If stop was not successful, the timer has fired (this can only happen 118 // in a race). But the deleting function is blocked on c.mu because the 119 // mutex was held by the caller of this function. 120 // 121 // Set deleted to true to abort the deleting function. When the lock is 122 // released, the delete function will acquire the lock, check the value 123 // of deleted and return. 124 entry.deleted = true 125 } 126 return entry, true 127 } 128 129 // Clear removes all entries, and runs the callbacks if runCallback is true. 130 func (c *TimeoutCache) Clear(runCallback bool) { 131 var entries []*cacheEntry 132 c.mu.Lock() 133 for key := range c.cache { 134 if e, ok := c.removeInternal(key); ok { 135 entries = append(entries, e) 136 } 137 } 138 c.mu.Unlock() 139 140 if !runCallback { 141 return 142 } 143 144 // removeInternal removes entries from cache, and also stops the timer, so 145 // the callback is guaranteed to be not called. If runCallback is true, 146 // manual execute all callbacks. 147 for _, entry := range entries { 148 entry.callback() 149 } 150 }