vxx2 / vlib / x / executor / run.v
178 lines · 168 sloc · 3.57 KB · e80386b6af8a071a2e13fd654ebcdb2105fb9202
Raw
1module executor
2
3import sync
4
5// run blocks on the caller thread and executes accepted jobs serially.
6//
7// It exits after stop() or the first job error closes admission and all accepted
8// jobs have been drained.
9pub fn (mut e Executor) run() ! {
10 e.begin_owner_pump(true)!
11 defer {
12 e.end_owner_pump()
13 }
14 for {
15 e.mutex.lock()
16 job := e.pop_job_locked() or {
17 if !e.accepting {
18 err := e.finish_terminal_locked()
19 e.mutex.unlock()
20 if err !is none {
21 return err
22 }
23 return
24 }
25 job_ready := e.job_ready
26 e.mutex.unlock()
27 _ := <-job_ready
28 continue
29 }
30 e.mutex.unlock()
31 e.execute_job(job)
32 }
33}
34
35// run_one executes at most one accepted job and reports whether a job ran.
36pub fn (mut e Executor) run_one() !bool {
37 e.begin_owner_pump(false)!
38 defer {
39 e.end_owner_pump()
40 }
41 e.mutex.lock()
42 job := e.pop_job_locked() or {
43 if !e.accepting {
44 err := e.finish_terminal_locked()
45 e.mutex.unlock()
46 if err !is none {
47 return err
48 }
49 return false
50 }
51 e.mutex.unlock()
52 return false
53 }
54 e.mutex.unlock()
55
56 job_err := e.execute_job(job)
57 terminal_err := e.finish_if_drained()
58 if job_err !is none {
59 return job_err
60 }
61 if terminal_err !is none {
62 return terminal_err
63 }
64 return true
65}
66
67// drain_pending executes up to max_jobs jobs that are already accepted.
68pub fn (mut e Executor) drain_pending(max_jobs int) !int {
69 if max_jobs <= 0 {
70 return error(err_drain_limit_invalid)
71 }
72 e.begin_owner_pump(false)!
73 defer {
74 e.end_owner_pump()
75 }
76 mut ran := 0
77 mut first_err := IError(none)
78 for ran < max_jobs {
79 e.mutex.lock()
80 job := e.pop_job_locked() or {
81 if !e.accepting {
82 err := e.finish_terminal_locked()
83 e.mutex.unlock()
84 if err !is none {
85 return err
86 }
87 if first_err !is none {
88 return first_err
89 }
90 return ran
91 }
92 e.mutex.unlock()
93 if first_err !is none {
94 return first_err
95 }
96 return ran
97 }
98 e.mutex.unlock()
99
100 job_err := e.execute_job(job)
101 ran++
102 if job_err !is none && first_err is none {
103 first_err = job_err
104 }
105 }
106 terminal_err := e.finish_if_drained()
107 if terminal_err !is none {
108 return terminal_err
109 }
110 if first_err !is none {
111 return first_err
112 }
113 return ran
114}
115
116// stop closes admission and wakes any blocked submitters or owner run loop.
117//
118// It is idempotent and non-blocking. Accepted jobs are drained by the owner
119// pump; stop() itself never executes user callbacks.
120pub fn (mut e Executor) stop() {
121 e.mutex.lock()
122 e.close_admission_locked()
123 if e.queue.len == 0 && e.active == 0 {
124 e.finish_terminal_locked()
125 }
126 e.mutex.unlock()
127}
128
129// wait waits for a running owner pump to reach terminal state.
130//
131// A valid wait is one-shot. Calling it from the owner callback, or before a
132// terminal pump is active, returns a stable precondition error without consuming
133// that one allowed wait.
134pub fn (mut e Executor) wait() ! {
135 thread_id := sync.thread_id()
136 e.mutex.lock()
137 if e.owner_active && e.owner_thread_id == thread_id {
138 e.mutex.unlock()
139 return error(err_wait_owner_thread)
140 }
141 if e.waited {
142 e.mutex.unlock()
143 return error(err_wait_called)
144 }
145 if e.stopped {
146 e.waited = true
147 err := e.first_err
148 e.mutex.unlock()
149 if err !is none {
150 return err
151 }
152 return
153 }
154 if !e.run_active {
155 e.mutex.unlock()
156 return error(err_wait_before_terminal)
157 }
158 e.waited = true
159 done := e.done
160 e.mutex.unlock()
161
162 _ := <-done
163 err := e.get_first_error()
164 if err !is none {
165 return err
166 }
167}
168
169fn (mut e Executor) finish_if_drained() IError {
170 e.mutex.lock()
171 if !e.accepting && e.queue.len == 0 && e.active == 0 {
172 err := e.finish_terminal_locked()
173 e.mutex.unlock()
174 return err
175 }
176 e.mutex.unlock()
177 return none
178}
179