Code review request for 6998871 "Support making the Throwable.stackTrace field immutable"
Rémi Forax
forax at univ-mlv.fr
Fri Apr 8 20:43:46 UTC 2011
Maybe I'm wrong but you can reuse the same clone.
I mean, it has to have a different identity from UNASSIGNED_STACK but
it can be the same for all clones.
Rémi
On 04/08/2011 08:27 PM, Joe Darcy wrote:
> Updated webrev described below...
>
> Joe Darcy wrote:
>> Joe Darcy wrote:
>>> Hi Rémi.
>>>
>>> Rémi Forax wrote:
>>>> On 04/07/2011 08:29 AM, Joe Darcy wrote:
>>>>> Hello.
>>>>>
>>>>> Returning to some earlier work, I've developed a proposed fix for
>>>>>
>>>>> 6998871 "Support making the Throwable.stackTrace field immutable"
>>>>> http://cr.openjdk.java.net/~darcy/6998871.2/
>>>>>
>>>>> One constructor of Throwable now takes an additional boolean
>>>>> argument to make the stack trace information immutable. Analogous
>>>>> constructors are added to Exception, RuntimeException, and Error.
>>>>>
>>>>> Mandy and David have already reviewed the change; I'm interested
>>>>> in getting additional feedback on the design of the API.
>>>>>
>>>>> Cheers,
>>>>>
>>>>> -Joe
>>>>
>>>> Joe,
>>>> I don't think you need the sentinel in the serialized form,
>>>> you have only two states: an immutable stacktrace (stacktrace ==
>>>> null) or
>>>> a stacktrace. I think it's better to don't serialize the field
>>>> "stacktrace" if
>>>> the stacktrace is immutable.
>>>>
>>>> Also, FILLED_IN_STACK is not necessary, you can use
>>>> EMPTY_STACKinstead,
>>>> or if you find it hard to understand, at least FILLED_IN_STACK
>>>> should be an empty array.
>>>>
>>>> Rémi
>>>>
>>>
>>> Here is an explanation and rationale of the protocol in more
>>> detail. As far as serialization goes, there are three cases of
>>> interest where "new" means JDK 7 and "old" means any release prior
>>> to JDK 7
>>>
>>> 1) Object serialized on *new* platform has same semantics after
>>> being deserialized on *new* platform.
>>>
>>> 2) Object serialized on *old* platform has same semantics after
>>> being deserialized on *new* platform.
>>>
>>> 3) Object serialized on *new* platform has reasonable semantics
>>> after being deserialized on *old* platform.
>>>
>>> (And of course when both source and destination platforms are old,
>>> JDK 7 isn't involved.)
>>>
>>> For point 1), a logically equivalent object should result, which in
>>> this case means the cause, stack trace, stack trace, message, etc.
>>> should be the same on the old and new objects.
>>>
>>> For 2), if an exception object serialized prior to the stack trace
>>> facility being added was deserialized in JDK 7, that is logically
>>> equivalent to there being not stack trace information, so an empty
>>> stack is a better result than an immutable stack. I've updated the
>>> readObject method accordingly.
>>>
>>> For 3), since null was not a valid value for stackTrace in the past,
>>> some other sentinel object should be written in out in the serial
>>> form to denote such a value, which is the role played by
>>> STACK_TRACE_SENTINEL as used in the writeObject method.
>>>
>>> A marker value other than EMPTY_STACK is needed for the following
>>> reason since the stack trace information is spread amongst two
>>> fields, backtrace and stackTrace. The transient backtrace field
>>> pre-dates the programmatic stack facility being added; that facility
>>> uses the stackTrace field. If fillInStackTrace is called *after* a
>>> call to setStackTrace, the real stack information is held in the
>>> backtrace field as written by fillInStackTrace. The FILLED_IN_STACK
>>> value alerts the getourstacktrace method to this situation.
>>>
>>> Revised webrev:
>>>
>>> http://cr.openjdk.java.net/~darcy/6998871.3/
>>>
>>> Thanks,
>>>
>>> -Joe
>>
>> PS A little more detail here, exception objects can be created via
>> deserialization as well as by a constructor. As currently written,
>> the writeObject method uses EMPTY_STACK for both a zero-length
>> incoming stack and a null stackTrace pointer (which can result from
>> an throwable object serialized from a JDK release before the stack
>> trace facility was added). The native fillInStackTrace method writes
>> a null into the stackTrace field to indicate it is no longer valid;
>> this goes against the new protocol and the Java-level of
>> fillInStackTraces needs to write a non-null "dirty" value into the
>> field to signal to getOurStackTrace that the stack trace array needs
>> to be recomputed if requested. Therefore, if fillInStackTrace were
>> called on one of these deserialized objects to logically replace its
>> stack trace, using EMPTY_STACK would not allow the getOutStackTrace
>> code to know that the stack trace had logically been replaced by a
>> new value in backtrace since EMPY_STACK could result from serialization.
>>
>> The serialization code could use EMPTY_STACK.clone() instead of
>> EMPTY_STACK, which would break the object equality being tested in
>> getOurStackTrace. I'll consider making this change in the
>> Throwable.readObject method.
>>
>> Cheers,
>>
>> -Joe
>
> I've put the updated webrev up at
>
> http://cr.openjdk.java.net/~darcy/6998871.4/
>
> Diff of Throwable compared to .3 version is below. EMPTY_STACK has
> been renamed to UNASSIGNED_STACK and that value (or a clone if it) is
> used as the sentential and for zero-length arrays. The distinct
> FILLED_IN_STACK value has been removed. The protocol around updating
> the fields is better documented. In other files, ArithmeticException,
> NullPointerException, and OutOfMemoryError are updated to state the VM
> may created them with immutable stack too.
>
> Thanks,
>
> -Joe
>
> 157,163d156
> < * A value indicating that the logical stack trace has been
> < * populated into the backtrace field.
> < */
> < private static final StackTraceElement[] FILLED_IN_STACK =
> < new StackTraceElement[] {new StackTraceElement("FILLED_IN",
> "STACK", null, -1)};
> <
> < /**
> 166c159
> < private static final StackTraceElement[] EMPTY_STACK = new
> StackTraceElement[0];
> ---
> > private static final StackTraceElement[] UNASSIGNED_STACK = new
> StackTraceElement[0];
> 211c204,205
> < * #setStackTrace()} and {@link #fillInStackTrace} will be be
> no-ops.
> ---
> > * #setStackTrace(StackTraceElement[])} and {@link
> > * #fillInStackTrace()} will be be no-ops.
> 216c210
> < private StackTraceElement[] stackTrace = EMPTY_STACK;
> ---
> > private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
> 330c324,325
> < * #fillInStackTrace()} and subsequent calls to {@code
> ---
> > * #fillInStackTrace()}, a {@code null} will be written to the
> > * {@code stackTrace} field, and subsequent calls to {@code
> 339,342c334,339
> < * conditions under which suppression is disabled. Disabling of
> < * suppression should only occur in exceptional circumstances
> < * where special requirements exist, such as a virtual machine
> < * reusing exception objects under low-memory situations.
> ---
> > * conditions under which suppression is disabled and document
> > * conditions under which the stack trace is not writable.
> > * Disabling of suppression should only occur in exceptional
> > * circumstances where special requirements exist, such as a
> > * virtual machine reusing exception objects under low-memory
> > * situations.
> 770c767
> < stackTrace = FILLED_IN_STACK;
> ---
> > stackTrace = UNASSIGNED_STACK;
> 807c804
> < if (stackTrace == FILLED_IN_STACK) {
> ---
> > if (stackTrace == UNASSIGNED_STACK) {
> 813c810
> < return EMPTY_STACK;
> ---
> > return UNASSIGNED_STACK;
> 886,888c883,886
> < * trace elements. A single-element stack trace whose entry is
> < * equal to {@code new StackTraceElement("", "", null,
> < * Integer.MIN_VALUE)} results in a {@code null} {@code
> ---
> > * trace elements. A null stack trace in the serial form results
> > * in a zero-length stack element array. A single-element stack
> > * trace whose entry is equal to {@code new StackTraceElement("",
> > * "", null, Integer.MIN_VALUE)} results in a {@code null} {@code
> 918c916,924
> < // Check for the marker of an immutable stack trace
> ---
> > /*
> > * For zero-length stack traces, use a clone of
> > * UNASSIGNED_STACK rather than UNASSIGNED_STACK itself to
> > * allow identity comparison against UNASSIGNED_STACK in
> > * getOurStackTrace. The identity of UNASSIGNED_STACK in
> > * stackTrace indicates to the getOurStackTrace method that
> > * the stackTrace needs to be constructed from the information
> > * in backtrace.
> > */
> 920d925
> < // Share zero-length stack traces
> 922c927
> < stackTrace = EMPTY_STACK;
> ---
> > stackTrace = UNASSIGNED_STACK.clone();
> 923a929
> > // Check for the marker of an immutable
> stack trace
> 937c943
> < stackTrace = EMPTY_STACK;
> ---
> > stackTrace = UNASSIGNED_STACK.clone();
> 976,977c982,983
> < * {@linkplain #Throwable(String, Throwable, boolean) via a
> < * constructor}. When suppression is disabled, this method does
> ---
> > * {@linkplain #Throwable(String, Throwable, boolean, boolean) via
> > * a constructor}. When suppression is disabled, this method does
> 1043,1044c1049,1050
> < * #Throwable(String, Throwable, boolean) suppression is disabled},
> < * an empty array is returned.
> ---
> > * #Throwable(String, Throwable, boolean, boolean) suppression is
> > * disabled}, an empty array is returned.
>
>
More information about the core-libs-dev
mailing list