
org.objectfabric.BlockQueue Maven / Gradle / Ivy
The newest version!
/**
* This file is part of ObjectFabric (http://objectfabric.org).
*
* ObjectFabric is licensed under the Apache License, Version 2.0, the terms
* of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html.
*
* Copyright ObjectFabric Inc.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.objectfabric;
import org.objectfabric.Connection.Write;
import org.objectfabric.ThreadAssert.SingleThreaded;
@SuppressWarnings("serial")
@SingleThreaded
abstract class BlockQueue extends Actor {
private static final Block REMOVED = new Block();
private Block[] _blocks = new Block[OpenMap.CAPACITY];
private final int[] _indexes = new int[16]; // TODO tune
private int _size = 0;
private int _first = 0;
private int _last = 0;
final void enqueueBlock(URI uri, long tick, Buff[] buffs, long[] removals, boolean requested) {
if (Debug.THREADS)
ThreadAssert.exchangeTake(buffs);
final Buff[] duplicates = new Buff[buffs.length];
for (int i = 0; i < buffs.length; i++) {
duplicates[i] = buffs[i].duplicate();
if (Debug.THREADS) {
ThreadAssert.exchangeGive(this, duplicates[i]);
ThreadAssert.exchangeGive(buffs, buffs[i]);
}
}
final Block block = new Block(uri, tick, duplicates, requested, removals);
addAndRun(new Message() {
@Override
void run() {
// TODO why can be added twice?
if (index(block.URI, block.Tick) >= 0) {
block.cancel();
return;
}
if (block.Removals != null) {
long[] value = block.Removals;
for (int removal = 0; removal < block.Removals.length; removal++) {
if (!Tick.isNull(block.Removals[removal])) {
int index = index(block.URI, block.Removals[removal]);
if (index >= 0) {
if (value == block.Removals)
value = Platform.get().clone(block.Removals);
value[removal] = Tick.REMOVED;
if (_blocks[index].Removals != null)
for (int i = 0; i < _blocks[index].Removals.length; i++)
if (!Tick.isNull(_blocks[index].Removals[i]))
value = Tick.add(value, _blocks[index].Removals[i]);
_blocks[index].cancel();
_blocks[index] = REMOVED;
if (_size <= _indexes.length)
remove(index);
onRemove();
if (Debug.ENABLED)
check();
}
}
}
block.Removals = value;
}
int index = add(block);
if (_size < _indexes.length)
enqueue(index);
if (_size == _indexes.length)
_last &= _blocks.length - 1;
_size++;
if (Stats.ENABLED)
Stats.Instance.BlockQueues.incrementAndGet();
if (Debug.ENABLED)
check();
}
});
}
private final void onRemove() {
_size--;
if (Stats.ENABLED)
Stats.Instance.BlockQueues.decrementAndGet();
if (_size == _indexes.length) {
_first = 0;
_last = 0;
for (int i = 0; i < _blocks.length; i++)
if (_blocks[i] != null && _blocks[i] != REMOVED)
enqueue(i);
}
}
final Block nextBlock() {
Block block = null;
if (_size > 0) {
if (_size <= _indexes.length) {
int index = dequeue();
block = _blocks[index];
_blocks[index] = REMOVED;
} else {
while (_blocks[_last] == null || _blocks[_last] == REMOVED)
_last = _last + 1 < _blocks.length ? _last + 1 : 0;
block = _blocks[_last];
_blocks[_last] = REMOVED;
}
onRemove();
}
if (Debug.ENABLED) {
Debug.assertion(block != REMOVED);
check();
}
return block;
}
final void recycleBlocks() {
for (int i = 0; i < _blocks.length; i++)
if (_blocks[i] != null && _blocks[i] != REMOVED)
for (int j = 0; j < _blocks[i].Buffs.length; j++)
_blocks[i].Buffs[j].recycle();
}
static final class Block extends Write {
final URI URI;
final long Tick;
final int Hash;
final Buff[] Buffs;
final boolean Requested;
long[] Removals;
Block(URI uri, long tick, Buff[] buffs, boolean requested, long[] removals) {
URI = uri;
Tick = tick;
Buffs = buffs;
Requested = requested;
Removals = removals;
Hash = hash(uri, tick);
if (Debug.ENABLED)
Debug.assertion(removals == null || removals.length > 0);
}
Block() {
URI = null;
Tick = 0;
Buffs = null;
Requested = false;
Removals = null;
Hash = 0;
}
static int hash(URI uri, long tick) {
return TKeyed.rehash(uri.hashCode() ^ (int) (tick ^ (tick >>> 32)));
}
@Override
void run(Connection connection) {
connection.write(Connection.COMMAND_ON_BLOCK, URI.path(), Tick, null);
}
@Override
int runEx(Connection connection, Queue queue, int room) {
room = Serialization.writeBlock(connection.writer(), queue, room, Buffs, Removals, Requested);
return room;
}
final void cancel() {
if (Debug.THREADS)
ThreadAssert.exchangeTake(Buffs);
for (int i = 0; i < Buffs.length; i++)
Buffs[i].recycle();
}
}
private final int index(URI uri, long tick) {
int index = Block.hash(uri, tick) & (_blocks.length - 1);
for (int i = OpenMap.attemptsStart(_blocks.length); i >= 0; i--) {
if (_blocks[index] == null)
break;
if (_blocks[index].URI == uri && _blocks[index].Tick == tick)
return index;
index = (index + 1) & (_blocks.length - 1);
}
return -1;
}
private final int add(Block block) {
int index;
while ((index = tryToAdd(block)) < 0) {
Block[] previous = _blocks;
for (;;) {
_blocks = new Block[_blocks.length << OpenMap.TIMES_TWO_SHIFT];
if (rehash(previous)) {
if (_size <= _indexes.length) {
_first = 0;
_last = 0;
for (int i = 0; i < _blocks.length; i++)
if (_blocks[i] != null)
enqueue(i);
}
break;
}
}
}
return index;
}
private final int tryToAdd(Block block) {
if (Debug.ENABLED)
Debug.assertion(index(block.URI, block.Tick) < 0);
int index = block.Hash & (_blocks.length - 1);
for (int i = OpenMap.attemptsStart(_blocks.length); i >= 0; i--) {
if (_blocks[index] == null || _blocks[index] == REMOVED) {
_blocks[index] = block;
return index;
}
if (Debug.ENABLED)
Debug.assertion(_blocks[index].URI != block.URI || _blocks[index].Tick != block.Tick);
index = (index + 1) & (_blocks.length - 1);
}
return -1;
}
private final boolean rehash(Block[] previous) {
for (int i = previous.length - 1; i >= 0; i--)
if (previous[i] != null && previous[i] != REMOVED)
if (tryToAdd(previous[i]) < 0)
return false;
return true;
}
// C.f. Queue
private final void enqueue(int index) {
_indexes[_last] = index;
_last = (_last + 1) & (_indexes.length - 1);
}
private final void remove(int item) {
int index;
for (index = 0; index < _size; index++)
if (_indexes[(_first + index) & (_indexes.length - 1)] == item)
break;
if (Debug.ENABLED)
Debug.assertion(index < _size);
for (int i = index - 1; i >= 0; i--) {
int a = (_first + i + 0) & (_indexes.length - 1);
int b = (_first + i + 1) & (_indexes.length - 1);
_indexes[b] = _indexes[a];
}
_first = (_first + 1) & (_indexes.length - 1);
}
private final int dequeue() {
int ret = _indexes[_first];
_first = (_first + 1) & (_indexes.length - 1);
return ret;
}
private final void check() {
if (!Debug.ENABLED)
throw new IllegalStateException();
if (_size <= _indexes.length) {
PlatformSet queued = new PlatformSet();
for (int i = 0; i < _size; i++)
queued.add(_indexes[(_first + i) & (_indexes.length - 1)]);
for (int i = 0; i < _blocks.length; i++) {
boolean empty = _blocks[i] == null || _blocks[i] == REMOVED;
Debug.assertion(queued.contains(i) != empty);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy