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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#![allow(unused_braces)]
mod txn_lib {
use ::std::{
sync::{Arc, Weak},
};
use ::safe_manually_drop::prelude::{
DropManually,
SafeManuallyDrop,
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub
enum DbState {
Committed,
RolledBack,
}
pub(crate)
struct RawTransaction<'r> {
db_state: &'r mut Option<DbState>,
}
impl<'r> RawTransaction<'r> {
pub(crate)
fn new(
db_state: &'r mut Option<DbState>,
) -> Self
{
assert_eq!(*db_state, None);
Self {
db_state,
}
}
/// Owned access for a safer, type-wise, API.
pub
fn commit(self) {
*self.db_state = Some(DbState::Committed);
}
/// Owned access for a safer, type-wise, API.
pub
fn roll_back(self) {
*self.db_state = Some(DbState::RolledBack);
}
}
/// Convenience wrapper around a [`RawTransaction`], which automatically rolls back on drop.
///
/// Users need to call [`.commit()`][`Self::commit()`] in order to instead, persist the changes.
///
/// Explicit calls to [`.roll_back()`][`Self::roll_back()`] remain possible.
pub
struct Transaction<'r> {
raw_txn: SafeManuallyDrop<RawTransaction<'r>, Self>,
_some_other_owned_resource: Weak<()>,
}
impl<'r> DropManually<RawTransaction<'r>> for Transaction<'r> {
fn drop_manually(raw_txn: RawTransaction<'r>) {
raw_txn.roll_back();
}
}
impl<'r> Transaction<'r> {
pub
fn roll_back(self) {
// already done in drop (of `self.raw_txn`).
}
/// The main point of this example: writing this kind of body is subtle and error-prone
/// when using `ManuallyDrop`:
///
/// 1. In order to obtain the necessary owned `RawTransaction` access in drop, such a field
/// would have needed to be wrapped in a `ManuallyDrop`, alongside writing a `Drop` impl
/// which would be doing:
/// `let raw_txn = unsafe { ManuallyDrop::take(&mut self.raw_txn ) }`
///
/// 2. Then, in this `fn commit` function, because now we have this extra, txn-rolling-back
/// logic in the `Drop` `impl`, we shall need to `ManuallyDrop::new()`-wrap *all of
/// `self` in order to ensure we do not roll_back when that function returns, and the
/// locals the function owned, such as `self`, get dropped.
///
/// 3. But in doing so, now there is a risk of having disabled too much drop glue, such as
/// resource-legitimate drop glue (symbolized, in this code sketch, by the owned `Weak`
/// handle).
///
/// 4. Hence why the `fn` body, here, would have had to do:
///
/// ```rust
/// fn commit(self) {
/// // Subtle! Destructure `self` into all of its owned fields, by bypassing/having
/// // defused the (preprended) `Drop` glue *only* (the rest of the drop glue, that
/// // of the fields, remains active by virtue of getting back said fields).
/// let (raw_txn, _some_other_owned_resource, _each, _other, _field) = unsafe {
/// // Granted, this whole block is yearning to be macro-ed.
/// // Or even better: "Rust, y u no offer `let Self { raw_txn, .. } = self;`??"
///
/// let this = ManuallyDrop::new(self);
/// let Self {
/// ref raw_txn,
/// ref _some_other_owned_resource,
/// ref each,
/// ref other,
/// ref field
/// } = *this;
/// (
/// <*const _>::read(raw_txn),
/// <*const _>::read(_some_other_owned_resource),
/// <*const _>::read(each),
/// <*const _>::read(other),
/// <*const _>::read(field),
/// )
/// };
///
/// // But remember, because of our inner `ManuallyDrop` around `raw_txn` itself
/// // (yes, there are *two* distinct `ManuallyDrop`s at play, here…) we need to
/// // unwrap this layer, now. Luckily, our so-obtained owned access makes it
/// // trivial:
/// let raw_txn: RawTransaction<'_> = ManuallyDrop::into_inner(raw_txn);
///
/// // At last:
/// raw_txn.commit(); // 😵💫
/// }
/// ```
///
/// Pretty scary, huh?
///
/// FWIW, the "awkwardness" of the pattern needed here cannot really be avoided (except
/// maybe by, perhaps counter-intuitively, wrapping as many fields within the initial
/// `ManuallyDrop`).
///
/// - That would have allowed rewriting the destructuring as follows:
///
/// ```rust
/// // 1. Defuse *all* the drop glue of `Self`, and thus, the prepended `Drop` logic.
/// let mut this = MD::new(self);
/// // 2. Take advantage of the other, inner, `MD`, to simply get owned access to the
/// // inner `AllFields` by `MD::take()`ing it.
/// // We get back *owned access* to the innermost fields; mainly, our `raw_txn`, but
/// // also, implicitly, the drop glue of the remaining fields as well: we have
/// // effectively re-infused that transitive drop glue, which means our prior
/// // defusing has *only* applied to the preprended `Drop` logic.
/// let AllFields {
/// raw_txn,
/// // no need to worry about `_some_other_owned_resource`, it's implicitly dropped.
/// ..
/// } = unsafe {
/// MD::take(&mut this.all_fields)
/// };
/// ```
///
/// But at least, when having to use `SafeManuallyDrop<>`, you shall not need to "come up"
/// with the right usage of `ManuallyDrop` / not need to think of using it, and how.
///
/// Instead, your end struct has no prepended `Drop` logic, only the special
/// `SafeManuallyDrop` field does, so you can then trivially do exactly what we were trying
/// to achieve here: call `SafeManuallyDrop::into_inner_defusing_impl_Drop()` so as to
/// extract owned access to the fields whilst having defused this prepended `Drop` logic
/// *only*. No *inherent*/deeper drop-glue casualties.
pub
fn commit(self) {
// no need to worry about `_some_other_owned_resource`, it's naturally properly dropped.
let raw_txn: RawTransaction<'_> = self.raw_txn.into_inner_defusing_impl_Drop();
raw_txn.commit();
}
}
impl<'r> Transaction<'r> {
pub
fn new(
db_state: &'r mut Option<DbState>,
ref_count: &Arc<()>,
) -> Self
{
Self {
raw_txn: SafeManuallyDrop::new(RawTransaction::new(db_state)),
_some_other_owned_resource: Arc::downgrade(ref_count),
}
}
}
}
#[test]
fn test_txn_lib() {
use ::std::{
sync::Arc,
};
use self::{
txn_lib::{DbState, Transaction},
};
let db_state = &mut None;
let ref_count = &Arc::new(());
*db_state = None;
{
assert_eq!(Arc::weak_count(ref_count), 0);
let _txn = Transaction::new(db_state, ref_count);
assert_eq!(Arc::weak_count(ref_count), 1);
} // drop(_txn);
assert_eq!(*db_state, Some(DbState::RolledBack));
assert_eq!(Arc::weak_count(ref_count), 0);
*db_state = None;
{
assert_eq!(Arc::weak_count(ref_count), 0);
let txn = Transaction::new(db_state, ref_count);
assert_eq!(Arc::weak_count(ref_count), 1);
txn.commit();
}
assert_eq!(*db_state, Some(DbState::Committed));
assert_eq!(Arc::weak_count(ref_count), 0);
*db_state = None;
{
assert_eq!(Arc::weak_count(ref_count), 0);
let txn = Transaction::new(db_state, ref_count);
assert_eq!(Arc::weak_count(ref_count), 1);
txn.roll_back();
}
assert_eq!(*db_state, Some(DbState::RolledBack));
assert_eq!(Arc::weak_count(ref_count), 0);
*db_state = None;
{
assert_eq!(Arc::weak_count(ref_count), 0);
let txn = Transaction::new(db_state, ref_count);
assert_eq!(Arc::weak_count(ref_count), 1);
::core::mem::forget(txn);
}
assert_eq!(*db_state, None);
assert_eq!(Arc::weak_count(ref_count), 1); // Leak!
// for the sake of test hygiene, let's manually undo the leak.
unsafe { <*const _>::read(&Arc::downgrade(ref_count)); }
assert_eq!(Arc::weak_count(ref_count), 0); // 🧙
}