Skip to content

Commit b7215c6

Browse files
committed
Split State into two.
There is no reason for the buffer and typeinfo caches to share the same lock. By splitting them it means we a) get slightly better performance, but more importantly b) it makes it harder to accidentally deadlock.
1 parent a84a45d commit b7215c6

File tree

1 file changed

+38
-43
lines changed

1 file changed

+38
-43
lines changed

tokio-postgres/src/client.rs

+38-43
Original file line numberDiff line numberDiff line change
@@ -54,26 +54,32 @@ impl Responses {
5454
}
5555
}
5656

57-
struct State {
58-
/// A cached prepared statement for basic information for a type from its
57+
/// A cache of type info and prepared statements for fetching type info
58+
/// (corresponding to the queries in the [prepare](prepare) module).
59+
#[derive(Default)]
60+
struct CachedTypeInfo {
61+
/// A statement for basic information for a type from its
5962
/// OID. Corresponds to [TYPEINFO_QUERY](prepare::TYPEINFO_QUERY) (or its
6063
/// fallback).
6164
typeinfo: Option<Statement>,
62-
/// A cached prepared statement for getting information for a composite type
63-
/// from its OID. Corresponds to
64-
/// [TYPEINFO_QUERY](prepare::TYPEINFO_COMPOSITE_QUERY).
65+
/// A statement for getting information for a composite type from its OID.
66+
/// Corresponds to [TYPEINFO_QUERY](prepare::TYPEINFO_COMPOSITE_QUERY).
6567
typeinfo_composite: Option<Statement>,
66-
/// A cached prepared statement for getting information for a composite type
67-
/// from its OID. Corresponds to
68-
/// [TYPEINFO_QUERY](prepare::TYPEINFO_COMPOSITE_QUERY) (or its fallback).
68+
/// A statement for getting information for a composite type from its OID.
69+
/// Corresponds to [TYPEINFO_QUERY](prepare::TYPEINFO_COMPOSITE_QUERY) (or
70+
/// its fallback).
6971
typeinfo_enum: Option<Statement>,
72+
73+
/// Cache of types already looked up.
7074
types: HashMap<Oid, Type>,
71-
buf: BytesMut,
7275
}
7376

7477
pub struct InnerClient {
7578
sender: mpsc::UnboundedSender<Request>,
76-
state: Mutex<State>,
79+
cached_typeinfo: Mutex<CachedTypeInfo>,
80+
81+
/// A buffer to use when writing out postgres commands.
82+
buffer: Mutex<BytesMut>,
7783
}
7884

7985
impl InnerClient {
@@ -91,7 +97,7 @@ impl InnerClient {
9197
}
9298

9399
pub fn typeinfo(&self) -> Option<Statement> {
94-
self.state.lock().typeinfo.clone()
100+
self.cached_typeinfo.lock().typeinfo.clone()
95101
}
96102

97103
pub fn set_typeinfo(&self, statement: &Statement) {
@@ -102,67 +108,61 @@ impl InnerClient {
102108
// Note: We need to be sure that we don't drop a Statement while holding
103109
// the state lock as its drop handling will call `with_buf`, which tries
104110
// to take the lock.
105-
let mut state = self.state.lock();
106-
if state.typeinfo.is_none() {
107-
state.typeinfo = Some(statement.clone());
111+
let mut cache = self.cached_typeinfo.lock();
112+
if cache.typeinfo.is_none() {
113+
cache.typeinfo = Some(statement.clone());
108114
}
109115
}
110116

111117
pub fn typeinfo_composite(&self) -> Option<Statement> {
112-
self.state.lock().typeinfo_composite.clone()
118+
self.cached_typeinfo.lock().typeinfo_composite.clone()
113119
}
114120

115121
pub fn set_typeinfo_composite(&self, statement: &Statement) {
116122
// We only insert the statement if there isn't already a cached
117123
// statement (this is safe as they are prepared statements for the same
118124
// query).
119-
//
120-
// Note: We need to be sure that we don't drop a Statement while holding
121-
// the state lock as its drop handling will call `with_buf`, which tries
122-
// to take the lock.
123-
let mut state = self.state.lock();
124-
if state.typeinfo_composite.is_none() {
125-
state.typeinfo_composite = Some(statement.clone());
125+
let mut cache = self.cached_typeinfo.lock();
126+
if cache.typeinfo_composite.is_none() {
127+
cache.typeinfo_composite = Some(statement.clone());
126128
}
127129
}
128130

129131
pub fn typeinfo_enum(&self) -> Option<Statement> {
130-
self.state.lock().typeinfo_enum.clone()
132+
self.cached_typeinfo.lock().typeinfo_enum.clone()
131133
}
132134

133135
pub fn set_typeinfo_enum(&self, statement: &Statement) {
134136
// We only insert the statement if there isn't already a cached
135137
// statement (this is safe as they are prepared statements for the same
136138
// query).
137-
//
138-
// Note: We need to be sure that we don't drop a Statement while holding
139-
// the state lock as its drop handling will call `with_buf`, which tries
140-
// to take the lock.
141-
let mut state = self.state.lock();
142-
if state.typeinfo_enum.is_none() {
143-
state.typeinfo_enum = Some(statement.clone());
139+
let mut cache = self.cached_typeinfo.lock();
140+
if cache.typeinfo_enum.is_none() {
141+
cache.typeinfo_enum = Some(statement.clone());
144142
}
145143
}
146144

147145
pub fn type_(&self, oid: Oid) -> Option<Type> {
148-
self.state.lock().types.get(&oid).cloned()
146+
self.cached_typeinfo.lock().types.get(&oid).cloned()
149147
}
150148

151149
pub fn set_type(&self, oid: Oid, type_: &Type) {
152-
self.state.lock().types.insert(oid, type_.clone());
150+
self.cached_typeinfo.lock().types.insert(oid, type_.clone());
153151
}
154152

155153
pub fn clear_type_cache(&self) {
156-
self.state.lock().types.clear();
154+
self.cached_typeinfo.lock().types.clear();
157155
}
158156

157+
/// Call the given function with a buffer to be used when writing out
158+
/// postgres commands.
159159
pub fn with_buf<F, R>(&self, f: F) -> R
160160
where
161161
F: FnOnce(&mut BytesMut) -> R,
162162
{
163-
let mut state = self.state.lock();
164-
let r = f(&mut state.buf);
165-
state.buf.clear();
163+
let mut buffer = self.buffer.lock();
164+
let r = f(&mut buffer);
165+
buffer.clear();
166166
r
167167
}
168168
}
@@ -199,13 +199,8 @@ impl Client {
199199
Client {
200200
inner: Arc::new(InnerClient {
201201
sender,
202-
state: Mutex::new(State {
203-
typeinfo: None,
204-
typeinfo_composite: None,
205-
typeinfo_enum: None,
206-
types: HashMap::new(),
207-
buf: BytesMut::new(),
208-
}),
202+
cached_typeinfo: Default::default(),
203+
buffer: Default::default(),
209204
}),
210205
#[cfg(feature = "runtime")]
211206
socket_config: None,

0 commit comments

Comments
 (0)