org.curjent.impl.agent
Class Controller

java.lang.Object
  extended by org.curjent.impl.agent.Controller

public final class Controller
extends Object

Coordinates the inner workings of the agent. The proxy creates new messages and sends them to the controller. The controller queues the messages and processes the message queue. It starts new messenger threads as needed and executes messages.


Field Summary
private  HashMap<Awaiter,Integer> blocked
          Set of blocked messages with associated reference counts.
(package private)  Config config
          External agent configuration.
(package private)  Expirations expirations
          Call expiration configuration.
(package private)  Listeners listeners
          Agent-wide call state listener configuration.
(package private)  ReentrantLock lock
          Primary synchronization mechanism.
private  Mark mark
          Marker state.
(package private)  ThreadLocal<Message> message
          Thread-local storage of the most recent message sent to this agent.
(package private)  Messages messages
          Ordered list of pending and accepted Message instances.
(package private)  Messengers messengers
          Starts new messengers in background tasks.
(package private)  HashMap<Method,CallInfo> synthetic
          Map of synthetic interface methods to synthetic call sites.
 
Constructor Summary
Controller(AgentLoader loader, Class<?>[] interfaces, AgentTasks tasks, Class<?> taskType)
          Initializes the controller.
 
Method Summary
(package private)  void blocked(Awaiter awaiter, boolean synchronous)
          Notification that an awaiter is waiting.
private  void deadcheckBlocked()
          Signals blocked messages to check for deadlock.
 void dispatch(Message message)
          Proxy methods implemented for Reentrant task methods call this method instead of send(Message).
private  Throwable dispatch(Object task, Message message)
          Executes the next message using the messenger's task.
(package private)  void finish(Message message)
          Notification that a message transitioned to FINISHED.
private  void finish(Messenger messenger)
          Releases the messenger and prepares to shutdown.
(package private)  Thread[] getBlockingThreads()
          Returns the messenger threads currently executing tasks.
(package private)  long getDeadcheckNanos()
          Returns the nanosecond value for Messages.getDeadcheckTimeout().
(package private)  void handle(Throwable exception)
          Forwards non-null exceptions to the handler for unhandled exceptions.
(package private)  void init(Proxy proxy, ProxyInfo info, CallInfo[] calls)
          Finishes initialization of the controller.
private  void pump(Messenger messenger)
          Main message loop for assigning messages to messengers for execution.
private  void pump(Messenger messenger, Throwable exception)
          Message pump for the messenger's main loop.
(package private)  void run(Messenger messenger)
          Main message loop for a messenger running in a background thread.
 void send(Message message)
          Adds a new message.
private  void start(Messenger messenger)
          Initializes a newly started messenger.
 String toString()
          Delegates to Config.toString().
(package private)  void unblocked(Awaiter awaiter)
          Notification that an awaiter is no longer waiting.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

lock

final ReentrantLock lock
Primary synchronization mechanism. The strategy is coarse grained locking and minimization of work performed while the lock is held. If it is necessary to acquire the controller's lock and synchronize on a message at the same time, always acquire the controller's lock first and then synchronize on the message.


messengers

final Messengers messengers
Starts new messengers in background tasks.


messages

final Messages messages
Ordered list of pending and accepted Message instances. Messages are generally added and processed in the same order (i.e., in first-in, first-out [FIFO] order). FIFO order can be altered, though, such as with Marker.


listeners

final Listeners listeners
Agent-wide call state listener configuration.


expirations

final Expirations expirations
Call expiration configuration.


mark

private Mark mark
Marker state. Replaced with a new instance for each new marker message.


message

final ThreadLocal<Message> message
Thread-local storage of the most recent message sent to this agent.

See Also:
Agent.getLastCall(Object)

synthetic

HashMap<Method,CallInfo> synthetic
Map of synthetic interface methods to synthetic call sites. Lazily initialized as needed.


blocked

private final HashMap<Awaiter,Integer> blocked
Set of blocked messages with associated reference counts. A reference count is kept since more than one thread can be blocked on the same message.


config

final Config config
External agent configuration.

Constructor Detail

Controller

Controller(AgentLoader loader,
           Class<?>[] interfaces,
           AgentTasks tasks,
           Class<?> taskType)
Initializes the controller.

Method Detail

init

void init(Proxy proxy,
          ProxyInfo info,
          CallInfo[] calls)
Finishes initialization of the controller. Resolves the circular dependency for the controller's proxy and also ensures correct memory semantics for non-final fields.


send

public void send(Message message)
          throws Throwable
Adds a new message. Called by the agent's proxy methods. The new message is added to the queue and associated with the current mark. If the message is a marker, a new mark is created for subsequent messages. Lastly, the message queue is checked for startable messages (possibly the message just added) and attempts to start a messenger to execute it.

In order to support call validation via call state listeners, the state of the message is checked before exiting. If has been finished with an exception, the exception is thrown to the caller. The state of the message is checked while the agent is still locked in order to avoid a race condition with an executing message.

Throws:
Throwable

dispatch

public void dispatch(Message message)
              throws Throwable
Proxy methods implemented for Reentrant task methods call this method instead of send(Message). This method delegates to the send method in the normal case where this is not a reentrant call. If it is a reentrant call, the message is dispatched directly to the task.

