// Path: src/main/java/org/terracotta/offheapstore/paging/PhantomReferenceLimitedPageSource.java
// public class PhantomReferenceLimitedPageSource implements PageSource {
// 
//   private final ReferenceQueue<ByteBuffer> allocatedBuffers = new ReferenceQueue<>();
//   private final Map<PhantomReference<ByteBuffer>, Integer> bufferSizes = new ConcurrentHashMap<>();
// 
//   private final AtomicLong max;
// 
//   /**
//    * Create a source that will allocate at most {@code max} bytes.
//    *
//    * @param max the maximum total size of all available buffers
//    */
//   public PhantomReferenceLimitedPageSource(long max) {
//       this.max = new AtomicLong(max);
//   }
// 
//   /**
//    * Allocates a byte buffer of the given size.
//    * <p>
//    * This {@code BufferSource} places no restrictions on the requested size of
//    * the buffer.
//    */
//   @Override
//   public Page allocate(int size, boolean thief, boolean victim, OffHeapStorageArea owner) {
//       while (true) {
//           processQueue();
//           long now = max.get();
//           if (now < size) {
//             return null;
//           } else if (max.compareAndSet(now, now - size)) {
//             ByteBuffer buffer;
//             try {
//               buffer = ByteBuffer.allocateDirect(size);
//             } catch (OutOfMemoryError e) {
//               return null;
//             }
//             bufferSizes.put(new PhantomReference<>(buffer, allocatedBuffers), size);
//             return new Page(buffer, owner);
//           }
//       }
//   }
// 
//   /**
//    * Frees the supplied buffer.
//    * <p>
//    * This implementation is a no-op, no validation of the supplied buffer is
//    * attempted, as freeing of allocated buffers is monitored via phantom
//    * references.
//    */
//   @Override
//   public void free(Page buffer) {
//     //no-op
//   }
// 
//   private void processQueue() {
//       while (true) {
//           Reference<?> ref = allocatedBuffers.poll();
// 
//           if (ref == null) {
//               return;
//           } else {
//               Integer size = bufferSizes.remove(ref);
//               max.addAndGet(size.longValue());
//           }
//       }
//   }
// 
//   @Override
//   public String toString() {
//     StringBuilder sb = new StringBuilder("PhantomReferenceLimitedBufferSource\n");
//     Collection<Integer> buffers = bufferSizes.values();
//     sb.append("Bytes Available   : ").append(max.get()).append('\n');
//     sb.append("Buffers Allocated : (count=").append(buffers.size()).append(") ").append(buffers);
//     return sb.toString();
//   }
// }
// 
// Path: src/test/java/org/terracotta/offheapstore/util/RetryAssert.java
// public static <T> void assertBy(long time, TimeUnit unit, Callable<T> value, Matcher<? super T> matcher) {
//     boolean interrupted = Thread.interrupted();
//     try {
//       long end = System.nanoTime() + unit.toNanos(time);
//         for (long sleep = 10; ; sleep <<= 1L) {
//             try {
//                 Assert.assertThat(value.call(), matcher);
//                 return;
//             } catch (Throwable t) {
//                 //ignore - wait for timeout
//             }
// 
//             long remaining = end - System.nanoTime();
//             if (remaining <= 0) {
//                 break;
//             } else {
//                 try {
//                     Thread.sleep(Math.min(sleep, TimeUnit.NANOSECONDS.toMillis(remaining) + 1));
//                 } catch (InterruptedException e) {
//                     interrupted = true;
//                 }
//             }
//         }
//     } finally {
//         if (interrupted) {
//             Thread.currentThread().interrupt();
//         }
//     }
//     try {
//         Assert.assertThat(value.call(), matcher);
//     } catch (RuntimeException e) {
//         throw e;
//     } catch (Exception e) {
//         throw new AssertionError(e);
//     }
// }


