pub struct UnsafeLock<T: ?Sized> { /* private fields */ }Implementations§
Source§impl<T> UnsafeLock<T>
impl<T> UnsafeLock<T>
Sourcepub unsafe fn new(value: T) -> Self
pub unsafe fn new(value: T) -> Self
§Safety
There must be no copies of the value. See Send implementation.
Examples found in repository?
9fn test_unsafelock() {
10 let Ok(num_cpus) = thread::available_parallelism() else {
11 return;
12 };
13 let mut num_threads = num_cpus.get();
14
15 let val = Rc::new(Cell::new(0_usize));
16
17 // UnsafeLock requires that there's no copies of the value.
18 // We're cloning the value here, but we're not going to use the original value so it's fine.
19 let lock = unsafe { UnsafeLock::new(val.clone()) };
20
21 // Threads will increase the value. The value will become N * num_threads.
22 const N: usize = 10_000;
23 let mut handles = Vec::new();
24 for _ in 0..num_threads {
25 let c_lock = lock.clone();
26 let handle = thread::spawn(move || {
27 for _ in 0..N {
28 unsafe {
29 let val = c_lock.lock().as_mut();
30 val.set(val.get() + 1);
31 c_lock.unlock();
32 }
33 }
34 });
35 handles.push(handle);
36 }
37
38 // UnsafeLock's guarantee is upheld when all data is under its protection. But, this block of
39 // code violate the safety. You can check this through thread sanitizer.
40 //
41 // num_threads += 1;
42 // for _ in 0..N {
43 // val.set(val.get() + 1); // val is outside the UnsafeLock
44 // }
45
46 for handle in handles {
47 handle.join().unwrap();
48 }
49
50 unsafe {
51 let val = lock.lock().as_ref();
52 assert_eq!(val.get(), N * num_threads);
53 lock.unlock();
54 }
55}Source§impl<T: ?Sized> UnsafeLock<T>
impl<T: ?Sized> UnsafeLock<T>
Sourcepub unsafe fn lock(&self) -> NonNull<T>
pub unsafe fn lock(&self) -> NonNull<T>
§Safety
- Do not dereference to the returned pointer after
unlock. - Do not make copies of
Tfrom the returned pointer. SeeSend implementation.
Examples found in repository?
9fn test_unsafelock() {
10 let Ok(num_cpus) = thread::available_parallelism() else {
11 return;
12 };
13 let mut num_threads = num_cpus.get();
14
15 let val = Rc::new(Cell::new(0_usize));
16
17 // UnsafeLock requires that there's no copies of the value.
18 // We're cloning the value here, but we're not going to use the original value so it's fine.
19 let lock = unsafe { UnsafeLock::new(val.clone()) };
20
21 // Threads will increase the value. The value will become N * num_threads.
22 const N: usize = 10_000;
23 let mut handles = Vec::new();
24 for _ in 0..num_threads {
25 let c_lock = lock.clone();
26 let handle = thread::spawn(move || {
27 for _ in 0..N {
28 unsafe {
29 let val = c_lock.lock().as_mut();
30 val.set(val.get() + 1);
31 c_lock.unlock();
32 }
33 }
34 });
35 handles.push(handle);
36 }
37
38 // UnsafeLock's guarantee is upheld when all data is under its protection. But, this block of
39 // code violate the safety. You can check this through thread sanitizer.
40 //
41 // num_threads += 1;
42 // for _ in 0..N {
43 // val.set(val.get() + 1); // val is outside the UnsafeLock
44 // }
45
46 for handle in handles {
47 handle.join().unwrap();
48 }
49
50 unsafe {
51 let val = lock.lock().as_ref();
52 assert_eq!(val.get(), N * num_threads);
53 lock.unlock();
54 }
55}Sourcepub unsafe fn unlock(&self)
pub unsafe fn unlock(&self)
Examples found in repository?
9fn test_unsafelock() {
10 let Ok(num_cpus) = thread::available_parallelism() else {
11 return;
12 };
13 let mut num_threads = num_cpus.get();
14
15 let val = Rc::new(Cell::new(0_usize));
16
17 // UnsafeLock requires that there's no copies of the value.
18 // We're cloning the value here, but we're not going to use the original value so it's fine.
19 let lock = unsafe { UnsafeLock::new(val.clone()) };
20
21 // Threads will increase the value. The value will become N * num_threads.
22 const N: usize = 10_000;
23 let mut handles = Vec::new();
24 for _ in 0..num_threads {
25 let c_lock = lock.clone();
26 let handle = thread::spawn(move || {
27 for _ in 0..N {
28 unsafe {
29 let val = c_lock.lock().as_mut();
30 val.set(val.get() + 1);
31 c_lock.unlock();
32 }
33 }
34 });
35 handles.push(handle);
36 }
37
38 // UnsafeLock's guarantee is upheld when all data is under its protection. But, this block of
39 // code violate the safety. You can check this through thread sanitizer.
40 //
41 // num_threads += 1;
42 // for _ in 0..N {
43 // val.set(val.get() + 1); // val is outside the UnsafeLock
44 // }
45
46 for handle in handles {
47 handle.join().unwrap();
48 }
49
50 unsafe {
51 let val = lock.lock().as_ref();
52 assert_eq!(val.get(), N * num_threads);
53 lock.unlock();
54 }
55}Trait Implementations§
Source§impl<T: Clone + ?Sized> Clone for UnsafeLock<T>
impl<T: Clone + ?Sized> Clone for UnsafeLock<T>
Source§fn clone(&self) -> UnsafeLock<T>
fn clone(&self) -> UnsafeLock<T>
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl<T: Debug> Debug for UnsafeLock<T>
impl<T: Debug> Debug for UnsafeLock<T>
impl<T: ?Sized> Send for UnsafeLock<T>
Unlike Mutex, this lock is Send and Sync regardless of whether T is Send or not.
That’s because T is always under the protection of this lock whenever clients uphold the
safety of the lock.
§Safety
There must be no copies of the value inside this lock. Clients must not have copies before
and after creation of this lock. Because this lock assumes that the value inside the lock
has no copies, so the lock is Send and Sync even if T isn’t.
For example, imagine that you have multiple Rc<T>, which is not Send, and make
UnsafeLock<Rc<T>> from one copy of them, then you send the lock to another thread. It can
cause data race because of Rc<T> outside this lock.
But if you have only one T and wrap it within UnsafeLock, then T is guaranteed to be
protected by this lock. Making copies of UnsafeLock<T>, sending it to another thread, and
accessing it from another thread does not break the guarantee. But you still can make copies
of T from its pointer, but you shouldn’t.