/*
 * 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.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

import com.github.benmanes.caffeine.base.UnsafeAccess;
import com.github.benmanes.caffeine.cache.ReadBuffer;

/**
 * A ring buffer implemented using the FastFlow technique to reduce contention caused by the
 * drain invalidating the read counter.
 *
 * @author ben.manes@gmail.com (Ben Manes)
 */
final class FastFlowBuffer<E> extends FastFlowHeader.ReadAndWriteCounterRef<E> {
  final AtomicReference<E>[] buffer;

  @SuppressWarnings({"unchecked", "cast", "rawtypes"})
  public FastFlowBuffer() {
    buffer = new AtomicReference[BUFFER_SIZE];
    for (int i = 0; i < BUFFER_SIZE; i++) {
      buffer[i] = new AtomicReference<>();
    }
  }

  @Override
  public int offer(E e) {
    long head = readCache;
    long tail = relaxedWriteCounter();

    long wrap = (tail - BUFFER_SIZE);
    if (head <= wrap) {
      head = readCounter;
      if (head <= wrap) {
        return FULL;
      }
      lazySetReadCache(head);
    }

    if (casWriteCounter(tail, tail + 1)) {
      int index = (int) (tail & BUFFER_MASK);
      buffer[index].lazySet(e);
      return SUCCESS;
    }
    return FAILED;
  }

  @Override
  public void drainTo(Consumer<E> consumer) {
    long head = readCounter;
    long tail = relaxedWriteCounter();
    long size = (tail - head);
    if (size == 0) {
      return;
    }
    do {
      int index = (int) (head & BUFFER_MASK);
      AtomicReference<E> slot = buffer[index];
      E e = slot.get();
      if (e == null) {
        // not published yet
        break;
      }
      slot.lazySet(null);
      consumer.accept(e);
      head++;
    } while (head != tail);
    lazySetReadCounter(head);
  }

  @Override
  public int reads() {
    return (int) readCounter;
  }

  @Override
  public int writes() {
    return (int) writeCounter;
  }
}

/** The namespace for field padding through inheritance. */
final class FastFlowHeader {

  abstract static class PadReadCache<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. */
  abstract static class ReadCacheRef<E> extends PadReadCache<E> {
    static final long READ_CACHE_OFFSET =
        UnsafeAccess.objectFieldOffset(ReadCacheRef.class, "readCache");

    volatile long readCache;

    void lazySetReadCache(long count) {
      UnsafeAccess.UNSAFE.putOrderedLong(this, READ_CACHE_OFFSET, count);
    }
  }

  abstract static class PadReadCounter<E> extends ReadCacheRef<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 read count. */
  abstract static 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);
    }
  }

  abstract static class PadWriteCounter<E> extends ReadCounterRef<E> {
    long p40, p41, p42, p43, p44, p45, p46, p47;
    long p50, p51, p52, p53, p54, p55, p56, p57;
  }

  /** Enforces a memory layout to avoid false sharing by padding the write count. */
  abstract static 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);
    }
  }
}