Question

Another question made me wonder if the seqlock can be efficiently implemented with a volatile version counter in Java.

Here's a prototypical implementation, for the case there will only ever be a single writer thread:

class Seqlock {
  private volatile long version = 0;
  private final byte[] data = new byte[10];

  void write(byte[] newData) {
    version++;  // 1
    System.arraycopy(newData, 0, data, 0, data.length);  // 2
    version++;  // 3
  }

  byte[] read() {
    long v1, v2;
    byte[] ret = new byte[data.length];
    do {
      v1 = version; // 4
      System.arraycopy(data, 0, ret, 0, data.length);  // 5
      v2 = version; // 6
    } while (v1 != v2 || (v1 & 1) == 1);
  }
}

The basic idea is to increment the version number before and after writing, and for readers to check they got a "consistent" read by verifying that the version numbers were the same and even, as odd indicates a "write in progress".

There are all sorts of happens-before relationships between the key actions in the writer thread and readers threads since version is volatile.

I can't, however, see what prevents the write at (2) from moving up above (1) and thus cause a reader to see a write in progress.

For example, the following synchronization order of volatile reads and writes, using the labels in the comments next to each line (showing also the data reads and writes which are not volatile and thus not part of the synchronization order, indented):

1a (version is now 1)
  2a (not part of the synchronization order)
3 (version is now 2)
4 (read version == 2, happens before 3)
  5 (not part of the synchronization order)
6 (read version == 2, happens before 4 and hence 3)
1b (second write, version is now 3)
  2b (not part of the synchronization order)

ISTM that there is no happens-before between 5 (the read of the data) and 2b (the second write of the data, so it is possible for 2b to occur before the read and wrong data to be read.

If that's true, does declaring write() as synchronized help?

Was it helpful?

Solution

In java you can implement a shared buffer (or other object) very simply:

public class SharedBuffer {

  private volatile byte[] _buf;

  public void write(byte[] buf) {
    _buf = buf;
  }

  public byte[] read() {
    // maybe copy here if you are worried about passing out the internal reference
    return _buf;
  }
}

obviously, this is not a "seqlock".

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top