(ctx context.Context)
| 86 | } |
| 87 | |
| 88 | func (fw *fsWatcher) loop(ctx context.Context) { |
| 89 | defer close(fw.done) |
| 90 | const batchWindow = 50 * time.Millisecond |
| 91 | var ( |
| 92 | batch []FSEvent |
| 93 | seen = make(map[string]struct{}) |
| 94 | timer *time.Timer |
| 95 | timerC <-chan time.Time |
| 96 | ) |
| 97 | flush := func() { |
| 98 | if len(batch) == 0 { |
| 99 | return |
| 100 | } |
| 101 | select { |
| 102 | case fw.events <- batch: |
| 103 | default: |
| 104 | fw.logger.Warn(ctx, "fs watcher dropping batch", slog.F("count", len(batch))) |
| 105 | } |
| 106 | batch = nil |
| 107 | seen = make(map[string]struct{}) |
| 108 | if timer != nil { |
| 109 | timer.Stop() |
| 110 | } |
| 111 | timer = nil |
| 112 | timerC = nil |
| 113 | } |
| 114 | addToBatch := func(ev FSEvent) { |
| 115 | if _, dup := seen[ev.Path]; dup { |
| 116 | return |
| 117 | } |
| 118 | seen[ev.Path] = struct{}{} |
| 119 | batch = append(batch, ev) |
| 120 | if timer == nil { |
| 121 | timer = time.NewTimer(batchWindow) |
| 122 | timerC = timer.C |
| 123 | } |
| 124 | } |
| 125 | for { |
| 126 | select { |
| 127 | case <-ctx.Done(): |
| 128 | flush() |
| 129 | return |
| 130 | case ev, ok := <-fw.w.Events: |
| 131 | if !ok { |
| 132 | flush() |
| 133 | return |
| 134 | } |
| 135 | fsev := translateEvent(ev) |
| 136 | if fsev == nil { |
| 137 | continue |
| 138 | } |
| 139 | if fsev.IsDir && fsev.Op == OpCreate { |
| 140 | for _, s := range fw.addRecursive(fsev.Path) { |
| 141 | addToBatch(s) |
| 142 | } |
| 143 | } |
| 144 | addToBatch(*fsev) |
| 145 | case err, ok := <-fw.w.Errors: |
no test coverage detected