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