| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package dyncfg |
| 4 | |
| 5 | import ( |
| 6 | "context" |
| 7 | "time" |
| 8 | ) |
| 9 | |
| 10 | const DefaultDownstreamHandoffCap = 10 * time.Second |
| 11 | |
| 12 | type BoundedSendResult uint8 |
| 13 | |
| 14 | const ( |
| 15 | BoundedSendOK BoundedSendResult = iota + 1 |
| 16 | BoundedSendContextDone |
| 17 | BoundedSendTimeout |
| 18 | ) |
| 19 | |
| 20 | // BoundedSend sends value to ch using bounded wait: |
| 21 | // wait = min(remaining request deadline, maxWait), with maxWait used when no deadline exists. |
| 22 | func BoundedSend[T any](ctx context.Context, ch chan<- T, value T, maxWait time.Duration) BoundedSendResult { |
| 23 | if maxWait <= 0 { |
| 24 | maxWait = DefaultDownstreamHandoffCap |
| 25 | } |
| 26 | if ctx == nil { |
| 27 | ctx = context.Background() |
| 28 | } |
| 29 | |
| 30 | wait := maxWait |
| 31 | if deadline, ok := ctx.Deadline(); ok { |
| 32 | remaining := time.Until(deadline) |
| 33 | if remaining <= 0 { |
| 34 | if ctx.Err() != nil { |
| 35 | return BoundedSendContextDone |
| 36 | } |
| 37 | return BoundedSendTimeout |
| 38 | } |
| 39 | if remaining < wait { |
| 40 | wait = remaining |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | timer := time.NewTimer(wait) |
| 45 | defer timer.Stop() |
| 46 | |
| 47 | select { |
| 48 | case <-ctx.Done(): |
| 49 | return BoundedSendContextDone |
| 50 | case ch <- value: |
| 51 | return BoundedSendOK |
| 52 | case <-timer.C: |
| 53 | return BoundedSendTimeout |
| 54 | } |
| 55 | } |