Class Fences
- java.lang.Object
-
- com.strobel.core.Fences
-
public class Fences extends java.lang.Object
A set of methods providing fine-grained control over happens-before and synchronization order relations among reads and/or writes. The methods of this class are designed for use in uncommon situations where declaring variablesvolatile
orfinal
, using instances of atomic classes, usingsynchronized
blocks or methods, or using other synchronization facilities are not possible or do not provide the desired control.Memory Ordering. There are three methods for controlling ordering relations among memory accesses (i.e., reads and writes). Method
orderWrites
is typically used to enforce order between two writes, andorderAccesses
between a write and a read. MethodorderReads
is used to enforce order between two reads with respect to otherorderWrites
and/ororderAccesses
invocations. The formally specified properties of these methods described below provide platform-independent guarantees that are honored by all levels of a platform (compilers, systems, processors). The use of these methods may result in the suppression of otherwise valid compiler transformations and optimizations that could visibly violate the specified orderings, and may or may not entail the use of processor-level "memory barrier" instructions.Each ordering method accepts a
ref
argument, and controls ordering among accesses with respect to this reference. Invocations must be placed between accesses performed in expression evaluations and assignment statements to control the orderings of prior versus subsequent accesses appearing in program order. These methods also return their arguments to simplify correct usage in these contexts.Usages of ordering methods almost always take one of the forms illustrated in the examples below. These idioms arrange some of the ordering properties associated with
volatile
and related language-based constructions, but without other compile-time and runtime benefits that make language-based constructions far better choices when they are applicable. Usages should be restricted to the control of strictly internal implementation matters inside a class or package, and must either avoid or document any consequent violations of ordering or safety properties expected by users of a class employing them.Reachability. Method
reachabilityFence
establishes an ordering for strong reachability (as defined in thejava.lang.ref
package specification) with respect to garbage collection. MethodreachabilityFence
differs from the others in that it controls relations that are otherwise only implicit in a program -- the reachability conditions triggering garbage collection. As illustrated in the sample usages below, this method is applicable only when reclamation may have visible effects, which is possible for objects with finalizers (see Section 12.6 of the Java Language Specification) that are implemented in ways that rely on ordering control for correctness.Sample Usages
Safe publication. With care, method
orderWrites
may be used to obtain the memory safety effects offinal
for a field that cannot be declared asfinal
, because its primary initialization cannot be performed in a constructor, in turn because it is used in a framework requiring that all classes have a no-argument constructor; as in:class WidgetHolder { private Widget widget; public WidgetHolder() {} public static WidgetHolder newWidgetHolder(Params params) { WidgetHolder h = new WidgetHolder(); h.widget = new Widget(params); return Fences.orderWrites(h); } }
Here, the invocation oforderWrites
ensures that the effects of the widget assignment are ordered before those of any (unknown) subsequent stores ofh
in other variables that makeh
available for use by other objects. Initialization sequences usingorderWrites
require more care than those involvingfinal
fields. Whenfinal
is not used, compilers cannot help you to ensure that the field is set correctly across all usages. You must fully initialize objects before theorderWrites
invocation that makes references to them safe to assign to accessible variables. Further, initialization sequences must not internally "leak" the reference by using it as an argument to a callback method or adding it to a static data structure. If less constrained usages were required, it may be possible to cope using more extensive sets of fences, or as a normally better choice, using synchronization (locking). Conversely, if it were possible to do so, the best option would be to rewrite classWidgetHolder
to usefinal
.An alternative approach is to place similar mechanics in the (sole) method that makes such objects available for use by others. Here is a stripped-down example illustrating the essentials. In practice, among other changes, you would use access methods instead of a public field.
class AnotherWidgetHolder { public Widget widget; void publish(Widget w) { this.widget = Fences.orderWrites(w); } // ... }
In this case, theorderWrites
invocation occurs before the store making the object available. Correctness again relies on ensuring that there are no leaks prior to invoking this method, and that it really is the only means of accessing the published object. This approach is not often applicable -- normally you would publish objects using a thread-safe collection that itself guarantees the expected ordering relations. However, it may come into play in the construction of such classes themselves.Safely updating fields. Outside of the initialization idioms illustrated above, Fence methods ordering writes must be paired with those ordering reads. To illustrate, suppose class
c
contains an accessible variabledata
that should have been declared asvolatile
but wasn't:class C { Object data; // need volatile access but not volatile // ... } class App { Object getData(C c) { return Fences.orderReads(c).data; } void setData(C c) { Object newValue = ...; c.data = Fences.orderWrites(newValue); Fences.orderAccesses(c); } // ... }
MethodgetData
provides an emulation ofvolatile
reads of (non-long/double) fields by ensuring that the read ofc
obtained as an argument is ordered before subsequent reads using this reference, and then performs the read of its field. MethodsetData
provides an emulation of volatile writes, ensuring that all other relevant writes have completed, then performing the assignment, and then ensuring that the write is ordered before any other access. These techniques may apply even when fields are not directly accessible, in which case calls to fence methods would surround calls to methods such asc.getData()
. However, these techniques cannot be applied tolong
ordouble
fields because reads and writes of fields of these types are not guaranteed to be atomic. Additionally, correctness may require that all accesses of such data use these kinds of wrapper methods, which you would need to manually ensure.More generally, Fence methods can be used in this way to achieve the safety properties of
volatile
. However their use does not necessarily guarantee the full sequential consistency properties specified in the Java Language Specification chapter 17 for programs usingvolatile
. In particular, emulation using Fence methods is not guaranteed to maintain the property thatvolatile
operations performed by different threads are observed in the same order by all observer threads.Acquire/Release management of thread safe objects. It may be possible to use weaker conventions for volatile-like variables when they are used to keep track of objects that fully manage their own thread-safety and synchronization. Here, an acquiring read operation remains the same as a volatile-read, but a releasing write differs by virtue of not itself ensuring an ordering of its write with subsequent reads, because the required effects are already ensured by the referenced objects. For example:
class Item { synchronized f(); // ALL methods are synchronized // ... } class ItemHolder { private Item item; Item acquireItem() { return Fences.orderReads(item); } void releaseItem(Item x) { item = Fences.orderWrites(x); } // ... }
Because this construction avoids use oforderAccesses
, which is typically more costly than the other fence methods, it may result in better performance than usingvolatile
or its emulation. However, as is the case with most applications of fence methods, correctness relies on the usage context -- here, the thread safety ofItem
, as well as the lack of need for full volatile semantics inside this class itself. However, the second concern means that it can be difficult to extend theItemHolder
class in this example to be more useful.Avoiding premature finalization. Finalization may occur whenever a Java Virtual Machine detects that no reference to an object will ever be stored in the heap: A garbage collector may reclaim an object even if the fields of that object are still in use, so long as the object has otherwise become unreachable. This may have surprising and undesirable effects in cases such as the following example in which the bookkeeping associated with a class is managed through array indices. Here, method
action
uses areachabilityFence
to ensure that the Resource object is not reclaimed before bookkeeping on an associated ExternalResource has been performed; in particular here, to ensure that the array slot holding the ExternalResource is not nulled out in methodObject.finalize()
, which may otherwise run concurrently.class Resource { private static ExternalResource[] externalResourceArray = ... int myIndex; Resource(...) { myIndex = ... externalResourceArray[myIndex] = ...; ... } protected void finalize() { externalResourceArray[myIndex] = null; ... } public void action() { try { // ... int i = myIndex; Resource.update(externalResourceArray[i]); } finally { Fences.reachabilityFence(this); } } private static void update(ExternalResource ext) { ext.status = ...; } }
Here, the call toreachabilityFence
is unintuitively placed after the call toupdate
, to ensure that the array slot is not nulled out byObject.finalize()
before the update, even if the call toaction
was the last use of this object. This might be the case if for example a usage in a user program had the formnew Resource().action();
which retains no other reference to this Resource. While probably overkill here,reachabilityFence
is placed in afinally
block to ensure that it is invoked across all paths in the method. In a method with more complex control paths, you might need further precautions to ensure thatreachabilityFence
is encountered along all of them.It is sometimes possible to better encapsulate use of
reachabilityFence
. Continuing the above example, if it were OK for the call to method update to proceed even if the finalizer had already executed (nulling out slot), then you could localize use ofreachabilityFence
:public void action2() { // ... Resource.update(getExternalResource()); } private ExternalResource getExternalResource() { ExternalResource ext = externalResourceArray[myIndex]; Fences.reachabilityFence(this); return ext; }
Method
reachabilityFence
is not required in constructions that themselves ensure reachability. For example, because objects that are locked cannot in general be reclaimed, it would suffice if all accesses of the object, in all methods of class Resource (includingfinalize
) were enclosed insynchronized (this)
blocks. (Further, such blocks must not include infinite loops, or themselves be unreachable, which fall into the corner case exceptions to the "in general" disclaimer.) However, methodreachabilityFence
remains a better option in cases where this approach is not as efficient, desirable, or possible; for example because it would encounter deadlock.Formal Properties.
Using the terminology of The Java Language Specification chapter 17, the rules governing the semantics of the methods of this class are as follows:
The following is still under construction.
- [Definitions]
-
- Define sequenced(a, b) to be true if a occurs before b in program order.
- Define accesses(a, p) to be true if a is a read or write of a field (or if an array, an element) of the object referenced by p.
- Define deeplyAccesses(a, p) to be true if either accesses(a, p) or deeplyAccesses(a, q) where q is the value seen by some read r such that accesses(r, p).
- [Matching]
- Given:
- p, a reference to an object
- wf, an invocation of
orderWrites(p)
ororderAccesses(p)
- w, a write of value p
- rf, an invocation of
orderReads(p)
ororderAccesses(p)
- r, a read returning value p
- sequenced(wf, w)
- read r sees write w
- sequenced(r, rf)
- wf happens-before rf
- wf precedes rf in the synchronization order
- If (r1, w1) and (r2, w2) are two pairs of reads and writes, both respectively satisfying the above conditions for p, and sequenced(r1, r2) then it is not the case that w2 happens-before w1.
- [Initial Reads]
- Given:
- p, a reference to an object
- a, an access where deeplyAccesses(a, p)
- wf, an invocation of
orderWrites(p)
ororderAccesses(p)
- w, a write of value p
- r, a read returning value p
- b, an access where accesses(b, p)
- sequenced(a, wf);
- sequenced(wf, w)
- read r sees write w, and r is the first read by some thread t that sees value p
- sequenced(r, b)
- the effects of b are constrained by the relation a happens-before b.
- [orderAccesses]
- Given:
- p, a reference to an object
- f, an invocation of
orderAccesses(p)
- sequenced(f, w)
- f is an element of the synchronization order.
- [Reachability]
- Given:
- p, a reference to an object
- f, an invocation of
reachabilityFence(p)
- a, an access where accesses(a, p)
- b, an action (by a garbage collector) taking
the form of an invocation of
p.finalize()
or of enqueuing anyReference
constructed with argument p
- sequenced(a, f)
- a happens-before b.
- Since:
- 1.7
-
-
Field Summary
Fields Modifier and Type Field Description private static int
theVolatile
-
Constructor Summary
Constructors Modifier Constructor Description private
Fences()
-
Method Summary
All Methods Static Methods Concrete Methods Modifier and Type Method Description static <T> T
orderAccesses(T ref)
Informally: Ensures that accesses (reads or writes) using the given reference prior to the invocation of this method occur before subsequent accesses.static <T> T
orderReads(T ref)
Informally: Ensures that a read of the given reference prior to the invocation of this method occurs before a subsequent use of the given reference with the effect of reading or writing a field (or if an array, element) of the referenced object.static <T> T
orderWrites(T ref)
Informally: Ensures that a use of the given reference with the effect of reading or writing a field (or if an array, element) of the referenced object, prior to the invocation of this method occur before a subsequent write of the reference.static void
reachabilityFence(java.lang.Object ref)
Ensures that the object referenced by the given reference remains strongly reachable (as defined in thejava.lang.ref
package documentation), regardless of any prior actions of the program that might otherwise cause the object to become unreachable; thus, the referenced object is not reclaimable by garbage collection at least until after the invocation of this method.
-
-
-
Method Detail
-
orderReads
public static <T> T orderReads(T ref)
Informally: Ensures that a read of the given reference prior to the invocation of this method occurs before a subsequent use of the given reference with the effect of reading or writing a field (or if an array, element) of the referenced object. The use of this method is sensible only when paired with other invocations oforderWrites(T)
and/ororderAccesses(T)
for the given reference. For details, see the class documentation for this class.- Parameters:
ref
- the reference. If null, this method has no effect.- Returns:
- the given ref, to simplify usage
-
orderWrites
public static <T> T orderWrites(T ref)
Informally: Ensures that a use of the given reference with the effect of reading or writing a field (or if an array, element) of the referenced object, prior to the invocation of this method occur before a subsequent write of the reference. For details, see the class documentation for this class.- Parameters:
ref
- the reference. If null, this method has no effect.- Returns:
- the given ref, to simplify usage
-
orderAccesses
public static <T> T orderAccesses(T ref)
Informally: Ensures that accesses (reads or writes) using the given reference prior to the invocation of this method occur before subsequent accesses. For details, see the class documentation for this class.- Parameters:
ref
- the reference. If null, this method has no effect.- Returns:
- the given ref, to simplify usage
-
reachabilityFence
public static void reachabilityFence(java.lang.Object ref)
Ensures that the object referenced by the given reference remains strongly reachable (as defined in thejava.lang.ref
package documentation), regardless of any prior actions of the program that might otherwise cause the object to become unreachable; thus, the referenced object is not reclaimable by garbage collection at least until after the invocation of this method. Invocation of this method does not itself initiate garbage collection or finalization.See the class-level documentation for further explanation and usage examples.
- Parameters:
ref
- the reference. If null, this method has no effect.
-
-