|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectorg.curjent.impl.agent.Controller
public final class Controller
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 |
---|
final ReentrantLock lock
final Messengers messengers
final Messages messages
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
.
final Listeners listeners
final Expirations expirations
private Mark mark
final ThreadLocal<Message> message
Agent.getLastCall(Object)
HashMap<Method,CallInfo> synthetic
private final HashMap<Awaiter,Integer> blocked
final Config config
Constructor Detail |
---|
Controller(AgentLoader loader, Class<?>[] interfaces, AgentTasks tasks, Class<?> taskType)
Method Detail |
---|
void init(Proxy proxy, ProxyInfo info, CallInfo[] calls)
public void send(Message message) throws Throwable
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.
Throwable
public void dispatch(Message message) throws Throwable
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.
Throwable
private void pump(Messenger messenger) throws Throwable
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.
Throwable
void run(Messenger messenger)
start(Messenger)
,
dispatch(Object, Message)
,
pump(Messenger, Throwable)
,
finish(Messenger)
private Throwable dispatch(Object task, Message message)
private void pump(Messenger messenger, Throwable exception)
private void start(Messenger messenger)
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.
private void finish(Messenger messenger)
void finish(Message message)
FINISHED
.
Assumes the controller is locked prior to calling the synchronized
Message.setState
method which in turn calls this method.
Message.setState(CallState, CallCompletion, Object, Throwable)
private void deadcheckBlocked()
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.
void blocked(Awaiter awaiter, boolean synchronous)
Notifies Deadlocks
, records the blockage in blocked
, and
calls deadcheckBlocked()
.
unblocked(Awaiter)
void unblocked(Awaiter awaiter)
Notifies Deadlocks
and removes the blockage from blocked
.
blocked(Awaiter, boolean)
Thread[] getBlockingThreads()
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.
void handle(Throwable exception)
long getDeadcheckNanos()
Messages.getDeadcheckTimeout()
.
public String toString()
Config.toString()
.
toString
in class Object
|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |