|
1 | 1 | use crate::NamespacedClient; |
2 | | -use std::borrow::Cow; |
3 | | -use std::sync::atomic::{AtomicU32, Ordering}; |
4 | | -use std::sync::{Arc, RwLock}; |
| 2 | +use std::{ |
| 3 | + borrow::Cow, |
| 4 | + sync::{ |
| 5 | + Arc, RwLock, |
| 6 | + atomic::{AtomicU32, Ordering}, |
| 7 | + }, |
| 8 | +}; |
5 | 9 |
|
6 | 10 | /// A client wrapper that allows replacing the underlying client at a later point in time. |
7 | 11 | /// Clones of this struct have a shared reference to the underlying client, and each clone also |
@@ -83,24 +87,26 @@ where |
83 | 87 | self.inner_cow().into_owned() |
84 | 88 | } |
85 | 89 |
|
86 | | - /// Returns a reference to this instance's cached clone of the underlying client if it's up to |
87 | | - /// date, or a fresh clone of the shared client otherwise. Because it's an immutable method, |
88 | | - /// it will not update this instance's cached clone. For this reason, prefer to use |
89 | | - /// [`refresh_inner()`](Self::refresh_inner) when possible. |
| 90 | + /// Returns an immutable reference to this instance's cached clone of the underlying client if |
| 91 | + /// it's up to date, or a fresh clone of the shared client otherwise. Because it's an immutable |
| 92 | + /// method, it will not update this instance's cached clone. For this reason, prefer to use |
| 93 | + /// [`inner_mut_refreshed()`](Self::inner_mut_refreshed) when possible. |
90 | 94 | pub fn inner_cow(&self) -> Cow<'_, C> { |
91 | 95 | self.shared_data |
92 | 96 | .fetch_newer_than(self.cloned_generation) |
93 | 97 | .map(|(c, _)| Cow::Owned(c)) |
94 | 98 | .unwrap_or_else(|| Cow::Borrowed(&self.cloned_client)) |
95 | 99 | } |
96 | 100 |
|
97 | | - /// Refreshes this instance's cached clone of the underlying client. Returns a mutable reference |
98 | | - /// to it. Called automatically by other mutable methods, in particular by all RPC calls. |
| 101 | + /// Returns a mutable reference to this instance's cached clone of the underlying client. If the |
| 102 | + /// cached clone is not up to date, it's refreshed before the reference is returned. This method |
| 103 | + /// is called automatically by most other mutable methods, in particular by all service calls, |
| 104 | + /// so most of the time it doesn't need to be called directly. |
99 | 105 | /// |
100 | 106 | /// While this method allows mutable access to the underlying client, any configuration changes |
101 | 107 | /// will not be shared with other instances, and will be lost if the client gets replaced from |
102 | | - /// anywhere. To make configuration changes, use [`replace_client()`](Self::refresh_client) instead. |
103 | | - pub fn refresh_inner(&mut self) -> &mut C { |
| 108 | + /// anywhere. To make configuration changes, use [`replace_client()`](Self::replace_client) instead. |
| 109 | + pub fn inner_mut_refreshed(&mut self) -> &mut C { |
104 | 110 | if let Some((client, generation)) = |
105 | 111 | self.shared_data.fetch_newer_than(self.cloned_generation) |
106 | 112 | { |
@@ -145,7 +151,8 @@ where |
145 | 151 |
|
146 | 152 | #[cfg(test)] |
147 | 153 | mod tests { |
148 | | - use crate::{NamespacedClient, SharedReplaceableClient}; |
| 154 | + use super::*; |
| 155 | + use crate::NamespacedClient; |
149 | 156 | use std::borrow::Cow; |
150 | 157 |
|
151 | 158 | #[derive(Debug, Clone)] |
@@ -185,7 +192,7 @@ mod tests { |
185 | 192 | }; |
186 | 193 | assert_eq!(inner.identity, "2"); |
187 | 194 |
|
188 | | - assert_eq!(client.refresh_inner().identity, "2"); |
| 195 | + assert_eq!(client.inner_mut_refreshed().identity, "2"); |
189 | 196 | let Cow::Borrowed(inner) = client.inner_cow() else { |
190 | 197 | panic!("expected borrowed inner"); |
191 | 198 | }; |
@@ -214,4 +221,33 @@ mod tests { |
214 | 221 | assert_eq!(original1.identity(), "2"); |
215 | 222 | assert_eq!(clone1.identity(), "2"); |
216 | 223 | } |
| 224 | + |
| 225 | + #[test] |
| 226 | + fn client_replaced_from_multiple_threads() { |
| 227 | + let mut client = SharedReplaceableClient::new(StubClient::new("original")); |
| 228 | + std::thread::scope(|scope| { |
| 229 | + for thread_no in 0..100 { |
| 230 | + let mut client = client.clone(); |
| 231 | + scope.spawn(move || { |
| 232 | + for i in 0..1000 { |
| 233 | + let old_generation = client.cloned_generation; |
| 234 | + client.inner_mut_refreshed(); |
| 235 | + let current_generation = client.cloned_generation; |
| 236 | + assert!(current_generation >= old_generation); |
| 237 | + let replace_identity = format!("{thread_no}-{i}"); |
| 238 | + client.replace_client(StubClient::new(&replace_identity)); |
| 239 | + client.inner_mut_refreshed(); |
| 240 | + assert!(client.cloned_generation > current_generation); |
| 241 | + let refreshed_identity = client.identity(); |
| 242 | + if refreshed_identity.split('-').next().unwrap() == thread_no.to_string() { |
| 243 | + assert_eq!(replace_identity, refreshed_identity); |
| 244 | + } |
| 245 | + } |
| 246 | + }); |
| 247 | + } |
| 248 | + }); |
| 249 | + client.inner_mut_refreshed(); |
| 250 | + assert_eq!(client.cloned_generation, 100_000); |
| 251 | + assert!(client.identity().ends_with("-999")); |
| 252 | + } |
217 | 253 | } |
0 commit comments