The proxy always calls Message.await() since by definition this is a synchronous call and the proxy doesn't know if we're reentering and dispatching the message directly here or if we're deferring to the send method for message queing and background dispatching. In the case of a reentrant call, the await method skips most of its processing except to throw the message exception if any. Unlike the algorithm for the send method, this algorithm assumes a synchronous call is FINISHED here, and the await method handles extracting and throwing any exception set by a call listener.

Throws:
Throwable

pump

private void pump(Messenger messenger)
           throws Throwable
Main message loop for assigning messages to messengers for execution. The queue is scanned for startable messages. Once found, if the current messenger is null, the scan is stopped and an attempt is made to start a new messenger to execute the message. If the messenger is non-null, the message is assigned to it and scanning continues. Marker messages are flagged as running but left in the queue in order to keep track of outstanding markers for ordering purposes.

Throws:
Throwable

run

void run(Messenger messenger)
Main message loop for a messenger running in a background thread. Delegates most of the work to helper methods. Continues executing messages until the message pump returns a null message. This indicates the queue is empty or a marker message is preventing further processing of messages. In either case, this loop is exited and its associated thread is released.

See Also:
start(Messenger), dispatch(Object, Message), pump(Messenger, Throwable), finish(Messenger)

dispatch

private Throwable dispatch(Object task,
                           Message message)
Executes the next message using the messenger's task. Updates the state of the message.


pump

private void pump(Messenger messenger,
                  Throwable exception)
Message pump for the messenger's main loop. Finishes the current message and initializes the messenger with the next message.


start

private void start(Messenger messenger)
Initializes a newly started messenger. Notifies messengers so additional messengers can start if needed. The first message, if any, is then retrieved from the message queue. This initial message pump streamlines the messenger's main message loop. The body of the main loop can assume a non-null message.


finish

private void finish(Messenger messenger)
Releases the messenger and prepares to shutdown. The agent's message queue is pumped after the messenger is released. This protects against timing gaps when the state of the message queue changes after this messenger's loop was exited but before the messenger is released. This could be accomplished in the messenger's main loop while the controller's lock is held, but moving it here streamlines the main loop.


finish

void finish(Message message)
Notification that a message transitioned to FINISHED.

Assumes the controller is locked prior to calling the synchronized Message.setState method which in turn calls this method.

See Also:
Message.setState(CallState, CallCompletion, Object, Throwable)

deadcheckBlocked

private void deadcheckBlocked()
Signals blocked messages to check for deadlock. The deadlock state for blocked messages changes when a messenger finishes. This method signals the blocked messages to check the new state for deadlock.

The following diagram shows an example scenario. Agent A1 is running messages M1 and M2 on threads T1 and T2. T2 is blocked on a synchronous message M4 to agent A2. A2 is running the M4 message on thread T3, and T3 is blocked on the synchronous message M5 to agent A1. The M5 message is queued behind the M3 message, both of which are waiting to run on either of A1's threads.

Threads T2 and T3 are potentially deadlocked. Thread T2 is waiting on thread T3, and T3 is waiting on T2. However, A1 has two threads. Once T1 finishes running M1, it is free to run M3 and then M5. The deadlock detection algorithm determines that M5 can eventually make progress and is therefore not deadlocked.

This might be true, except in this scenario M3 is an isolated message. It cannot run until both M1 and M2 are finished, and M5 cannot run until M3 runs and finishes. With this additional information, we can now see that while T1 is free to finish normally, T2 and T3 are deadlocked.

In order to keep the overhead of deadlock detection to a minimum, the deadlock heuristics do not consider marker dependencies (an isolated message is a type of marker message). Without data on marker dependencies, the deadlock heuristics do not immediately detect this deadlock scenario. The detection algorithm instead concludes that T3 has the potential to make progress when T1 finishes. The heuristic is to wait until all of A1's threads are deadlocked, and T1 is not.

The scenario changes when T1 finishes. Now the deadlock heuristics conclude T2 and T3 are deadlocked. Since the detection algorithm is only run within the context of waiting threads, this method notifies all blocked threads to check for deadlock. In our example scenario, A1's controller signals the blocked M5 message to check for deadlock, and the M5 message notifies the waiting T3 thread. The T3 thread checks for deadlock, finds it, and throws a DeadlockException. If the M4 task does not catch the exception, the A2 agent throws it to the M2 task running on T2.


blocked

void blocked(Awaiter awaiter,
             boolean synchronous)
Notification that an awaiter is waiting. The synchronous parameter indicates whether or not a message is waiting synchronously (versus pending, for example, or waiting as part of a future's get operation).

Notifies Deadlocks, records the blockage in blocked, and calls deadcheckBlocked().

See Also:
unblocked(Awaiter)

unblocked

void unblocked(Awaiter awaiter)
Notification that an awaiter is no longer waiting.

Notifies Deadlocks and removes the blockage from blocked.

See Also:
blocked(Awaiter, boolean)

getBlockingThreads

Thread[] getBlockingThreads()
Returns the messenger threads currently executing tasks. Returns null if no messengers are active or if a new messenger is starting. Threads waiting on a blocked message cannot be deadlocked if a new messenger is starting since the new messenger can potentially execute the blocked message.


handle

void handle(Throwable exception)
Forwards non-null exceptions to the handler for unhandled exceptions. Ensures no exceptions are thrown by this method.


getDeadcheckNanos

long getDeadcheckNanos()
Returns the nanosecond value for Messages.getDeadcheckTimeout().


toString

public String toString()
Delegates to Config.toString().

Overrides:
toString in class Object


Copyright 2009-2011 Tom Landon
Apache License 2.0