Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.objectfabric.Connection Maven / Gradle / Ivy
/**
* 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.CloseCounter.Callback;
import org.objectfabric.InFlight.Provider;
@SuppressWarnings({ "serial", "rawtypes" })
abstract class Connection extends BlockQueue implements Runnable, Provider {
// Per resource commands
static final byte COMMAND_PERMISSION = 0;
static final byte COMMAND_GET_KNOWN = 1;
// TODO send delta instead of full array?
static final byte COMMAND_ON_KNOWN = 2;
static final byte COMMAND_GET_BLOCK = 3;
static final byte COMMAND_CANCEL_BLOCK = 4;
static final byte COMMAND_ON_BLOCK = 5;
static final byte COMMAND_ACK_BLOCK = 6;
static final byte COMMAND_SUBSCRIBE = 7;
static final byte COMMAND_UNSUBSCRIBE = 8;
static final byte COMMAND_UNRESOLVED = 9;
// Shared commands
static final byte COMMAND_HEADERS = 10;
static final byte COMMAND_ADDRESS = 11;
private static final Permission[] PERMISSIONS = Permission.values();
//
private final Location _location;
private Session _session;
private Headers _headers;
// Read thread
private Address _address;
private final ImmutableReader _reader = new ImmutableReader(new List());
private final byte[] _leftover = new byte[Buff.getLargestUnsplitable()];
private int _leftoverSize = -1;
// Write thread
private final ImmutableWriter _writer = new ImmutableWriter(new List());
private final Queue _buffs = new Queue();
private final PlatformConcurrentQueue _writes = new PlatformConcurrentQueue();
private final PlatformMap _subscribed;
private static final int WRITE_IDLE = 0, WRITE_ONGOING = 1, WRITE_ONGOING_INTERRUPTED = 2;
private volatile int _writeStatus;
Connection(Location location, Headers nativeHeaders) {
_location = location;
_headers = nativeHeaders;
if (Debug.THREADS) {
ThreadAssert.exchangeGiveList(_reader, _reader.getThreadContextObjects());
ThreadAssert.exchangeGiveList(_writer, _writer.getThreadContextObjects());
ThreadAssert.exchangeGive(_writer, this);
}
post(new Write() {
@Override
void run(Connection connection) {
connection._writer.putByte(TObject.SERIALIZATION_VERSION);
}
}, false);
if (_location instanceof Remote) {
final Remote remote = (Remote) _location;
final Headers headers = remote.headers();
if (headers != null)
postHeaders(headers, false);
post(new Write() {
@Override
void run(Connection connection) {
connection.write(COMMAND_ADDRESS, null, 0, null);
}
@Override
int runEx(Connection connection, Queue queue, int room) {
Serialization.writeAddress(connection._writer, remote.address());
return room;
}
}, false);
_subscribed = null;
} else
_subscribed = new PlatformMap();
requestRun();
}
@Override
void onClose(Callback callback) {
if (_subscribed != null)
for (ServerView view : _subscribed.values())
view.unsubscribe(this);
closeRead();
closeWrite();
super.onClose(callback);
}
//
final Location location() {
return _location;
}
final Address address() {
return _address;
}
final PlatformMap subscribed() {
return _subscribed;
}
final ImmutableWriter writer() {
return _writer;
}
//
final void post(Write write) {
post(write, true);
}
final void post(Write write, boolean push) {
_writes.add(write);
if (Stats.ENABLED)
Stats.Instance.ConnectionQueues.incrementAndGet();
if (push)
requestRun();
}
final void postHeaders(final Headers headers, boolean push) {
post(new Write() {
@Override
void run(Connection connection) {
connection.write(COMMAND_HEADERS, null, 0, null);
}
@Override
int runEx(Connection connection, Queue queue, int room) {
Serialization.writeHeaders(connection._writer, headers.asStrings());
return room;
}
}, push);
}
final void postPermission(final URI uri, final Permission permission, boolean push) {
post(new Write() {
@Override
void run(Connection connection) {
connection.write(COMMAND_PERMISSION, uri.path(), 0, null);
}
@Override
int runEx(Connection connection, Queue queue, int room) {
if (!connection._writer.canWriteByte()) {
connection._writer.interrupt(null);
return 0;
}
connection._writer.writeByte((byte) permission.ordinal());
return room;
}
}, push);
}
final void postSubscribe(final URI uri) {
post(new Write() {
@Override
void run(Connection connection) {
connection.write(Connection.COMMAND_SUBSCRIBE, uri.path(), 0, null);
}
});
}
final void postUnsubscribe(final URI uri) {
post(new Write() {
@Override
void run(Connection connection) {
connection.write(Connection.COMMAND_UNSUBSCRIBE, uri.path(), 0, null);
}
});
}
final void postKnown(final URI uri, final long[] ticks) {
post(new Write() {
@Override
void run(Connection connection) {
connection.write(Connection.COMMAND_ON_KNOWN, uri.path(), 0, ticks);
}
});
}
final void postGet(final URI uri, final long tick) {
post(new Write() {
@Override
void run(Connection connection) {
if (InFlight.starting(uri, tick, connection))
connection.write(COMMAND_GET_BLOCK, uri.path(), tick, null);
}
});
if (Stats.ENABLED)
Stats.Instance.BlockRequestsSent.incrementAndGet();
}
@Override
public final void cancel(final URI uri, final long tick) {
post(new Write() {
@Override
void run(Connection connection) {
connection.write(COMMAND_CANCEL_BLOCK, uri.path(), tick, null);
}
});
}
final void postAck(final URI uri, final long tick) {
post(new Write() {
@Override
void run(Connection connection) {
connection.write(COMMAND_ACK_BLOCK, uri.path(), tick, null);
}
});
}
/*
* IO reader thread.
*/
final boolean resumeRead() {
boolean started;
if (Debug.ENABLED) {
started = Helper.instance().startRead(this);
if (started) {
ThreadAssert.resume(_reader, false);
if (Debug.THREADS)
ThreadAssert.exchangeTake(_reader);
}
}
boolean result = !isClosingOrClosed();
if (!result) {
if (Debug.ENABLED)
Helper.instance().assertReadClosing(this);
if (Debug.THREADS && started)
ThreadAssert.removePrivateList(_reader.getThreadContextObjects());
}
return result;
}
final void read(Buff buff) {
int initialLimit;
if (Debug.ENABLED)
initialLimit = buff.limit();
if (!Debug.RANDOMIZE_TRANSFER_LENGTHS)
readImpl(buff);
else {
int limit = buff.limit();
while (buff.position() < limit) {
int rand = Platform.get().randomInt(limit - buff.position() + 1);
buff.limit(Math.min(buff.position() + rand, limit));
readImpl(buff);
Debug.assertion(buff.remaining() == 0);
}
}
if (Debug.ENABLED) {
Debug.assertion(buff.limit() == initialLimit);
Debug.assertion(buff.remaining() == 0);
}
}
final void suspendRead() {
if (Debug.ENABLED) {
if (Helper.instance().stopRead(this))
ThreadAssert.suspend(_reader);
else if (Debug.THREADS)
ThreadAssert.removePrivateList(_reader.getThreadContextObjects());
}
}
private final void closeRead() {
if (Debug.ENABLED) {
if (Helper.instance().closeRead(this)) {
Object key = new Object();
ThreadAssert.suspend(key);
ThreadAssert.resume(_reader, false);
if (Debug.THREADS) {
ThreadAssert.exchangeTake(_reader);
ThreadAssert.removePrivateList(_reader.getThreadContextObjects());
}
ThreadAssert.resume(key);
}
}
}
private final void readImpl(Buff buff) {
if (Debug.ENABLED)
Debug.assertion(buff.position() >= Buff.getLargestUnsplitable());
if (buff.remaining() > 0) {
_reader.setBuff(buff);
if (_leftoverSize < 0)
_reader.startRead();
else {
buff.position(buff.position() - _leftoverSize);
buff.putImmutably(_leftover, 0, _leftoverSize);
}
if (Debug.ENABLED)
buff.lock(buff.limit());
readImpl();
_leftoverSize = buff.remaining();
buff.getImmutably(_leftover, 0, _leftoverSize);
buff.position(buff.limit());
}
}
private static final int STEP_READ_CODE = 0;
private static final int STEP_READ_URI = 1;
private static final int STEP_READ_COMMAND = 2;
private final void readImpl() {
for (;;) {
int step = 0;
byte code = -1;
String path = null;
URI uri = null;
if (_reader.interrupted()) {
step = _reader.resumeInt();
code = _reader.resumeByte();
path = (String) _reader.resume();
uri = (URI) _reader.resume();
}
switch (step) {
case STEP_READ_CODE: {
if (!_reader.canReadByte()) {
_reader.interrupt(uri);
_reader.interrupt(path);
_reader.interruptByte(code);
_reader.interruptInt(STEP_READ_CODE);
return;
}
code = _reader.readByte(Writer.DEBUG_TAG_CONNECTION);
if (Debug.COMMUNICATIONS_LOG)
Log.write("Read command: " + getCommandString(code));
}
case STEP_READ_URI: {
if (code < COMMAND_HEADERS) { // TODO ew
path = _reader.readString();
if (_reader.interrupted()) {
_reader.interrupt(uri);
_reader.interrupt(path);
_reader.interruptByte(code);
_reader.interruptInt(STEP_READ_URI);
return;
}
if (_location instanceof Server)
uri = ((Server) _location).resolver().resolve(_address, path);
else
uri = ((Origin) _location).uris().get(path);
}
}
case STEP_READ_COMMAND: {
switch (code) {
case COMMAND_PERMISSION: {
if (!_reader.canReadByte()) {
_reader.interrupt(uri);
_reader.interrupt(path);
_reader.interruptByte(code);
_reader.interruptInt(STEP_READ_COMMAND);
return;
}
byte ordinal = _reader.readByte();
if (uri != null) {
ClientView view = (ClientView) uri.getOrCreate(_location);
view.readPermission(uri, PERMISSIONS[ordinal]);
}
break;
}
case COMMAND_GET_KNOWN: {
// TODO
break;
}
case COMMAND_ON_KNOWN: {
long[] ticks = Serialization.readTicks(_reader);
if (_reader.interrupted()) {
_reader.interrupt(uri);
_reader.interrupt(path);
_reader.interruptByte(code);
_reader.interruptInt(STEP_READ_COMMAND);
return;
}
if (uri != null) {
if (ticks == null)
ticks = Tick.EMPTY;
onKnown(uri, ticks);
}
break;
}
case COMMAND_GET_BLOCK: {
long tick = Serialization.readTick(_reader);
if (_reader.interrupted()) {
_reader.interrupt(uri);
_reader.interrupt(path);
_reader.interruptByte(code);
_reader.interruptInt(STEP_READ_COMMAND);
return;
}
if (uri != null) {
ServerView view = (ServerView) uri.getOrCreate(_location);
view.readGetBlock(uri, tick, this);
}
if (Stats.ENABLED)
Stats.Instance.BlockRequestsReceived.incrementAndGet();
break;
}
case COMMAND_CANCEL_BLOCK: {
long tick = Serialization.readTick(_reader);
if (_reader.interrupted()) {
_reader.interrupt(uri);
_reader.interrupt(path);
_reader.interruptByte(code);
_reader.interruptInt(STEP_READ_COMMAND);
return;
}
if (uri != null) {
ServerView view = (ServerView) uri.getOrCreate(_location);
view.readCancelBlock(uri, tick, this);
}
break;
}
case COMMAND_ON_BLOCK: {
View view = null;
if (uri != null)
view = uri.getOrCreate(_location);
Serialization.readBlock(_reader, this, uri, view);
if (_reader.interrupted()) {
_reader.interrupt(uri);
_reader.interrupt(path);
_reader.interruptByte(code);
_reader.interruptInt(STEP_READ_COMMAND);
return;
}
break;
}
case COMMAND_ACK_BLOCK: {
long tick = Serialization.readTick(_reader);
if (_reader.interrupted()) {
_reader.interrupt(uri);
_reader.interrupt(path);
_reader.interruptByte(code);
_reader.interruptInt(STEP_READ_COMMAND);
return;
}
if (uri != null) {
ClientView view = (ClientView) uri.getOrCreate(_location);
view.readAck(uri, tick);
}
if (Stats.ENABLED)
Stats.Instance.AckReceived.incrementAndGet();
break;
}
case COMMAND_SUBSCRIBE: {
if (uri != null) {
if (_session != null) {
final URI uri_ = uri;
_session.onRequest(uri, new PermissionCallback() {
@Override
public void set(Permission permission) {
if (permission == Permission.NONE)
postPermission(uri_, permission, true);
else
onPermission(uri_, permission);
}
});
} else
onPermission(uri, Permission.WRITE);
} else {
final String path_ = path;
post(new Write() {
@Override
void run(Connection connection) {
connection.write(COMMAND_UNRESOLVED, path_, 0, null);
}
});
}
break;
}
case COMMAND_UNSUBSCRIBE: {
if (uri != null) {
ServerView view = (ServerView) uri.getOrCreate(_location);
view.readUnsubscribe(uri, this);
}
break;
}
case COMMAND_UNRESOLVED: {
if (uri != null) {
ClientView view = (ClientView) uri.getOrCreate(_location);
view.readUnresolved(uri);
}
break;
}
case COMMAND_HEADERS: {
_headers = Serialization.readHeaders(_reader);
if (_reader.interrupted()) {
_reader.interrupt(uri);
_reader.interrupt(path);
_reader.interruptByte(code);
_reader.interruptInt(STEP_READ_COMMAND);
return;
}
break;
}
case COMMAND_ADDRESS: {
_address = Serialization.readAddress(_reader);
if (_reader.interrupted()) {
_reader.interrupt(uri);
_reader.interrupt(path);
_reader.interruptByte(code);
_reader.interruptInt(STEP_READ_COMMAND);
return;
}
_session = onConnection(_headers);
_headers = null;
break;
}
default:
throw new IllegalStateException();
}
}
}
}
}
protected Session onConnection(Headers headers) {
return null;
}
private final void onPermission(URI uri, Permission permission) {
((ServerView) uri.getOrCreate(_location)).onPermission(uri, this, permission);
}
void onKnown(URI uri, long[] ticks) {
ClientView view = (ClientView) uri.getOrCreate(_location);
view.readKnown(uri, ticks);
}
final void onBlock(URI uri, View view, long tick, Buff[] buffs, long[] removals, boolean requested, boolean ack) {
Exception exception = uri.onBlock(view, tick, buffs, removals, requested, this, ack, null);
if (exception != null)
Log.write(exception);
}
/*
* IO writer thread.
*/
@Override
protected void enqueue() {
Platform.get().execute(this);
}
@Override
public void run() {
if (onRunStarting()) {
if (Debug.ENABLED)
ThreadAssert.resume(_writer, false);
if (Debug.THREADS)
ThreadAssert.exchangeTake(_writer);
runMessages(false);
if (_writeStatus == WRITE_IDLE)
write();
if (Debug.ENABLED)
ThreadAssert.suspend(_writer);
onRunEnded(false);
}
}
abstract void write();
final Queue fill(int limit) {
boolean done = fillImpl(limit);
if (_buffs.size() > 0) {
_writeStatus = done ? WRITE_ONGOING : WRITE_ONGOING_INTERRUPTED;
return _buffs;
}
return null;
}
final void writeComplete() {
if (_writeStatus == WRITE_ONGOING)
_writeStatus = WRITE_IDLE;
else if (_writeStatus == WRITE_ONGOING_INTERRUPTED)
_writeStatus = WRITE_IDLE;
else
throw new IllegalStateException();
requestRun();
}
private final void closeWrite() {
Object key;
if (Debug.ENABLED) {
ThreadAssert.suspend(key = new Object());
ThreadAssert.resume(_writer, false);
}
if (Debug.THREADS)
ThreadAssert.exchangeTake(_writer);
if (Stats.ENABLED) {
for (;;) {
Write message = _writes.poll();
if (message == null)
break;
Stats.Instance.ConnectionQueues.decrementAndGet();
}
}
recycleBlocks();
while (_buffs.size() > 0)
_buffs.poll().recycle();
if (Debug.THREADS) {
ThreadAssert.removePrivateList(_writer.getThreadContextObjects());
ThreadAssert.removePrivate(this);
}
if (Debug.ENABLED)
ThreadAssert.resume(key);
}
//
private final boolean fillImpl(int limit) {
int room = limit;
for (int i = 0; i < _buffs.size(); i++)
room -= _buffs.get(i).remaining();
boolean done = write(_buffs, room);
if (Debug.ENABLED) {
for (int i = 0; i < _buffs.size(); i++) {
Debug.assertion(_buffs.get(i).getDuplicates() > 0);
Debug.assertion(_buffs.get(i).remaining() != 0);
}
int total = 0;
for (int i = 0; i < _buffs.size(); i++)
total += _buffs.get(i).remaining();
Debug.assertion(total <= limit);
}
return done;
}
private final boolean write(Queue queue, int room) {
Buff buff = Buff.getOrCreate();
_writer.setBuff(buff);
for (;;) {
if (!Debug.RANDOMIZE_TRANSFER_LENGTHS)
room = writeImpl(queue, room);
else {
for (;;) {
int position = buff.position();
int room1 = Platform.get().randomInt(room - 1) + 1;
int room2 = writeImpl(queue, room1);
room -= room1 - room2;
room -= Serialization.enqueueWritten(queue, buff);
if (_writer.interrupted() && room1 > Buff.getLargestUnsplitable())
break;
if (!_writer.interrupted() && buff.position() == position)
break;
}
}
boolean exit = !_writer.interrupted() || buff.limit() < buff.capacity();
int position = buff.position();
buff.reset();
buff.limit(position);
if (Debug.ENABLED)
buff.lock(buff.limit());
if (buff.remaining() > 0) {
queue.add(buff);
room -= buff.remaining();
} else
buff.recycle();
if (exit) {
_writer.setBuff(null);
return !_writer.interrupted();
}
buff = Buff.getOrCreate();
_writer.setBuff(buff);
}
}
private static final int STEP_RUN = 0;
private static final int STEP_RUN_EX = 1;
@SuppressWarnings("fallthrough")
private final int writeImpl(Queue queue, int room) {
Buff buff = _writer.getBuff();
buff.limit(Math.min(buff.position() + room, buff.capacity()));
for (;;) {
int step = STEP_COMMAND;
Write write;
if (_writer.interrupted()) {
step = _writer.resumeInt();
write = (Write) _writer.resume();
} else {
write = _writes.poll();
if (Stats.ENABLED && write != null)
Stats.Instance.ConnectionQueues.decrementAndGet();
if (write == null)
write = nextBlock();
if (write == null)
return room;
if (Debug.THREADS)
ThreadAssert.exchangeTake(this);
}
switch (step) {
case STEP_RUN: {
write.run(this);
if (_writer.interrupted()) {
_writer.interrupt(write);
_writer.interruptInt(STEP_RUN);
return room;
}
}
case STEP_RUN_EX: {
room = write.runEx(this, queue, room);
if (_writer.interrupted()) {
_writer.interrupt(write);
_writer.interruptInt(STEP_RUN_EX);
return room;
}
}
}
}
}
private static final int STEP_COMMAND = 0;
private static final int STEP_URI = 1;
private static final int STEP_BLOCK = 2;
private static final int STEP_SET = 3;
final void write(byte command, String path, long tick, long[] ticks) {
int step = STEP_COMMAND;
if (_writer.interrupted())
step = _writer.resumeInt();
switch (step) {
case STEP_COMMAND: {
if (!_writer.canWriteByte()) {
_writer.interruptInt(STEP_COMMAND);
return;
}
_writer.writeByte(command, Writer.DEBUG_TAG_CONNECTION);
if (Debug.COMMUNICATIONS_LOG)
Log.write("Write command: " + getCommandString(command));
}
case STEP_URI: {
if (path != null) {
_writer.writeString(path);
if (_writer.interrupted()) {
_writer.interruptInt(STEP_URI);
return;
}
}
}
case STEP_BLOCK: {
if (tick != 0) {
Serialization.writeTick(_writer, tick);
if (_writer.interrupted()) {
_writer.interruptInt(STEP_BLOCK);
return;
}
}
}
case STEP_SET: {
if (ticks != null) {
Serialization.writeTicks(_writer, ticks);
if (_writer.interrupted()) {
_writer.interruptInt(STEP_SET);
return;
}
}
}
}
}
static abstract class Write {
abstract void run(Connection connection);
int runEx(Connection connection, Queue queue, int room) {
return room;
}
}
// Debug
public static String getCommandString(int code) {
if (!Debug.COMMUNICATIONS)
throw new IllegalStateException();
switch (code) {
case COMMAND_GET_KNOWN:
return "GET_KNOWN";
case COMMAND_PERMISSION:
return "PERMISSION";
case COMMAND_ON_KNOWN:
return "ON_KNOWN";
case COMMAND_GET_BLOCK:
return "GET_BLOCK";
case COMMAND_CANCEL_BLOCK:
return "CANCEL_BLOCK";
case COMMAND_ON_BLOCK:
return "ON_BLOCK";
case COMMAND_ACK_BLOCK:
return "ACK_BLOCK";
case COMMAND_SUBSCRIBE:
return "SUBSCRIBE";
case COMMAND_UNSUBSCRIBE:
return "UNSUBSCRIBE";
case COMMAND_UNRESOLVED:
return "UNRESOLVED";
case COMMAND_HEADERS:
return "HEADERS";
case COMMAND_ADDRESS:
return "ADDRESS";
default:
throw new IllegalStateException();
}
}
}