@marcel91200 / caddy Mirror · read-only
Fork 0

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

master
go 229 lines 6.42 KB
Raw
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 }