github.com/grahambrereton-form3/tilt@v0.10.18/internal/store/dirty.go (about) 1 package store 2 3 import ( 4 "sync" 5 "time" 6 ) 7 8 // A little synchronization primitive that comes up frequently in build systems, 9 // with three assumptions: 10 // 11 // 1) An event can come in at any time that marks the status as "dirty" 12 // (think: the user edits a file). 13 // 2) Builds can take a long time. 14 // 3) When the build finishes, we want to mark the status as "clean" iff 15 // nothing has changed since the build started. 16 // 17 // Don't use this primitive if you do synchronization at a higher 18 // level (as EngineState does), or need more granular dirtyness tracking 19 // (as EngineState also does, see PendingFileChanges). But EngineState 20 // uses a similar architecture internally. 21 22 type DirtyBit struct { 23 mu sync.Mutex 24 dirtyAsOf time.Time 25 } 26 27 func NewDirtyBit() *DirtyBit { 28 return &DirtyBit{} 29 } 30 31 // Mark the bit as dirty. 32 // If the change happens and this is marked dirty later, that's usually ok. 33 // It just means IsDirty might have false positives (i.e., we do spurious builds). 34 func (b *DirtyBit) MarkDirty() { 35 b.mu.Lock() 36 defer b.mu.Unlock() 37 b.dirtyAsOf = time.Now() 38 } 39 40 func (b *DirtyBit) IsDirty() bool { 41 b.mu.Lock() 42 defer b.mu.Unlock() 43 44 return !b.dirtyAsOf.IsZero() 45 } 46 47 // If the bit is currently marked dirty, returns a StartToken 48 // to pass to FinishBuild. Otherwise, return false. 49 func (b *DirtyBit) StartBuildIfDirty() (DirtyStartToken, bool) { 50 b.mu.Lock() 51 defer b.mu.Unlock() 52 53 if b.dirtyAsOf.IsZero() { 54 return DirtyStartToken{}, false 55 } 56 57 return DirtyStartToken(time.Now()), true 58 } 59 60 func (b *DirtyBit) FinishBuild(t DirtyStartToken) { 61 b.mu.Lock() 62 defer b.mu.Unlock() 63 64 if time.Time(b.dirtyAsOf).After(time.Time(t)) { 65 return 66 } 67 b.dirtyAsOf = time.Time{} 68 } 69 70 type DirtyStartToken time.Time