v2 / vlib / x / async / periodic.v
68 lines · 62 sloc · 1.49 KB · 15fb60b77ea6073658aa8355b247f2e1ae03b714
Raw
1module async
2
3import context
4import time
5
6// every runs f repeatedly with interval spacing until ctx is canceled or f fails.
7//
8// This helper is intentionally blocking: it does not start a hidden scheduler or
9// background loop. The first iteration runs after one interval. Iterations never
10// overlap; a slow job delays the next interval. Cancellation is cooperative, so
11// a running job must observe `ctx.done()` if it needs to stop before returning.
12pub fn every(parent context.Context, interval time.Duration, f JobFn) ! {
13 if interval.nanoseconds() <= 0 {
14 return error(err_interval_invalid)
15 }
16 if f == unsafe { nil } {
17 return error(err_nil_job)
18 }
19
20 mut ctx := parent
21 initial_err := ctx.err()
22 if initial_err !is none {
23 return initial_err
24 }
25
26 done_ch := ctx.done()
27 mut watch_done := true
28 // context.background().done() is closed in V but err() remains none. Treat
29 // that as a non-cancelable context instead of returning immediately.
30 select {
31 _ := <-done_ch {
32 err := ctx.err()
33 if err !is none {
34 return err
35 }
36 watch_done = false
37 }
38 else {}
39 }
40
41 for {
42 if watch_done {
43 select {
44 _ := <-done_ch {
45 err := ctx.err()
46 if err !is none {
47 return err
48 }
49 watch_done = false
50 }
51 interval {
52 run_periodic_iteration(mut ctx, f)!
53 }
54 }
55 } else {
56 time.sleep(interval)
57 run_periodic_iteration(mut ctx, f)!
58 }
59 }
60}
61
62fn run_periodic_iteration(mut ctx context.Context, f JobFn) ! {
63 f(mut ctx)!
64 err := ctx.err()
65 if err !is none {
66 return err
67 }
68}
69