+25
.tangled/workflows/checks.yaml
+25
.tangled/workflows/checks.yaml
···
1
+
when:
2
+
- event: ["manual", "push"]
3
+
branch: ["main"]
4
+
5
+
engine: "nixery"
6
+
7
+
# default clone
8
+
9
+
dependencies:
10
+
nixpkgs:
11
+
- rustup
12
+
- cargo-mutants
13
+
# need a linker
14
+
- gcc
15
+
16
+
steps:
17
+
- name: "install rust with miri"
18
+
command: "rustup toolchain install nightly -c miri,rust-src --profile minimal"
19
+
- name: "run regular tests"
20
+
command: "cargo test"
21
+
- name: "run mutants"
22
+
command: 'cargo mutants -E "$(cat known_mutants_regex.txt)"'
23
+
- name: "run miri tests"
24
+
command: "cargo +nightly miri test"
25
+
+15
-1
CHANGELOG.md
+15
-1
CHANGELOG.md
···
3
3
This file is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
4
4
This project follows semver and every release is checked by cargo-semver-checks.
5
5
6
-
## [Unreleased]
6
+
## [0.2.3] - 2025-10-01
7
+
8
+
### Fixed
9
+
- detect when a guard was forgotten to avoid UB
10
+
11
+
## [0.2.2] - 2025-08-09
12
+
13
+
### Changed
14
+
- remove '#[inline]' annotations from public functions (mtomsoop)
15
+
- remove '#[must_use]' from 'try_lock' because it doesn't really fit the usecase of must_use (oxotmzyv)
16
+
- Change repo url to use the DID identifier
17
+
18
+
## [0.2.1] - 2025-03-21
7
19
8
20
### Added
9
21
- Changelog file
22
+
- LICENSE Files
10
23
11
24
### Changed
12
25
- Panicking in T::default or T::clone can no longer leak a T.
26
+
- Enabled Inlining on public function
-7
Cargo.lock
-7
Cargo.lock
+3
-3
Cargo.toml
+3
-3
Cargo.toml
···
1
1
[package]
2
2
name = "simple-left-right"
3
-
version = "0.2.1"
3
+
version = "0.2.3"
4
4
edition = "2021"
5
5
rust-version = "1.82"
6
6
readme = "README.md"
···
8
8
keywords = ["real-time", "lock-free", "read-write", "concurrency", "no-std"]
9
9
categories = ["concurrency"]
10
10
license = "MIT OR Apache-2.0"
11
-
repository = "https://tangled.sh/@increasing.bsky.social/simple-left-right/"
11
+
repository = "https://tangled.sh/did:plc:54jgbo4psy24qu2bk4njtpc4/simple-left-right/"
12
12
description = "Lockfree, realtime safe and copy-free Synchronisation"
13
-
# workspace = "../"
13
+
exclude = ["known_mutants_regex.txt", ".tangled", "mutants.out", "mutants.out.old"]
14
14
15
15
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+73
LICENSES/Apache-2.0.txt
+73
LICENSES/Apache-2.0.txt
···
1
+
Apache License
2
+
Version 2.0, January 2004
3
+
http://www.apache.org/licenses/
4
+
5
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+
1. Definitions.
8
+
9
+
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
10
+
11
+
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
12
+
13
+
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
14
+
15
+
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
16
+
17
+
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
18
+
19
+
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
20
+
21
+
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
22
+
23
+
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
24
+
25
+
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
26
+
27
+
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
28
+
29
+
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
30
+
31
+
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
32
+
33
+
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
34
+
35
+
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
36
+
37
+
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
38
+
39
+
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
40
+
41
+
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
42
+
43
+
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
44
+
45
+
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
46
+
47
+
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
48
+
49
+
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
50
+
51
+
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
52
+
53
+
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
54
+
55
+
END OF TERMS AND CONDITIONS
56
+
57
+
APPENDIX: How to apply the Apache License to your work.
58
+
59
+
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
60
+
61
+
Copyright [yyyy] [name of copyright owner]
62
+
63
+
Licensed under the Apache License, Version 2.0 (the "License");
64
+
you may not use this file except in compliance with the License.
65
+
You may obtain a copy of the License at
66
+
67
+
http://www.apache.org/licenses/LICENSE-2.0
68
+
69
+
Unless required by applicable law or agreed to in writing, software
70
+
distributed under the License is distributed on an "AS IS" BASIS,
71
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
72
+
See the License for the specific language governing permissions and
73
+
limitations under the License.
+18
LICENSES/MIT.txt
+18
LICENSES/MIT.txt
···
1
+
MIT License
2
+
3
+
Copyright (c) 2025 Lucas Baumann
4
+
5
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+
associated documentation files (the "Software"), to deal in the Software without restriction, including
7
+
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9
+
following conditions:
10
+
11
+
The above copyright notice and this permission notice shall be included in all copies or substantial
12
+
portions of the Software.
13
+
14
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
+22
-1
README.md
+22
-1
README.md
···
1
1
# Simple left right
2
2
3
3
Simpler Version of [left-right](https://crates.io/crates/left-right), which only allows one reader.
4
-
Also allows real-time safe read operations and is designed for this use-case.
4
+
Also allows real-time safe read operations and is designed for this use-case.
5
+
6
+
## Testing
7
+
This library is tested both with miri and cargo-mutants. Some tests are disabled for miri as they
8
+
leak memory or block on unlucky thread scheduling. They can be run by ignoring leaks or choosing a
9
+
threading seed that doesn't lock itself.
10
+
11
+
cargo-mutants currently has one mutation that isn't caught by the tests, because it changes a bitwise
12
+
operation to one that has the same result on the possible inputs. It also produces a couple of timeouts,
13
+
as the write locking spins forever.
14
+
15
+
PRs should keep this state as much as possible.
16
+
17
+
## std::mem::forget
18
+
Forgetting a Read/Write guard could lead to UB, so i detect that and panic if it happens. In order to make
19
+
the detection cheap it doesn't differentiate between cases where a forget would lead to UB and cases where
20
+
it doesn't. Just don't forget the guards and it won't panic.
21
+
22
+
## Git
23
+
This project is hosted on [tangled](https://tangled.org/did:plc:54jgbo4psy24qu2bk4njtpc4/simple-left-right/),
24
+
[github](https://github.com/luca3s/simple-left-right) and [codeberg](https://codeberg.org/increasing/simple-left-right).
25
+
You can create issues and PRs on any platform you like.
+1
known_mutants_regex.txt
+1
known_mutants_regex.txt
···
1
+
(replace \| with \^ in State::with_read|replace Shared<T>::release_read_lock with \(\)|replace State::can_write -> bool with false|replace Writer<T, O>::try_lock -> Option<WriteGuard<'_, T, O>> with None|replace <impl Drop for ReadGuard<'_, T>>::drop with \(\))
+35
-39
src/lib.rs
+35
-39
src/lib.rs
···
1
+
// SPDX-FileCopyrightText: 2025 Lucas Baumann
2
+
// SPDX-FileCopyrightText: Lucas Baumann
3
+
//
4
+
// SPDX-License-Identifier: Apache-2.0
5
+
// SPDX-License-Identifier: MIT
6
+
1
7
//! Simpler version of the left-right from Jon Gjengset library.
2
8
//!
3
9
//! Uses two copies of the value to allow doing small changes, while still allowing non-blocking reading.
···
30
36
#[derive(Debug)]
31
37
pub struct Reader<T> {
32
38
shared: NonNull<Shared<T>>,
39
+
locked: bool,
33
40
/// for drop check
34
41
_own: PhantomData<Shared<T>>,
35
42
}
···
42
49
}
43
50
44
51
/// this function never blocks. (`fetch_update` loop doesn't count)
45
-
#[inline]
46
52
pub fn lock(&mut self) -> ReadGuard<'_, T> {
47
-
let shared_ref = self.shared_ref();
48
-
53
+
if self.locked {
54
+
self.locked = false;
55
+
panic!("ReadGuard was forgotten");
56
+
}
57
+
self.locked = true;
58
+
// SAFETY: value just locked
59
+
let value = unsafe { &*self.shared_ref().lock_read().get() };
49
60
ReadGuard {
50
-
shared: shared_ref,
51
-
value: shared_ref.lock_read(),
52
-
reader: PhantomData,
61
+
value,
62
+
reader: self,
53
63
}
54
64
}
55
65
}
···
58
68
unsafe impl<T: Send> Send for Reader<T> {}
59
69
60
70
impl<T> Drop for Reader<T> {
61
-
#[inline]
62
71
fn drop(&mut self) {
63
72
// SAFETY: self.shared is valid and not used after this.
64
73
unsafe { Shared::drop(self.shared) };
74
+
assert!(!self.locked, "ReadGuard was forgotten");
65
75
}
66
76
}
67
77
···
71
81
/// Doesn't implement Clone as that would require refcounting to know when to unlock.
72
82
#[derive(Debug)]
73
83
pub struct ReadGuard<'a, T> {
74
-
shared: &'a Shared<T>,
75
-
value: Ptr,
76
-
/// `PhantomData` makes the borrow checker prove that there only ever is one `ReadGuard`.
77
-
/// This allows resetting the readstate without some kind of counter
78
-
reader: PhantomData<&'a mut Reader<T>>,
84
+
reader: &'a mut Reader<T>,
85
+
value: &'a T,
79
86
}
80
87
81
88
impl<T> Deref for ReadGuard<'_, T> {
82
89
type Target = T;
83
90
84
-
#[inline]
85
91
fn deref(&self) -> &Self::Target {
86
-
// SAFETY: ReadGuard was created, so the Writer knows not to write in this spot
87
-
unsafe { self.shared.get_value_ref(self.value) }
92
+
self.value
88
93
}
89
94
}
90
95
···
93
98
E: ?Sized,
94
99
T: AsRef<E>,
95
100
{
96
-
#[inline]
97
101
fn as_ref(&self) -> &E {
98
102
self.deref().as_ref()
99
103
}
100
104
}
101
105
102
-
// /// SAFETY: behaves like a ref to T. https://doc.rust-lang.org/std/marker/trait.Sync.html
103
-
// unsafe impl<T: Sync> Send for ReadGuard<'_, T> {}
104
-
// /// SAFETY: behaves like a ref to T. https://doc.rust-lang.org/std/marker/trait.Sync.html
105
-
// unsafe impl<T: Sync> Sync for ReadGuard<'_, T> {}
106
-
107
106
impl<T> Drop for ReadGuard<'_, T> {
108
-
#[inline]
109
107
fn drop(&mut self) {
110
108
// release the read lock
111
-
self.shared.release_read_lock();
109
+
self.reader.shared_ref().release_read_lock();
110
+
self.reader.locked = false;
112
111
}
113
112
}
114
113
···
121
120
write_ptr: Ptr,
122
121
// buffer is pushed at the back and popped at the front.
123
122
op_buffer: VecDeque<O>,
123
+
locked: bool,
124
124
// needed for drop_check
125
125
_own: PhantomData<Shared<T>>,
126
126
}
···
152
152
}
153
153
154
154
/// get a Reader if none exists
155
-
#[inline]
156
155
pub fn build_reader(&mut self) -> Option<Reader<T>> {
157
156
let shared_ref = self.shared_ref();
158
157
// SAFETY: all is_unique_with_increase requirements are satisfied.
···
162
161
Reader {
163
162
shared: self.shared,
164
163
_own: PhantomData,
164
+
locked: false,
165
165
}
166
166
})
167
167
}
···
170
170
171
171
impl<T: Absorb<O>, O> Writer<T, O> {
172
172
/// doesn't block. Returns None if the Reader has a `ReadGuard` pointing to the old value.
173
-
#[must_use]
174
173
pub fn try_lock(&mut self) -> Option<WriteGuard<'_, T, O>> {
174
+
if self.locked {
175
+
self.locked = false;
176
+
panic!("WriteGuard was forgotten");
177
+
}
175
178
self.shared_ref()
176
179
.lock_write(self.write_ptr)
177
180
.ok()
178
181
// locking was successful
179
182
.map(|()| {
180
-
// WriteGuard::new(self)
183
+
self.locked = true;
181
184
let mut guard = WriteGuard { writer: self };
182
185
while let Some(operation) = guard.writer.op_buffer.pop_front() {
183
186
guard.get_data_mut().absorb(operation);
···
190
193
impl<T: Clone, O> Writer<T, O> {
191
194
/// Creates a new Writer by cloning the value once to get two values
192
195
/// `T::clone()` shoulnd't give a different value, as that would make this library pretty useless
193
-
#[inline]
194
196
pub fn new(value: T) -> Self {
195
197
let (shared, write_ptr) = Shared::new(value, |value_1| value_1.clone());
196
198
Self {
···
198
200
write_ptr,
199
201
op_buffer: VecDeque::new(),
200
202
_own: PhantomData,
203
+
locked: false,
201
204
}
202
205
}
203
206
}
···
206
209
/// Creates a new Writer by calling `T::default()` twice to create the two values
207
210
///
208
211
/// Default impl of T needs to give the same result every time. Not upholding this doens't lead to UB, but turns the library basically useless
209
-
#[inline]
210
212
fn default() -> Self {
211
213
let (shared, write_ptr) = Shared::new(T::default(), |_| T::default());
212
214
Self {
···
214
216
write_ptr,
215
217
op_buffer: VecDeque::new(),
216
218
_own: PhantomData,
219
+
locked: false,
217
220
}
218
221
}
219
222
}
···
239
242
unsafe impl<T: Sync, O> Sync for Writer<T, O> {}
240
243
241
244
impl<T, O> Drop for Writer<T, O> {
242
-
#[inline]
243
245
fn drop(&mut self) {
244
246
// SAFETY: self.shared is valid and not used after this.
245
247
unsafe { Shared::drop(self.shared) };
248
+
assert!(!self.locked, "WriteGuard was forgotten");
246
249
}
247
250
}
248
251
···
254
257
/// Dropping this makes all changes available to the Reader.
255
258
#[derive(Debug)]
256
259
pub struct WriteGuard<'a, T, O> {
260
+
// can't hold a mut ref to T, as then it wouldn't be possible to write to both at the same time,
261
+
// which is an optimization i want to keep.
257
262
writer: &'a mut Writer<T, O>,
258
263
}
259
264
260
265
impl<T, O> WriteGuard<'_, T, O> {
261
266
/// Makes the changes available to the reader. Equivalent to `std::mem::drop(self)`
262
-
#[inline]
263
267
pub fn swap(self) {}
264
268
265
269
/// Gets the value currently being written to.
266
-
#[inline]
267
270
pub fn read(&self) -> &T {
268
271
// SAFETY: Only the WriteGuard can write to the values / create mut refs to them.
269
272
// The WriteGuard holds a mut ref to the writer so this function can't be called while a writeguard exists
···
293
296
impl<T: Absorb<O>, O: Clone> WriteGuard<'_, T, O> {
294
297
/// applies operation to the current write Value and stores it to apply to the other later.
295
298
/// If there is no reader the operation is applied to both values immediately and not stored.
296
-
#[inline]
297
299
pub fn apply_op(&mut self, operation: O) {
298
300
if let Some(shared) = self.writer.shared_mut() {
299
301
shared.value_1.get_mut().absorb(operation.clone());
···
305
307
}
306
308
}
307
309
308
-
// /// SAFETY: behaves like a &mut T and &mut Vec<O>. https://doc.rust-lang.org/stable/std/marker/trait.Sync.html
309
-
// unsafe impl<T: Send, O: Send> Send for WriteGuard<'_, T, O> {}
310
-
311
-
// /// Safety: can only create shared refs to T, not to O. https://doc.rust-lang.org/stable/std/marker/trait.Sync.html
312
-
// unsafe impl<T: Sync, O> Sync for WriteGuard<'_, T, O> {}
313
-
314
310
impl<T, O> Drop for WriteGuard<'_, T, O> {
315
-
#[inline]
316
311
fn drop(&mut self) {
317
312
self.writer.swap();
313
+
self.writer.locked = false;
318
314
}
319
315
}
320
316
+66
-1
tests/tests.rs
+66
-1
tests/tests.rs
···
1
1
#[cfg(test)]
2
2
mod usage_test {
3
-
use simple_left_right::{Absorb, WriteGuard, Writer};
3
+
use simple_left_right::{Absorb, ReadGuard, WriteGuard, Writer};
4
4
use std::{cell::Cell, hint, time::Duration};
5
5
6
6
fn spin_lock<T: Absorb<O>, O>(writer: &mut Writer<T, O>) -> WriteGuard<'_, T, O> {
···
107
107
108
108
let read_lock = reader.lock();
109
109
assert_eq!(*read_lock, 1);
110
+
}
111
+
112
+
#[test]
113
+
fn assert_writeguard_none() {
114
+
let mut writer = Writer::new(0);
115
+
let mut reader = writer.build_reader().unwrap();
116
+
let read_lock = reader.lock();
117
+
let mut write_lock = writer.try_lock().unwrap();
118
+
write_lock.apply_op(CounterAddOp(1));
119
+
assert_eq!(*read_lock, 0);
120
+
drop(write_lock);
121
+
// read lock is still being held, so can't lock
122
+
assert!(writer.try_lock().is_none());
123
+
drop(read_lock);
124
+
// now do it all again, to also trigger code paths for the second value
125
+
let read_lock = reader.lock();
126
+
let mut write_lock = writer.try_lock().unwrap();
127
+
write_lock.apply_op(CounterAddOp(1));
128
+
assert_eq!(*read_lock, 1);
129
+
drop(write_lock);
130
+
// read lock is still being held, so can't lock
131
+
assert!(writer.try_lock().is_none());
110
132
}
111
133
112
134
#[test]
···
274
296
lock.apply_op(CounterAddOp(1));
275
297
drop(lock);
276
298
}
299
+
}
300
+
301
+
#[test]
302
+
// attempt to find similar breakge to: https://github.com/rust-lang/rust/issues/63787
303
+
// i don't think it's possible, because you can't get a ref to the other value while still passing
304
+
// a ref through the function boundary. You can only get a ref to the other value when passing Reader,
305
+
// which already has a raw ptr. So i think i am not affected by this issue.
306
+
fn break_ref() {
307
+
#[derive(Clone, Copy)]
308
+
struct SetToNone;
309
+
impl Absorb<SetToNone> for Option<Box<i32>> {
310
+
fn absorb(&mut self, _operation: SetToNone) {
311
+
*self = None;
312
+
}
313
+
}
314
+
315
+
let mut writer = Writer::new(Some(Box::new(0)));
316
+
let mut reader = writer.build_reader().unwrap();
317
+
318
+
two_refs(&mut writer.try_lock().unwrap(), reader.lock());
319
+
320
+
fn two_refs(
321
+
writer: &mut WriteGuard<'_, Option<Box<i32>>, SetToNone>,
322
+
reader: ReadGuard<'_, Option<Box<i32>>>,
323
+
) {
324
+
drop(reader);
325
+
writer.apply_op(SetToNone);
326
+
}
327
+
}
328
+
329
+
#[test]
330
+
#[should_panic]
331
+
fn forget_lock() {
332
+
let mut writer: Writer<i32, CounterAddOp> = Writer::new(0);
333
+
let mut reader = writer.build_reader().unwrap();
334
+
335
+
let write = writer.try_lock().unwrap();
336
+
core::mem::forget(write);
337
+
let _ = writer.try_lock().unwrap();
338
+
339
+
let read = reader.lock();
340
+
core::mem::forget(read);
341
+
reader.lock();
277
342
}
278
343
}