Inline classes and the memory model

Florian Weimer fw at deneb.enyo.de
Thu Mar 5 16:51:06 UTC 2020


Sorry if this has been brought up before.  Consisder this example,
which is intended to simmulate a buffer-like class that has a pointer
to a backing array, a capacity, and a size (where the size must be no
greater than the capacity etc.):

import java.util.Random;

public class Consistency {
    public static void main(String[] args) {
        Holder holder = new Holder();

        new Thread() {
            public void run() {
                while (true) {
                    Slice slice = holder.slice;
                    if (!slice.consistent())
                        System.out.println(slice);
                    ++holder.counter;
                }
            }
        }.start();

        while (true) {
            holder.update();
        }
    }

    static inline class Slice {
        final long ptr;
        final int capacity;
        final int size;

        Slice(Random random) {
            capacity = random.nextInt(1 << 20);
            size = random.nextInt(capacity + 1);
            // Use the pointer to encode both the capacity and size.
            ptr = capacity * ((1L << 20) + 1) + size;
        }

        boolean consistent() {
            return ptr == capacity * ((1L << 20) + 1) + size
                && size <= capacity;
        }

        public String toString() {
            return String.format("ptr=0x%x capacity=%d size=%d",
                ptr, capacity, size);
        }
    }

    static class Holder {
        final Random random = new Random(1);
        Slice slice = new Slice(random);
        volatile int counter;

        void update() {
            slice = new Slice(random);
            ++counter;
        }
    }
}

If Slice is not an inline class, the barrier at the end of such
constructors together with the barrier implied in a pointer load
ensures that all created objects are consistent and always observed as
such.  With the inline keyword added, this is no longer the case.

This suggests to me that inline classes cannot protect their
invariants.  Will that limit their usefulness?

If you wonder how Go solves this problem with its built-in slice
types, the answer is that it does not.  Go is simply not memory-safe
if goroutines can execute concurrently on multiple processors.


More information about the valhalla-dev mailing list