Skip to content

Commit 0f3d022

Browse files
author
Ariel Ben-Yehuda
committed
ensure first-writer-wins consistently
1 parent ce862ae commit 0f3d022

File tree

2 files changed

+73
-11
lines changed

2 files changed

+73
-11
lines changed

library/core/src/error/provide.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,7 +1225,17 @@ impl<'a> Tagged<dyn Erased<'a> + 'a> {
12251225
if self.is_virtual() {
12261226
// consume returns None if the space is not satisfied
12271227
// SAFETY: `&raw const self.value` is valid
1228-
unsafe { (&raw const self.value).consume(TypeId::of::<I>()).is_some() }
1228+
// SAFETY: consume is defined to return either None or Some(I::Reified)
1229+
unsafe {
1230+
if let Some(res) = (&raw const self.value).consume(TypeId::of::<I>()) {
1231+
let ptr: NonNull<Option<I::Reified>> = res.cast();
1232+
// cast is fine since consume returns a pointer to an Option<I::Reified>
1233+
// value can be satisfied if the cell is empty
1234+
ptr.as_ref().is_none()
1235+
} else {
1236+
false
1237+
}
1238+
}
12291239
} else {
12301240
matches!(self.downcast::<I>(), Some(TaggedOption(None)))
12311241
}
@@ -1242,8 +1252,10 @@ impl<'a> Tagged<dyn Erased<'a> + 'a> {
12421252
if let Some(res) = (&raw const self.value).consume(TypeId::of::<I>()) {
12431253
let mut ptr: NonNull<Option<I::Reified>> = res.cast();
12441254
// cast is fine since consume_mut returns a pointer to an Option<I::Reified>
1245-
// could use `ptr::write` here, but this is not expected to be important enough
1246-
*ptr.as_mut() = Some(value);
1255+
let opt: &mut Option<I::Reified> = ptr.as_mut();
1256+
if opt.is_none() {
1257+
*opt = Some(value);
1258+
}
12471259
}
12481260
}
12491261
} else {
@@ -1259,13 +1271,15 @@ impl<'a> Tagged<dyn Erased<'a> + 'a> {
12591271
I: tags::Type<'a>,
12601272
{
12611273
if self.is_virtual() {
1262-
// SAFETY: consume_mut is defined to return either None or Some(I::Reified)
1274+
// SAFETY: consume is defined to return either None or Some(I::Reified)
12631275
unsafe {
12641276
if let Some(res) = (&raw const self.value).consume(TypeId::of::<I>()) {
12651277
let mut ptr: NonNull<Option<I::Reified>> = res.cast();
1266-
// cast is fine since consume_mut returns a pointer to an Option<I::Reified>
1267-
// could use `ptr::write` here, but this is not expected to be important enough
1268-
*ptr.as_mut() = Some(fulfil());
1278+
// cast is fine since consume returns a pointer to an Option<I::Reified>
1279+
let opt: &mut Option<I::Reified> = ptr.as_mut();
1280+
if opt.is_none() {
1281+
*opt = Some(fulfil());
1282+
}
12691283
}
12701284
}
12711285
} else {

library/coretests/tests/error.rs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use core::error::provide::{MultiRequestBuilder, MultiResponse, Request};
22
use core::error::{Error, request_ref, request_value};
33

4+
struct Invalid;
5+
#[derive(Debug, PartialEq, Eq)]
6+
struct Valid;
7+
48
// Test the `Request` API.
59
#[derive(Debug)]
610
struct SomeConcreteType {
@@ -13,10 +17,6 @@ impl std::fmt::Display for SomeConcreteType {
1317
}
1418
}
1519

16-
struct Invalid;
17-
#[derive(Debug, PartialEq, Eq)]
18-
struct Valid;
19-
2020
impl std::error::Error for SomeConcreteType {
2121
fn provide<'a>(&'a self, request: &mut Request<'a>) {
2222
request
@@ -128,6 +128,54 @@ fn test_error_combined_access_dyn() {
128128
assert_eq!(request.retrieve_value::<*const ()>(), None);
129129
}
130130

131+
#[derive(Debug)]
132+
struct ProvideMultipleTimes;
133+
134+
impl std::fmt::Display for ProvideMultipleTimes {
135+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136+
write!(f, "A")
137+
}
138+
}
139+
140+
impl std::error::Error for ProvideMultipleTimes {
141+
fn provide<'a>(&'a self, request: &mut Request<'a>) {
142+
let previous_satisfied_by_ref_of_string = request.would_be_satisfied_by_ref_of::<String>();
143+
request.provide_value::<String>("good".to_string());
144+
assert!(
145+
!request.would_be_satisfied_by_value_of::<String>(),
146+
"already provided String by value"
147+
);
148+
// "overwriting" the String should just be ignored
149+
request.provide_value::<String>("bad".to_string());
150+
// but providing a ref works
151+
if previous_satisfied_by_ref_of_string {
152+
assert!(request.would_be_satisfied_by_ref_of::<String>());
153+
request.provide_ref::<String>(const { &String::new() });
154+
}
155+
}
156+
}
157+
158+
#[test]
159+
fn test_provide_multiple_times_single() {
160+
let obj = ProvideMultipleTimes;
161+
let obj: &dyn Error = &obj;
162+
163+
assert_eq!(*request_ref::<String>(&*obj).unwrap(), String::new());
164+
assert_eq!(request_value::<String>(&*obj).unwrap(), "good");
165+
}
166+
167+
#[test]
168+
fn test_provide_multiple_times_multi() {
169+
let obj = ProvideMultipleTimes;
170+
let obj: &dyn Error = &obj;
171+
172+
let mut request =
173+
MultiRequestBuilder::new().with_value::<String>().with_ref::<String>().request(obj);
174+
175+
assert_eq!(*request.retrieve_ref::<String>().unwrap(), String::new());
176+
assert_eq!(request.retrieve_value::<String>().unwrap(), "good");
177+
}
178+
131179
fn assert_copy<T: Copy>(_t: T) {}
132180

133181
#[test]

0 commit comments

Comments
 (0)