vxx2 / vlib / x / executor / submit.v
170 lines · 163 sloc · 3.84 KB · e80386b6af8a071a2e13fd654ebcdb2105fb9202
Raw
1module executor
2
3import context
4import sync
5import time
6
7// try_post accepts f if the executor is open and queue space is available.
8//
9// It never waits for queue capacity. A full queue returns
10// `executor: queue is full`, making backpressure explicit.
11pub fn (mut e Executor) try_post(f JobFn) ! {
12 if f == unsafe { nil } {
13 return error(err_nil_job)
14 }
15 e.enqueue_job_now(ExecutorJob{
16 f: f
17 report_error: true
18 })!
19}
20
21// post_with_context waits until f can be admitted, parent is canceled, or the executor closes.
22//
23// The parent context bounds admission only. Once accepted, f will run on a
24// future owner pump unless the application never pumps the executor.
25// Active-owner submissions are accepted while capacity is available. If they
26// would need to wait for capacity, they fail with
27// `executor: owner thread cannot wait for queue capacity`.
28pub fn (mut e Executor) post_with_context(parent context.Context, f JobFn) ! {
29 if f == unsafe { nil } {
30 return error(err_nil_job)
31 }
32 e.enqueue_job_with_context(parent, ExecutorJob{
33 f: f
34 report_error: true
35 })!
36}
37
38// post_with_timeout waits up to timeout for f to be admitted.
39//
40// The timeout bounds admission only and does not preempt a running callback.
41// Active-owner submissions are accepted while capacity is available. If they
42// would need to wait for capacity, they fail with
43// `executor: owner thread cannot wait for queue capacity`.
44pub fn (mut e Executor) post_with_timeout(timeout time.Duration, f JobFn) ! {
45 if f == unsafe { nil } {
46 return error(err_nil_job)
47 }
48 if timeout <= 0 {
49 return error(err_timeout)
50 }
51 e.enqueue_job_with_timeout(time.now().add(timeout), ExecutorJob{
52 f: f
53 report_error: true
54 })!
55}
56
57fn (mut e Executor) enqueue_job_now(job ExecutorJob) ! {
58 e.mutex.lock()
59 if !e.accepting {
60 e.mutex.unlock()
61 return error(err_executor_closed)
62 }
63 if e.queue.len >= e.queue_size {
64 e.mutex.unlock()
65 return error(err_queue_full)
66 }
67 e.queue << job
68 e.wake_job_ready_locked()
69 e.mutex.unlock()
70}
71
72fn (mut e Executor) enqueue_job_with_context(parent context.Context, job ExecutorJob) ! {
73 thread_id := sync.thread_id()
74 mut submit_ctx := parent
75 initial_err := submit_ctx.err()
76 if initial_err !is none {
77 return initial_err
78 }
79 done := submit_ctx.done()
80 mut watch_done := true
81 select {
82 _ := <-done {
83 err := submit_ctx.err()
84 if err !is none {
85 return err
86 }
87 watch_done = false
88 }
89 else {}
90 }
91 for {
92 e.mutex.lock()
93 if !e.accepting {
94 e.mutex.unlock()
95 return error(err_executor_closed)
96 }
97 if watch_done {
98 err := submit_ctx.err()
99 if err !is none {
100 e.mutex.unlock()
101 return err
102 }
103 }
104 if e.queue.len < e.queue_size {
105 e.queue << job
106 e.wake_job_ready_locked()
107 e.mutex.unlock()
108 return
109 }
110 if e.owner_active && e.owner_thread_id == thread_id {
111 e.mutex.unlock()
112 return error(err_owner_submit_wait)
113 }
114 submit_ready := e.submit_ready
115 e.mutex.unlock()
116 if !watch_done {
117 _ := <-submit_ready
118 continue
119 }
120 select {
121 _ := <-submit_ready {
122 continue
123 }
124 _ := <-done {
125 err := submit_ctx.err()
126 if err !is none {
127 return err
128 }
129 watch_done = false
130 continue
131 }
132 }
133 }
134}
135
136fn (mut e Executor) enqueue_job_with_timeout(deadline time.Time, job ExecutorJob) ! {
137 thread_id := sync.thread_id()
138 for {
139 e.mutex.lock()
140 if !e.accepting {
141 e.mutex.unlock()
142 return error(err_executor_closed)
143 }
144 remaining := deadline - time.now()
145 if remaining <= 0 {
146 e.mutex.unlock()
147 return error(err_timeout)
148 }
149 if e.queue.len < e.queue_size {
150 e.queue << job
151 e.wake_job_ready_locked()
152 e.mutex.unlock()
153 return
154 }
155 if e.owner_active && e.owner_thread_id == thread_id {
156 e.mutex.unlock()
157 return error(err_owner_submit_wait)
158 }
159 submit_ready := e.submit_ready
160 e.mutex.unlock()
161 select {
162 _ := <-submit_ready {
163 continue
164 }
165 remaining {
166 return error(err_timeout)
167 }
168 }
169 }
170}
171