GithubHelp home page GithubHelp logo

seqlock's Introduction

SeqLock

Crates.io

Documentation

This library provides the SeqLock type, which is a form of reader-writer lock that is heavily optimized for readers.

In certain situations, SeqLock can be two orders of magnitude faster than the standard library RwLock type. Another advantage is that readers cannot starve writers: a writer will never block even if there are readers currently accessing the SeqLock.

The only downside of SeqLock is that it only works on types that are Copy. This means that it is unsuitable for types that contains pointers to owned data.

You should instead use a RwLock if you need a reader-writer lock for types that are not Copy.

Implementation

The implementation is based on the Linux seqlock type. Each SeqLock contains a sequence counter which tracks modifications to the locked data. The basic idea is that a reader will perform the following operations:

  1. Read the sequence counter.
  2. Read the data in the lock.
  3. Read the sequence counter again.
  4. If the two sequence counter values differ, loop back and try again.
  5. Otherwise return the data that was read in step 2.

Similarly, a writer will increment the sequence counter before and after writing to the data in the lock. Once a reader sees that the sequence counter has not changed while it was reading the data, it can safely return that data to the caller since it is known to be in a consistent state.

Example

use seqlock::SeqLock;

let lock = SeqLock::new(5);

{
    // Writing to the data involves a lock
    let mut w = lock.lock_write();
    *w += 1;
    assert_eq!(*w, 6);
}

{
    // Reading the data is a very fast operation
    let r = lock.read();
    assert_eq!(r, 6);
}

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

seqlock's People

Contributors

amanieu avatar nico-abram avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

seqlock's Issues

Is it OK to use ptr::read() instead of ptr::read_volatile() in SeqLock::read() ?

Hi, I'm learning concurrency programming these days, just curious to know if it is OK to use ptr::read() below:

let result = unsafe { ptr::read_volatile(self.data.get() as *mut MaybeUninit<T>) };

when write operation finished, there is a Release ording here:

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

And to read seq1, there is a Acquire ording:

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

So these is a Release-Acquire relationship here, then we can get these conclusions:

  1. if a write operation is completed before reading seq1, I think we can always get the latest value by using ptr::read() because the Release-Acquire relationship.
  2. if a write operation is before reading seq1, but not finished when reading seq1, then seq1 & 1 would be 1, just retry.
  3. if a write operation begins after reading seq1, then seq1 must be different with seq2, then it's also ok to use ptr::read().

I don't know if these conclusions are correct, hope to get some reviews, thanks in advance.

Technically torn reads can succeed

This is a ridiculous corner case.

Thread 1:
Starts a read, getting a sequence number, and reads some of the data.

Other threads:
Successfully perform exactly 2^(mem::size_of::<usize>() - 1) writes, leaving the sequence number effectively unchanged.

Thread 1:
Finishes reading the data, and sees the sequence number unchanged. It then returns the torn read.

On 64-bit machines, this would take a few lifetimes to occur, on 32-bit machines it might require the reader thread to be inactive for around one second.

Possible memory order issue

Hi!

I read a code of create and noticed this segment:

            // We need to use a volatile read here because the data may be
            // concurrently modified by a writer. We also use MaybeUninit in
            // case we read the data in the middle of a modification.
            let result = unsafe { ptr::read_volatile(self.data.get() as *mut MaybeUninit<T>) };

            // Make sure the seq2 read occurs after reading the data. What we
            // ideally want is a load(Release), but the Release ordering is not
            // available on loads.
            fence(Ordering::Acquire);

            // If the sequence number is the same then the data wasn't modified
            // while we were reading it, and can be returned.
            let seq2 = self.seq.load(Ordering::Relaxed);
            if seq1 == seq2 {
                return unsafe { result.assume_init() };
            }

Shouldn't fence here use AcqRel? While using Acquire indeed prevents moving second atomic load up, it doesn't prevent moving read_volatile down. So CPU may reorder your code like this:

seq1 = load_atomic;
seq2 = load_atomic;
if seq1 == seq2 {
   return load value; // This is valid for moving down because acquire doesn't prevent moving down.
}

Make 1.0 version?

Since the API hasn't changed much in ~5 years, could this crate be bumped to 1.0?

Crates implementing concurrency primitives with version numbers < 1.0 look (justifiably) scary, which I don't think should apply to this crate.

cannot call non-const fn `parking_lot::lock_api::Mutex::<parking_lot::RawMutex, ()>::new` in constant functions

I found a use-case for seqlock in my repo where I had a Copy type—yay! I added seqlock, and replaced an RwLock with SeqLock. Unfortunately I hit this build error:

error[E0015]: cannot call non-const fn `parking_lot::lock_api::Mutex::<parking_lot::RawMutex, ()>::new` in constant functions
   --> /home/.cargo/registry/src/index.crates.io-6f17d22bba15001f/seqlock-0.2.0/src/lib.rs:101:20
    |
101 |             mutex: Mutex::new(()),
    |                    ^^^^^^^^^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants

I see that older versions of lock-api did not have const on Mutext::new()1, but now it does. Is there a good fix for this? I see that lock-api has a feature (has_const_fn_trait_bound) that sets the const-ness. Since seqlock does not depend on lock-api directly, but instead through parking_lot, I'm not sure what's the right way to handle this. Maybe there's something I can set on my end to resolve this though?

Footnotes

  1. https://docs.rs/lock_api/0.4.6/lock_api/struct.Mutex.html#method.new

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.