UnsafeLock

Struct UnsafeLock 

Source
pub struct UnsafeLock<T: ?Sized> { /* private fields */ }

Implementations§

Source§

impl<T> UnsafeLock<T>

Source

pub unsafe fn new(value: T) -> Self

§Safety

There must be no copies of the value. See Send implementation.

Examples found in repository?
tests-san/tsan.rs (line 19)
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>

Source

pub unsafe fn lock(&self) -> NonNull<T>

§Safety
  • Do not dereference to the returned pointer after unlock.
  • Do not make copies of T from the returned pointer. See Send implementation.
Examples found in repository?
tests-san/tsan.rs (line 29)
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

pub unsafe fn unlock(&self)

§Safety

Must follow lock.

Examples found in repository?
tests-san/tsan.rs (line 31)
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>

Source§

fn clone(&self) -> UnsafeLock<T>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug> Debug for UnsafeLock<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

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.

Source§

impl<T: ?Sized> Sync for UnsafeLock<T>

Auto Trait Implementations§

§

impl<T> Freeze for UnsafeLock<T>
where T: ?Sized,

§

impl<T> !RefUnwindSafe for UnsafeLock<T>

§

impl<T> Unpin for UnsafeLock<T>
where T: ?Sized,

§

impl<T> !UnwindSafe for UnsafeLock<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.