Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From swctx-0.2.2 To swctx-0.3.0
2024-09-09
| ||
21:31 | Change log fixup. check-in: 72448a9f79 user: jan tags: trunk | |
21:25 | Release maintenance. check-in: 696d6ef1dc user: jan tags: swctx-0.3.0, trunk | |
19:58 | Doc style fixups. check-in: 65005083ab user: jan tags: trunk | |
2024-01-14
| ||
10:24 | Update changelog. check-in: fa1ba701e6 user: jan tags: trunk | |
10:15 | Separators in changelog. check-in: b1ba094b8a user: jan tags: swctx-0.2.2, trunk | |
10:13 | Remove dev-docs feature. check-in: 82c7000574 user: jan tags: trunk | |
Changes to Cargo.toml.
1 2 | [package] name = "swctx" | | > > > > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | [package] name = "swctx" version = "0.3.0" edition = "2021" license = "0BSD" # https://crates.io/category_slugs categories = [ "concurrency", "asynchronous" ] keywords = [ "channel", "threads", "sync", "message-passing" ] repository = "https://repos.qrnch.tech/pub/swctx" description = "One-shot channel with some special semantics." rust-version = "1.56" exclude = [ ".fossil-settings", ".efiles", ".fslckout", "www", "rustfmt.toml" ] # https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section [badges] maintenance = { status = "passively-maintained" } [dependencies] parking_lot = { version = "0.12.3" } [dev-dependencies] tokio = { version = "1.40.0", features = ["rt-multi-thread"] } [package.metadata.docs.rs] rustdoc-args = ["--generate-link-to-definition"] |
Added bacon.toml.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | # This is a configuration file for the bacon tool # # Bacon repository: https://github.com/Canop/bacon # Complete help on configuration: https://dystroy.org/bacon/config/ # You can also check bacon's own bacon.toml file # as an example: https://github.com/Canop/bacon/blob/main/bacon.toml # For information about clippy lints, see: # https://github.com/rust-lang/rust-clippy/blob/master/README.md default_job = "check" [jobs.check] command = ["cargo", "check", "--color", "always"] need_stdout = false [jobs.check-all] command = ["cargo", "check", "--all-targets", "--color", "always"] need_stdout = false # Run clippy on the default target [jobs.clippy] command = [ "cargo", "clippy", "--color", "always", ] need_stdout = false # Run clippy on all targets # To disable some lints, you may change the job this way: # [jobs.clippy-all] # command = [ # "cargo", "clippy", # "--all-targets", # "--color", "always", # "--", # "-A", "clippy::bool_to_int_with_if", # "-A", "clippy::collapsible_if", # "-A", "clippy::derive_partial_eq_without_eq", # ] # need_stdout = false [jobs.clippy-all] command = [ "cargo", "clippy", "--all-targets", "--color", "always", ] need_stdout = false [jobs.clippy-pedantic] command = [ "cargo", "clippy", "--color", "always", "--", "-Wclippy::all", "-Wclippy::pedantic", "-Wclippy::nursery", "-Wclippy::cargo" ] need_stdout = false [jobs.clippy-all-pedantic] command = [ "cargo", "clippy", "--all-targets", "--color", "always", "--", "-Wclippy::all", "-Wclippy::pedantic", "-Wclippy::nursery", "-Wclippy::cargo" ] need_stdout = false # This job lets you run # - all tests: bacon test # - a specific test: bacon test -- config::test_default_files # - the tests of a package: bacon test -- -- -p config [jobs.test] command = [ "cargo", "test", "--color", "always", "--", "--color", "always", # see https://github.com/Canop/bacon/issues/124 ] need_stdout = true [jobs.doc] command = ["cargo", "doc", "--color", "always", "--no-deps"] need_stdout = false # If the doc compiles, then it opens in your browser and bacon switches # to the previous job [jobs.doc-open] command = ["cargo", "doc", "--color", "always", "--no-deps", "--open"] need_stdout = false on_success = "back" # so that we don't open the browser at each change # You can run your application and have the result displayed in bacon, # *if* it makes sense for this crate. # Don't forget the `--color always` part or the errors won't be # properly parsed. # If your program never stops (eg a server), you may set `background` # to false to have the cargo run output immediately displayed instead # of waiting for program's end. [jobs.run] command = [ "cargo", "run", "--color", "always", # put launch parameters for your program behind a `--` separator ] need_stdout = true allow_warnings = true background = true # This parameterized job runs the example of your choice, as soon # as the code compiles. # Call it as # bacon ex -- my-example [jobs.ex] command = ["cargo", "run", "--color", "always", "--example"] need_stdout = true allow_warnings = true # You may define here keybindings that would be specific to # a project, for example a shortcut to launch a specific job. # Shortcuts to internal functions (scrolling, toggling, etc.) # should go in your personal global prefs.toml file instead. [keybindings] # alt-m = "job:my-job" c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target |
Changes to src/err.rs.
1 2 3 4 5 | //! Error management module. use std::fmt; /// Errors that can be returned by the `swctx` library. | | > > > > > > > | | | | > | > > | > > > | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | //! Error management module. use std::fmt; /// Errors that can be returned by the `swctx` library. #[derive(Debug, PartialEq, Eq)] pub enum Error<S, E> { /// The [`SetCtx`](super::SetCtx) was dropped while in state 'S'. Aborted(S), /// The [`WaitCtx`](super::WaitCtx) was dropped. /// /// This will be returned by [`SetCtx`](super::SetCtx) if it attempts to /// update the context if there's no longer a `WaitCtx` to receive the /// changes. LostWaiter, /// The [`SetCtx`](super::SetCtx) triggered an application-defined error. App(E) } impl<S, E> Error<S, E> { /// Return application-specific error, if set. /// /// If the error is not `Error::App(E)` then return `None`, otherwise return /// Some(E). pub fn into_apperr(self) -> Option<E> { match self { Self::App(e) => Some(e), _ => None } } /// Return application-specific error. /// /// # Panics /// This method will panic if the error is not `Error::App(E)`. pub fn unwrap_apperr(self) -> E { match self { Self::App(e) => e, _ => panic!("Not an Error::App") } } } impl<S: fmt::Debug, E> std::error::Error for Error<S, E> where E: std::error::Error { } impl<S, E> fmt::Display for Error<S, E> where E: std::error::Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Aborted(_) => { write!(f, "The set context was dropped prematurely") } Self::LostWaiter => write!(f, "WaitCtx disappeared"), Self::App(err) => write!(f, "Application error; {:?}", err) } } } // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : |
Changes to src/lib.rs.
|
| | > | | 1 2 3 4 5 6 7 8 9 10 | //! _swctx_ is similar to a cross-thread/task one-shot channel, with the added //! ability to store a generic "current state" of the channel prior to passing //! a value over the channel. //! //! ``` //! use std::thread; //! use swctx::mkpair; //! //! let (sctx, wctx) = mkpair::<&str, &str, &str>(); //! let jh = thread::spawn(move || { |
︙ | ︙ | |||
58 59 60 61 62 63 64 | /// An error occurred. Err(Error<S, E>) } struct Inner<T, S, E> { state: State<T, S, E>, sctx_state: S, | | > | > | | > | > | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | /// An error occurred. Err(Error<S, E>) } struct Inner<T, S, E> { state: State<T, S, E>, sctx_state: S, waker: Option<Waker>, wctx_dropped: bool } impl<T, S, E> Inner<T, S, E> { fn try_get(&mut self) -> Result<Option<T>, Error<S, E>> { match self.state { State::Waiting => Ok(None), State::Data(_) => { let old = std::mem::replace(&mut self.state, State::Finalized); let State::Data(data) = old else { panic!("Unable to extract data"); }; Ok(Some(data)) } State::Err(_) => { let old = std::mem::replace(&mut self.state, State::Finalized); let State::Err(err) = old else { panic!("Unable to extract error"); }; Err(err) } State::Finalized => { // Shouldn't be possible unimplemented!("Unexpected state"); } } } } struct Shared<T, S, E> { inner: Mutex<Inner<T, S, E>>, signal: Condvar } impl<T, S, E> Shared<T, S, E> { fn notify_waiter(&self, inner: &mut Inner<T, S, E>) { self.signal.notify_one(); if let Some(waker) = inner.waker.take() { waker.wake(); } } } /// Create a linked [`SetCtx`] and [`WaitCtx`] pair. /// /// The `WaitCtx` is used to wait for a value to arrive from the `SetCtx`. #[must_use] pub fn mkpair<T, S, E>() -> (SetCtx<T, S, E>, WaitCtx<T, S, E>) where S: Clone + Default { let inner = Inner { state: State::Waiting, sctx_state: S::default(), waker: None, wctx_dropped: false }; let sh = Shared { inner: Mutex::new(inner), signal: Condvar::new() }; let sh = Arc::new(sh); |
︙ | ︙ |
Changes to src/sctx.rs.
︙ | ︙ | |||
36 37 38 39 40 41 42 | /// sctx.set_state(State::InThread); /// // sctx is prematurely dropped here /// }); /// jh.join().unwrap(); /// /// assert_eq!(wctx.wait(), Err(Error::Aborted(State::InThread))); /// ``` | > > > > | > > > > > > > > > | < < < > > > > > > > > > > > | > > > > > > > > < < < | | > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | /// sctx.set_state(State::InThread); /// // sctx is prematurely dropped here /// }); /// jh.join().unwrap(); /// /// assert_eq!(wctx.wait(), Err(Error::Aborted(State::InThread))); /// ``` /// /// # Errors /// If the corresponding [`WaitCtx`](super::WaitCtx) has been dropped, /// [`Error::LostWaiter`] will be returned. pub fn set_state(&self, s: S) -> Result<(), Error<S, E>> { let mut inner = self.0.inner.lock(); if inner.wctx_dropped { return Err(Error::LostWaiter); } inner.sctx_state = s; drop(inner); Ok(()) } /// Consume the `SetCtx` and store a value for the wait context to return. /// /// # Errors /// If the corresponding [`WaitCtx`](super::WaitCtx) has been dropped, /// [`Error::LostWaiter`] will be returned. pub fn set(self, data: T) -> Result<(), Error<S, E>> { // - This and `fail()` consume `self`. // - The wait context does not modify the state. // - The only other method is `set_state()` and it only touches // `sctx_state` and not `state`. // // These facts allow us to safely assume that the state does not need to be // checked here -- it can only be `Waiting`. let mut inner = self.0.inner.lock(); if inner.wctx_dropped { return Err(Error::LostWaiter); } inner.state = State::Data(data); self.0.notify_waiter(&mut inner); drop(inner); Ok(()) } /// Consume the `SetCtx` and store an error value for the wait context to /// return. /// /// # Errors /// If the corresponding [`WaitCtx`](super::WaitCtx) has been dropped, /// [`Error::LostWaiter`] will be returned. pub fn fail(self, error: E) -> Result<(), Error<S, E>> { // See comments in SetCtx::set() for implementation details. let mut inner = self.0.inner.lock(); if inner.wctx_dropped { return Err(Error::LostWaiter); } inner.state = State::Err(Error::App(error)); self.0.notify_waiter(&mut inner); drop(inner); Ok(()) } } impl<T, S, E> Drop for SetCtx<T, S, E> where S: Clone { fn drop(&mut self) { let mut inner = self.0.inner.lock(); match inner.state { State::Waiting => { inner.state = State::Err(Error::Aborted(inner.sctx_state.clone())); self.0.notify_waiter(&mut inner); drop(inner); } State::Data(_) | State::Err(_) | State::Finalized => { // Do nothing. // For the Data base, assume the waiter will handle the data. } } } } // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : |
Changes to src/wctx.rs.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /// End-point used to wait for a value to be sent from the paired /// [`SetCtx`](super::SetCtx). #[repr(transparent)] pub struct WaitCtx<T, S, E>(pub(crate) Arc<Shared<T, S, E>>); impl<T, S, E> WaitCtx<T, S, E> { /// Wait for the paired [`SetCtx`](super::SetCtx) to set a value or fail. pub fn wait(self) -> Result<T, Error<S, E>> { let mut inner = self.0.inner.lock(); loop { match inner.state { State::Waiting => { self.0.signal.wait(&mut inner); } State::Data(_) => { let old = std::mem::replace(&mut inner.state, State::Finalized); let State::Data(data) = old else { | > > > > > | > | | > | > > > > | | | > | | > > > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | /// End-point used to wait for a value to be sent from the paired /// [`SetCtx`](super::SetCtx). #[repr(transparent)] pub struct WaitCtx<T, S, E>(pub(crate) Arc<Shared<T, S, E>>); impl<T, S, E> WaitCtx<T, S, E> { /// Wait for the paired [`SetCtx`](super::SetCtx) to set a value or fail. /// /// # Errors /// Returns application-specific error wrapped in an [`Error::App`] if the /// `SetCtx` reported failure. pub fn wait(self) -> Result<T, Error<S, E>> { let mut inner = self.0.inner.lock(); loop { match inner.state { State::Waiting => { self.0.signal.wait(&mut inner); } State::Data(_) => { let old = std::mem::replace(&mut inner.state, State::Finalized); drop(inner); let State::Data(data) = old else { unimplemented!("Unable to extract data"); }; break Ok(data); } State::Err(_) => { let old = std::mem::replace(&mut inner.state, State::Finalized); drop(inner); let State::Err(err) = old else { unimplemented!("Unable to extract error"); }; break Err(err); } State::Finalized => { // Shouldn't be possible unimplemented!("Unexpected state") } } } } /// Non-blocking attempt to get the get the stored value. /// /// Returns `Ok(Some(T))` if a value has been stored. Returns `Ok(None)` if /// no value has been stored. /// /// # Errors /// Returns application-specific error wrapped in an [`Error::App`] if the /// [`SetCtx`](super::SetCtx) reported failure. /// /// # Panics /// This function will panic if called again after it has resolved to either /// data or error. pub fn try_get(&self) -> Result<Option<T>, Error<S, E>> { let mut inner = self.0.inner.lock(); inner.try_get() } /// Return a `Future` that will wait for either data to be set or an error to /// occur. /// /// # Cancel safety /// The returned `Future` is cancel safe. /// /// # Panics /// This function will panic if called again after it has resolved to either /// data or error. #[must_use] pub const fn wait_async(&self) -> WaitFuture<T, S, E> { WaitFuture(self) } } impl<T, S, E> Drop for WaitCtx<T, S, E> { fn drop(&mut self) { let mut inner = self.0.inner.lock(); inner.wctx_dropped = true; } } impl<T, S, E> Future for WaitCtx<T, S, E> { type Output = Result<T, Error<S, E>>; fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> { let mut inner = self.0.inner.lock(); |
︙ | ︙ | |||
86 87 88 89 90 91 92 93 | } } } /// Used to wait for the paired [`SetCtx`](super::SetCtx) to set a value in an /// `async` context. #[repr(transparent)] | > > > > > | | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | } } } /// Used to wait for the paired [`SetCtx`](super::SetCtx) to set a value in an /// `async` context. // A reference to the `WaitCtx` is used (rather than a clone of its // Arc<Shared>) to tie the lifetime of the `WaitCtx` and the `WaitFuture` // together, to avoid `WaitFuture` outliving the `WaitCtx` (which wouldn't make // sense, because dropping `WaitCtx` sets `wctx_dropped` in the shared // context). #[repr(transparent)] pub struct WaitFuture<'wctx, T, S, E>(&'wctx WaitCtx<T, S, E>); impl<'wctx, T, S, E> Future for WaitFuture<'wctx, T, S, E> { type Output = Result<T, Error<S, E>>; fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> { let mut inner = self.0 .0.inner.lock(); match inner.try_get() { Ok(Some(v)) => Poll::Ready(Ok(v)), Ok(None) => { inner.waker = Some(ctx.waker().clone()); Poll::Pending } Err(e) => Poll::Ready(Err(e)) } } } // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : |
Changes to tests/errors.rs.
︙ | ︙ | |||
41 42 43 44 45 46 47 | // Trigger a no-reply error before wait is called (hopefully). #[test] fn noreply_before_wait() { let (sctx, wctx) = mkpair::<(), State, ()>(); let jh = thread::spawn(move || { | | | | | > > > > > > > > | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | // Trigger a no-reply error before wait is called (hopefully). #[test] fn noreply_before_wait() { let (sctx, wctx) = mkpair::<(), State, ()>(); let jh = thread::spawn(move || { sctx.set_state(State::NoReply).unwrap(); }); jh.join().unwrap(); assert_eq!(wctx.wait(), Err(Error::Aborted(State::NoReply))); } // Trigger an no-reply error after wait is called (hopefully). #[test] fn noreply_after_wait() { let (sctx, wctx) = mkpair::<(), State, ()>(); let jh = thread::spawn(move || { sctx.set_state(State::NoReply).unwrap(); std::thread::sleep(std::time::Duration::from_millis(500)); }); assert_eq!(wctx.wait(), Err(Error::Aborted(State::NoReply))); jh.join().unwrap(); } // Trigger an no-reply error before wait is called (hopefully). #[test] fn apperr_before_wait() { let (sctx, wctx) = mkpair::<(), State, &str>(); let jh = thread::spawn(move || { sctx.fail("yikes").unwrap(); }); jh.join().unwrap(); assert_eq!(wctx.wait(), Err(Error::App("yikes"))); } // Trigger an no-reply error after wait is called (hopefully). #[test] fn apperr_after_wait() { let (sctx, wctx) = mkpair::<(), State, &str>(); let jh = thread::spawn(move || { std::thread::sleep(std::time::Duration::from_millis(500)); sctx.fail("yikes").unwrap(); }); assert_eq!(wctx.wait(), Err(Error::App("yikes"))); jh.join().unwrap(); } #[test] fn lost_waiter() { let (sctx, wctx) = mkpair::<(), State, &str>(); drop(wctx); assert_eq!(sctx.set_state(State::Abort), Err(Error::LostWaiter)); assert_eq!(sctx.set(()), Err(Error::LostWaiter)); } // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : |
Changes to tests/errors_async.rs.
︙ | ︙ | |||
49 50 51 52 53 54 55 | #[test] fn noreply_before_wait() { let tokrt = tokio::runtime::Runtime::new().unwrap(); let (sctx, wctx) = mkpair::<(), State, ()>(); let jh = thread::spawn(move || { | | | | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | #[test] fn noreply_before_wait() { let tokrt = tokio::runtime::Runtime::new().unwrap(); let (sctx, wctx) = mkpair::<(), State, ()>(); let jh = thread::spawn(move || { sctx.set_state(State::NoReply).unwrap(); }); jh.join().unwrap(); tokrt.block_on(async { assert_eq!(wctx.wait_async().await, Err(Error::Aborted(State::NoReply))); }); } // Trigger an no-reply error after wait is called (hopefully). #[test] fn noreply_after_wait() { let tokrt = tokio::runtime::Runtime::new().unwrap(); let (sctx, wctx) = mkpair::<(), State, ()>(); let jh = thread::spawn(move || { sctx.set_state(State::NoReply).unwrap(); std::thread::sleep(std::time::Duration::from_millis(500)); }); tokrt.block_on(async { assert_eq!(wctx.wait_async().await, Err(Error::Aborted(State::NoReply))); }); jh.join().unwrap(); } // Trigger an no-reply error before wait is called (hopefully). #[test] fn apperr_before_wait() { let tokrt = tokio::runtime::Runtime::new().unwrap(); let (sctx, wctx) = mkpair::<(), State, &str>(); let jh = thread::spawn(move || { sctx.fail("yikes").unwrap(); }); jh.join().unwrap(); tokrt.block_on(async { assert_eq!(wctx.wait_async().await, Err(Error::App("yikes"))); }); } // Trigger an no-reply error after wait is called (hopefully). #[test] fn apperr_after_wait() { let tokrt = tokio::runtime::Runtime::new().unwrap(); let (sctx, wctx) = mkpair::<(), State, &str>(); let jh = thread::spawn(move || { std::thread::sleep(std::time::Duration::from_millis(500)); sctx.fail("yikes").unwrap(); }); tokrt.block_on(async { assert_eq!(wctx.wait_async().await, Err(Error::App("yikes"))); }); jh.join().unwrap(); } // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : |
Changes to tests/simple.rs.
1 2 3 4 5 6 7 8 9 | use std::{thread, time}; use swctx::mkpair; #[test] fn say_hello_before_wait() { let (sctx, wctx) = mkpair::<&str, (), &str>(); let jh = thread::spawn(move || { | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | use std::{thread, time}; use swctx::mkpair; #[test] fn say_hello_before_wait() { let (sctx, wctx) = mkpair::<&str, (), &str>(); let jh = thread::spawn(move || { sctx.set("hello").unwrap(); }); // join ensures that SetCtx::set() has been called jh.join().unwrap(); assert_eq!(wctx.wait().unwrap(), "hello"); } #[test] fn say_hello_after_wait() { let (sctx, wctx) = mkpair::<&str, (), &str>(); let jh = thread::spawn(move || { // yolo assume 500ms is sufficient thread::sleep(time::Duration::from_millis(500)); sctx.set("hello").unwrap(); }); assert_eq!(wctx.wait().unwrap(), "hello"); jh.join().unwrap(); } #[test] fn async_say_hello_before_wait() { let tokrt = tokio::runtime::Runtime::new().unwrap(); let (sctx, wctx) = mkpair::<&str, (), &str>(); let jh = thread::spawn(move || { sctx.set("hello").unwrap(); }); // join ensures that SetCtx::set() has been called jh.join().unwrap(); tokrt.block_on(async { assert_eq!(wctx.wait_async().await.unwrap(), "hello"); }); } #[test] fn async_say_hello_after_wait() { let tokrt = tokio::runtime::Runtime::new().unwrap(); let (sctx, wctx) = mkpair::<&str, (), &str>(); let jh = thread::spawn(move || { // yolo assume 500ms is sufficient thread::sleep(time::Duration::from_millis(500)); sctx.set("hello").unwrap(); }); tokrt.block_on(async { assert_eq!(wctx.wait_async().await.unwrap(), "hello"); }); jh.join().unwrap(); } // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : |
Changes to www/changelog.md.
1 2 3 4 | # Change Log ## [Unreleased] | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | # Change Log ⚠️ indicates a breaking change. ## [Unreleased] [Details](/vdiff?from=swctx-0.3.0&to=trunk) ### Added ### Changed ### Removed --- ## [0.3.0] [Details](/vdiff?from=swctx-0.2.2&to=swctx-0.3.0) ### Added - ⚠️ `Error::LostWaiter` will be returned by `SetCtx` functions that are intended to make changes that would have affected the resolution of the `WaitCtx`, but it was dropped. ### Changed - `SetCtx`'s functions for passing a value to the `WaitCtx`, setting state or failing now return a `Result`. - ⚠️ The `E` bound used for `Error::App(E)` has been changed to `std::error::Error` from `fmt::Debug`. - ⚠️ Tie the lifetime of `WaitFuture` to the lifetime of `WaitCtx` to avoid `WaitFuture` outliving its `WaitCtx`. --- ## [0.2.2] - 2024-01-14 [Details](/vdiff?from=swctx-0.2.1&to=swctx-0.2.2) ### Changed - Export `WaitFuture`. - Implement `Future` on `WaitCtx`. ### Removed |
︙ | ︙ |