1#![cfg(feature = "std")]
2
3use crate::alloc::{Allocator, SliceWrapper};
4use core::mem;
5use std;
6use std::sync::RwLock;
8use std::sync::{Arc, Condvar, Mutex};
9
10use crate::enc::backward_references::UnionHasher;
11use crate::enc::fixed_queue::{FixedQueue, MAX_THREADS};
12use crate::enc::threading::{
13 BatchSpawnableLite, BrotliEncoderThreadError, CompressMulti, CompressionThreadResult,
14 InternalOwned, InternalSendAlloc, Joinable, Owned, SendAlloc,
15};
16use crate::enc::{BrotliAlloc, BrotliEncoderParams};
17
18struct JobReply<T: Send + 'static> {
19 result: T,
20 work_id: u64,
21}
22
23struct JobRequest<
24 ReturnValue: Send + 'static,
25 ExtraInput: Send + 'static,
26 Alloc: BrotliAlloc + Send + 'static,
27 U: Send + 'static + Sync,
28> {
29 func: fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue,
30 extra_input: ExtraInput,
31 index: usize,
32 thread_size: usize,
33 data: Arc<RwLock<U>>,
34 alloc: Alloc,
35 work_id: u64,
36}
37
38struct WorkQueue<
39 ReturnValue: Send + 'static,
40 ExtraInput: Send + 'static,
41 Alloc: BrotliAlloc + Send + 'static,
42 U: Send + 'static + Sync,
43> {
44 jobs: FixedQueue<JobRequest<ReturnValue, ExtraInput, Alloc, U>>,
45 results: FixedQueue<JobReply<ReturnValue>>,
46 shutdown: bool,
47 immediate_shutdown: bool,
48 num_in_progress: usize,
49 cur_work_id: u64,
50}
51impl<
52 ReturnValue: Send + 'static,
53 ExtraInput: Send + 'static,
54 Alloc: BrotliAlloc + Send + 'static,
55 U: Send + 'static + Sync,
56> Default for WorkQueue<ReturnValue, ExtraInput, Alloc, U>
57{
58 fn default() -> Self {
59 WorkQueue {
60 jobs: FixedQueue::default(),
61 results: FixedQueue::default(),
62 num_in_progress: 0,
63 immediate_shutdown: false,
64 shutdown: false,
65 cur_work_id: 0,
66 }
67 }
68}
69
70pub struct GuardedQueue<
71 ReturnValue: Send + 'static,
72 ExtraInput: Send + 'static,
73 Alloc: BrotliAlloc + Send + 'static,
74 U: Send + 'static + Sync,
75>(Arc<(Mutex<WorkQueue<ReturnValue, ExtraInput, Alloc, U>>, Condvar)>);
76pub struct WorkerPool<
77 ReturnValue: Send + 'static,
78 ExtraInput: Send + 'static,
79 Alloc: BrotliAlloc + Send + 'static,
80 U: Send + 'static + Sync,
81> {
82 queue: GuardedQueue<ReturnValue, ExtraInput, Alloc, U>,
83 join: [Option<std::thread::JoinHandle<()>>; MAX_THREADS],
84}
85
86impl<
87 ReturnValue: Send + 'static,
88 ExtraInput: Send + 'static,
89 Alloc: BrotliAlloc + Send + 'static,
90 U: Send + 'static + Sync,
91> Drop for WorkerPool<ReturnValue, ExtraInput, Alloc, U>
92{
93 fn drop(&mut self) {
94 {
95 let (lock, cvar) = &*self.queue.0;
96 let mut local_queue = lock.lock().unwrap();
97 local_queue.immediate_shutdown = true;
98 cvar.notify_all();
99 }
100 for thread_handle in self.join.iter_mut() {
101 if let Some(th) = thread_handle.take() {
102 th.join().unwrap();
103 }
104 }
105 }
106}
107impl<
108 ReturnValue: Send + 'static,
109 ExtraInput: Send + 'static,
110 Alloc: BrotliAlloc + Send + 'static,
111 U: Send + 'static + Sync,
112> WorkerPool<ReturnValue, ExtraInput, Alloc, U>
113{
114 fn do_work(queue: Arc<(Mutex<WorkQueue<ReturnValue, ExtraInput, Alloc, U>>, Condvar)>) {
115 loop {
116 let ret;
117 {
118 let possible_job;
123 {
124 let (lock, cvar) = &*queue;
125 let mut local_queue = lock.lock().unwrap();
126 if local_queue.immediate_shutdown {
127 break;
128 }
129 possible_job = match local_queue.jobs.pop() {
130 Some(res) => {
131 cvar.notify_all();
132 local_queue.num_in_progress += 1;
133 res
134 }
135 _ => {
136 if local_queue.shutdown {
137 break;
138 } else {
139 let _lock = cvar.wait(local_queue); continue;
141 }
142 }
143 };
144 }
145 ret = match possible_job.data.read() {
146 Ok(job_data) => JobReply {
147 result: (possible_job.func)(
148 possible_job.extra_input,
149 possible_job.index,
150 possible_job.thread_size,
151 &*job_data,
152 possible_job.alloc,
153 ),
154 work_id: possible_job.work_id,
155 },
156 _ => {
157 break; }
159 };
160 }
161 {
162 let (lock, cvar) = &*queue;
163 let mut local_queue = lock.lock().unwrap();
164 local_queue.num_in_progress -= 1;
165 local_queue.results.push(ret).unwrap();
166 cvar.notify_all();
167 }
168 }
169 }
170 fn _push_job(&mut self, job: JobRequest<ReturnValue, ExtraInput, Alloc, U>) {
171 let (lock, cvar) = &*self.queue.0;
172 let mut local_queue = lock.lock().unwrap();
173 loop {
174 if local_queue.jobs.size() + local_queue.num_in_progress + local_queue.results.size()
175 < MAX_THREADS
176 {
177 local_queue.jobs.push(job).unwrap();
178 cvar.notify_all();
179 break;
180 }
181 local_queue = cvar.wait(local_queue).unwrap();
182 }
183 }
184 fn _try_push_job(
185 &mut self,
186 job: JobRequest<ReturnValue, ExtraInput, Alloc, U>,
187 ) -> Result<(), JobRequest<ReturnValue, ExtraInput, Alloc, U>> {
188 let (lock, cvar) = &*self.queue.0;
189 let mut local_queue = lock.lock().unwrap();
190 if local_queue.jobs.size() + local_queue.num_in_progress + local_queue.results.size()
191 < MAX_THREADS
192 {
193 local_queue.jobs.push(job).unwrap();
194 cvar.notify_all();
195 Ok(())
196 } else {
197 Err(job)
198 }
199 }
200 fn start(
201 queue: Arc<(Mutex<WorkQueue<ReturnValue, ExtraInput, Alloc, U>>, Condvar)>,
202 ) -> std::thread::JoinHandle<()> {
203 std::thread::spawn(move || Self::do_work(queue))
204 }
205 pub fn new(num_threads: usize) -> Self {
206 let queue = Arc::new((Mutex::new(WorkQueue::default()), Condvar::new()));
207 WorkerPool {
208 queue: GuardedQueue(queue.clone()),
209 join: [
210 Some(Self::start(queue.clone())),
211 if 1 < num_threads {
212 Some(Self::start(queue.clone()))
213 } else {
214 None
215 },
216 if 2 < num_threads {
217 Some(Self::start(queue.clone()))
218 } else {
219 None
220 },
221 if 3 < num_threads {
222 Some(Self::start(queue.clone()))
223 } else {
224 None
225 },
226 if 4 < num_threads {
227 Some(Self::start(queue.clone()))
228 } else {
229 None
230 },
231 if 5 < num_threads {
232 Some(Self::start(queue.clone()))
233 } else {
234 None
235 },
236 if 6 < num_threads {
237 Some(Self::start(queue.clone()))
238 } else {
239 None
240 },
241 if 7 < num_threads {
242 Some(Self::start(queue.clone()))
243 } else {
244 None
245 },
246 if 8 < num_threads {
247 Some(Self::start(queue.clone()))
248 } else {
249 None
250 },
251 if 9 < num_threads {
252 Some(Self::start(queue.clone()))
253 } else {
254 None
255 },
256 if 10 < num_threads {
257 Some(Self::start(queue.clone()))
258 } else {
259 None
260 },
261 if 11 < num_threads {
262 Some(Self::start(queue.clone()))
263 } else {
264 None
265 },
266 if 12 < num_threads {
267 Some(Self::start(queue.clone()))
268 } else {
269 None
270 },
271 if 13 < num_threads {
272 Some(Self::start(queue.clone()))
273 } else {
274 None
275 },
276 if 14 < num_threads {
277 Some(Self::start(queue.clone()))
278 } else {
279 None
280 },
281 if 15 < num_threads {
282 Some(Self::start(queue.clone()))
283 } else {
284 None
285 },
286 ],
287 }
288 }
289}
290
291pub fn new_work_pool<
292 Alloc: BrotliAlloc + Send + 'static,
293 SliceW: SliceWrapper<u8> + Send + 'static + Sync,
294>(
295 num_threads: usize,
296) -> WorkerPool<
297 CompressionThreadResult<Alloc>,
298 UnionHasher<Alloc>,
299 Alloc,
300 (SliceW, BrotliEncoderParams),
301>
302where
303 <Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
304 <Alloc as Allocator<u16>>::AllocatedMemory: Send + Sync,
305 <Alloc as Allocator<u32>>::AllocatedMemory: Send + Sync,
306{
307 WorkerPool::new(num_threads)
308}
309
310pub struct WorkerJoinable<
311 ReturnValue: Send + 'static,
312 ExtraInput: Send + 'static,
313 Alloc: BrotliAlloc + Send + 'static,
314 U: Send + 'static + Sync,
315> {
316 queue: GuardedQueue<ReturnValue, ExtraInput, Alloc, U>,
317 work_id: u64,
318}
319impl<
320 ReturnValue: Send + 'static,
321 ExtraInput: Send + 'static,
322 Alloc: BrotliAlloc + Send + 'static,
323 U: Send + 'static + Sync,
324> Joinable<ReturnValue, BrotliEncoderThreadError>
325 for WorkerJoinable<ReturnValue, ExtraInput, Alloc, U>
326{
327 fn join(self) -> Result<ReturnValue, BrotliEncoderThreadError> {
328 let (lock, cvar) = &*self.queue.0;
329 let mut local_queue = lock.lock().unwrap();
330 loop {
331 match local_queue
332 .results
333 .remove(|data: &Option<JobReply<ReturnValue>>| {
334 if let Some(ref item) = *data {
335 item.work_id == self.work_id
336 } else {
337 false
338 }
339 }) {
340 Some(matched) => return Ok(matched.result),
341 None => local_queue = cvar.wait(local_queue).unwrap(),
342 };
343 }
344 }
345}
346
347impl<
348 ReturnValue: Send + 'static,
349 ExtraInput: Send + 'static,
350 Alloc: BrotliAlloc + Send + 'static,
351 U: Send + 'static + Sync,
352> BatchSpawnableLite<ReturnValue, ExtraInput, Alloc, U>
353 for WorkerPool<ReturnValue, ExtraInput, Alloc, U>
354where
355 <Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
356 <Alloc as Allocator<u16>>::AllocatedMemory: Send + Sync,
357 <Alloc as Allocator<u32>>::AllocatedMemory: Send + Sync,
358{
359 type FinalJoinHandle = Arc<RwLock<U>>;
360 type JoinHandle = WorkerJoinable<ReturnValue, ExtraInput, Alloc, U>;
361
362 fn make_spawner(&mut self, input: &mut Owned<U>) -> Self::FinalJoinHandle {
363 std::sync::Arc::<RwLock<U>>::new(RwLock::new(
364 mem::replace(input, Owned(InternalOwned::Borrowed)).unwrap(),
365 ))
366 }
367 fn spawn(
368 &mut self,
369 locked_input: &mut Self::FinalJoinHandle,
370 work: &mut SendAlloc<ReturnValue, ExtraInput, Alloc, Self::JoinHandle>,
371 index: usize,
372 num_threads: usize,
373 f: fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue,
374 ) {
375 assert!(num_threads <= MAX_THREADS);
376 let (lock, cvar) = &*self.queue.0;
377 let mut local_queue = lock.lock().unwrap();
378 loop {
379 if local_queue.jobs.size() + local_queue.num_in_progress + local_queue.results.size()
380 <= MAX_THREADS
381 {
382 let work_id = local_queue.cur_work_id;
383 local_queue.cur_work_id += 1;
384 let (local_alloc, local_extra) = work.replace_with_default();
385 local_queue
386 .jobs
387 .push(JobRequest {
388 func: f,
389 extra_input: local_extra,
390 index,
391 thread_size: num_threads,
392 data: locked_input.clone(),
393 alloc: local_alloc,
394 work_id,
395 })
396 .unwrap();
397 *work = SendAlloc(InternalSendAlloc::Join(WorkerJoinable {
398 queue: GuardedQueue(self.queue.0.clone()),
399 work_id,
400 }));
401 cvar.notify_all();
402 break;
403 } else {
404 local_queue = cvar.wait(local_queue).unwrap(); }
406 }
407 }
408}
409
410pub fn compress_worker_pool<
411 Alloc: BrotliAlloc + Send + 'static,
412 SliceW: SliceWrapper<u8> + Send + 'static + Sync,
413>(
414 params: &BrotliEncoderParams,
415 owned_input: &mut Owned<SliceW>,
416 output: &mut [u8],
417 alloc_per_thread: &mut [SendAlloc<
418 CompressionThreadResult<Alloc>,
419 UnionHasher<Alloc>,
420 Alloc,
421 <WorkerPool<
422 CompressionThreadResult<Alloc>,
423 UnionHasher<Alloc>,
424 Alloc,
425 (SliceW, BrotliEncoderParams),
426 > as BatchSpawnableLite<
427 CompressionThreadResult<Alloc>,
428 UnionHasher<Alloc>,
429 Alloc,
430 (SliceW, BrotliEncoderParams),
431 >>::JoinHandle,
432 >],
433 work_pool: &mut WorkerPool<
434 CompressionThreadResult<Alloc>,
435 UnionHasher<Alloc>,
436 Alloc,
437 (SliceW, BrotliEncoderParams),
438 >,
439) -> Result<usize, BrotliEncoderThreadError>
440where
441 <Alloc as Allocator<u8>>::AllocatedMemory: Send,
442 <Alloc as Allocator<u16>>::AllocatedMemory: Send + Sync,
443 <Alloc as Allocator<u32>>::AllocatedMemory: Send + Sync,
444{
445 CompressMulti(params, owned_input, output, alloc_per_thread, work_pool)
446}
447
448