|
ref_send API 2.17 defensive programming in Java |
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Object org.ref_send.promise.Eventual
public class Eventual
The eventual operator.
This class decorates an event loop with methods implementing the core eventual control flow statements needed for defensive programming. The primary aim of these new control flow statements is preventing plan interference.
The implementation of a public method can be thought of as a plan in which an object makes a series of state changes based on a list of invocation arguments and the object's own current state. As part of executing this plan, the object may need to notify other objects of the changes in progress. These other objects may have their own plans to execute, based on this notification. Plan interference occurs when execution of these other plans interferes with execution of the original plan.
Interleaving plan execution is vulnerable to many kinds of interference. Each kind of interference is explained below, using the following example code:
public final class Account { private int balance; private final ArrayList<Receiver<Integer>> observers; Account(final int initial) { balance = initial; observers = new ArrayList<Receiver<Integer>>(); } public void observe(final Receiver<Integer> observer) { if (null == observer) { throw new NullPointerException(); } observers.add(observer); } public int getBalance() { return balance; } public void setBalance(final int newBalance) { balance = newBalance; for (final Receiver<Integer> observer : observers) { observer.apply(newBalance); } } }
A method can terminate execution of its plan by throwing an exception.
The plan may be terminated because it would violate one of the object's
invariants or because the request is malformed. Unfortunately, throwing an
exception may terminate not just the current plan, but also any other
currently executing plans. For example, if one of the observers throws a
RuntimeException
from its apply()
implementation, the
remaining observers are not notified of the new account balance. These other
observers may then continue operating using stale data about the account
balance.
When a method implementation invokes a method on another object, it
temporarily suspends progress on its own plan to let the called method
execute its plan. When the called method returns, the calling method
resumes its own plan where it left off. Unfortunately, the called method
may have changed the application state in such a way that resuming the
original plan no longer makes sense. For example, if one of the observers
invokes setBalance()
in its apply()
implementation,
the remaining observers will first be notified of the balance after the
update, and then be notified of the balance before the update. Again, these
other observers may then continue operating using stale data about the
account balance.
A called method may also initiate an unanticipated state transition in
the calling object, while the current transition is still incomplete. For
example, in the default state, an Account
is always ready to accept a
new observer; however, this constraint is temporarily not met when the
observer list is being iterated over. An observer could catch the
Account
in this transitional state by invoking observe()
in
its apply()
implementation. As a result, a
ConcurrentModificationException
will be thrown when
iteration over the observer list resumes. Again, this exception prevents
notification of the remaining observers.
The above plan interference problems are only possible because execution of one plan is interleaved with execution of another. Interleaving plan execution can be prevented by scheduling other plans for future execution, instead of allowing them to preempt execution of the current plan. This class provides control flow statements for scheduling future execution and receiving its results.
Since the control flow statements defined by this class schedule future
execution, instead of immediate execution, they behave differently from the
native control flow constructs in the Java language. To make the difference
between eventual and immediate execution readily recognized by programmers
when scanning code, some naming conventions are proposed. By convention, an
instance of Eventual
is held in a variable named "_
".
Additional ways of marking eventual operations with the '_
' character
are specified in the documentation for the methods defined by this class. All
of these conventions make eventual control flow statements distinguishable by
the character sequence "_.
". Example uses are also shown in the
method documentation for this class. The '_
' character should only be
used to identify eventual operations so that a programmer can readily
identify operations that are expected to be eventual by looking for the
_.
pseudo-operator.
Field Summary | |
---|---|
Receiver<?> |
destruct
destruct the vat |
Log |
log
debugging output |
Constructor Summary | |
---|---|
Eventual(Receiver<Promise<?>> enqueue)
Constructs an instance. |
|
Eventual(Receiver<Promise<?>> enqueue,
java.lang.String here,
Log log,
Receiver<?> destruct)
Constructs an instance. |
Method Summary | ||
---|---|---|
|
_(T referent)
Ensures a reference is an eventual reference. |
|
|
_(T x)
Causes a compile error for code that attempts to create an eventual reference of a concrete type. |
|
static
|
cast(java.lang.Class<?> type,
Promise<T> promise)
Casts a promise to a specified type. |
|
|
defer()
Creates a promise in the unresolved state. |
|
boolean |
equals(java.lang.Object x)
|
|
int |
hashCode()
|
|
static
|
near(Promise<T> promise)
Gets a corresponding immediate reference. |
|
static
|
near(T reference)
Gets a corresponding immediate reference. |
|
static
|
ref(T referent)
Gets a corresponding promise. |
|
static
|
reject(java.lang.Exception reason)
Constructs a rejected promise. |
|
|
spawn(java.lang.String label,
java.lang.Class<?> maker,
java.lang.Object... optional)
Creates a sub-vat. |
|
|
when(P reference,
Do<P,R> conditional)
Does an eventual conditional operation on an eventual reference. |
|
|
when(Promise<P> promise,
Do<P,R> conditional)
Does an eventual conditional operation on a promise. |
Methods inherited from class java.lang.Object |
---|
getClass, notify, notifyAll, toString, wait, wait, wait |
Field Detail |
---|
public final Log log
public final Receiver<?> destruct
call like: destruct.apply(null)
Constructor Detail |
---|
public Eventual(Receiver<Promise<?>> enqueue, java.lang.String here, Log log, Receiver<?> destruct)
enqueue
- raw event loophere
- URI for the event looplog
- log
destruct
- destruct
public Eventual(Receiver<Promise<?>> enqueue)
enqueue
- raw event loopMethod Detail |
---|
public int hashCode()
hashCode
in interface Selfless
hashCode
in class java.lang.Object
public boolean equals(java.lang.Object x)
equals
in class java.lang.Object
public final <P,R> R when(Promise<P> promise, Do<P,R> conditional)
The conditional
code block will be notified of the
promise
's state at most once, in a future event loop turn. If
there is no referent, the conditional
's reject
method will be called with the reason; otherwise, the
fulfill
method will be called with either an immediate
reference for a local referent, or an eventual reference
for a remote referent. For example:
import static org.ref_send.promise.Eventual.ref; … final Promise<Account> mine = … final Promise<Integer> balance = _.when(mine, new Do<Account,Promise<Integer>>() { public Promise<Integer> fulfill(final Account x) { return ref(x.getBalance()); } });
A null
promise
argument is treated like a rejected
promise with a reason of NullPointerException
.
The conditional
in successive calls to this method with the same
promise
will be notified in the same order as the calls were
made.
This method will not throw an Exception
. Neither the
promise
, nor the conditional
, argument will be given the
opportunity to execute in the current event loop turn.
P
- parameter typeR
- return typepromise
- observed promiseconditional
- conditional code block, MUST NOT be null
conditional
's return, or null
if the
conditional
's return type is Void
public final <P,R> R when(P reference, Do<P,R> conditional)
The implementation behavior is the same as that documented for the
promise based when
statement.
P
- parameter typeR
- return typereference
- observed referenceconditional
- conditional code block, MUST NOT be null
conditional
's return, or null
if the
conditional
's return type is Void
public final <T> Deferred<T> defer()
The return from this method is a ( promise,
resolver ) pair. The promise is initially in the
unresolved state and can only be resolved by the resolver once. If the
promise is fulfilled, the promise will
forever refer to the provided referent. If the promise, is
rejected, the promise will forever be in the
rejected state, with the provided reason. If the promise is
resolved, the promise will forever be in
the same state as the provided promise. After this initial state
transition, all subsequent invocations of either fulfill
, reject
or resolve
are silently ignored. Any observer
registered on the promise will only be notified after
the promise is either fulfilled or rejected.
T
- referent type
public final <T> T _(T referent)
An eventual reference queues invocations, instead of processing them immediately. Each queued invocation will be processed, in order, in a future event loop turn.
Use this method to vet received arguments. For example:
import static org.joe_e.ConstArray.array; public final class Account { private final Eventual _; private int balance; private ConstArray<Receiver<Integer>> observer_s; public Account(final Eventual _, final int initial) { this._ = _; balance = initial; observer_s = array(); } public void observe(final Receiver<Integer> observer) { // Vet the received arguments. final Receiver<Integer> observer_ = _._(observer); // Use the vetted arguments. observer_s = observer_s.with(observer_); } public int getBalance() { return balance; } public void setBalance(final int newBalance) { balance = newBalance; for (final Receiver<Integer> observer_ : observer_s) { // Schedule future execution of notification. observer_.apply(newBalance); } } }
By convention, the return from this method is held in a variable whose
name is suffixed with an '_
' character. The main part of the
variable name should use Java's camelCaseConvention. A list of eventual
references is suffixed with "_s
". This naming convention
creates the appearance of a new operator in the Java language, the
eventual operator: "_.
".
If this method returns successfully, the returned eventual reference
will not throw an Exception
on invocation of any of the methods
defined by its type, provided the invoked method's return type is either
void
, an allowed proxy
type or assignable from Promise
. Invocations on the eventual
reference will not give the referent
, nor any of the invocation
arguments, an opportunity to execute in the current event loop turn.
Invocations of methods defined by Object
are not
queued, and so can cause plan interference, or throw an exception.
T
- referent type, MUST be an
allowed proxy typereferent
- immediate or eventual reference,
MUST be non-null
java.lang.NullPointerException
- null
referent
java.lang.ClassCastException
- T
not an
allowed proxy typepublic final <T extends java.io.Serializable> void _(T x)
If you encounter a compile error because your code is linking to this method, insert an explicit cast to the allowed proxy type. For example,
_._(this).apply(null);becomes:
_._((Receiver<?>)this).apply(null);
x
- ignored
java.lang.AssertionError
- always thrownpublic static <T> T cast(java.lang.Class<?> type, Promise<T> promise) throws java.lang.ClassCastException
For example,
final Channel<Receiver<Integer>> x = _.defer(); final Receiver<Integer> r_ = cast(Receiver.class, x.promise);
T
- referent type to implementtype
- referent type to implementpromise
- promise for the referent
java.lang.ClassCastException
- no cast to type
public static <T> Promise<T> ref(T referent)
This method is the inverse of cast
; it gets the
corresponding promise for a given reference.
This method will not throw an Exception
.
T
- referent typereferent
- immediate or eventual referencepublic static <T> T near(T reference)
This method should only be used when the application knows the provided
reference refers to a local object. Any other condition is treated as a
fatal error. Use the call
method to check the status
of a promise.
This method will not throw an Exception
.
T
- referent typereference
- possibly eventual reference for a local referent
public static <T> T near(Promise<T> promise)
This method should only be used when the application knows the provided
promise refers to a local object. Any other condition is treated as a
fatal error. Use the call
method to check the status
of a promise.
This method will not throw an Exception
.
T
- referent typepromise
- a promise
public static <T> Promise<T> reject(java.lang.Exception reason)
T
- referent typereason
- rejection reasonpublic <R> Vat<R> spawn(java.lang.String label, java.lang.Class<?> maker, java.lang.Object... optional)
All created vats will be destructed when this vat is destructed.
The maker
MUST be a public
Joe-E class with a method of signature:
static public R
make(Eventual
_, …)
The ellipsis means the method can have any number of additional
arguments. The Eventual
parameter, if present, MUST be the first
parameter.
This method will not throw an Exception
. None of the arguments
will be given the opportunity to execute in the current event loop turn.
R
- return type, MUST be either an interface, or a Promise
label
- optional vat label,
if null
a label will be generatedmaker
- constructor classoptional
- more arguments for maker
's make method
maker
|
ref_send API 2.17 defensive programming in Java |
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
Copyright 1998-2009 Waterken Inc. under the terms of the MIT X license.