Skip to content

Commit 7e98e1f

Browse files
Separate channel creation and state probe, and error handling thereof. (#2465)
## Motivation Currently the `Channel` object is calling into the `PersistentChannel` internals to do things like provision an active channel and check state. The division of where work and error handling is managed becomes muddied in this situation, which complicates handling errors in ways that aren't `unwrap`. This was also motivated by work to implement the always-failing PersistentChannel PR, but it seemed like it could be broken off into it's own PR. ## Solution Moving the actual mechanics into the `PersistentChannel` object, which owns channel config, and allows `Channel` to be a lightweight handles for users to interact with. Specifically state probing and channel creation are now in `PersistentChannel`. `PersistentChannel` calls are wrapped in `Result` objects that are translated at the `Channel` level to the appropriate public-facing API objects. `get_or_create_active_channel()` just becomes `get_active_channel()` and passes the 'create' flag, which controls whether a new channel is created if it does not already exist. The external semantics should stay the same as they have been. This does not yet address the error that can surface in `call()`, because it's not yet clear what the error reporting story is.
1 parent 54fab77 commit 7e98e1f

File tree

1 file changed

+43
-31
lines changed

1 file changed

+43
-31
lines changed

grpc/src/client/channel.rs

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -154,23 +154,10 @@ impl Channel {
154154

155155
// TODO: enter_idle(&self) and graceful_stop()?
156156

157-
/// Returns the current state of the channel.
157+
/// Returns the current state of the channel. If there is no underlying active channel,
158+
/// returns Idle. If `connect` is true, will create a new active channel.
158159
pub fn state(&mut self, connect: bool) -> ConnectivityState {
159-
let ac = if !connect {
160-
// If !connect and we have no active channel already, return idle.
161-
let ac = self.inner.active_channel.lock().unwrap();
162-
if ac.is_none() {
163-
return ConnectivityState::Idle;
164-
}
165-
ac.as_ref().unwrap().clone()
166-
} else {
167-
// Otherwise, get or create the active channel.
168-
self.get_or_create_active_channel()
169-
};
170-
if let Some(s) = ac.connectivity_state.cur() {
171-
return s;
172-
}
173-
ConnectivityState::Idle
160+
self.inner.state(connect)
174161
}
175162

176163
/// Waits for the state of the channel to change from source. Times out and
@@ -183,20 +170,8 @@ impl Channel {
183170
todo!()
184171
}
185172

186-
fn get_or_create_active_channel(&self) -> Arc<ActiveChannel> {
187-
let mut s = self.inner.active_channel.lock().unwrap();
188-
if s.is_none() {
189-
*s = Some(ActiveChannel::new(
190-
self.inner.target.clone(),
191-
&self.inner.options,
192-
self.inner.runtime.clone(),
193-
));
194-
}
195-
s.clone().unwrap()
196-
}
197-
198173
pub async fn call(&self, method: String, request: Request) -> Response {
199-
let ac = self.get_or_create_active_channel();
174+
let ac = self.inner.get_active_channel();
200175
ac.call(method, request).await
201176
}
202177
}
@@ -213,8 +188,8 @@ struct PersistentChannel {
213188
}
214189

215190
impl PersistentChannel {
216-
// Channels begin idle so new is a simple constructor. Required parameters
217-
// are not in ChannelOptions.
191+
// Channels begin idle so `new()` does not automatically connect.
192+
// ChannelOption contain only optional parameters.
218193
fn new(
219194
target: &str,
220195
_credentials: Option<Box<dyn Credentials>>,
@@ -228,6 +203,43 @@ impl PersistentChannel {
228203
runtime,
229204
}
230205
}
206+
207+
/// Returns the current state of the channel. If there is no underlying active channel,
208+
/// returns Idle. If `connect` is true, will create a new active channel iff none exists.
209+
fn state(&self, connect: bool) -> ConnectivityState {
210+
// Done this away to avoid potentially locking twice.
211+
let active_channel = if connect {
212+
self.get_active_channel()
213+
} else {
214+
match self.active_channel.lock().unwrap().clone() {
215+
Some(x) => x,
216+
None => {
217+
return ConnectivityState::Idle;
218+
}
219+
}
220+
};
221+
222+
active_channel
223+
.connectivity_state
224+
.cur()
225+
.unwrap_or(ConnectivityState::Idle)
226+
}
227+
228+
/// Gets the underlying active channel. If there is no current connection, it will create one.
229+
/// This cannot fail and will always return a valid active channel.
230+
fn get_active_channel(&self) -> Arc<ActiveChannel> {
231+
let mut active_channel = self.active_channel.lock().unwrap();
232+
233+
if active_channel.is_none() {
234+
*active_channel = Some(ActiveChannel::new(
235+
self.target.clone(),
236+
&self.options,
237+
self.runtime.clone(),
238+
));
239+
}
240+
241+
active_channel.clone().unwrap() // We have ensured this is not None.
242+
}
231243
}
232244

233245
struct ActiveChannel {

0 commit comments

Comments
 (0)