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

Code before:
            final_pair.0, final_pair.1,
            "Final read observed inconsistent values"
        );
        assert_eq!(
            final_pair.0, 2000usize,
            "Final value should be last writer value"
        );
    }

    /// ```compile_fail
    /// use std::sync::Arc;
    /// use std::thread;
    /// use super::*;
    ///
    /// // Attempting to share the writer across threads via Arc should fail to compile,
    /// // because SeqLockWriter is intentionally not Sync.
    /// let (_reader, writer) = SeqLock::new_reader_writer(Pair { a: 0, b: 0 });
    /// let shared = Arc::new(writer);
    /// let shared_clone = shared.clone();
    ///
    /// // Moving `shared_clone` into a new thread requires `Arc<SeqLockWriter<_>>` to be Send,
    /// // which in turn requires `SeqLockWriter<_>` to be Sync. This should fail to compile.
    /// thread::spawn(move || {
    ///     shared_clone.write(|p| { p.a = 1; p.b = 1; });
    /// }).join().unwrap();
    /// ```
    #[test]
    fn writer_cannot_be_shared_across_threads() {
        // The relevant check is the `compile_fail` doctest.
    }
}


Review comment (bug):
This test doesn't run.
- Doctests are not collected for items under `#[cfg(test)]`/`#[test]`.
- On CI we use `nextest` which doesn't support doctests.
- And even if we fix it, we can't be sure that it fails for the right reason. (see the bot comment above)

I suggest not to use doctests and use `static_assertions::assert_not_impl_any!` instead to prove it's not `Sync`/`Send`.

Or, at least, make them far shorter and follow https://rust-unofficial.github.io/too-many-lists/sixth-send-sync.html, and manually verify that they fail for the right reason by running `cargo test -p trififo --doc -- --no-capture`.

Code after:
            });
            reader_handles.push(handle);
        }

        let num_writes = 20000;
        // Move the writer into its own thread and perform many updates.
        let writer_handle = {
            thread::spawn(move || {
                // Perform many updates. Within the write callback we intentionally
                // perform a split update (write 'a' then sleep then write 'b')
                // to create a window where a naive reader could observe inconsistent state.
                for i in 1..=num_writes {
                    writer.write(|p| {
                        p.a = i;
                        // Small pause to widen the race window if seqlock were broken.
                        thread::sleep(Duration::from_nanos(100));
                        p.b = i;
                    });
                    // Give readers some time to run between writes.
                    if i % 100 == 0 {
                        thread::sleep(Duration::from_micros(10));
                    }
                }
            })
        };

        // Wait for writer to finish, then tell readers to stop.
        writer_handle.join().expect("writer panicked");
        writer_done.store(true, Ordering::Relaxed);

        // Collect reader results and ensure they performed some reads.
        let mut total_reads = 0usize;
        for h in reader_handles {
            let reads = h.join().expect("reader panicked");
            total_reads += reads;
        }

        dbg!(total_reads);
        assert!(
            total_reads > 10,
            "Readers should have performed at least ten successful reads"
        );

        // Final sanity check: read final value and ensure a == b with expected final value.
        let final_pair = reader.read(|p| (p.a, p.b));
        assert_eq!(
            final_pair.0, final_pair.1,
            "Final read observed inconsistent values"
        );
        assert_eq!(
            final_pair.0, num_writes,