|
|||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |
See:
Description
Interface Summary | |
---|---|
AgentCall<V> | Monitor and control an agent call. |
AgentConfig | Configure an agent. |
AgentLoader | Creates a generated class. |
AgentMark | Synthetic mark for tracing the progress of an agent's calls. |
AgentStats | Statistics for an agent. |
AgentTasks | Source for agent tasks. |
CallSite | Metadata and configuration for an agent's method. |
CallStateListener<V> | Validate, monitor and control call state transitions. |
ExceptionHandler | Handler for exceptions. |
Class Summary | |
---|---|
Agent | Agent creation and configuration. |
AgentResult<V> | Holder for a future's value. |
CustomLoader | Custom class loader. |
DelegatingLoader | Delegates class creation and loading to an existing ClassLoader . |
DynamicTasks | Source for dynamically created tasks. |
FixedTasks | Source for a fixed number of tasks. |
ReusableTask | Source for a task that can be used concurrently by multiple threads. |
SingletonTask | Source for one task. |
Enum Summary | |
---|---|
CallCompletion | Reason codes for why a call finished. |
CallState | Execution state of a call. |
MarkerType | Type of Marker . |
Exception Summary | |
---|---|
AgentException | Generic agent exception and wrapper for undeclared checked exceptions. |
CallSiteNotFoundException | Lookup for an agent's call site failed. |
CapacityExceededException | Number of accepted and/or pending calls has exceeded an agent's configured capacity. |
ConfigLockedException | Agent's configuration is locked. |
DeadlockException | Two or more synchronous agent calls have deadlocked. |
ExpiredException | Call waited too long to begin executing. |
Annotation Types Summary | |
---|---|
Capacity | Specifies the maximum number of messages an agent accepts for processing. |
Expiration | Agent call expirations. |
Isolated | Specifies that a call should execute by itself. |
Leading | Specifies that a call should finish executing before any subsequent calls start executing. |
Marker | Specifies that a call should not start executing until all prior calls have finished executing. |
Reentrant | Specifies that a synchronous call can be executed within the context of an existing agent call without blocking. |
Synchronous | Specifies that a call should execute synchronously. |
Agent public API. Curjent agents are a concurrency mechanism. Writing to standard Java interfaces, clients of an agent execute commands asynchronously in background threads. The designer of an agent provides one or more interfaces and a class that implements those interfaces. The curjent agent library provides the facilities for running and coordinating client calls concurrently in one or more background threads.
The following demonstrates how to say "Hello World!" asynchronously using an agent:
interface World { void hello(); } class WorldTask { void hello() { System.out.println("Hello World!"); } } class WorldTest { void run() { World world = Agent.newInstance(World.class, new WorldTask()); world.hello(); } }
Proxy
class, the curjent library
generates custom bytecode for each agent proxy and message to avoid the
execution overhead of reflection and the memory overhead of parameter boxing.
This enables the agent to call the task's methods directly using standard JVM
method invocation instructions, including the use of primitive arguments and
return values without boxing.
Future
return types which
enables a caller to explicitly monitor the progress of an asynchronous call.
Executor
facilities. Threads are taken as needed
to start new messengers and released when the agent's queue is empty.Agent
AgentLoader
AgentTasks
AgentConfig
Capacity
AgentStats
Executor
:
AgentConfig.setExecutor(java.util.concurrent.Executor)
AgentConfig.setUnhandledExceptionHandler(ExceptionHandler)
Marker
Leading
Isolated
Synchronous
Reentrant
Expiration
CallStateListener
AgentCall
Java-based alternative to concurrent programming languages. Another active area of work is concurrency-friendly programming languages. Erlang was developed in the 1980's for telecommunications and has recently gained popularity for general computing. Scala and Clojure are modern examples built atop the JVM. For Java programmers, the curjent library provides an agent framework for the Java programming language. It works with standard Java interfaces and classes.
@Synchronous
. The default behavior for methods with
non-void return types is synchronous. The one exception is methods with
Future
return types.
A method with a Future
return type is executed asynchronously.
The caller can use the returned Future
object to monitor the
call, and to wait and get the eventual result (see
Future
). When the agent's task executes the
call, it returns its result to the agent which the agent makes available to
the caller via one of the Future
object's get
methods.
Of course, the result returned by the task can be of any type, such as an
int
, a List
, or whatever. However, because the Java
language does not have built-in support for future values, the task's class
will not compile since the interface's method has a Future
return type but the task's method has some other return type.
The task designer has two options. One is to not include the agent's
interfaces in the task class's implements
clause. The primary
disadvantage of this option is that method signature problems are reported at
runtime when the agent is first created instead of at compile time. The
second option is to have the task return a Future
wrapper for
the actual result. The AgentResult
class is
provided for this purpose. When the task method's return type is
Future
, the agent returns the value obtained from the task
result's get()
method instead of the future object.
If the intent is to truly return a Future
result, annotate the
task's method with @Synchronous
. The agent will return the
task's result directly instead of calling and returning the value from its
get()
method.
An alternative to Future
is AgentCall
which extends
Future
with advanced functionality.
Future
result are handled as
documented for the get
methods of the
Future
interface. Specifically, exceptions
thrown by an agent's task are wrapped as the cause in an
ExecutionException
. More typically, though, calls to an agent
are executed asynchronously. The agent cannot propagate task exceptions to
the caller of asynchronous methods, so the agent instead calls its handler
for unhandled exceptions (see
AgentConfig.setUnhandledExceptionHandler(ExceptionHandler)
for details).
A task's method is permitted to throw any type of exception. Of course, if
the task's class includes the agent's interfaces in its
implements
clause, the task must follow the Java language rules
in order to compile. But if the task does not include the interfaces
in its implements
clause, a task's method is free to declare any
type of exception in its throws
clause. This can simplify
exception handling in some cases. One example is the common case where the
task method is called asynchronously. Because the exception cannot propagate
to the caller, an effective solution is to let the handler for unhandled
exceptions deal with these exceptions generically for all of the task's
methods, such as logging them. If an undeclared checked exception is thrown
by a synchronous task method, the agent wraps the exception as the cause in
an AgentException
. For methods returning a Future
,
the Future
object always wraps exceptions in an
ExecutionException
, as specified by the Future
interface.
CallStateListener
and
AgentCall
. Clients can listen for all call state
transitions or target more specific transitions. A listener can validate
calls during the initial CallState.STARTING
state,
providing immediate feedback to clients, even for otherwise asynchronous
calls. Listeners can also implement cross-cutting functionality, such as
debug tracing. A listener can finish a call at any time and set a call's
result value or exception.
java.util.concurrent
.
Memory consistency holds for unsynchronized mutable values if users of the
agent and the agent's task adhere to a transfer of ownership protocol.
Following this protocol, only one thread can own a mutable object at a time,
and only the owner can read and write mutable attributes of the object. For
example, a caller can pass an unsynchronized ArrayList
as a
parameter to an agent. This transfers ownership of the list to the agent.
When the agent dispatches the call to the task, the task can safely use and
update the list. Furthermore, if the call is synchronous or returns a
Future
, the completion of the call transfers ownership back to
the caller. The caller can then inspect the task's changes.
Synchronous
override this behavior. Calls with
non-void and non-Future return types are also implicitly synchronous.
An example deadlock scenario is one agent making a synchronous call on a second agent, and the second agent making a synchronous call on the first. Both agents wait indefinitely for the other to finish.
The practicality of automatically detecting deadlock depends on the context. Java and most programming languages and operating systems do not, whereas relational databases typically do.
Agent usage and design patterns are ideally suited for automatic deadlock detection. No overhead is incurred for asynchronous calls. And even synchronous calls incur no overhead until the wait time exceeds a few seconds. After the minimum wait time, deadlock processing is performed only by the threads that are already waiting. This avoids overhead for the vast majority of agents and agent calls, and leverages multi-core systems for background detection of deadlocks.
|
|||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |