Akash Reddy Kankanala | c001463 | 2025-05-21 17:12:20 +0530 | [diff] [blame^] | 1 | // Copyright 2022 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 rate |
| 6 | |
| 7 | import ( |
| 8 | "sync" |
| 9 | "time" |
| 10 | ) |
| 11 | |
| 12 | // Sometimes will perform an action occasionally. The First, Every, and |
| 13 | // Interval fields govern the behavior of Do, which performs the action. |
| 14 | // A zero Sometimes value will perform an action exactly once. |
| 15 | // |
| 16 | // # Example: logging with rate limiting |
| 17 | // |
| 18 | // var sometimes = rate.Sometimes{First: 3, Interval: 10*time.Second} |
| 19 | // func Spammy() { |
| 20 | // sometimes.Do(func() { log.Info("here I am!") }) |
| 21 | // } |
| 22 | type Sometimes struct { |
| 23 | First int // if non-zero, the first N calls to Do will run f. |
| 24 | Every int // if non-zero, every Nth call to Do will run f. |
| 25 | Interval time.Duration // if non-zero and Interval has elapsed since f's last run, Do will run f. |
| 26 | |
| 27 | mu sync.Mutex |
| 28 | count int // number of Do calls |
| 29 | last time.Time // last time f was run |
| 30 | } |
| 31 | |
| 32 | // Do runs the function f as allowed by First, Every, and Interval. |
| 33 | // |
| 34 | // The model is a union (not intersection) of filters. The first call to Do |
| 35 | // always runs f. Subsequent calls to Do run f if allowed by First or Every or |
| 36 | // Interval. |
| 37 | // |
| 38 | // A non-zero First:N causes the first N Do(f) calls to run f. |
| 39 | // |
| 40 | // A non-zero Every:M causes every Mth Do(f) call, starting with the first, to |
| 41 | // run f. |
| 42 | // |
| 43 | // A non-zero Interval causes Do(f) to run f if Interval has elapsed since |
| 44 | // Do last ran f. |
| 45 | // |
| 46 | // Specifying multiple filters produces the union of these execution streams. |
| 47 | // For example, specifying both First:N and Every:M causes the first N Do(f) |
| 48 | // calls and every Mth Do(f) call, starting with the first, to run f. See |
| 49 | // Examples for more. |
| 50 | // |
| 51 | // If Do is called multiple times simultaneously, the calls will block and run |
| 52 | // serially. Therefore, Do is intended for lightweight operations. |
| 53 | // |
| 54 | // Because a call to Do may block until f returns, if f causes Do to be called, |
| 55 | // it will deadlock. |
| 56 | func (s *Sometimes) Do(f func()) { |
| 57 | s.mu.Lock() |
| 58 | defer s.mu.Unlock() |
| 59 | if s.count == 0 || |
| 60 | (s.First > 0 && s.count < s.First) || |
| 61 | (s.Every > 0 && s.count%s.Every == 0) || |
| 62 | (s.Interval > 0 && time.Since(s.last) >= s.Interval) { |
| 63 | f() |
| 64 | s.last = time.Now() |
| 65 | } |
| 66 | s.count++ |
| 67 | } |