Repository: qdrant/qdrant
File: lib/trififo/src/seqlock.rs (Rust)

Code before:
    }

    fn read<U, F: Fn(&T) -> U>(&self, callback: F) -> U {
        loop {
            let seq1 = self.seq.load(Ordering::Acquire);

            // if odd, it is locked, wait until unlocked.
            if seq1 & 1 == 1 {
                std::hint::spin_loop();
                continue;
            }

            let result = callback(unsafe { &*self.inner.get() });

            // ensure that the read is complete before checking seq again
            fence(Ordering::Acquire);
            let seq2 = self.seq.load(Ordering::Relaxed);

            // only return if seq did not change.
            if seq1 == seq2 {
                return result;
            }
        }
    }

    fn write(&self, callback: impl FnOnce(&mut T)) {
        let seq = self.seq.load(Ordering::Relaxed);
        self.seq.store(seq.wrapping_add(1), Ordering::Relaxed);
        // ensure seq has been written before running the callback
        fence(Ordering::Release);

        callback(unsafe { &mut *self.inner.get() });

        self.seq.store(seq.wrapping_add(2), Ordering::Release);
    }
}

/// This structure can read the protected resource at any time, but it will:
/// - busy-wait if a write is taking place.
/// - or repeat the read if a write took place in between the start and end
///   of the read.
pub struct SeqLockReader<T> {
    lock: Arc<SeqLock<T>>,
}

unsafe impl<T: Send> Send for SeqLockReader<T> {}
unsafe impl<T: Sync> Sync for SeqLockReader<T> {}

impl<T> Clone for SeqLockReader<T> {
    fn clone(&self) -> Self {
        SeqLockReader {

Review comment (suggestion):
This function has no protection against concurrent writes.

In my opinion we should either:
- make it `&mut` (which we don't like)
- panic if the seq we read is odd

Code after:
            let seq1 = self.seq.load(Ordering::Acquire);

            // if odd, it is locked, wait until unlocked.
            if seq1 & 1 == 1 {
                std::hint::spin_loop();
                continue;
            }

            let result = callback(unsafe { &*self.inner.get() });

            // ensure that the read is complete before checking seq again
            fence(Ordering::Acquire);
            let seq2 = self.seq.load(Ordering::Relaxed);

            // only return if seq did not change.
            if seq1 == seq2 {
                return result;
            }
        }
    }
}

/// This structure can read the protected resource at any time, but it will:
/// - busy-wait if a write is taking place.
/// - or repeat the read if a write took place in between the start and end
///   of the read.
pub struct SeqLockReader<T: SeqLockSafe> {
    lock: Arc<SeqLock<T>>,
}

unsafe impl<T: Send + SeqLockSafe> Send for SeqLockReader<T> {}
unsafe impl<T: Sync + SeqLockSafe> Sync for SeqLockReader<T> {}

impl<T: SeqLockSafe> Clone for SeqLockReader<T> {
    fn clone(&self) -> Self {
        SeqLockReader {
            lock: Arc::clone(&self.lock),
        }
    }
}

impl<T: SeqLockSafe> SeqLockReader<T> {
    pub fn read<U, F: Fn(&T) -> U>(&self, callback: F) -> U {
        self.lock.read(callback)
    }
}

/// This structure can get mutable access to the structure at any time.
/// It is intentionally `!Sync`, so there should not be other writers.
///
/// Due to the nature of a seqlock, this will not contend with the