File: debounce.go

package info (click to toggle)
golang-golang-x-tools 1%3A0.1.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 12,588 kB
  • sloc: javascript: 2,011; asm: 1,458; sh: 174; yacc: 155; makefile: 21; ansic: 17
file content (81 lines) | stat: -rw-r--r-- 2,046 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package lsp

import (
	"sync"
	"time"
)

type debounceFunc struct {
	order uint64
	done  chan struct{}
}

type debouncer struct {
	mu    sync.Mutex
	funcs map[string]*debounceFunc
}

func newDebouncer() *debouncer {
	return &debouncer{
		funcs: make(map[string]*debounceFunc),
	}
}

// debounce waits timeout before running f, if no subsequent call is made with
// the same key in the intervening time. If a later call to debounce with the
// same key occurs while the original call is blocking, the original call will
// return immediately without running its f.
//
// If order is specified, it will be used to order calls logically, so calls
// with lesser order will not cancel calls with greater order.
func (d *debouncer) debounce(key string, order uint64, timeout time.Duration, f func()) {
	if timeout == 0 {
		// Degenerate case: no debouncing.
		f()
		return
	}

	// First, atomically acquire the current func, cancel it, and insert this
	// call into d.funcs.
	d.mu.Lock()
	current, ok := d.funcs[key]
	if ok && current.order > order {
		// If we have a logical ordering of events (as is the case for snapshots),
		// don't overwrite a later event with an earlier event.
		d.mu.Unlock()
		return
	}
	if ok {
		close(current.done)
	}
	done := make(chan struct{})
	next := &debounceFunc{
		order: order,
		done:  done,
	}
	d.funcs[key] = next
	d.mu.Unlock()

	// Next, wait to be cancelled or for our wait to expire. There is a race here
	// that we must handle: our timer could expire while another goroutine holds
	// d.mu.
	select {
	case <-done:
	case <-time.After(timeout):
		d.mu.Lock()
		if d.funcs[key] != next {
			// We lost the race: another event has arrived for the key and started
			// waiting. We could reasonably choose to run f at this point, but doing
			// nothing is simpler.
			d.mu.Unlock()
			return
		}
		delete(d.funcs, key)
		d.mu.Unlock()
		f()
	}
}