
groovyx.gpars.actor.AbstractLoopingActor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gpars Show documentation
Show all versions of gpars Show documentation
The Groovy and Java high-level concurrency library offering actors, dataflow, CSP, agents, parallel collections, fork/join and more
// GPars - Groovy Parallel Systems
//
// Copyright © 2008-11 The original author or authors
//
// 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 groovyx.gpars.actor;
import groovy.lang.Closure;
import groovyx.gpars.actor.impl.ActorReplyException;
import groovyx.gpars.actor.impl.MessageStream;
import groovyx.gpars.group.PGroup;
import groovyx.gpars.util.AsyncMessagingCore;
/**
* Wraps all actors that repeatedly loop through incoming messages and hold no implicit state between subsequent messages.
*
* @author Vaclav Pech
* Date: Aug 23, 2010
*/
public abstract class AbstractLoopingActor extends Actor {
private static final long serialVersionUID = -7638132628355085829L;
private volatile boolean stoppedFlag = true;
private volatile boolean terminatedFlag = true;
volatile boolean terminatingFlag = true;
private ActorTimerTask currentTimerTask = null;
private int timeoutCounter = 0;
private MessageStream currentSender;
/**
* Holds the particular instance of async messaging core to use
*/
private AsyncMessagingCore core;
final AsyncMessagingCore getCore() {
return core;
}
/**
* Builds the async messaging core using the supplied code handler
*
* @param code Code to run on each message
*/
protected final void initialize(final Closure code) {
//noinspection OverlyComplexAnonymousInnerClass
this.core = new AsyncMessagingCore(parallelGroup.getThreadPool()) {
@Override
protected void registerError(final Throwable e) {
if (e instanceof InterruptedException) {
handleInterrupt((InterruptedException) e);
} else {
handleException(e);
}
terminate();
}
@Override
protected void handleMessage(final Object message) {
if (message == START_MESSAGE) handleStart();
else {
if (message == TIMEOUT_MESSAGE) {
final ActorTimerTask localTimerTask = currentTimerTask;
if (localTimerTask != null) {
cancelCurrentTimeoutTask();
if (timeoutCounter != localTimerTask.getId()) return; //ignore obsolete timeout messages
} else return;
handleTimeout();
} else {
if (currentTimerTask != null) cancelCurrentTimeoutTask();
}
timeoutCounter = (timeoutCounter + 1) % Integer.MAX_VALUE;
if (terminatingFlag || message == STOP_MESSAGE) {
if (!terminatedFlag) {
handleTermination();
terminatedFlag = true;
getJoinLatch().bindUnique(null);
}
} else {
final ActorMessage actorMessage = (ActorMessage) message;
try {
runEnhancedWithoutRepliesOnMessages(actorMessage, code, actorMessage.getPayLoad());
} finally {
currentSender = null;
}
}
}
}
@Override
protected boolean continueProcessingMessages() {
return isActive();
}
@Override
protected void threadAssigned() {
registerCurrentActorWithThread(AbstractLoopingActor.this);
currentThread = Thread.currentThread();
}
@Override
protected void threadUnassigned() {
deregisterCurrentActorWithThread();
currentThread = null;
}
};
}
/**
* Retrieves the actor's fairness flag
* Fair actors give up the thread after processing each message, non-fair actors keep a thread until their message queue is empty.
* Non-fair actors tend to perform better than fair ones.
*
* @return True for fair actors, false for non-fair ones. actors are non-fair by default.
*/
public final boolean isFair() {
return core.isFair();
}
/**
* Makes the actor fair. Actors are non-fair by default.
* Fair actors give up the thread after processing each message, non-fair actors keep a thread until their message queue is empty.
* Non-fair actors tend to perform better than fair ones.
*/
public final void makeFair() {
core.makeFair();
}
protected final void setTimeout(final long timeout) {
if (timeout < 0L) throw new IllegalArgumentException("Actor timeout must be a non-negative value");
currentTimerTask = new ActorTimerTask(this, timeoutCounter);
timer.schedule(currentTimerTask, timeout);
}
private void cancelCurrentTimeoutTask() {
assert currentTimerTask != null;
currentTimerTask.cancel();
currentTimerTask = null;
}
/**
* Starts the Actor without sending the START_MESSAGE message to speed the start-up.
* The potential custom afterStart handlers won't be run.
* No messages can be sent or received before an Actor is started.
*
* @return same actor
*/
@Override
public Actor silentStart() {
return doStart();
}
/**
* Starts the Actor and sends it the START_MESSAGE to run any afterStart handlers.
* No messages can be sent or received before an Actor is started.
*
* @return same actor
*/
@Override
public Actor start() {
doStart();
send(START_MESSAGE);
return this;
}
private Actor doStart() {
if (!hasBeenStopped()) throw new IllegalStateException(ACTOR_HAS_ALREADY_BEEN_STARTED);
stoppedFlag = false;
terminatedFlag = false;
terminatingFlag = false;
return this;
}
@Override
public final Actor stop() {
if (!hasBeenStopped()) {
stoppedFlag = true;
send(STOP_MESSAGE);
}
return this;
}
@Override
public final Actor terminate() {
synchronized (this) {
if (!isActive()) return this;
stop();
terminatingFlag = true;
}
if (isActorThread()) {
terminatedFlag = true;
handleTermination();
getJoinLatch().bindUnique(null);
}
//noinspection CallToThreadYield
Thread.yield();
if (!isActorThread() && currentThread != null) {
try {
currentThread.interrupt(); //NPE may occur non-deterministically, since we're running concurrently with the actor's thread
} catch (Exception e) {
send(TERMINATE_MESSAGE);
}
} else send(TERMINATE_MESSAGE);
return this;
}
@Override
public final boolean isActive() {
return !terminatedFlag;
}
@Override
protected final boolean hasBeenStopped() {
return stoppedFlag;
}
/**
* Removes the head of the message queue
*
* @return The head message, or null, if the message queue is empty
*/
@Override
protected ActorMessage sweepNextMessage() {
return (ActorMessage) core.sweepNextMessage();
}
@Override
public MessageStream send(final Object message) {
core.store(createActorMessage(message));
return this;
}
@Override
public void setParallelGroup(final PGroup group) {
super.setParallelGroup(group);
core.attachToThreadPool(group.getThreadPool());
}
/**
* Retrieves the sender actor of the currently processed message.
*
* @return The sender of the currently processed message or null, if the message was not sent by an actor
* @throws groovyx.gpars.actor.impl.ActorReplyException
* If some of the replies failed to be sent.
*/
protected final MessageStream getSender() {
return currentSender;
}
/**
* Sends a reply to all currently processed messages. Throws ActorReplyException if some messages
* have not been sent by an actor. For such cases use replyIfExists().
*
* @param message reply message
* @throws groovyx.gpars.actor.impl.ActorReplyException
* If some of the replies failed to be sent.
*/
protected final void reply(final Object message) {
if (currentSender == null) {
throw new ActorReplyException(CANNOT_SEND_REPLIES_NO_SENDER_HAS_BEEN_REGISTERED);
} else {
currentSender.send(message);
}
}
/**
* Sends a reply to all currently processed messages, which have been sent by an actor.
* Ignores potential errors when sending the replies, like no sender or sender already stopped.
*
* @param message reply message
*/
protected final void replyIfExists(final Object message) {
if (currentSender == null) return;
try {
currentSender.send(message);
} catch (IllegalStateException ignore) {
}
}
private void runEnhancedWithoutRepliesOnMessages(final ActorMessage message, final Closure code, final Object arguments) {
assert message != null;
currentSender = message.getSender();
code.call(arguments);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy