github.com/jd-ly/tools@v0.5.7/internal/lsp/debounce.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package lsp 6 7 import ( 8 "sync" 9 "time" 10 ) 11 12 type debounceFunc struct { 13 order uint64 14 done chan struct{} 15 } 16 17 type debouncer struct { 18 mu sync.Mutex 19 funcs map[string]*debounceFunc 20 } 21 22 func newDebouncer() *debouncer { 23 return &debouncer{ 24 funcs: make(map[string]*debounceFunc), 25 } 26 } 27 28 // debounce waits timeout before running f, if no subsequent call is made with 29 // the same key in the intervening time. If a later call to debounce with the 30 // same key occurs while the original call is blocking, the original call will 31 // return immediately without running its f. 32 // 33 // If order is specified, it will be used to order calls logically, so calls 34 // with lesser order will not cancel calls with greater order. 35 func (d *debouncer) debounce(key string, order uint64, timeout time.Duration, f func()) { 36 if timeout == 0 { 37 // Degenerate case: no debouncing. 38 f() 39 return 40 } 41 42 // First, atomically acquire the current func, cancel it, and insert this 43 // call into d.funcs. 44 d.mu.Lock() 45 current, ok := d.funcs[key] 46 if ok && current.order > order { 47 // If we have a logical ordering of events (as is the case for snapshots), 48 // don't overwrite a later event with an earlier event. 49 d.mu.Unlock() 50 return 51 } 52 if ok { 53 close(current.done) 54 } 55 done := make(chan struct{}) 56 next := &debounceFunc{ 57 order: order, 58 done: done, 59 } 60 d.funcs[key] = next 61 d.mu.Unlock() 62 63 // Next, wait to be cancelled or for our wait to expire. There is a race here 64 // that we must handle: our timer could expire while another goroutine holds 65 // d.mu. 66 select { 67 case <-done: 68 case <-time.After(timeout): 69 d.mu.Lock() 70 if d.funcs[key] != next { 71 // We lost the race: another event has arrived for the key and started 72 // waiting. We could reasonably choose to run f at this point, but doing 73 // nothing is simpler. 74 d.mu.Unlock() 75 return 76 } 77 delete(d.funcs, key) 78 d.mu.Unlock() 79 f() 80 } 81 }