|
1 | | -// Copyright (c) 2015-2023 The Decred developers |
| 1 | +// Copyright (c) 2015-2026 The Decred developers |
2 | 2 | // Use of this source code is governed by an ISC |
3 | 3 | // license that can be found in the LICENSE file. |
4 | 4 |
|
@@ -230,10 +230,22 @@ func (c *SubsidyCache) CalcBlockSubsidy(height int64) int64 { |
230 | 230 | // subsidy became zero when applicable. The cached intervals are stored in |
231 | 231 | // a map for O(1) lookup and also tracked via a sorted array to support the |
232 | 232 | // binary searches for efficient sparse interval query support. |
| 233 | + // |
| 234 | + // Note that the mutex is not held while performing the calculation above, |
| 235 | + // so another goroutine might have already cached the same interval in the |
| 236 | + // interim. Avoid inserting the interval again in that case since doing so |
| 237 | + // would add duplicate entries to the sorted interval array, causing it to |
| 238 | + // grow without bound and require repeated sorting. |
| 239 | + // |
| 240 | + // The calculation is fully deterministic so any subsidy another goroutine |
| 241 | + // cached for the interval is necessarily identical to the one calculated |
| 242 | + // here. |
233 | 243 | c.mtx.Lock() |
234 | | - c.cache[reqInterval] = subsidy |
235 | | - c.cachedIntervals = append(c.cachedIntervals, reqInterval) |
236 | | - sort.Sort((*uint64s)(&c.cachedIntervals)) |
| 244 | + if _, ok := c.cache[reqInterval]; !ok { |
| 245 | + c.cache[reqInterval] = subsidy |
| 246 | + c.cachedIntervals = append(c.cachedIntervals, reqInterval) |
| 247 | + sort.Sort((*uint64s)(&c.cachedIntervals)) |
| 248 | + } |
237 | 249 | c.mtx.Unlock() |
238 | 250 | return subsidy |
239 | 251 | } |
|
0 commit comments