Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 10, 22:30 UTC
| 1 | // Copyright 2015 Matthew Holt and The Caddy Authors |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package caddy |
| 16 | |
| 17 | import ( |
| 18 | "fmt" |
| 19 | "sync" |
| 20 | "sync/atomic" |
| 21 | ) |
| 22 | |
| 23 | // UsagePool is a thread-safe map that pools values |
| 24 | // based on usage (reference counting). Values are |
| 25 | // only inserted if they do not already exist. There |
| 26 | // are two ways to add values to the pool: |
| 27 | // |
| 28 | // 1. LoadOrStore will increment usage and store the |
| 29 | // value immediately if it does not already exist. |
| 30 | // 2. LoadOrNew will atomically check for existence |
| 31 | // and construct the value immediately if it does |
| 32 | // not already exist, or increment the usage |
| 33 | // otherwise, then store that value in the pool. |
| 34 | // When the constructed value is finally deleted |
| 35 | // from the pool (when its usage reaches 0), it |
| 36 | // will be cleaned up by calling Destruct(). |
| 37 | // |
| 38 | // The use of LoadOrNew allows values to be created |
| 39 | // and reused and finally cleaned up only once, even |
| 40 | // though they may have many references throughout |
| 41 | // their lifespan. This is helpful, for example, when |
| 42 | // sharing thread-safe io.Writers that you only want |
| 43 | // to open and close once. |
| 44 | // |
| 45 | // There is no way to overwrite existing keys in the |
| 46 | // pool without first deleting it as many times as it |
| 47 | // was stored. Deleting too many times will panic. |
| 48 | // |
| 49 | // The implementation does not use a sync.Pool because |
| 50 | // UsagePool needs additional atomicity to run the |
| 51 | // constructor functions when creating a new value when |
| 52 | // LoadOrNew is used. (We could probably use sync.Pool |
| 53 | // but we'd still have to layer our own additional locks |
| 54 | // on top.) |
| 55 | // |
| 56 | // An empty UsagePool is NOT safe to use; always call |
| 57 | // NewUsagePool() to make a new one. |
| 58 | type UsagePool struct { |
| 59 | sync.RWMutex |
| 60 | pool map[any]*usagePoolVal |
| 61 | } |
| 62 | |
| 63 | // NewUsagePool returns a new usage pool that is ready to use. |
| 64 | func NewUsagePool() *UsagePool { |
| 65 | return &UsagePool{ |
| 66 | pool: make(map[any]*usagePoolVal), |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | // LoadOrNew loads the value associated with key from the pool if it |
| 71 | // already exists. If the key doesn't exist, it will call construct |
| 72 | // to create a new value and then stores that in the pool. An error |
| 73 | // is only returned if the constructor returns an error. The loaded |
| 74 | // or constructed value is returned. The loaded return value is true |
| 75 | // if the value already existed and was loaded, or false if it was |
| 76 | // newly constructed. |
| 77 | func (up *UsagePool) LoadOrNew(key any, construct Constructor) (value any, loaded bool, err error) { |
| 78 | var upv *usagePoolVal |
| 79 | up.Lock() |
| 80 | upv, loaded = up.pool[key] |
| 81 | if loaded { |
| 82 | upv.refs.Add(1) |
| 83 | up.Unlock() |
| 84 | upv.RLock() |
| 85 | value = upv.value |
| 86 | err = upv.err |
| 87 | upv.RUnlock() |
| 88 | } else { |
| 89 | upv = &usagePoolVal{} |
| 90 | upv.refs.Store(1) |
| 91 | upv.Lock() |
| 92 | up.pool[key] = upv |
| 93 | up.Unlock() |
| 94 | value, err = construct() |
| 95 | if err == nil { |
| 96 | upv.value = value |
| 97 | upv.Unlock() |
| 98 | } else { |
| 99 | upv.err = err |
| 100 | upv.Unlock() |
| 101 | up.Lock() |
| 102 | // Another constructor may have replaced a failed entry while |
| 103 | // this goroutine waited for the pool lock. |
| 104 | if up.pool[key] == upv { |
| 105 | delete(up.pool, key) |
| 106 | } |
| 107 | up.Unlock() |
| 108 | } |
| 109 | } |
| 110 | return value, loaded, err |
| 111 | } |
| 112 | |
| 113 | // LoadOrStore loads the value associated with key from the pool if it |
| 114 | // already exists, or stores it if it does not exist. It returns the |
| 115 | // value that was either loaded or stored, and true if the value already |
| 116 | // existed and was loaded, false if the value didn't exist and was stored. |
| 117 | func (up *UsagePool) LoadOrStore(key, val any) (value any, loaded bool) { |
| 118 | var upv *usagePoolVal |
| 119 | up.Lock() |
| 120 | upv, loaded = up.pool[key] |
| 121 | if loaded { |
| 122 | upv.refs.Add(1) |
| 123 | up.Unlock() |
| 124 | upv.Lock() |
| 125 | if upv.err == nil { |
| 126 | value = upv.value |
| 127 | } else { |
| 128 | upv.value = val |
| 129 | upv.err = nil |
| 130 | } |
| 131 | upv.Unlock() |
| 132 | } else { |
| 133 | upv = &usagePoolVal{value: val} |
| 134 | upv.refs.Store(1) |
| 135 | up.pool[key] = upv |
| 136 | up.Unlock() |
| 137 | value = val |
| 138 | } |
| 139 | return value, loaded |
| 140 | } |
| 141 | |
| 142 | // Range iterates the pool similarly to how sync.Map.Range() does: |
| 143 | // it calls f for every key in the pool, and if f returns false, |
| 144 | // iteration is stopped. Ranging does not affect usage counts. |
| 145 | // |
| 146 | // This method is somewhat naive and acquires a read lock on the |
| 147 | // entire pool during iteration, so do your best to make f() really |
| 148 | // fast, m'kay? |
| 149 | func (up *UsagePool) Range(f func(key, value any) bool) { |
| 150 | up.RLock() |
| 151 | defer up.RUnlock() |
| 152 | for key, upv := range up.pool { |
| 153 | upv.RLock() |
| 154 | if upv.err != nil { |
| 155 | upv.RUnlock() |
| 156 | continue |
| 157 | } |
| 158 | val := upv.value |
| 159 | upv.RUnlock() |
| 160 | if !f(key, val) { |
| 161 | break |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | // Delete decrements the usage count for key and removes the |
| 167 | // value from the underlying map if the usage is 0. It returns |
| 168 | // true if the usage count reached 0 and the value was deleted. |
| 169 | // It panics if the usage count drops below 0; always call |
| 170 | // Delete precisely as many times as LoadOrStore. |
| 171 | func (up *UsagePool) Delete(key any) (deleted bool, err error) { |
| 172 | up.Lock() |
| 173 | upv, ok := up.pool[key] |
| 174 | if !ok { |
| 175 | up.Unlock() |
| 176 | return false, nil |
| 177 | } |
| 178 | refs := upv.refs.Add(-1) |
| 179 | if refs == 0 { |
| 180 | delete(up.pool, key) |
| 181 | up.Unlock() |
| 182 | upv.RLock() |
| 183 | val := upv.value |
| 184 | upv.RUnlock() |
| 185 | if destructor, ok := val.(Destructor); ok { |
| 186 | err = destructor.Destruct() |
| 187 | } |
| 188 | deleted = true |
| 189 | } else { |
| 190 | up.Unlock() |
| 191 | if refs < 0 { |
| 192 | panic(fmt.Sprintf("deleted more than stored: %#v (usage: %d)", |
| 193 | upv.value, upv.refs.Load())) |
| 194 | } |
| 195 | } |
| 196 | return deleted, err |
| 197 | } |
| 198 | |
| 199 | // References returns the number of references (count of usages) to a |
| 200 | // key in the pool, and true if the key exists, or false otherwise. |
| 201 | func (up *UsagePool) References(key any) (int, bool) { |
| 202 | up.RLock() |
| 203 | upv, loaded := up.pool[key] |
| 204 | up.RUnlock() |
| 205 | if loaded { |
| 206 | // I wonder if it'd be safer to read this value during |
| 207 | // our lock on the UsagePool... guess we'll see... |
| 208 | refs := upv.refs.Load() |
| 209 | return int(refs), true |
| 210 | } |
| 211 | return 0, false |
| 212 | } |
| 213 | |
| 214 | // Constructor is a function that returns a new value |
| 215 | // that can destruct itself when it is no longer needed. |
| 216 | type Constructor func() (Destructor, error) |
| 217 | |
| 218 | // Destructor is a value that can clean itself up when |
| 219 | // it is deallocated. |
| 220 | type Destructor interface { |
| 221 | Destruct() error |
| 222 | } |
| 223 | |
| 224 | type usagePoolVal struct { |
| 225 | refs atomic.Int32 |
| 226 | value any |
| 227 | err error |
| 228 | sync.RWMutex |
| 229 | } |