![JAR search and dependency download from the Maven repository](/logo.png)
org.objectfabric.Actor 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 java.util.concurrent.atomic.AtomicInteger;
import org.objectfabric.CloseCounter.Callback;
/**
* Base class for any single-thread components which can receive tasks from other threads.
* ObjectFabric is structured as a set of actors running on a thread pool.
*/
@SuppressWarnings("serial")
abstract class Actor extends AtomicInteger {
private static final int STARTING = 0;
private static final int STARTING_SCHEDULED = 1;
private static final int IDLE = 2;
private static final int SCHEDULED = 3;
private static final int RUNNING = 4;
private static final int RUNNING_SCHEDULED = 5;
private static final int CLOSING = 6;
private static final int CLOSED = 7;
static abstract class Message {
abstract void run(Actor actor);
}
static abstract class Flush extends Message {
abstract void onSuccess();
abstract void onException(Exception e);
@Override
final void run(Actor actor) {
if (actor._currentFlushes == null)
actor._currentFlushes = new List();
actor._currentFlushes.add(this);
}
}
private final PlatformConcurrentQueue _messages = new PlatformConcurrentQueue();
private List _currentFlushes;
private Callback _closeCallback;
final boolean addAndRun(Message message) {
_messages.add(message);
if (!requestRun()) {
_messages.poll();
return false;
}
return true;
}
//
final void runMessages() {
for (;;) {
Message message = _messages.poll();
if (Debug.THREADS)
ThreadAssert.exchangeTake(this);
if (message == null)
break;
message.run(this);
}
}
// State machine
final void readState() {
get();
}
final void volatileWrite() {
if (Debug.ENABLED)
Debug.assertion(get() == STARTING);
set(STARTING);
}
final void onStarted() {
if (!compareAndSet(STARTING, IDLE)) {
if (Debug.ENABLED)
Debug.assertion(get() == STARTING_SCHEDULED);
set(IDLE);
requestRun();
}
}
/*
* TODO: split into request for a message and request for full run. Should help
* staying on same thread for work stealing thread pools. Also would allow running
* messages while waiting on write acknowledgment.
*/
final boolean requestRun() {
for (;;) {
int state = get();
switch (state) {
case STARTING_SCHEDULED:
case RUNNING_SCHEDULED:
case SCHEDULED:
return true;
case STARTING: {
if (compareAndSet(state, STARTING_SCHEDULED))
return true;
break;
}
case IDLE: {
if (compareAndSet(state, SCHEDULED)) {
execute();
return true;
}
break;
}
case RUNNING: {
if (compareAndSet(state, RUNNING_SCHEDULED))
return true;
break;
}
case CLOSING:
case CLOSED:
return false;
default:
throw new IllegalStateException("" + get());
}
}
}
private final void execute() {
Object key;
if (Debug.ENABLED)
ThreadAssert.suspend(key = new Object());
enqueue();
if (Debug.ENABLED)
ThreadAssert.resume(key);
}
abstract void enqueue();
final boolean onRunStarting() {
if (!compareAndSet(SCHEDULED, RUNNING)) {
if (Debug.ENABLED) {
int state = get();
Debug.assertion(state == CLOSING || state == CLOSED);
}
return false;
}
return true;
}
final void onRunEnded() {
if (_currentFlushes != null && _currentFlushes.size() > 0) {
for (int i = 0; i < _currentFlushes.size(); i++)
_currentFlushes.get(i).onSuccess();
_currentFlushes.clear();
}
for (;;) {
int state = get();
switch (state) {
case RUNNING: {
if (compareAndSet(state, IDLE))
return;
break;
}
case RUNNING_SCHEDULED: {
if (compareAndSet(state, SCHEDULED)) {
execute();
return;
}
break;
}
case CLOSING: {
close();
return;
}
default:
throw new IllegalStateException("" + get());
}
}
}
final void requestClose(Callback callback) {
if (Debug.ENABLED)
Debug.assertion(_closeCallback == null);
_closeCallback = callback;
for (;;) {
int state = get();
switch (state) {
case STARTING:
case STARTING_SCHEDULED:
case SCHEDULED:
case IDLE: {
if (compareAndSet(state, CLOSING)) {
close();
return;
}
break;
}
case RUNNING:
case RUNNING_SCHEDULED: {
if (compareAndSet(state, CLOSING))
return;
break;
}
case CLOSING:
case CLOSED: {
return;
}
default:
throw new IllegalStateException("" + get());
}
}
}
private final void close() {
OverrideAssert.add(this);
onClose(_closeCallback);
OverrideAssert.end(this);
if (Debug.ENABLED)
Debug.assertion(get() == CLOSING);
set(CLOSED);
// To assert messages do not accumulate after close
_messages.clear();
}
void onClose(Callback callback) {
OverrideAssert.set(this);
if (callback != null)
callback.call();
}
final boolean isStarting() {
int state = get();
return state == STARTING || state == STARTING_SCHEDULED;
}
final boolean isScheduled() {
int state = get();
return state == SCHEDULED;
}
final boolean isRunning() {
int state = get();
return state == RUNNING || state == RUNNING_SCHEDULED;
}
final boolean isClosingOrClosed() {
int state = get();
return state == CLOSING || state == CLOSED;
}
final boolean isClosed() {
return get() == CLOSED;
}
@Override
public String toString() {
return Platform.get().defaultToString(this);
}
// Debug
final void assertNoMessages() {
if (!Debug.ENABLED)
throw new RuntimeException();
Message message = _messages.poll();
if (message != null) {
Debug.fail();
message.run(null);
}
}
final void assertStarting() {
if (!Debug.ENABLED)
throw new RuntimeException();
int state = get();
Debug.assertion(state == STARTING);
}
final void assertScheduled() {
if (!Debug.ENABLED)
throw new RuntimeException();
int state = get();
Debug.assertion(state == SCHEDULED || state == CLOSING || state == CLOSED);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy