RFR [9] 8071474: Better failure atomicity for default read object

Peter Levart peter.levart at gmail.com
Fri Mar 20 14:06:49 UTC 2015


On 03/19/2015 10:26 PM, Chris Hegarty wrote:
> The current implementation of defaultReadObject sets non-primitive 
> field values one at a time, without first checking that all their 
> types are assignable. If, for example, the assignment of the last 
> non-primitive value fails, a CCE is thrown, and the previously set 
> fields remain set. The setting of field values, including primitives, 
> can be deferred until after they have been "validated", at a minimum 
> that the non-primitive types are assignable. This is achievable per 
> Class in the hierarchy up until the first user visible affect, i.e. a 
> readObject(NoData) method. Either all fields will be set, or contain 
> their default values.
>
> http://cr.openjdk.java.net/~chegar/8071474/webrev.00/webrev/
>
> I think there are further improvements that can be made in this area, 
> but I would like to move things forward incrementally.
>
> -Chris.

Hi Chris,

I'd just ask you a few questions to confirm my understanding of the code:

In ObjectInputStream:

1886         // Best effort Failure Atomicity; Each element in 
'slotFieldValues'
1887         // contains the stream field values for the same element in 
'slots',
1888         // up to the first slot with a readObject(NoData) method ( 
a user
1889         // visible effect ).
1890         int index = 1;
1891         for (; index < slots.length; index++) {
1892             ObjectStreamClass slotDesc = slots[index].desc;
1893             if (slotDesc.hasReadObjectMethod()
1894                 || slotDesc.hasReadObjectNoDataMethod()) {
1895                 break;
1896             }
1897         }
1898         // Store, and defer setting, values for index slots, ignore 
if just one.
1899         StreamFieldValues[] slotFieldValues = null;
1900         if (index > 1 && obj != null)
1901             slotFieldValues =  new StreamFieldValues[index];


...you scan slots starting from 1 and up. Is this because slots[0] is an 
uninteresting slot that belongs to j.l.Object class?

In this loop, you scan slots from 0 and up. Is this to catch corrupted 
streams that contain data for Object slot?

1971     /** Sets slot field values in the given obj. */
1972     private void setSlotFieldValues(Object obj,
1973 ObjectStreamClass.ClassDataSlot[] slots,
1974                                     StreamFieldValues[] 
slotFieldValues) {
1975         int length = slotFieldValues.length;
1976         for (int i = 0; i < length; i++) {
1977             if (slotFieldValues[i] != null)
1978                 defaultSetFieldValues(obj, slots[i].desc, 
slotFieldValues[i]);
1979         }
1980     }

I agree that this is an incremental improvement. You buffer values read 
from stream, check their types and delay setting them in particular 
class slots for classes not declaring  readObject[NoData] methods (or a 
span of slots in hierarchy ranging from Object to one before the class 
that declares readObject[NoData] method) which guarantees either success 
or atomic failure.

But have you considered a strategy that we discussed before where you 
would allow gradual building of state in object being deserialized and 
in case this fails anywhere (even in class slots declaring 
readObject[NoData] method), you roll-back the state (setting fields to 
default values) in all class slots from the one throwing an exception 
down to the 1st non-Serializable superclass (or Object). This would 
cover failure atomicity for the whole object, not just the slots not 
declaring readObject[NoData] methods. The premise is that 
deserialization starts with an uninitialized object (just class slots 
from 1st non-Serializable superclass down to Object are initialized by 
default accessible constructor). So rolling back the object to this 
state can be achieved by setting all instance fields of Serializable 
class slots to default values. A package-private 
FieldSetterContext.FieldsMap could be reused for this purpose (by adding 
a resetAllFields(Object obj) method). The FieldsMap instance is 
obtainable from ObjectStreamClass.getFieldAccessMap() which caches it 
(although internal, these type and method need renaming).

If you think this is a promising alternative to your failure atomicity 
proposal, I volunteer to add resetAllFields() method. I understand that 
this can only be added as part or on the top of FieldSetter API proposal.

Regards, Peter




More information about the core-libs-dev mailing list