Is it possible to compare the same UnsafeCell with itself?

I don't want to run in the x-y-problem, so this is what I am trying to achieve:
I want to finally have a PartialEq implementation for a Mutex.

Comparing it in a single threaded environment would be to just lock the mutex temporarily and the other mutex and then compare the dereferenced values like this:

let first = Mutex::new(1);
let other = Mutex::new(1);

assert!(first.lock().unwrap().deref() == other.lock().unwrap().deref());

This can cause problems with an Arc or Rc, because the above code would result in a deadlock.

Implementing the trait directly will give me access to the internal UnsafeCell, which should make it possible to check if they point to the same location, by doing something like this

let cell = /* UnsafeCell<T> */;
let other_cell = /* UnsafeCell<T> which is the same cell as `cell`*/;
assert!(cell.get() == other_cell.get());

Which trait are you referring to here? You can't add a PartialEq impl for Mutex<T> because Mutex is defined by the standard library and not your code.

One way of approaching this is to first check for reference equality using std::ptr::eq() or Arc::ptr_eq() (if two &Mutex<T> or Arc<T>'s point at the same Mutex<T>, they're equal), falling back to locking both mutexes and comparing the inner T.

fn mutexes_are_equal<T>(first: &Mutex<T>, second: &Mutex<T>) -> bool
where T: PartialEq
{
  std::ptr::eq(first, second) || first.lock().unwrap() == second.lock().unwrap()
}

fn arc_mutexes_are_equal<T>(first: &Arc<Mutex<T>>, second: &Arc<Mutex<T>>) -> bool
where T: PartialEq
{
  Arc::ptr_eq(first, second) || first.lock().unwrap() == second.lock().unwrap()
}

You probably want to stay away from using UnsafeCell directly. I feel like there would be too many possible footguns and you could accidentally break Mutex's invariants.

3 Likes

Thank you for your response. My goal is to make it possible to compare 2 mutexes from the standard library (I will submit a PR this weekend), but I have to come up with a safe way to compare 2 mutexes, that doesn't deadlock.

I didn't know how this could be done in a safe way, so I decided to ask here, for a possible solution :hugs:

This is what my implementation would look like;

impl<T: PartialEq> PartialEq for Mutex<T> {
    fn eq(&self, other: &Self) -> bool {
        if ::core::ptr::eq(&self, &other) {
              true
        } else {
            other.lock().unwrap().deref() == self.lock().unwrap().deref()
    }
}

of course this can still cause dead-locks, if the Mutex is locked by another thread and never unlocked, but this should be expected behavior.

Thank you :blush:

You would need T: Eq for this implementation, otherwise reflexive equality is not guaranteed. For example, Mutex<f64> containing NAN should return false even when compared to itself. To keep general T: PartialEq, you could still take just the self lock and compare the inner value to itself.

1 Like

oh thanks for pointing this out