4.1.1. Using transactions in constructors
Examples throughout this manual have used transactions in the implementation of constructors for new persistent objects. This is deliberate because it guarantees correct propagation of the state of the object to the object store. Recall that the state of a modified persistent object is only written to the object store when the top-level transaction commits. Thus, if the constructor transaction is top-level and it commits, then the newly created object is written to the store and becomes available immediately. If however, the constructor transaction commits but is nested because some other transaction started prior to object creation is running, then the state will be written only if all of the parent transactions commit.
On the other hand, if the constructor does not use transactions then it is possible for inconsistencies in the system to arise. For example, if no transaction is active when the object is created then its state will not be saved to the store until the next time the object is modified under the control of some transaction.
Consider this simple example:
AtomicAction A = new AtomicAction();
obj1 = new Object(); // create new object
obj2 = new Object("old"); // existing object
obj2.remember(obj1.get_uid()); // obj2 now contains reference to obj1
A.commit(true); // obj2 saved but obj1 is not
Here the two objects are created outside of the control of the top-level action A.
obj1 is a new object;
obj2 an old existing object. When the remember operation of
obj2 is invoked the object will be activated and the Uid of
obj1 remembered. Since this action commits the persistent state of
obj2 could now contain the Uid of
obj1. However, the state of
obj1 itself has not been saved since it has not been manipulated under the control of any action. In fact, unless it is modified under the control of some action later in the application it will never be saved. If, however, the constructor had used an atomic action the state of
obj1 would have automatically been saved at the time it was constructed and this inconsistency could not arise.
4.1.2. More on save_state and restore_state
TxCore may invoke the user-defined
save_state operation of an object effectively at any time during the lifetime of an object including during the execution of the body of the object’s constructor (particularly if it uses atomic actions). It is important, therefore, that all of the variables saved by save_state are correctly initialised.
Caution must be also exercised when writing the
restore_state operations to ensure that no transactions are started (either explicitly in the operation or implicitly through use of some other operation). This restriction arises due to the fact that TxCore may invoke
restore_state as part of its commit processing resulting in the attempt to execute an atomic transaction during the commit or abort phase of another transaction. This might violate the atomicity properties of the transaction being committed (aborted) and is thus discouraged.
In order to support crash recovery for persistent objects it is necessary for all
restore_state methods of user objects to call
All of the basic types of Java (int, long, etc.) can be saved and restored from an
Input/OutputObjectState instance by using the pack (and unpack) routines provided by
Input/OutputObjectState. However packing and unpacking objects should be handled differently. This is because packing objects brings in the additional problems of aliasing. That is two different object references may in actual fact point at the same item. For example:
public class Test
public Test (String s);
private String s1;
private String s2;
public Test (String s)
s1 = s;
s2 = s;
Here, both s1 and s2 point at the same string and a naive implementation of
save_state could end up by copying the string twice. From a
save_state perspective this is simply inefficient. However, it makes
restore_state incorrect since it would unpack the two strings into different areas of memory destroying the original aliasing information. The current version of TxCore will pack and unpack separate object references.