|
1 | | -// Copyright (c) 2015-2022 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 |
|
@@ -262,10 +262,22 @@ func (c *SubsidyCache) CalcBlockSubsidy(height int64) int64 { |
262 | 262 | // subsidy became zero when applicable. The cached intervals are stored in |
263 | 263 | // a map for O(1) lookup and also tracked via a sorted array to support the |
264 | 264 | // binary searches for efficient sparse interval query support. |
| 265 | + // |
| 266 | + // Note that the mutex is not held while performing the calculation above, |
| 267 | + // so another goroutine might have already cached the same interval in the |
| 268 | + // interim. Avoid inserting the interval again in that case since doing so |
| 269 | + // would add duplicate entries to the sorted interval array, causing it to |
| 270 | + // grow without bound and require repeated sorting. |
| 271 | + // |
| 272 | + // The calculation is fully deterministic so any subsidy another goroutine |
| 273 | + // cached for the interval is necessarily identical to the one calculated |
| 274 | + // here. |
265 | 275 | c.mtx.Lock() |
266 | | - c.cache[reqInterval] = subsidy |
267 | | - c.cachedIntervals = append(c.cachedIntervals, reqInterval) |
268 | | - sort.Sort((*uint64s)(&c.cachedIntervals)) |
| 276 | + if _, ok := c.cache[reqInterval]; !ok { |
| 277 | + c.cache[reqInterval] = subsidy |
| 278 | + c.cachedIntervals = append(c.cachedIntervals, reqInterval) |
| 279 | + sort.Sort((*uint64s)(&c.cachedIntervals)) |
| 280 | + } |
269 | 281 | c.mtx.Unlock() |
270 | 282 | return subsidy |
271 | 283 | } |
|
0 commit comments