1use core::fmt;
4
5#[allow(clippy::module_name_repetitions)]
7#[must_use]
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum TaskError {
10 Cancelled,
12 Panicked,
14 Timeout,
16 ResourceExhausted,
18 InvalidOperation,
20 ExecutionFailed(TaskErrorKind),
22 ExecutionTimeout,
24 ResultNotFound,
26 SpawnFailed,
28 InvalidState,
30 AlreadyCompleted,
32}
33
34#[must_use]
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub enum TaskErrorKind {
38 Io,
40 Network,
42 FileSystem,
44 PermissionDenied,
46 NotFound,
48 WouldBlock,
50 Interrupted,
52 InvalidInput,
54 Other,
56}
57
58impl fmt::Display for TaskError {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 match self {
61 Self::Cancelled => write!(f, "Task was cancelled"),
62 Self::Panicked => write!(f, "Task panicked during execution"),
63 Self::ExecutionTimeout => write!(f, "Task execution timed out"),
64 Self::ResultNotFound => write!(f, "Task result not found"),
65 Self::SpawnFailed => write!(f, "Task failed to spawn"),
66 Self::Timeout => write!(f, "Task exceeded execution time limit"),
67 Self::ResourceExhausted => write!(f, "Task failed due to resource exhaustion"),
68 Self::InvalidOperation => write!(f, "Invalid operation"),
69 Self::ExecutionFailed(kind) => write!(f, "Task execution failed: {kind}"),
70 Self::InvalidState => write!(f, "Task is not in a valid state for the operation"),
71 Self::AlreadyCompleted => write!(f, "Task has already completed"),
72 }
73 }
74}
75
76impl fmt::Display for TaskErrorKind {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 match self {
79 Self::Io => write!(f, "I/O error"),
80 Self::Network => write!(f, "Network error"),
81 Self::FileSystem => write!(f, "File system error"),
82 Self::PermissionDenied => write!(f, "Permission denied"),
83 Self::NotFound => write!(f, "Resource not found"),
84 Self::WouldBlock => write!(f, "Operation would block"),
85 Self::Interrupted => write!(f, "Operation interrupted"),
86 Self::InvalidInput => write!(f, "Invalid input"),
87 Self::Other => write!(f, "Other error"),
88 }
89 }
90}
91
92#[allow(clippy::module_name_repetitions)]
94#[must_use]
95#[derive(Debug, Clone, PartialEq, Eq)]
96pub enum ExecutorError {
97 ShuttingDown,
99 AlreadyRunning,
101 InvalidConfiguration,
103 InvalidLocalQueueInitialCapacity {
105 requested: usize,
107 },
108 ThreadPoolCreationFailed,
110 SpawnFailed(TaskError),
112 ResourceExhausted(String),
114 PerformanceAnomaly(String),
116 NoSchedulerAvailable,
118 SchedulerError(SchedulerError),
120 WorkerPlacementFailed {
127 worker: usize,
129 processor: u32,
131 cause: PlacementFailure,
133 },
134}
135
136#[must_use]
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141#[non_exhaustive]
142pub enum PlacementFailure {
143 Unsupported,
145 OutOfRange,
147 Os {
150 code: i32,
153 },
154}
155
156impl fmt::Display for ExecutorError {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 Self::ShuttingDown => write!(f, "Executor is shutting down"),
160 Self::AlreadyRunning => write!(f, "Executor is already running"),
161 Self::InvalidConfiguration => write!(f, "Invalid executor configuration"),
162 Self::InvalidLocalQueueInitialCapacity { requested } => write!(
163 f,
164 "local queue initial capacity {requested} cannot form a supported allocation"
165 ),
166 Self::ThreadPoolCreationFailed => write!(f, "Failed to create thread pool"),
167 Self::SpawnFailed(err) => write!(f, "Failed to spawn task: {err}"),
168 Self::ResourceExhausted(msg) => write!(f, "Resource exhausted: {msg}"),
169 Self::PerformanceAnomaly(msg) => write!(f, "Performance anomaly: {msg}"),
170 Self::NoSchedulerAvailable => write!(f, "No scheduler available"),
171 Self::SchedulerError(err) => write!(f, "Scheduler error: {err}"),
172 Self::WorkerPlacementFailed {
173 worker, processor, ..
174 } => write!(
175 f,
176 "worker {worker} could not be pinned to logical processor {processor}"
177 ),
178 }
179 }
180}
181
182impl fmt::Display for PlacementFailure {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 match self {
185 Self::Unsupported => write!(f, "thread binding is unsupported on this target"),
186 Self::OutOfRange => {
187 write!(f, "the processor is outside the range this target can bind")
188 }
189 Self::Os { code } => write!(
190 f,
191 "the operating system refused the binding (error code {code})"
192 ),
193 }
194 }
195}
196
197#[allow(clippy::module_name_repetitions)]
199#[must_use]
200#[derive(Debug, Clone, PartialEq, Eq)]
201pub enum SchedulerError {
202 QueueFull,
204 QueueEmpty,
206 StealFailed,
208 InvalidState,
210 SystemFailure(String),
212 InvalidScheduler,
214}
215
216impl fmt::Display for SchedulerError {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 match self {
219 Self::QueueFull => write!(f, "Task queue is full"),
220 Self::QueueEmpty => write!(f, "Task queue is empty"),
221 Self::StealFailed => write!(f, "Work stealing failed"),
222 Self::InvalidState => write!(f, "Invalid scheduler state"),
223 Self::SystemFailure(msg) => write!(f, "System failure: {msg}"),
224 Self::InvalidScheduler => write!(f, "Invalid scheduler reference"),
225 }
226 }
227}
228
229pub type TaskResult<T> = Result<T, TaskError>;
231
232pub type ExecutorResult<T> = Result<T, ExecutorError>;
234
235pub type SchedulerResult<T> = Result<T, SchedulerError>;
237
238#[cfg(feature = "std")]
239impl std::error::Error for TaskError {}
240
241#[cfg(feature = "std")]
242impl std::error::Error for ExecutorError {
243 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
244 match self {
245 Self::WorkerPlacementFailed { cause, .. } => Some(cause),
246 _ => None,
247 }
248 }
249}
250
251#[cfg(feature = "std")]
252impl std::error::Error for PlacementFailure {}
253
254#[cfg(feature = "std")]
255impl std::error::Error for SchedulerError {}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 #[test]
262 fn test_error_display() {
263 assert_eq!(format!("{}", TaskError::Cancelled), "Task was cancelled");
264 assert_eq!(
265 format!("{}", TaskError::ExecutionFailed(TaskErrorKind::Io)),
266 "Task execution failed: I/O error"
267 );
268 assert_eq!(
269 format!("{}", ExecutorError::ShuttingDown),
270 "Executor is shutting down"
271 );
272 assert_eq!(
273 format!("{}", SchedulerError::QueueFull),
274 "Task queue is full"
275 );
276 }
277
278 #[cfg(feature = "std")]
279 #[test]
280 fn placement_failure_names_worker_processor_and_keeps_cause_as_source() {
281 let error = ExecutorError::WorkerPlacementFailed {
282 worker: 3,
283 processor: 17,
284 cause: PlacementFailure::Os { code: 87 },
285 };
286 assert_eq!(
287 format!("{error}"),
288 "worker 3 could not be pinned to logical processor 17"
289 );
290 let source = std::error::Error::source(&error).expect("invariant: cause is the source");
291 assert_eq!(
292 format!("{source}"),
293 "the operating system refused the binding (error code 87)"
294 );
295 assert!(std::error::Error::source(&ExecutorError::ShuttingDown).is_none());
296 assert_eq!(
297 format!("{}", PlacementFailure::Unsupported),
298 "thread binding is unsupported on this target"
299 );
300 assert_eq!(
301 format!("{}", PlacementFailure::OutOfRange),
302 "the processor is outside the range this target can bind"
303 );
304 }
305}