@Documented @Retention(value=RUNTIME) @Target(value=FIELD) public @interface UniqueInRegion
Borrowed. That is, Unique values can be
safely passed to the parameter or used as the receiver with the guarantee
that they will still be unique when the method returns. Said another way we
create a temporary alias on the stack then ensure that it goes away.
Setting the allowRead=true attribute weakens the uniqueness guarantees:
it permits there to be @ReadOnly aliases.
In other words, only the ability to write (mutate) is restricted to this alias.
Once a unique reference has granted allowRead, there is no going back,
unlike with (temporary) borrowed references. This attribute is not currently
checked by analysis.
This annotation differs from Unique only with regard to the region
the state referenced by the annotated field is mapped into. This annotation
declares that the Instance region of the object referenced by the
annotated field is mapped into a named region of the object that contains the
annotated field. Unique maps the state into the Instance
region of the object that contains the annotated field if the field is
final; into the field itself if the field is not final.
Therefore, the two
annotations below on the friends field are equivalent.
@Unique
private final Set<Person> friends = new HashSet<Person>();
@UniqueInRegion("Instance")
private final Set<Person> friends = new HashSet<Person>();
In addition, a more complex syntax is allowed where regions of the object referenced
by the annotated field are explicitly mapped to regions of the
object that contains the annotated field. Using this syntax the annotation below is
equivalent to the two previous examples.
@UniqueInRegion("Instance into Instance")
private final Set<Person> friends = new HashSet<Person>();
This syntax should be rare in practice, however we show an example of its use
in the Examples section below.
In the case that the annotated field is non-final the field and the
Instance region of the object referenced by the annotated field are
mapped into a named region of the object that contains the annotated field.
This situation is illustrated in the code below.
@Region("private ContactData")
class ContactData {
@UniqueInRegion("ContactData")
private final Set<Person> friends = new HashSet<Person>()
@UniqueInRegion("ContactData")
private Set<Person> family = new HashSet<Person>()
...
}
In this example, the non-final field family is mapped into
the ContactData region but final field friends is
not. The state of the objects referenced by both fields is mapped into the
ContactData region.
null or
is an object that is not referenced by a field of any other object or another
field of the same object. The Instance region (and any named regions
if the more complex into syntax is used) of the object referenced by
the annotated field is mapped into a named region of the object that contains
the annotated field.
In the case that the annotated field is non-final the field and the
Instance region (and any named regions if the more complex
into syntax is used) of the object referenced by the annotated field
are mapped into a named region of the object that contains the annotated
field.
ObserverRegion, that
includes the integer notifyCounter and the state referenced by the
observers field (i.e., the internals of the HashSet object).
@Region("private ObserverRegion")
class Observer {
@InRegion("ObserverRegion")
private int notifyCounter = 0;
@UniqueInRegion("ObserverRegion")
private final Set<Callback> observers = new HashSet<Callback>()
...
}
The same result can be accomplished with the more complex syntax shown in the
example below. However, in this case the more complex syntax is
not recommended.
@Region("private ObserverRegion")
class Observer {
@InRegion("ObserverRegion")
private int notifyCounter = 0;
@UniqueInRegion("Instance into ObserverRegion")
private final Set<Callback> observers = new HashSet<Callback>()
...
}
The more complex syntax is necessary if only a portion of a referenced object
is to be aggregated is more complex. Consider the highly contrived example
below.
@Regions({
@Region("private OrderRegion"),
@Region("private InvRegion") })
@RegionLocks({
@RegionLock("ShopLock is this protects OrderRegion"),
@RegionLock("InvLock is inv protects InvRegion") })
public class Shop {
@InRegion("OrderRegion")
private int ordersPlaced;
private static class SandwichInventory {
int bread;
int mustard;
int ordersPlaced;
}
@UniqueInRegion("bread into InvRegion, mustard into InvRegion, ordersPlaced into OrderRegion, Instance into Instance")
private final SandwichInventory inv = new SandwichInventory();
void order() {
synchronized (inv) {
inv.bread++;
inv.mustard++;
}
synchronized (this) {
ordersPlaced++;
inv.ordersPlaced++;
}
}
...
}
In the above example the fields that count the number of orders placed are
protected by synchronizing on this while the inventory fields (
bread and mustard) are protected by synchronizing on
inv. All the state of the SandwichInventory must be placed
into named regions (even if you never use one of the named regions, e.g., you
create a JunkRegion) and the aggregation must be completed with a
mapping of the region Instance of the referenced object, usually
Instance into Instance. Finally, it is important in this example that
the field inv is declared to be final—otherwise the
field itself would have to be mapped into each of the named regions, which,
in this case, would be illegal because while inv could be mapped into
InvRegion or OrderRegion, it cannot be mapped into both.
Models of the sort shown for the Shop class above are discouraged and
should occur rarely in Java code that follows object-oriented design
principles.
@annotate tag.
/**
* @annotate Region("private ObserverRegion")
*/
class Observer {
/**
* @annotate InRegion("ObserverRegion")
*/
private int notifyCounter = 0;
/**
* @annotate UniqueInRegion("ObserverRegion")
*/
private final Set<Callback> observers = new HashSet<Callback>()
...
}
| Modifier and Type | Required Element and Description |
|---|---|
String |
value
The value of this attribute must conform to the following grammar (in Augmented Backus–Naur
Form):
|
| Modifier and Type | Optional Element and Description |
|---|---|
boolean |
allowRead
Setting the this attribute to
true weakens the uniqueness
guarantees: it permits there to be @ReadOnly aliases of
the referenced object. |
public abstract String value
value = regionSpecification / regionMapping *("," regionMapping)
regionMapping = simpleRegionSpecification "into" regionSpecification
regionSpecification = simpleRegionSpecificaion / qualifiedRegionName
simpleRegionSpecification = IDENTIFIER ; Region of the class being annotated
qualifedRegionName = IDENTIFIER *("." IDENTIFIER) : IDENTIFER ; Static region from the named, optionally qualified, class
IDENTIFIER = Legal Java Identifier
In A into B, the first RegionSpecification is relative to the
object referenced by the field; the second is relative to the object that
contains the field.
public abstract boolean allowRead
true weakens the uniqueness
guarantees: it permits there to be @ReadOnly aliases of
the referenced object. In other words, only the ability to write (mutate)
is restricted to this alias. This attribute is not currently
checked by analysis.Copyright © 2012 Surelogic, Inc.. All Rights Reserved.