/* * Copyright 2015 Ben Manes. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.benmanes.caffeine.cache.buffer; import java.util.function.Consumer; import com.github.benmanes.caffeine.base.UnsafeAccess; import com.github.benmanes.caffeine.cache.ReadBuffer; /** * A simple ring buffer implementation that watches both the head and tail counts to acquire a * slot. This version uses a contiguous array that spaces each element to avoid false sharing. * * @author ben.manes@gmail.com (Ben Manes) */ final class ManyToOneSpacedBuffer<E> extends ManyToOneSpacedHeader.ReadAndWriteCounterRef<E> { static final int BASE = UnsafeAccess.UNSAFE.arrayBaseOffset(Object[].class); static final int SHIFT = 31 - Integer.numberOfLeadingZeros( UnsafeAccess.UNSAFE.arrayIndexScale(Object[].class)); // Assume 4-byte references and 64-byte cache line (16 elements per line) static final int SPACED_SIZE = BUFFER_SIZE << 4; static final int SPACED_MASK = SPACED_SIZE - 1; static final int OFFSET = 16; final Object[] buffer; ManyToOneSpacedBuffer() { buffer = new Object[SPACED_SIZE]; } @Override public int offer(E e) { long head = readCounter; long tail = relaxedWriteCounter(); long size = (tail - head); if (size >= SPACED_SIZE) { return FULL; } if (casWriteCounter(tail, tail + OFFSET)) { long offset = ((tail & SPACED_MASK) << SHIFT) + BASE; UnsafeAccess.UNSAFE.putOrderedObject(buffer, offset, e); return SUCCESS; } return FAILED; } @Override @SuppressWarnings("unchecked") public void drainTo(Consumer<E> consumer) { long head = readCounter; long tail = relaxedWriteCounter(); long size = (tail - head); if (size == 0) { return; } do { long offset = ((head & SPACED_MASK) << SHIFT) + BASE; E e = (E) UnsafeAccess.UNSAFE.getObjectVolatile(buffer, offset); if (e == null) { // not published yet break; } UnsafeAccess.UNSAFE.putOrderedObject(buffer, offset, null); consumer.accept(e); head += OFFSET; } while (head != tail); lazySetReadCounter(head); } @Override public int reads() { return (int) readCounter / OFFSET; } @Override public int writes() { return (int) writeCounter / OFFSET; } } /** The namespace for field padding through inheritance. */ final class ManyToOneSpacedHeader { static abstract class PadReadCounter<E> extends ReadBuffer<E> { long p00, p01, p02, p03, p04, p05, p06, p07; long p10, p11, p12, p13, p14, p15, p16, p17; } /** Enforces a memory layout to avoid false sharing by padding the read count. */ static abstract class ReadCounterRef<E> extends PadReadCounter<E> { static final long READ_OFFSET = UnsafeAccess.objectFieldOffset(ReadCounterRef.class, "readCounter"); volatile long readCounter; void lazySetReadCounter(long count) { UnsafeAccess.UNSAFE.putOrderedLong(this, READ_OFFSET, count); } } static abstract class PadWriteCounter<E> extends ReadCounterRef<E> { long p20, p21, p22, p23, p24, p25, p26, p27; long p30, p31, p32, p33, p34, p35, p36, p37; } /** Enforces a memory layout to avoid false sharing by padding the write count. */ static abstract class ReadAndWriteCounterRef<E> extends PadWriteCounter<E> { static final long WRITE_OFFSET = UnsafeAccess.objectFieldOffset(ReadAndWriteCounterRef.class, "writeCounter"); volatile long writeCounter; long relaxedWriteCounter() { return UnsafeAccess.UNSAFE.getLong(this, WRITE_OFFSET); } boolean casWriteCounter(long expect, long update) { return UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_OFFSET, expect, update); } } }