import org.terracotta.offheapstore.paging.Page;
import org.terracotta.offheapstore.paging.PageSource;
import org.terracotta.offheapstore.paging.PhantomReferenceLimitedPageSource;
import static org.terracotta.offheapstore.util.RetryAssert.assertBy;
import static org.hamcrest.core.IsNull.notNullValue;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Test;
/*
 * Copyright 2015 Terracotta, Inc., a Software AG company.
 *
 * 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 org.terracotta.offheapstore.paging;




public class PhantomReferenceLimitedPageSourceTest {

  @Test
  public void testExhaustion() {
    testExhaustion(new PhantomReferenceLimitedPageSource(64));
  }

  private static void testExhaustion(PageSource source) {
    Page p = source.allocate(64, false, false, null);
    Assert.assertNotNull(p);

    Assert.assertNull(source.allocate(1, false, false, null));

    Assert.assertNotNull(p);
  }

  @Test
  public void testRecycle() throws InterruptedException {
    final PageSource source = new PhantomReferenceLimitedPageSource(64);

    testExhaustion(source);

// Path: src/main/java/org/terracotta/offheapstore/paging/PhantomReferenceLimitedPageSource.java
// public class PhantomReferenceLimitedPageSource implements PageSource {
// 
//   private final ReferenceQueue<ByteBuffer> allocatedBuffers = new ReferenceQueue<>();
//   private final Map<PhantomReference<ByteBuffer>, Integer> bufferSizes = new ConcurrentHashMap<>();
// 
//   private final AtomicLong max;
// 
//   /**
//    * Create a source that will allocate at most {@code max} bytes.
//    *
//    * @param max the maximum total size of all available buffers
//    */
//   public PhantomReferenceLimitedPageSource(long max) {
//       this.max = new AtomicLong(max);
//   }
// 
//   /**
//    * Allocates a byte buffer of the given size.
//    * <p>
//    * This {@code BufferSource} places no restrictions on the requested size of
//    * the buffer.
//    */
//   @Override
//   public Page allocate(int size, boolean thief, boolean victim, OffHeapStorageArea owner) {
//       while (true) {
//           processQueue();
//           long now = max.get();
//           if (now < size) {
//             return null;
//           } else if (max.compareAndSet(now, now - size)) {
//             ByteBuffer buffer;
//             try {
//               buffer = ByteBuffer.allocateDirect(size);
//             } catch (OutOfMemoryError e) {
//               return null;
//             }
//             bufferSizes.put(new PhantomReference<>(buffer, allocatedBuffers), size);
//             return new Page(buffer, owner);
//           }
//       }
//   }
// 
//   /**
//    * Frees the supplied buffer.
//    * <p>
//    * This implementation is a no-op, no validation of the supplied buffer is
//    * attempted, as freeing of allocated buffers is monitored via phantom
//    * references.
//    */
//   @Override
//   public void free(Page buffer) {
//     //no-op
//   }
// 
//   private void processQueue() {
//       while (true) {
//           Reference<?> ref = allocatedBuffers.poll();
// 
//           if (ref == null) {
//               return;
//           } else {
//               Integer size = bufferSizes.remove(ref);
//               max.addAndGet(size.longValue());
//           }
//       }
//   }
// 
//   @Override
//   public String toString() {
//     StringBuilder sb = new StringBuilder("PhantomReferenceLimitedBufferSource\n");
//     Collection<Integer> buffers = bufferSizes.values();
//     sb.append("Bytes Available   : ").append(max.get()).append('\n');
//     sb.append("Buffers Allocated : (count=").append(buffers.size()).append(") ").append(buffers);
//     return sb.toString();
//   }
// }
// 
// Path: src/test/java/org/terracotta/offheapstore/util/RetryAssert.java
// public static <T> void assertBy(long time, TimeUnit unit, Callable<T> value, Matcher<? super T> matcher) {
//     boolean interrupted = Thread.interrupted();
//     try {
//       long end = System.nanoTime() + unit.toNanos(time);
//         for (long sleep = 10; ; sleep <<= 1L) {
//             try {
//                 Assert.assertThat(value.call(), matcher);
//                 return;
//             } catch (Throwable t) {
//                 //ignore - wait for timeout
//             }
// 
//             long remaining = end - System.nanoTime();
//             if (remaining <= 0) {
//                 break;
//             } else {
//                 try {
//                     Thread.sleep(Math.min(sleep, TimeUnit.NANOSECONDS.toMillis(remaining) + 1));
//                 } catch (InterruptedException e) {
//                     interrupted = true;
//                 }
//             }
//         }
//     } finally {
//         if (interrupted) {
//             Thread.currentThread().interrupt();
//         }
//     }
//     try {
//         Assert.assertThat(value.call(), matcher);
//     } catch (RuntimeException e) {
//         throw e;
//     } catch (Exception e) {
//         throw new AssertionError(e);
//     }
// }


// Path: src/test/java/org/terracotta/offheapstore/paging/PhantomReferenceLimitedPageSourceTest.java
import org.terracotta.offheapstore.paging.Page;
import org.terracotta.offheapstore.paging.PageSource;
import org.terracotta.offheapstore.paging.PhantomReferenceLimitedPageSource;
import static org.terracotta.offheapstore.util.RetryAssert.assertBy;
import static org.hamcrest.core.IsNull.notNullValue;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Test;

/*
 * Copyright 2015 Terracotta, Inc., a Software AG company.
 *
 * 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 org.terracotta.offheapstore.paging;




public class PhantomReferenceLimitedPageSourceTest {

  @Test
  public void testExhaustion() {
    testExhaustion(new PhantomReferenceLimitedPageSource(64));
  }

  private static void testExhaustion(PageSource source) {
    Page p = source.allocate(64, false, false, null);
    Assert.assertNotNull(p);

    Assert.assertNull(source.allocate(1, false, false, null));

    Assert.assertNotNull(p);
  }

  @Test
  public void testRecycle() throws InterruptedException {
    final PageSource source = new PhantomReferenceLimitedPageSource(64);

    testExhaustion(source);

    assertBy(30, TimeUnit.SECONDS, () -> {