github.com/amazechain/amc@v0.1.3/internal/tracers/tracker.go (about) 1 // Copyright 2023 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The AmazeChain library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package tracers 18 19 import ( 20 "fmt" 21 "sync" 22 ) 23 24 // stateTracker is an auxiliary tool used to cache the release functions of all 25 // used trace states, and to determine whether the creation of trace state needs 26 // to be paused in case there are too many states waiting for tracing. 27 type stateTracker struct { 28 limit int // Maximum number of states allowed waiting for tracing 29 oldest uint64 // The number of the oldest state which is still using for trace 30 used []bool // List of flags indicating whether the trace state has been used up 31 releases []StateReleaseFunc // List of trace state release functions waiting to be called 32 cond *sync.Cond 33 lock *sync.RWMutex 34 } 35 36 // newStateTracker initializes the tracker with provided state limits and 37 // the number of the first state that will be used. 38 func newStateTracker(limit int, oldest uint64) *stateTracker { 39 lock := new(sync.RWMutex) 40 return &stateTracker{ 41 limit: limit, 42 oldest: oldest, 43 used: make([]bool, limit), 44 cond: sync.NewCond(lock), 45 lock: lock, 46 } 47 } 48 49 // releaseState marks the state specified by the number as released and caches 50 // the corresponding release functions internally. 51 func (t *stateTracker) releaseState(number uint64, release StateReleaseFunc) { 52 t.lock.Lock() 53 defer t.lock.Unlock() 54 55 // Set the state as used, the corresponding flag is indexed by 56 // the distance between the specified state and the oldest state 57 // which is still using for trace. 58 t.used[int(number-t.oldest)] = true 59 60 // If the oldest state is used up, update the oldest marker by moving 61 // it to the next state which is not used up. 62 if number == t.oldest { 63 var count int 64 for _, used := range t.used { 65 if !used { 66 break 67 } 68 count += 1 69 } 70 t.oldest += uint64(count) 71 copy(t.used, t.used[count:]) 72 73 // Clean up the array tail since they are useless now. 74 for i := t.limit - count; i < t.limit; i++ { 75 t.used[i] = false 76 } 77 // Fire the signal to all waiters that oldest marker is updated. 78 t.cond.Broadcast() 79 } 80 t.releases = append(t.releases, release) 81 } 82 83 // callReleases invokes all cached release functions. 84 func (t *stateTracker) callReleases() { 85 t.lock.Lock() 86 defer t.lock.Unlock() 87 88 for _, release := range t.releases { 89 release() 90 } 91 t.releases = t.releases[:0] 92 } 93 94 // wait blocks until the accumulated trace states are less than the limit. 95 func (t *stateTracker) wait(number uint64) error { 96 t.lock.Lock() 97 defer t.lock.Unlock() 98 99 for { 100 if number < t.oldest { 101 return fmt.Errorf("invalid state number %d head %d", number, t.oldest) 102 } 103 if number < t.oldest+uint64(t.limit) { 104 // number is now within limit, wait over 105 return nil 106 } 107 t.cond.Wait() 108 } 109 }