/* * Galaxy * Copyright (c) 2012-2014, Parallel Universe Software Co. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 3.0 * as published by the Free Software Foundation. */ package co.paralleluniverse.galaxy.core; import java.util.ArrayList; import java.util.List; /** * * @author pron */ class IdAllocator implements RefAllocator.RefAllocationsListener { private final Cache cache; private final RefAllocator refAllocator; private final static int REFS_TO_ALLOCATE = 10000; // TODO: dynamic private List<Op> pendingOps = new ArrayList<Op>(); private long nextId = -1; private long minId = -1; private long maxId = -1; private boolean requestedMoreIds; private volatile long nextMinId = -1; private volatile long nextMaxId = -1; private boolean ready; public IdAllocator(Cache cache, RefAllocator refAllocator) { this.cache = cache; this.refAllocator = refAllocator; refAllocator.addRefAllocationsListener(this); } public RefAllocator getRefAllocator() { return refAllocator; } @Override public synchronized void counterReady() { ready = true; cache.allocatorReady(); } public synchronized boolean isReady() { return ready; } private synchronized long allocateIds(int count) { if (nextId + count > maxId && nextMinId > minId) { minId = nextMinId; maxId = nextMaxId; nextId = minId; } final long id; if (nextId + count - 1 <= maxId) { id = nextId; nextId += count; } else id = -1; if (!requestedMoreIds && (id == -1 || shouldAllocateMoreIds())) { allocateMoreRefs(count); requestedMoreIds = true; } return id; } public synchronized long allocateIds(Op op, int count) { final long id = allocateIds(count); if (id == -1) { if (!op.hasFuture()) op.createFuture(); pendingOps.add(op); } return id; } private boolean shouldAllocateMoreIds() { return nextId > (minId + (maxId - minId) / 2); } void allocateMoreRefs(int count) { refAllocator.allocateRefs(Math.max(2 * count, REFS_TO_ALLOCATE)); } @Override public void refsAllocated(long start, int num) { List<Op> pending; synchronized (this) { assert start > nextId || (start == minId); assert start + num > maxId; if (start == minId) { // allow for repeated calls, esp. in tests assert start + num > maxId; maxId = start + num - 1; } else { assert start > nextId; requestedMoreIds = false; this.nextMinId = start; this.nextMaxId = start + num - 1; } pending = pendingOps; pendingOps = new ArrayList<Op>(); } for (Op op : pending) cache.runOp(op); } }