Skip to main content

task_exec_queue/
exec.rs

1use std::fmt::Debug;
2use std::future::Future;
3use std::hash::Hash;
4use std::marker::Unpin;
5use std::pin::Pin;
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::sync::Arc;
8use std::task::{Context, Poll};
9
10use futures::channel::mpsc;
11use futures::task::AtomicWaker;
12use futures::{Sink, SinkExt, Stream, StreamExt};
13use parking_lot::Mutex;
14use parking_lot::RwLock;
15#[cfg(feature = "rate")]
16use rate_counter::Counter as RateCounter;
17
18use queue_ext::{Action, QueueExt, Reply};
19
20use super::{
21    assert_future, close::Close, flush::Flush, Counter, Error, ErrorType, GroupTaskExecQueue,
22    IndexSet, Spawner, TrySpawner,
23};
24
25type DashMap<K, V> = dashmap::DashMap<K, V, ahash::RandomState>;
26type GroupChannels<G> = Arc<DashMap<G, Arc<Mutex<GroupTaskExecQueue<TaskType>>>>>;
27
28pub type TaskType = Box<dyn std::future::Future<Output = ()> + Send + 'static + Unpin>;
29
30pub struct TaskExecQueue<Tx = mpsc::Sender<((), TaskType)>, G = (), D = ()> {
31    pub(crate) tx: Tx,
32    workers: usize,
33    queue_max: isize,
34    active_count: Counter,
35    pub(crate) waiting_count: Counter,
36    #[cfg(feature = "rate")]
37    rate_counter: RateCounter,
38    pub(crate) flush_waker: Arc<AtomicWaker>,
39    pub(crate) is_flushing: Arc<AtomicBool>,
40    is_closed: Arc<AtomicBool>,
41    pending_wakers: Wakers,
42    pub(crate) waiting_wakers: Wakers,
43    //group
44    group_channels: GroupChannels<G>,
45    _d: std::marker::PhantomData<D>,
46}
47
48impl<Tx, G, D> Clone for TaskExecQueue<Tx, G, D>
49where
50    Tx: Clone,
51{
52    #[inline]
53    fn clone(&self) -> Self {
54        Self {
55            tx: self.tx.clone(),
56            workers: self.workers,
57            queue_max: self.queue_max,
58            active_count: self.active_count.clone(),
59            waiting_count: self.waiting_count.clone(),
60            #[cfg(feature = "rate")]
61            rate_counter: self.rate_counter.clone(),
62            flush_waker: self.flush_waker.clone(),
63            is_flushing: self.is_flushing.clone(),
64            is_closed: self.is_closed.clone(),
65            pending_wakers: self.pending_wakers.clone(),
66            waiting_wakers: self.waiting_wakers.clone(),
67            group_channels: self.group_channels.clone(),
68            _d: std::marker::PhantomData,
69        }
70    }
71}
72
73impl<Tx, G, D> TaskExecQueue<Tx, G, D>
74where
75    Tx: Clone + Sink<(D, TaskType)> + Unpin + Send + Sync + 'static,
76    G: Hash + Eq + Clone + Debug + Send + Sync + 'static,
77{
78    #[inline]
79    pub(crate) fn with_channel<Rx>(
80        workers: usize,
81        queue_max: usize,
82        tx: Tx,
83        rx: Rx,
84    ) -> (Self, impl Future<Output = ()>)
85    where
86        Rx: Stream<Item = (D, TaskType)> + Unpin,
87    {
88        let exec = Self {
89            tx,
90            workers,
91            queue_max: queue_max as isize,
92            active_count: Counter::new(),
93            waiting_count: Counter::new(),
94            #[cfg(feature = "rate")]
95            rate_counter: RateCounter::new(std::time::Duration::from_secs(3)),
96            flush_waker: Arc::new(AtomicWaker::new()),
97            is_flushing: Arc::new(AtomicBool::new(false)),
98            is_closed: Arc::new(AtomicBool::new(false)),
99            pending_wakers: new_wakers(),
100            waiting_wakers: new_wakers(),
101            group_channels: Arc::new(DashMap::default()),
102            _d: std::marker::PhantomData,
103        };
104        let runner = exec.clone().run(rx);
105        (exec, runner)
106    }
107
108    #[inline]
109    pub fn try_spawn_with<T>(&self, msg: T, name: D) -> TrySpawner<'_, T, Tx, G, D>
110    where
111        D: Clone,
112        T: Future + Send + 'static,
113        T::Output: Send + 'static,
114    {
115        let fut = TrySpawner::new(self, msg, name);
116        assert_future::<Result<(), _>, _>(fut)
117    }
118
119    #[inline]
120    pub fn spawn_with<T>(&self, msg: T, name: D) -> Spawner<'_, T, Tx, G, D>
121    where
122        D: Clone,
123        T: Future + Send + 'static,
124        T::Output: Send + 'static,
125    {
126        let fut = Spawner::new(self, msg, name);
127        assert_future::<Result<(), _>, _>(fut)
128    }
129
130    #[inline]
131    pub fn flush(&self) -> Flush<'_, Tx, G, D> {
132        self.is_flushing.store(true, Ordering::SeqCst);
133        Flush::new(self)
134    }
135
136    #[inline]
137    pub fn close(&self) -> Close<'_, Tx, G, D> {
138        self.is_flushing.store(true, Ordering::SeqCst);
139        self.is_closed.store(true, Ordering::SeqCst);
140        Close::new(self)
141    }
142
143    #[inline]
144    pub fn workers(&self) -> usize {
145        self.workers
146    }
147
148    #[inline]
149    pub fn active_count(&self) -> isize {
150        self.active_count.value()
151    }
152
153    #[inline]
154    pub fn waiting_count(&self) -> isize {
155        self.waiting_count.value()
156    }
157
158    #[inline]
159    #[cfg(feature = "rate")]
160    pub async fn completed_count(&self) -> isize {
161        self.rate_counter.total()
162    }
163
164    #[inline]
165    pub fn pending_wakers_count(&self) -> usize {
166        self.pending_wakers.len()
167    }
168
169    #[inline]
170    pub fn waiting_wakers_count(&self) -> usize {
171        self.waiting_wakers.len()
172    }
173
174    #[inline]
175    #[cfg(feature = "rate")]
176    pub async fn rate(&self) -> f64 {
177        self.rate_counter.rate()
178    }
179
180    #[inline]
181    pub fn is_full(&self) -> bool {
182        self.waiting_count() >= self.queue_max
183    }
184
185    #[inline]
186    pub fn is_active(&self) -> bool {
187        self.waiting_count() > 0
188            || self.active_count() > 0
189            || self.pending_wakers_count() > 0
190            || self.waiting_wakers_count() > 0
191    }
192
193    #[inline]
194    pub fn is_closed(&self) -> bool {
195        self.is_closed.load(Ordering::SeqCst)
196    }
197
198    #[inline]
199    pub fn is_flushing(&self) -> bool {
200        self.is_flushing.load(Ordering::SeqCst)
201    }
202
203    async fn run<Rx>(self, mut task_rx: Rx)
204    where
205        Rx: Stream<Item = (D, TaskType)> + Unpin,
206    {
207        let exec = self;
208        let pending_wakers = exec.pending_wakers.clone();
209
210        let channel = || {
211            let rx = OneValue::new().queue_stream(|s, _| match s.take() {
212                None => Poll::Pending,
213                Some(m) => Poll::Ready(Some(m)),
214            });
215
216            let tx = rx.clone().queue_sender(|s, action| match action {
217                Action::Send(item) => Reply::Send(s.set(item)),
218                Action::IsFull => Reply::IsFull(s.is_full()),
219                Action::IsEmpty => Reply::IsEmpty(s.is_empty()),
220                Action::Len => Reply::Len(s.len()),
221            });
222
223            (tx, rx)
224        };
225
226        let idle_idxs = IndexSet::new();
227        let mut txs = Vec::new();
228        let mut rxs = Vec::new();
229        for i in 0..exec.workers {
230            let (tx, mut rx) = channel();
231            let pending_wakers = pending_wakers.clone();
232            let idle_idxs = idle_idxs.clone();
233            idle_idxs.insert(i);
234            let exec = exec.clone();
235            let rx_fut = async move {
236                loop {
237                    match rx.next().await {
238                        Some(task) => {
239                            exec.active_count.inc();
240                            task.await;
241                            exec.active_count.dec();
242                            #[cfg(feature = "rate")]
243                            exec.rate_counter.inc();
244                        }
245                        None => break,
246                    }
247
248                    if !rx.is_full() {
249                        idle_idxs.insert(i);
250                        if let Some(w) = pending_wakers.pop() {
251                            w.wake();
252                        }
253                    }
254
255                    if exec.is_flushing() && rx.is_empty() && !exec.is_active() {
256                        exec.flush_waker.wake();
257                    }
258                }
259            };
260
261            txs.push(tx);
262            rxs.push(rx_fut);
263        }
264
265        let tasks_bus = async move {
266            while let Some((_, task)) = task_rx.next().await {
267                loop {
268                    if idle_idxs.is_empty() {
269                        //sleep ...
270                        let w = Arc::new(AtomicWaker::new());
271                        pending_wakers.push(w.clone());
272                        PendingOnce::new(w).await;
273                        // Drain stale wakers that accumulated while we slept
274                        // (happens when a new mpsc message wakes us instead of a worker)
275                        while pending_wakers.pop().is_some() {}
276                    } else if let Some(idx) = idle_idxs.pop() {
277                        //select ...
278                        if let Some(tx) = txs.get_mut(idx) {
279                            if let Err(_t) = tx.send(task).await {
280                                log::error!("send error ...");
281                                // task = t.into_inner();
282                            }
283                        }
284                        break;
285                    };
286                }
287            }
288        };
289
290        futures::future::join(tasks_bus, futures::future::join_all(rxs)).await;
291        log::info!("exit task execution queue");
292    }
293}
294
295impl<Tx, G> TaskExecQueue<Tx, G, ()>
296where
297    Tx: Clone + Sink<((), TaskType)> + Unpin + Send + Sync + 'static,
298    G: Hash + Eq + Clone + Debug + Send + Sync + 'static,
299{
300    #[inline]
301    pub fn try_spawn<T>(&self, task: T) -> TrySpawner<'_, T, Tx, G, ()>
302    where
303        T: Future + Send + 'static,
304        T::Output: Send + 'static,
305    {
306        let fut = TrySpawner::new(self, task, ());
307        assert_future::<Result<(), _>, _>(fut)
308    }
309
310    #[inline]
311    pub fn spawn<T>(&self, task: T) -> Spawner<'_, T, Tx, G, ()>
312    where
313        T: Future + Send + 'static,
314        T::Output: Send + 'static,
315    {
316        let fut = Spawner::new(self, task, ());
317        assert_future::<Result<(), _>, _>(fut)
318    }
319
320    #[inline]
321    pub(crate) async fn group_send(&self, name: G, task: TaskType) -> Result<(), Error<TaskType>> {
322        let gt_queue = self
323            .group_channels
324            .entry(name.clone())
325            .or_insert_with(|| Arc::new(Mutex::new(GroupTaskExecQueue::new())))
326            .value()
327            .clone();
328
329        let exec = self.clone();
330        let group_channels = self.group_channels.clone();
331        let runner_task = {
332            let mut task_tx = gt_queue.lock();
333            if task_tx.is_running() {
334                task_tx.push(task);
335                drop(task_tx);
336                drop(gt_queue);
337                None
338            } else {
339                task_tx.set_running(true);
340                drop(task_tx);
341                let task_rx = gt_queue; //.clone();
342                let runner_task = async move {
343                    exec.active_count.inc();
344                    task.await;
345                    exec.active_count.dec();
346                    loop {
347                        let task: Option<TaskType> = task_rx.lock().pop();
348                        if let Some(task) = task {
349                            exec.active_count.inc();
350                            task.await;
351                            exec.active_count.dec();
352                            #[cfg(feature = "rate")]
353                            exec.rate_counter.inc();
354                        } else {
355                            group_channels.remove(&name);
356                            break;
357                        }
358                    }
359                };
360                Some(runner_task)
361            }
362        };
363
364        if let Some(runner_task) = runner_task {
365            if (self
366                .tx
367                .clone()
368                .send(((), Box::new(Box::pin(runner_task))))
369                .await)
370                .is_err()
371            {
372                Err(Error::SendError(ErrorType::Closed(None)))
373            } else {
374                Ok(())
375            }
376        } else {
377            Ok(())
378        }
379    }
380}
381
382#[derive(Clone)]
383struct OneValue(Arc<RwLock<Option<TaskType>>>);
384
385impl OneValue {
386    #[inline]
387    fn new() -> Self {
388        #[allow(clippy::arc_with_non_send_sync)]
389        Self(Arc::new(RwLock::new(None::<TaskType>)))
390    }
391
392    #[inline]
393    fn set(&self, val: TaskType) -> Option<TaskType> {
394        self.0.write().replace(val)
395    }
396
397    #[inline]
398    fn take(&self) -> Option<TaskType> {
399        self.0.write().take()
400    }
401
402    #[inline]
403    fn is_full(&self) -> bool {
404        self.0.read().is_some()
405    }
406
407    #[inline]
408    fn len(&self) -> usize {
409        if self.0.read().is_some() {
410            1
411        } else {
412            0
413        }
414    }
415
416    #[inline]
417    fn is_empty(&self) -> bool {
418        self.0.read().is_none()
419    }
420}
421
422pub(crate) struct PendingOnce {
423    w: Arc<AtomicWaker>,
424    is_ready: bool,
425}
426
427impl PendingOnce {
428    #[inline]
429    pub(crate) fn new(w: Arc<AtomicWaker>) -> Self {
430        Self { w, is_ready: false }
431    }
432}
433
434impl Future for PendingOnce {
435    type Output = ();
436    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
437        if self.is_ready {
438            Poll::Ready(())
439        } else {
440            self.w.register(cx.waker());
441            self.is_ready = true;
442            Poll::Pending
443        }
444    }
445}
446
447type Wakers = Arc<crossbeam_queue::SegQueue<Arc<AtomicWaker>>>;
448
449#[inline]
450fn new_wakers() -> Wakers {
451    Arc::new(crossbeam_queue::SegQueue::new())
452}