1#![cfg_attr(docsrs, feature(doc_cfg))]
4#![allow(unused_features)]
5#![warn(missing_docs)]
6#![deny(rustdoc::broken_intra_doc_links)]
7#![doc(
8 html_logo_url = "https://github.com/compio-rs/compio-logo/raw/refs/heads/master/generated/colored-bold.svg"
9)]
10#![doc(
11 html_favicon_url = "https://github.com/compio-rs/compio-logo/raw/refs/heads/master/generated/colored-bold.svg"
12)]
13
14use std::{any::Any, fmt::Debug, ptr::NonNull, task::Waker};
15
16use crate::queue::{TaskId, TaskQueue};
17
18mod join_handle;
19mod queue;
20mod task;
21mod util;
22mod waker;
23
24use compio_log::{instrument, trace};
25use compio_send_wrapper::SendWrapper;
26use crossbeam_queue::ArrayQueue;
27pub use join_handle::{JoinError, JoinHandle, ResumeUnwind};
28use util::panic_guard;
29
30cfg_select! {
31 loom => {
32 use loom::{cell::UnsafeCell, hint, sync::atomic::*, thread::yield_now};
33 }
34 _ => {
35 use std::{hint, sync::atomic::*, thread::yield_now};
36
37 #[repr(transparent)]
38 struct UnsafeCell<T>(std::cell::UnsafeCell<T>);
39
40 impl<T> UnsafeCell<T> {
41 pub fn new(value: T) -> Self {
42 Self(std::cell::UnsafeCell::new(value))
43 }
44
45 #[inline(always)]
46 pub fn with_mut<F, R>(&self, f: F) -> R
47 where
48 F: FnOnce(*mut T) -> R,
49 {
50 f(self.0.get())
51 }
52
53 #[inline(always)]
54 pub fn with<F, R>(&self, f: F) -> R
55 where
56 F: FnOnce(*const T) -> R,
57 {
58 f(self.0.get())
59 }
60 }
61 }
62}
63
64pub(crate) type PanicResult<T> = Result<T, Panic>;
65pub(crate) type Panic = Box<dyn Any + Send + 'static>;
66
67#[derive(Debug)]
82pub struct Executor {
83 ptr: NonNull<Shared>,
84 config: ExecutorConfig,
85}
86
87#[derive(Debug, Clone)]
89pub struct ExecutorConfig {
90 pub sync_queue_size: usize,
95
96 pub local_queue_size: usize,
101
102 pub max_interval: u32,
104
105 pub waker: Option<Waker>,
110}
111
112impl Default for ExecutorConfig {
113 fn default() -> Self {
114 Self {
115 sync_queue_size: 64,
116 local_queue_size: 64,
117 max_interval: 61,
118 waker: None,
119 }
120 }
121}
122
123pub(crate) struct Shared {
124 waker: Option<Waker>,
125 sync: ArrayQueue<TaskId>,
126 pending: AtomicUsize,
127 queue: SendWrapper<TaskQueue>,
128}
129
130impl Shared {
131 #[inline]
137 pub(crate) fn drain_sync(&self, queue: &TaskQueue) {
138 if self.pending.load(Ordering::Acquire) == 0 {
139 return;
140 }
141
142 let mut drained: usize = 0;
143 while let Some(id) = self.sync.pop() {
144 queue.make_hot(id);
145 drained += 1;
146 }
147
148 if drained != 0 {
149 self.pending.fetch_sub(drained, Ordering::Release);
150 }
151 }
152}
153
154impl Executor {
155 pub fn new() -> Self {
157 Self::with_config(ExecutorConfig::default())
158 }
159
160 pub fn with_config(mut config: ExecutorConfig) -> Self {
162 let ptr = Box::into_raw(Box::new(Shared {
163 waker: config.waker.take(),
164 sync: ArrayQueue::new(config.sync_queue_size),
165 pending: AtomicUsize::new(0),
166 queue: SendWrapper::new(TaskQueue::new(config.local_queue_size)),
167 }));
168
169 Self {
170 config,
171 ptr: unsafe { NonNull::new_unchecked(ptr) },
172 }
173 }
174
175 pub fn spawn<F: Future + 'static>(&self, fut: F) -> JoinHandle<F::Output> {
177 let shared = self.shared();
178 let tracker = shared.queue.tracker();
179 let queue = unsafe { shared.queue.get_unchecked() };
181 let task = queue.insert(self.ptr, tracker, fut);
182
183 JoinHandle::new(task)
184 }
185
186 pub fn tick(&self) -> bool {
196 let queue = self.queue();
197
198 self.shared().drain_sync(queue);
199
200 for id in queue.iter_hot().take(self.config.max_interval as _) {
201 queue.make_cold(id);
202 let task = queue.take(id).expect("Task was not reset back");
203 let res = unsafe { task.run() };
204 if res.is_ready() {
205 unsafe { task.drop() };
209 queue.remove(id);
210 } else {
211 queue.reset(id, task);
212 }
213 }
214
215 queue.has_hot()
216 }
217
218 #[doc(hidden)]
220 pub fn has_task(&self) -> bool {
221 self.queue().hot_head().is_some()
222 }
223
224 pub fn clear(&self) {
231 instrument!(compio_log::Level::TRACE, "Executor::drop");
232 trace!("Dropping Executor");
233
234 while self.shared().sync.pop().is_some() {}
235 unsafe { self.queue().clear() };
236 }
237
238 #[inline(always)]
239 fn shared(&self) -> &Shared {
240 unsafe { self.ptr.as_ref() }
241 }
242
243 #[inline(always)]
244 fn queue(&self) -> &TaskQueue {
245 unsafe { self.shared().queue.get_unchecked() }
247 }
248}
249
250impl Drop for Executor {
251 fn drop(&mut self) {
252 self.clear();
253 unsafe { drop(Box::from_raw(self.ptr.as_ptr())) };
254 }
255}
256
257impl Default for Executor {
258 fn default() -> Self {
259 Self::new()
260 }
261}