├── .gitignore ├── Cargo.toml ├── README.md ├── examples ├── Cargo.toml ├── hello_coro.rs ├── hello_world.rs ├── run_loop.rs └── single_thread.rs ├── exec-core ├── Cargo.toml └── src │ ├── lib.rs │ ├── operation_state.rs │ ├── receiver.rs │ ├── scheduler.rs │ └── sender.rs ├── exec-executor ├── Cargo.toml └── src │ ├── lib.rs │ ├── macros │ ├── addr_of.rs │ └── mod.rs │ ├── run_loop.rs │ ├── single_thread_context.rs │ └── utils │ ├── linked_list.rs │ └── mod.rs ├── exec-test ├── Cargo.toml └── src │ ├── errors.rs │ ├── lib.rs │ └── receivers.rs └── exec ├── Cargo.toml └── src ├── adaptors ├── mod.rs └── then.rs ├── consumers ├── into_awaitable.rs ├── mod.rs ├── start_detached.rs ├── submit.rs └── sync_wait.rs ├── factories ├── just.rs ├── just_error.rs └── mod.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /target 3 | /Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "exec", 5 | "exec-core", 6 | "exec-executor", 7 | "exec-test", 8 | "examples", 9 | ] 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Exec 2 | 3 | exec-rs is a rust implementation of the Senders model of asynchronous programming proposed by P2300 - std::execution. 4 | 5 | ## Reference 6 | [std::execution](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2300r7.html) 7 | 8 | [Senders - A Standard Model for Asynchronous Execution in C++](https://github.com/NVIDIA/stdexec) 9 | 10 | [txrx](https://github.com/AndWass/txrx) 11 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dev-dependencies] 7 | exec = { path = "../exec" } 8 | exec-core = { path = "../exec-core" } 9 | exec-executor = { path = "../exec-executor" } 10 | futures = "0.3" 11 | 12 | [[example]] 13 | name = "hello_world" 14 | path = "hello_world.rs" 15 | 16 | [[example]] 17 | name = "hello_coro" 18 | path = "hello_coro.rs" 19 | 20 | [[example]] 21 | name = "run_loop" 22 | path = "run_loop.rs" 23 | 24 | [[example]] 25 | name = "single_thread" 26 | path = "single_thread.rs" 27 | -------------------------------------------------------------------------------- /examples/hello_coro.rs: -------------------------------------------------------------------------------- 1 | use exec::just; 2 | use futures::executor::block_on; 3 | 4 | fn main() { 5 | block_on(async { 6 | let sender = just(13); 7 | println!("result = {}", sender.await.unwrap().unwrap()); 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /examples/hello_world.rs: -------------------------------------------------------------------------------- 1 | use exec::{just, sync_wait, then}; 2 | 3 | fn main() { 4 | let sender = just(13); 5 | let sender = then(sender, |x| { println!("Hello, world"); x + 1}); 6 | let result = sync_wait(sender).unwrap(); 7 | println!("result = {}", result.unwrap()); 8 | } 9 | -------------------------------------------------------------------------------- /examples/run_loop.rs: -------------------------------------------------------------------------------- 1 | use exec::{start_detached, then}; 2 | use exec_core::Scheduler; 3 | use exec_executor::RunLoop; 4 | 5 | fn main() { 6 | let run_loop = RunLoop::new(); 7 | let mut scheduler = run_loop.get_scheduler(); 8 | 9 | // Spawn tasks in run loop 10 | for i in 0..5 { 11 | start_detached(then(scheduler.schedule(), move |_| { 12 | println!("Run in loop with value: {}", i); 13 | })); 14 | } 15 | 16 | // Drain the works in run loop 17 | run_loop.finish(); 18 | run_loop.run(); 19 | } 20 | -------------------------------------------------------------------------------- /examples/single_thread.rs: -------------------------------------------------------------------------------- 1 | use exec::{start_detached, then}; 2 | use exec_core::Scheduler; 3 | use exec_executor::SingleThreadContext; 4 | use std::thread; 5 | 6 | fn main() { 7 | let context = SingleThreadContext::new(); 8 | let mut scheduler = context.get_scheduler(); 9 | 10 | println!("Main run in thread: {:?}", thread::current().id()); 11 | 12 | for i in 0..5 { 13 | start_detached(then(scheduler.schedule(), move |_| { 14 | println!( 15 | "Run in thread {:?} loop with value: {}", 16 | thread::current().id(), 17 | i, 18 | ); 19 | })); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /exec-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exec-core" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /exec-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod operation_state; 2 | pub use operation_state::OperationState; 3 | 4 | pub mod receiver; 5 | 6 | mod sender; 7 | pub use sender::Sender; 8 | 9 | mod scheduler; 10 | pub use scheduler::Scheduler; 11 | -------------------------------------------------------------------------------- /exec-core/src/operation_state.rs: -------------------------------------------------------------------------------- 1 | pub trait OperationState { 2 | fn start(&mut self); 3 | } 4 | -------------------------------------------------------------------------------- /exec-core/src/receiver.rs: -------------------------------------------------------------------------------- 1 | pub trait SetValue { 2 | type Value; 3 | 4 | fn set_value(self, value: Self::Value); 5 | } 6 | 7 | pub trait SetError { 8 | type Error: std::error::Error; 9 | 10 | fn set_error(self, error: Self::Error); 11 | } 12 | 13 | pub trait SetStopped { 14 | fn set_stopped(self); 15 | } 16 | -------------------------------------------------------------------------------- /exec-core/src/scheduler.rs: -------------------------------------------------------------------------------- 1 | use crate::Sender; 2 | 3 | pub trait Scheduler: Send + Clone { 4 | type Sender: Sender; 5 | 6 | fn schedule(&mut self) -> Self::Sender; 7 | } 8 | -------------------------------------------------------------------------------- /exec-core/src/sender.rs: -------------------------------------------------------------------------------- 1 | use super::operation_state::OperationState; 2 | 3 | pub trait Sender { 4 | type Value; 5 | type Error; 6 | 7 | type Operation: OperationState; 8 | 9 | fn connect(self, receiver: R) -> Self::Operation; 10 | } 11 | -------------------------------------------------------------------------------- /exec-executor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exec-executor" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | exec-core = { path="../exec-core" } 8 | 9 | [dev-dependencies] 10 | exec-test = { path="../exec-test" } 11 | -------------------------------------------------------------------------------- /exec-executor/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod macros; 3 | 4 | mod run_loop; 5 | pub use run_loop::RunLoop; 6 | 7 | mod single_thread_context; 8 | pub use single_thread_context::SingleThreadContext; 9 | 10 | mod utils; 11 | -------------------------------------------------------------------------------- /exec-executor/src/macros/addr_of.rs: -------------------------------------------------------------------------------- 1 | //! This module defines a macro that lets you go from a raw pointer to a struct 2 | //! to a raw pointer to a field of the struct. 3 | 4 | #[cfg(not(tokio_no_addr_of))] 5 | macro_rules! generate_addr_of_methods { 6 | ( 7 | impl<$($gen:ident)*> $struct_name:ty {$( 8 | $(#[$attrs:meta])* 9 | $vis:vis unsafe fn $fn_name:ident(self: NonNull) -> NonNull<$field_type:ty> { 10 | &self$(.$field_name:tt)+ 11 | } 12 | )*} 13 | ) => { 14 | impl<$($gen)*> $struct_name {$( 15 | $(#[$attrs])* 16 | $vis unsafe fn $fn_name(me: ::core::ptr::NonNull) -> ::core::ptr::NonNull<$field_type> { 17 | let me = me.as_ptr(); 18 | let field = ::std::ptr::addr_of_mut!((*me) $(.$field_name)+ ); 19 | ::core::ptr::NonNull::new_unchecked(field) 20 | } 21 | )*} 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /exec-executor/src/macros/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod addr_of; 3 | -------------------------------------------------------------------------------- /exec-executor/src/run_loop.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::linked_list::{self, LinkedList}; 2 | use exec_core::receiver::SetValue; 3 | use exec_core::{OperationState, Scheduler, Sender}; 4 | use std::marker::{PhantomData, PhantomPinned}; 5 | use std::ptr::NonNull; 6 | use std::sync::{Condvar, Mutex}; 7 | 8 | type TaskQueue = LinkedList::Target>; 9 | 10 | struct Task { 11 | pointers: linked_list::Pointers, 12 | execute: fn(*mut Task), 13 | _p: PhantomPinned, 14 | } 15 | 16 | generate_addr_of_methods! { 17 | impl<> Task<> { 18 | unsafe fn addr_of_pointers(self: NonNull) -> NonNull> { 19 | &self.pointers 20 | } 21 | } 22 | } 23 | 24 | #[repr(C)] 25 | pub struct Operation { 26 | base: Task, 27 | receiver: Option, 28 | run_loop: NonNull, 29 | } 30 | 31 | impl Operation 32 | where 33 | R: SetValue, 34 | { 35 | fn execute(task: *mut Task) { 36 | let operation = unsafe { &mut *(task as *mut Operation) }; 37 | if let Some(receiver) = operation.receiver.take() { 38 | receiver.set_value(()); 39 | } 40 | } 41 | } 42 | 43 | impl OperationState for Operation { 44 | fn start(&mut self) { 45 | unsafe { 46 | self.run_loop.as_mut().push_front(NonNull::from(&self.base)); 47 | } 48 | } 49 | } 50 | 51 | pub struct RunLoop { 52 | inner: Mutex, 53 | cv: Condvar, 54 | } 55 | 56 | struct Inner { 57 | queue: TaskQueue, 58 | stop: bool, 59 | } 60 | 61 | impl RunLoop { 62 | pub fn new() -> Self { 63 | Self { 64 | inner: Mutex::new(Inner { 65 | queue: TaskQueue::new(), 66 | stop: false, 67 | }), 68 | cv: Condvar::new(), 69 | } 70 | } 71 | 72 | fn push_front(&self, task: ::Handle) { 73 | let mut inner = self.inner.lock().unwrap(); 74 | inner.queue.push_front(task); 75 | self.cv.notify_one(); 76 | } 77 | 78 | fn pop_back(&self) -> Option<::Handle> { 79 | let mut inner = self.inner.lock().unwrap(); 80 | loop { 81 | let item = inner.queue.pop_back(); 82 | if inner.stop || item.is_some() { 83 | break item; 84 | } else { 85 | inner = self.cv.wait(inner).unwrap(); 86 | } 87 | } 88 | } 89 | 90 | pub fn finish(&self) { 91 | let mut inner = self.inner.lock().unwrap(); 92 | inner.stop = true; 93 | self.cv.notify_all(); 94 | } 95 | 96 | pub fn run(&self) { 97 | while let Some(mut task) = self.pop_back() { 98 | unsafe { 99 | (task.as_mut().execute)(task.as_ptr()); 100 | } 101 | } 102 | } 103 | 104 | pub fn get_scheduler(&self) -> RunLoopScheduler { 105 | RunLoopScheduler { 106 | run_loop: NonNull::from(self), 107 | } 108 | } 109 | } 110 | 111 | #[derive(Copy, Clone)] 112 | pub struct RunLoopScheduler { 113 | run_loop: NonNull, 114 | } 115 | 116 | impl Scheduler for RunLoopScheduler 117 | where 118 | R: SetValue, 119 | { 120 | type Sender = ScheduleTask; 121 | 122 | fn schedule(&mut self) -> Self::Sender { 123 | ScheduleTask { 124 | run_loop: self.run_loop, 125 | _marker: PhantomData, 126 | } 127 | } 128 | } 129 | 130 | unsafe impl Send for RunLoopScheduler {} 131 | 132 | /// Sender to schedule task in run loop. 133 | pub struct ScheduleTask { 134 | run_loop: NonNull, 135 | _marker: PhantomData, 136 | } 137 | 138 | impl Sender for ScheduleTask 139 | where 140 | R: SetValue, 141 | { 142 | type Value = R::Value; 143 | type Error = (); 144 | 145 | type Operation = Operation; 146 | 147 | fn connect(self, receiver: R) -> Self::Operation { 148 | Operation { 149 | base: Task { 150 | pointers: linked_list::Pointers::new(), 151 | execute: Operation::::execute, 152 | _p: PhantomPinned, 153 | }, 154 | receiver: Some(receiver), 155 | run_loop: self.run_loop, 156 | } 157 | } 158 | } 159 | 160 | unsafe impl linked_list::Link for Task { 161 | type Handle = NonNull; 162 | type Target = Task; 163 | 164 | fn as_raw(handle: &NonNull) -> NonNull { 165 | *handle 166 | } 167 | 168 | unsafe fn from_raw(ptr: NonNull) -> NonNull { 169 | ptr 170 | } 171 | 172 | unsafe fn pointers(target: NonNull) -> NonNull> { 173 | Task::addr_of_pointers(target) 174 | } 175 | } 176 | 177 | #[cfg(test)] 178 | mod tests { 179 | use super::*; 180 | use exec_test::receivers::ExpectValueReceiver; 181 | 182 | #[test] 183 | fn test_run_loop() { 184 | let run_loop = RunLoop::new(); 185 | let mut scheduler = run_loop.get_scheduler(); 186 | let sender = scheduler.schedule(); 187 | let mut op = sender.connect(ExpectValueReceiver::new(())); 188 | // Schedule the work on run loop 189 | op.start(); 190 | run_loop.finish(); 191 | // Drain the works in run loop 192 | run_loop.run(); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /exec-executor/src/single_thread_context.rs: -------------------------------------------------------------------------------- 1 | use crate::run_loop::RunLoopScheduler; 2 | use crate::RunLoop; 3 | use std::sync::Arc; 4 | use std::thread; 5 | 6 | pub struct SingleThreadContext { 7 | thread: Option>, 8 | run_loop: Arc, 9 | } 10 | 11 | impl SingleThreadContext { 12 | pub fn new() -> Self { 13 | let run_loop = Arc::new(RunLoop::new()); 14 | let thread; 15 | { 16 | let run_loop = run_loop.clone(); 17 | thread = thread::spawn(move || { 18 | run_loop.clone().run(); 19 | }); 20 | } 21 | Self { 22 | thread: Some(thread), 23 | run_loop, 24 | } 25 | } 26 | 27 | pub fn get_scheduler(&self) -> RunLoopScheduler { 28 | self.run_loop.get_scheduler() 29 | } 30 | } 31 | 32 | impl Default for SingleThreadContext { 33 | fn default() -> Self { 34 | Self::new() 35 | } 36 | } 37 | 38 | impl Drop for SingleThreadContext { 39 | fn drop(&mut self) { 40 | self.run_loop.finish(); 41 | self.thread.take().unwrap().join().unwrap(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /exec-executor/src/utils/linked_list.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "full"), allow(dead_code))] 2 | //! An intrusive double linked list of data. 3 | //! 4 | //! The data structure supports tracking pinned nodes. Most of the data 5 | //! structure's APIs are `unsafe` as they require the caller to ensure the 6 | //! specified node is actually contained by the list. 7 | 8 | use core::cell::UnsafeCell; 9 | use core::fmt; 10 | use core::marker::{PhantomData, PhantomPinned}; 11 | use core::mem::ManuallyDrop; 12 | use core::ptr::{self, NonNull}; 13 | 14 | /// An intrusive linked list. 15 | /// 16 | /// Currently, the list is not emptied on drop. It is the caller's 17 | /// responsibility to ensure the list is empty before dropping it. 18 | pub(crate) struct LinkedList { 19 | /// Linked list head 20 | head: Option>, 21 | 22 | /// Linked list tail 23 | tail: Option>, 24 | 25 | /// Node type marker. 26 | _marker: PhantomData<*const L>, 27 | } 28 | 29 | unsafe impl Send for LinkedList where L::Target: Send {} 30 | unsafe impl Sync for LinkedList where L::Target: Sync {} 31 | 32 | /// Defines how a type is tracked within a linked list. 33 | /// 34 | /// In order to support storing a single type within multiple lists, accessing 35 | /// the list pointers is decoupled from the entry type. 36 | /// 37 | /// # Safety 38 | /// 39 | /// Implementations must guarantee that `Target` types are pinned in memory. In 40 | /// other words, when a node is inserted, the value will not be moved as long as 41 | /// it is stored in the list. 42 | pub(crate) unsafe trait Link { 43 | /// Handle to the list entry. 44 | /// 45 | /// This is usually a pointer-ish type. 46 | type Handle; 47 | 48 | /// Node type. 49 | type Target; 50 | 51 | /// Convert the handle to a raw pointer without consuming the handle. 52 | #[allow(clippy::wrong_self_convention)] 53 | fn as_raw(handle: &Self::Handle) -> NonNull; 54 | 55 | /// Convert the raw pointer to a handle 56 | unsafe fn from_raw(ptr: NonNull) -> Self::Handle; 57 | 58 | /// Return the pointers for a node 59 | /// 60 | /// # Safety 61 | /// 62 | /// The resulting pointer should have the same tag in the stacked-borrows 63 | /// stack as the argument. In particular, the method may not create an 64 | /// intermediate reference in the process of creating the resulting raw 65 | /// pointer. 66 | unsafe fn pointers(target: NonNull) -> NonNull>; 67 | } 68 | 69 | /// Previous / next pointers. 70 | pub(crate) struct Pointers { 71 | inner: UnsafeCell>, 72 | } 73 | /// We do not want the compiler to put the `noalias` attribute on mutable 74 | /// references to this type, so the type has been made `!Unpin` with a 75 | /// `PhantomPinned` field. 76 | /// 77 | /// Additionally, we never access the `prev` or `next` fields directly, as any 78 | /// such access would implicitly involve the creation of a reference to the 79 | /// field, which we want to avoid since the fields are not `!Unpin`, and would 80 | /// hence be given the `noalias` attribute if we were to do such an access. 81 | /// As an alternative to accessing the fields directly, the `Pointers` type 82 | /// provides getters and setters for the two fields, and those are implemented 83 | /// using raw pointer casts and offsets, which is valid since the struct is 84 | /// #[repr(C)]. 85 | /// 86 | /// See this link for more information: 87 | /// 88 | #[repr(C)] 89 | struct PointersInner { 90 | /// The previous node in the list. null if there is no previous node. 91 | /// 92 | /// This field is accessed through pointer manipulation, so it is not dead code. 93 | #[allow(dead_code)] 94 | prev: Option>, 95 | 96 | /// The next node in the list. null if there is no previous node. 97 | /// 98 | /// This field is accessed through pointer manipulation, so it is not dead code. 99 | #[allow(dead_code)] 100 | next: Option>, 101 | 102 | /// This type is !Unpin due to the heuristic from: 103 | /// 104 | _pin: PhantomPinned, 105 | } 106 | 107 | unsafe impl Send for Pointers {} 108 | unsafe impl Sync for Pointers {} 109 | 110 | // ===== impl LinkedList ===== 111 | 112 | impl LinkedList { 113 | /// Creates an empty linked list. 114 | pub(crate) const fn new() -> LinkedList { 115 | LinkedList { 116 | head: None, 117 | tail: None, 118 | _marker: PhantomData, 119 | } 120 | } 121 | } 122 | 123 | impl LinkedList { 124 | /// Adds an element first in the list. 125 | pub(crate) fn push_front(&mut self, val: L::Handle) { 126 | // The value should not be dropped, it is being inserted into the list 127 | let val = ManuallyDrop::new(val); 128 | let ptr = L::as_raw(&val); 129 | assert_ne!(self.head, Some(ptr)); 130 | unsafe { 131 | L::pointers(ptr).as_mut().set_next(self.head); 132 | L::pointers(ptr).as_mut().set_prev(None); 133 | 134 | if let Some(head) = self.head { 135 | L::pointers(head).as_mut().set_prev(Some(ptr)); 136 | } 137 | 138 | self.head = Some(ptr); 139 | 140 | if self.tail.is_none() { 141 | self.tail = Some(ptr); 142 | } 143 | } 144 | } 145 | 146 | /// Removes the last element from a list and returns it, or None if it is 147 | /// empty. 148 | pub(crate) fn pop_back(&mut self) -> Option { 149 | unsafe { 150 | let last = self.tail?; 151 | self.tail = L::pointers(last).as_ref().get_prev(); 152 | 153 | if let Some(prev) = L::pointers(last).as_ref().get_prev() { 154 | L::pointers(prev).as_mut().set_next(None); 155 | } else { 156 | self.head = None 157 | } 158 | 159 | L::pointers(last).as_mut().set_prev(None); 160 | L::pointers(last).as_mut().set_next(None); 161 | 162 | Some(L::from_raw(last)) 163 | } 164 | } 165 | 166 | /// Returns whether the linked list does not contain any node 167 | pub(crate) fn is_empty(&self) -> bool { 168 | if self.head.is_some() { 169 | return false; 170 | } 171 | 172 | assert!(self.tail.is_none()); 173 | true 174 | } 175 | 176 | /// Removes the specified node from the list 177 | /// 178 | /// # Safety 179 | /// 180 | /// The caller **must** ensure that exactly one of the following is true: 181 | /// - `node` is currently contained by `self`, 182 | /// - `node` is not contained by any list, 183 | pub(crate) unsafe fn remove(&mut self, node: NonNull) -> Option { 184 | if let Some(prev) = L::pointers(node).as_ref().get_prev() { 185 | debug_assert_eq!(L::pointers(prev).as_ref().get_next(), Some(node)); 186 | L::pointers(prev) 187 | .as_mut() 188 | .set_next(L::pointers(node).as_ref().get_next()); 189 | } else { 190 | if self.head != Some(node) { 191 | return None; 192 | } 193 | 194 | self.head = L::pointers(node).as_ref().get_next(); 195 | } 196 | 197 | if let Some(next) = L::pointers(node).as_ref().get_next() { 198 | debug_assert_eq!(L::pointers(next).as_ref().get_prev(), Some(node)); 199 | L::pointers(next) 200 | .as_mut() 201 | .set_prev(L::pointers(node).as_ref().get_prev()); 202 | } else { 203 | // This might be the last item in the list 204 | if self.tail != Some(node) { 205 | return None; 206 | } 207 | 208 | self.tail = L::pointers(node).as_ref().get_prev(); 209 | } 210 | 211 | L::pointers(node).as_mut().set_next(None); 212 | L::pointers(node).as_mut().set_prev(None); 213 | 214 | Some(L::from_raw(node)) 215 | } 216 | } 217 | 218 | impl fmt::Debug for LinkedList { 219 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 220 | f.debug_struct("LinkedList") 221 | .field("head", &self.head) 222 | .field("tail", &self.tail) 223 | .finish() 224 | } 225 | } 226 | 227 | // ===== impl Pointers ===== 228 | 229 | impl Pointers { 230 | /// Create a new set of empty pointers 231 | pub(crate) fn new() -> Pointers { 232 | Pointers { 233 | inner: UnsafeCell::new(PointersInner { 234 | prev: None, 235 | next: None, 236 | _pin: PhantomPinned, 237 | }), 238 | } 239 | } 240 | 241 | pub(crate) fn get_prev(&self) -> Option> { 242 | // SAFETY: prev is the first field in PointersInner, which is #[repr(C)]. 243 | unsafe { 244 | let inner = self.inner.get(); 245 | let prev = inner as *const Option>; 246 | ptr::read(prev) 247 | } 248 | } 249 | pub(crate) fn get_next(&self) -> Option> { 250 | // SAFETY: next is the second field in PointersInner, which is #[repr(C)]. 251 | unsafe { 252 | let inner = self.inner.get(); 253 | let prev = inner as *const Option>; 254 | let next = prev.add(1); 255 | ptr::read(next) 256 | } 257 | } 258 | 259 | fn set_prev(&mut self, value: Option>) { 260 | // SAFETY: prev is the first field in PointersInner, which is #[repr(C)]. 261 | unsafe { 262 | let inner = self.inner.get(); 263 | let prev = inner as *mut Option>; 264 | ptr::write(prev, value); 265 | } 266 | } 267 | fn set_next(&mut self, value: Option>) { 268 | // SAFETY: next is the second field in PointersInner, which is #[repr(C)]. 269 | unsafe { 270 | let inner = self.inner.get(); 271 | let prev = inner as *mut Option>; 272 | let next = prev.add(1); 273 | ptr::write(next, value); 274 | } 275 | } 276 | } 277 | 278 | impl fmt::Debug for Pointers { 279 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 280 | let prev = self.get_prev(); 281 | let next = self.get_next(); 282 | f.debug_struct("Pointers") 283 | .field("prev", &prev) 284 | .field("next", &next) 285 | .finish() 286 | } 287 | } 288 | 289 | #[cfg(test)] 290 | mod tests { 291 | use super::*; 292 | 293 | struct Entry { 294 | pointers: Pointers, 295 | val: i32, 296 | _p: PhantomPinned, 297 | } 298 | 299 | impl Entry { 300 | fn new(val: i32) -> Entry { 301 | Entry { 302 | pointers: Pointers::new(), 303 | val, 304 | _p: PhantomPinned, 305 | } 306 | } 307 | } 308 | 309 | generate_addr_of_methods! { 310 | impl<> Entry { 311 | unsafe fn addr_of_pointers(self: NonNull) -> NonNull> { 312 | &self.pointers 313 | } 314 | } 315 | } 316 | 317 | unsafe impl Link for Entry { 318 | type Handle = NonNull; 319 | type Target = Entry; 320 | 321 | fn as_raw(handle: &NonNull) -> NonNull { 322 | *handle 323 | } 324 | 325 | unsafe fn from_raw(ptr: NonNull) -> NonNull { 326 | ptr 327 | } 328 | 329 | unsafe fn pointers(target: NonNull) -> NonNull> { 330 | Entry::addr_of_pointers(target) 331 | } 332 | } 333 | 334 | type EntryList = LinkedList::Target>; 335 | 336 | #[test] 337 | fn test_push_front() { 338 | let mut list = EntryList::new(); 339 | let a = Entry::new(1); 340 | let b = Entry::new(2); 341 | let c = Entry::new(3); 342 | 343 | list.push_front(NonNull::from(&a)); 344 | list.push_front(NonNull::from(&b)); 345 | list.push_front(NonNull::from(&c)); 346 | 347 | let e = unsafe { list.pop_back().unwrap().as_mut() }; 348 | assert_eq!(e.val, 1); 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /exec-executor/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod linked_list; 2 | -------------------------------------------------------------------------------- /exec-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exec-test" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "Common utilities for testing senders and receivers" 6 | 7 | [dependencies] 8 | exec-core = { path="../exec-core" } 9 | -------------------------------------------------------------------------------- /exec-test/src/errors.rs: -------------------------------------------------------------------------------- 1 | #[derive(PartialEq, Copy, Clone)] 2 | pub struct TestError; 3 | 4 | impl std::fmt::Debug for TestError { 5 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 6 | f.write_str("TestError") 7 | } 8 | } 9 | 10 | impl std::fmt::Display for TestError { 11 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 12 | f.write_str("TestError") 13 | } 14 | } 15 | 16 | impl std::error::Error for TestError {} 17 | -------------------------------------------------------------------------------- /exec-test/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod errors; 2 | pub mod receivers; 3 | -------------------------------------------------------------------------------- /exec-test/src/receivers.rs: -------------------------------------------------------------------------------- 1 | use exec_core::receiver::{SetError, SetValue}; 2 | use std::error::Error; 3 | use std::fmt::Debug; 4 | 5 | pub struct ExpectValueReceiver { 6 | expected: T, 7 | } 8 | 9 | impl ExpectValueReceiver { 10 | pub fn new(expected: T) -> Self { 11 | Self { expected } 12 | } 13 | } 14 | 15 | impl SetValue for ExpectValueReceiver 16 | where 17 | T: PartialEq + Debug, 18 | { 19 | type Value = T; 20 | 21 | fn set_value(self, value: Self::Value) { 22 | println!("Expected: {:?}, Actual: {:?}", self.expected, value); 23 | assert_eq!(self.expected, value); 24 | } 25 | } 26 | 27 | pub struct ExpectErrorReceiver { 28 | expected: E, 29 | } 30 | 31 | impl ExpectErrorReceiver { 32 | pub fn new(expected: E) -> Self { 33 | Self { expected } 34 | } 35 | } 36 | 37 | impl SetError for ExpectErrorReceiver { 38 | type Error = E; 39 | 40 | fn set_error(self, error: Self::Error) { 41 | println!("Expected: {:?}, Actual: {:?}", self.expected, error); 42 | assert_eq!(self.expected, error); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /exec/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exec" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | exec-core = { path="../exec-core" } 8 | exec-executor = { path="../exec-executor" } 9 | scopeguard = "1.1" 10 | 11 | [dev-dependencies] 12 | exec-test = { path="../exec-test" } 13 | futures = "0.3" 14 | -------------------------------------------------------------------------------- /exec/src/adaptors/mod.rs: -------------------------------------------------------------------------------- 1 | mod then; 2 | 3 | pub use then::{then, Then}; 4 | -------------------------------------------------------------------------------- /exec/src/adaptors/then.rs: -------------------------------------------------------------------------------- 1 | use exec_core::receiver::SetValue; 2 | use exec_core::{OperationState, Sender}; 3 | 4 | use std::marker::PhantomData; 5 | 6 | pub fn then(sender: S, func: F) -> Then { 7 | Then::new(sender, func) 8 | } 9 | 10 | pub struct Then { 11 | sender: S, 12 | func: F, 13 | _phantom: PhantomData, 14 | } 15 | 16 | impl Then { 17 | pub fn new(sender: S, func: F) -> Self { 18 | Self { 19 | sender, 20 | func, 21 | _phantom: PhantomData, 22 | } 23 | } 24 | } 25 | 26 | pub struct ThenReceiver { 27 | func: F, 28 | receiver: R, 29 | _phantom: PhantomData, 30 | } 31 | 32 | impl SetValue for ThenReceiver 33 | where 34 | F: FnOnce(I) -> O, 35 | R: SetValue, 36 | { 37 | type Value = I; 38 | 39 | fn set_value(self, value: Self::Value) { 40 | self.receiver.set_value((self.func)(value)); 41 | } 42 | } 43 | 44 | pub struct ThenOperation { 45 | operation: O, 46 | } 47 | 48 | impl OperationState for ThenOperation 49 | where 50 | O: OperationState, 51 | { 52 | fn start(&mut self) { 53 | self.operation.start() 54 | } 55 | } 56 | 57 | impl Sender for Then 58 | where 59 | S: Sender>, 60 | F: FnOnce(I) -> O + Send + 'static, 61 | R: SetValue, 62 | { 63 | type Value = R::Value; 64 | type Error = (); 65 | 66 | type Operation = ThenOperation; 67 | 68 | fn connect(self, receiver: R) -> Self::Operation { 69 | ThenOperation { 70 | operation: self.sender.connect(ThenReceiver { 71 | func: self.func, 72 | receiver, 73 | _phantom: PhantomData, 74 | }), 75 | } 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use super::*; 82 | use crate::factories::just; 83 | use exec_test::receivers::ExpectValueReceiver; 84 | 85 | #[test] 86 | fn test_then() { 87 | let just_sender = just(42); 88 | let then_sender = Then::new(just_sender, |x| x + 1); 89 | let then_sender = Then::new(then_sender, |x| x + 1); 90 | let then_sender = Then::new(then_sender, |x| x + 1); 91 | let mut operation = then_sender.connect(ExpectValueReceiver::new(45)); 92 | operation.start(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /exec/src/consumers/into_awaitable.rs: -------------------------------------------------------------------------------- 1 | use exec_core::receiver::{SetError, SetValue}; 2 | use exec_core::{OperationState, Sender}; 3 | use std::cell::UnsafeCell; 4 | use std::error::Error; 5 | use std::future::Future; 6 | use std::pin::Pin; 7 | use std::rc::Rc; 8 | use std::task::{Context, Poll, Waker}; 9 | 10 | #[allow(dead_code)] 11 | #[derive(Debug)] 12 | pub enum AwaitResult { 13 | Value(V), 14 | Error(E), 15 | Stopped, 16 | } 17 | 18 | struct SharedState { 19 | result: Option>, 20 | waker: Option, 21 | } 22 | 23 | pub struct Receiver { 24 | state: Rc>>, 25 | } 26 | 27 | impl SetValue for Receiver { 28 | type Value = V; 29 | 30 | fn set_value(self, value: Self::Value) { 31 | unsafe { 32 | let _ = (*self.state.get()).result.insert(AwaitResult::Value(value)); 33 | if let Some(waker) = (*self.state.get()).waker.take() { 34 | waker.wake(); 35 | } 36 | } 37 | } 38 | } 39 | 40 | impl SetError for Receiver { 41 | type Error = E; 42 | 43 | fn set_error(self, error: Self::Error) { 44 | unsafe { 45 | let _ = (*self.state.get()).result.insert(AwaitResult::Error(error)); 46 | if let Some(waker) = (*self.state.get()).waker.take() { 47 | waker.wake(); 48 | } 49 | } 50 | } 51 | } 52 | 53 | pub struct SenderAwaitable 54 | where 55 | S: Sender, Value = V, Error = E>, 56 | { 57 | state: Rc>>, 58 | operation: S::Operation, 59 | } 60 | 61 | impl SenderAwaitable 62 | where 63 | S: Sender, Value = V, Error = E>, 64 | { 65 | pub(crate) fn new(sender: S) -> Self { 66 | let state = Rc::new(UnsafeCell::new(SharedState { 67 | result: None, 68 | waker: None, 69 | })); 70 | 71 | let receiver = Receiver { 72 | state: state.clone(), 73 | }; 74 | 75 | Self { 76 | state, 77 | operation: sender.connect(receiver), 78 | } 79 | } 80 | } 81 | 82 | impl Future for SenderAwaitable 83 | where 84 | S: Sender, Value = V, Error = E>, 85 | { 86 | type Output = Result, E>; 87 | 88 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 89 | let me = unsafe { self.get_unchecked_mut() }; 90 | 91 | if let Some(result) = unsafe { (*me.state.get()).result.take() } { 92 | match result { 93 | AwaitResult::Value(value) => Poll::Ready(Ok(Some(value))), 94 | AwaitResult::Error(error) => Poll::Ready(Err(error)), 95 | AwaitResult::Stopped => Poll::Ready(Ok(None)), 96 | } 97 | } else { 98 | unsafe { 99 | let _ = (*me.state.get()).waker.insert(cx.waker().clone()); 100 | } 101 | me.operation.start(); 102 | Poll::Pending 103 | } 104 | } 105 | } 106 | 107 | #[cfg(test)] 108 | mod tests { 109 | use crate::consumers::SenderAwaitable; 110 | use crate::factories::just_error; 111 | use crate::{just, then}; 112 | use exec_test::errors::TestError; 113 | use futures::executor::block_on; 114 | 115 | #[test] 116 | fn test_awaitable() { 117 | block_on(async { 118 | let sender = just(1); 119 | let sender = then(sender, |x| x + 1); 120 | let awaitable = SenderAwaitable::new(sender); 121 | println!("{:?}", awaitable.await); 122 | }); 123 | } 124 | 125 | #[test] 126 | fn test_awaitable_error() { 127 | block_on(async { 128 | let sender = just_error(TestError); 129 | let awaitable = SenderAwaitable::new(sender); 130 | println!("{:?}", awaitable.await); 131 | }); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /exec/src/consumers/mod.rs: -------------------------------------------------------------------------------- 1 | mod sync_wait; 2 | pub use sync_wait::sync_wait; 3 | 4 | mod into_awaitable; 5 | pub mod start_detached; 6 | pub mod submit; 7 | 8 | pub use into_awaitable::SenderAwaitable; 9 | pub use start_detached::start_detached; 10 | pub use submit::submit; 11 | -------------------------------------------------------------------------------- /exec/src/consumers/start_detached.rs: -------------------------------------------------------------------------------- 1 | use crate::consumers::submit; 2 | use crate::consumers::submit::SubmitReceiver; 3 | use exec_core::receiver::{SetError, SetValue}; 4 | use exec_core::Sender; 5 | use std::error::Error; 6 | 7 | pub fn start_detached(sender: S) 8 | where 9 | S: Sender>, Value = V, Error = E>, 10 | { 11 | submit( 12 | sender, 13 | StartDetachedReceiver { 14 | _phantom: std::marker::PhantomData, 15 | }, 16 | ) 17 | } 18 | 19 | pub struct StartDetachedReceiver { 20 | _phantom: std::marker::PhantomData<(V, E)>, 21 | } 22 | 23 | impl SetValue for StartDetachedReceiver { 24 | type Value = V; 25 | 26 | fn set_value(self, _value: Self::Value) {} 27 | } 28 | 29 | impl SetError for StartDetachedReceiver { 30 | type Error = E; 31 | 32 | fn set_error(self, error: Self::Error) { 33 | panic!( 34 | "StartDetachedReceiver::set_error called with error: {:?}", 35 | error 36 | ); 37 | } 38 | } 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use super::*; 43 | use crate::factories::{just, just_error}; 44 | use exec_test::errors::TestError; 45 | 46 | #[test] 47 | fn test_start_detached() { 48 | let sender = just(1); 49 | start_detached(sender); 50 | } 51 | 52 | #[test] 53 | #[should_panic] 54 | fn test_start_detached_with_error() { 55 | let sender = just_error(TestError); 56 | start_detached(sender); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /exec/src/consumers/submit.rs: -------------------------------------------------------------------------------- 1 | use exec_core::receiver::{SetError, SetValue}; 2 | use exec_core::{OperationState, Sender}; 3 | use scopeguard::defer; 4 | use std::cell::UnsafeCell; 5 | use std::ptr::NonNull; 6 | 7 | pub fn submit(sender: S, receiver: R) 8 | where 9 | S: Sender>, 10 | { 11 | let op = Box::leak(SubmitOperation::new(sender, receiver)); 12 | op.op_state.as_mut().unwrap().start(); 13 | } 14 | 15 | pub struct SubmitReceiver { 16 | op_state: NonNull>, 17 | } 18 | 19 | impl SetValue for SubmitReceiver { 20 | type Value = R::Value; 21 | 22 | fn set_value(self, value: Self::Value) { 23 | unsafe { 24 | defer! { 25 | (self.op_state.as_ref().delete_fn)(self.op_state.as_ptr()); 26 | } 27 | (*self.op_state.as_ref().receiver.get()) 28 | .take() 29 | .unwrap() 30 | .set_value(value); 31 | } 32 | } 33 | } 34 | 35 | impl SetError for SubmitReceiver { 36 | type Error = R::Error; 37 | 38 | fn set_error(self, error: Self::Error) { 39 | unsafe { 40 | defer! { 41 | (self.op_state.as_ref().delete_fn)(self.op_state.as_ptr()); 42 | } 43 | (*self.op_state.as_ref().receiver.get()) 44 | .take() 45 | .unwrap() 46 | .set_error(error); 47 | } 48 | } 49 | } 50 | 51 | struct SubmitOperationBase { 52 | receiver: UnsafeCell>, 53 | delete_fn: unsafe fn(*mut SubmitOperationBase), 54 | } 55 | 56 | #[repr(C)] 57 | struct SubmitOperation>, R> { 58 | base: SubmitOperationBase, 59 | op_state: Option, 60 | } 61 | 62 | impl SubmitOperation 63 | where 64 | S: Sender>, 65 | { 66 | fn new(sender: S, receiver: R) -> Box { 67 | let mut op = Box::new(Self { 68 | base: SubmitOperationBase { 69 | receiver: UnsafeCell::new(Some(receiver)), 70 | delete_fn: |op| { 71 | unsafe { 72 | let _ = Box::from_raw(op as *mut Self); 73 | }; 74 | }, 75 | }, 76 | op_state: None, 77 | }); 78 | op.op_state = Some(sender.connect(SubmitReceiver { 79 | op_state: NonNull::from(&op.base), 80 | })); 81 | 82 | op 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod tests { 88 | use super::*; 89 | use crate::factories::just; 90 | use exec_test::receivers::ExpectValueReceiver; 91 | 92 | #[test] 93 | fn test_submit() { 94 | let just_sender = just(42); 95 | let receiver = ExpectValueReceiver::new(42); 96 | submit(just_sender, receiver); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /exec/src/consumers/sync_wait.rs: -------------------------------------------------------------------------------- 1 | use exec_core::receiver::{SetError, SetStopped, SetValue}; 2 | use exec_core::{OperationState, Sender}; 3 | use exec_executor::RunLoop; 4 | use std::cell::UnsafeCell; 5 | use std::error::Error; 6 | use std::ptr::NonNull; 7 | 8 | struct State { 9 | value: UnsafeCell>>, 10 | } 11 | 12 | impl State { 13 | fn new() -> Self { 14 | Self { 15 | value: UnsafeCell::new(None), 16 | } 17 | } 18 | } 19 | 20 | #[derive(Debug)] 21 | pub enum WaitResult { 22 | Value(V), 23 | Error(E), 24 | Stopped, 25 | } 26 | 27 | pub struct SyncWaitReceiver { 28 | state: NonNull>, 29 | run_loop: NonNull, 30 | } 31 | 32 | impl SyncWaitReceiver { 33 | fn new(state: &State, run_loop: &RunLoop) -> Self { 34 | Self { 35 | state: NonNull::from(state), 36 | run_loop: NonNull::from(run_loop), 37 | } 38 | } 39 | } 40 | 41 | impl SetValue for SyncWaitReceiver { 42 | type Value = V; 43 | 44 | fn set_value(self, value: Self::Value) { 45 | unsafe { 46 | let _ = (*self.state.as_ref().value.get()).insert(WaitResult::Value(value)); 47 | self.run_loop.as_ref().finish(); 48 | } 49 | } 50 | } 51 | 52 | impl SetError for SyncWaitReceiver { 53 | type Error = E; 54 | 55 | fn set_error(self, error: Self::Error) { 56 | unsafe { 57 | let _ = (*self.state.as_ref().value.get()).insert(WaitResult::Error(error)); 58 | self.run_loop.as_ref().finish(); 59 | } 60 | } 61 | } 62 | 63 | impl SetStopped for SyncWaitReceiver { 64 | fn set_stopped(self) { 65 | unsafe { 66 | let _ = (*self.state.as_ref().value.get()).insert(WaitResult::Stopped); 67 | self.run_loop.as_ref().finish(); 68 | } 69 | } 70 | } 71 | 72 | pub fn sync_wait(sender: S) -> Result, E> 73 | where 74 | S: Sender, Value = V, Error = E>, 75 | { 76 | let run_loop = RunLoop::new(); 77 | let mut state = State::new(); 78 | 79 | // Launch the sender with a continuation that will fill in a variant 80 | // and notify a condition variable. 81 | let mut op = sender.connect(SyncWaitReceiver::new(&state, &run_loop)); 82 | op.start(); 83 | 84 | // Wait for the variant to be filled in. 85 | run_loop.run(); 86 | 87 | match state.value.get_mut().take().unwrap() { 88 | WaitResult::Value(v) => Ok(Some(v)), 89 | WaitResult::Error(e) => Err(e), 90 | WaitResult::Stopped => Ok(None), 91 | } 92 | } 93 | 94 | #[cfg(test)] 95 | mod tests { 96 | use super::*; 97 | use crate::adaptors::then; 98 | use crate::factories::just; 99 | 100 | #[test] 101 | fn test_sync_wait() { 102 | let sender = just(42); 103 | let sender = then(sender, |v| v + 1); 104 | println!("{:?}", sync_wait(sender)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /exec/src/factories/just.rs: -------------------------------------------------------------------------------- 1 | use crate::consumers::SenderAwaitable; 2 | use exec_core::receiver::SetValue; 3 | use exec_core::{OperationState, Sender}; 4 | use std::future::IntoFuture; 5 | 6 | pub fn just(value: T) -> Just { 7 | Just::new(value) 8 | } 9 | 10 | pub struct Just { 11 | value: T, 12 | } 13 | 14 | impl Just { 15 | pub fn new(value: T) -> Self { 16 | Self { value } 17 | } 18 | } 19 | 20 | pub struct JustOperation { 21 | data: Option, 22 | receiver: Option, 23 | } 24 | 25 | impl OperationState for JustOperation 26 | where 27 | R: SetValue, 28 | { 29 | fn start(&mut self) { 30 | if let (Some(receiver), Some(data)) = (self.receiver.take(), self.data.take()) { 31 | receiver.set_value(data); 32 | } 33 | } 34 | } 35 | 36 | impl Sender for Just 37 | where 38 | R: SetValue, 39 | { 40 | type Value = R::Value; 41 | type Error = (); 42 | 43 | type Operation = JustOperation; 44 | 45 | fn connect(self, receiver: R) -> Self::Operation { 46 | JustOperation { 47 | data: Some(self.value), 48 | receiver: Some(receiver), 49 | } 50 | } 51 | } 52 | 53 | impl IntoFuture for Just { 54 | type Output = Result, ()>; 55 | type IntoFuture = SenderAwaitable; 56 | 57 | fn into_future(self) -> Self::IntoFuture { 58 | SenderAwaitable::new(self) 59 | } 60 | } 61 | 62 | #[cfg(test)] 63 | mod tests { 64 | use super::*; 65 | use exec_test::receivers::ExpectValueReceiver; 66 | 67 | #[test] 68 | fn test_just() { 69 | let sender = Just::new(42); 70 | let mut operation = sender.connect(ExpectValueReceiver::new(42)); 71 | operation.start(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /exec/src/factories/just_error.rs: -------------------------------------------------------------------------------- 1 | use exec_core::receiver::SetError; 2 | use exec_core::{OperationState, Sender}; 3 | use std::error::Error; 4 | 5 | pub fn just_error(error: E) -> JustError { 6 | JustError::new(error) 7 | } 8 | 9 | pub struct JustError { 10 | error: E, 11 | } 12 | 13 | impl JustError { 14 | pub fn new(error: E) -> Self { 15 | Self { error } 16 | } 17 | } 18 | 19 | pub struct JustOperation { 20 | error: Option, 21 | receiver: Option, 22 | } 23 | 24 | impl OperationState for JustOperation 25 | where 26 | R: SetError, 27 | { 28 | fn start(&mut self) { 29 | if let (Some(receiver), Some(error)) = (self.receiver.take(), self.error.take()) { 30 | receiver.set_error(error); 31 | } 32 | } 33 | } 34 | 35 | impl Sender for JustError 36 | where 37 | R: SetError, 38 | { 39 | type Value = (); 40 | type Error = R::Error; 41 | 42 | type Operation = JustOperation; 43 | 44 | fn connect(self, receiver: R) -> Self::Operation { 45 | JustOperation { 46 | error: Some(self.error), 47 | receiver: Some(receiver), 48 | } 49 | } 50 | } 51 | 52 | #[cfg(test)] 53 | mod tests { 54 | use super::*; 55 | use exec_test::errors::TestError; 56 | use exec_test::receivers::ExpectErrorReceiver; 57 | 58 | #[test] 59 | fn test_just_error() { 60 | let sender = JustError::new(TestError); 61 | let mut operation = sender.connect(ExpectErrorReceiver::new(TestError)); 62 | operation.start(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /exec/src/factories/mod.rs: -------------------------------------------------------------------------------- 1 | mod just; 2 | mod just_error; 3 | 4 | pub use just::just; 5 | pub use just_error::just_error; 6 | -------------------------------------------------------------------------------- /exec/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod adaptors; 2 | pub use adaptors::then; 3 | 4 | mod consumers; 5 | pub use consumers::start_detached; 6 | pub use consumers::submit; 7 | pub use consumers::sync_wait; 8 | 9 | mod factories; 10 | pub use factories::just; 11 | --------------------------------------------------------------------------------