RFR: jsr166 jdk9 integration wave 2
Peter Levart
peter.levart at gmail.com
Wed Nov 25 15:01:39 UTC 2015
Hi Doug,
On 11/25/2015 12:47 PM, Doug Lea wrote:
> On 11/25/2015 03:05 AM, Peter Levart wrote:
>> On 11/25/2015 01:59 AM, Martin Buchholz wrote:
>>> On Tue, Nov 24, 2015 at 2:30 PM, Peter Levart <peter.levart at gmail.com
>>> <mailto:peter.levart at gmail.com>> wrote:
>>> What do you think of exception cloning?
>>> Making copies of java objects has historically been troublesome (e.g.
>>> Cloneable).
>>
>> I was only thinking of Cloneable. Serialization is troublesome. If
>> Throwable was
>> retrofitted to implement Cloneable and have the following new static
>> method:
>
> I still think this would be a useful feature, assuming no hidden snags.
> In addition to CompletableFuture, we might be able to use it instead of
> emulating a variant of it in ForkJoinTask.getThrowableException.
> There are probably other candidate uses elsewhere (although I can't
> think of any offhand).
For ForkJoinTask you would need an additional feature. Reinitializing of
the cause and clearing any suppressed exceptions on the clone. This
could be controlled via two additional boolean flags:
/**
* Returns a {@link Object#clone() clone} of given {@code exception}
* which shares all state with original exception (shallow clone)
and is
* augmented in the following way:
* <p>
* If {@code resetCause} parameter is {@code true}, then clone's
* {@link #getCause() cause} is reset to a state where it can be
* {@link #initCause(Throwable) initialized} again. If {@code
resetCause}
* parameter is {@code false}, then clone's cause is inherited from
original
* exception (either initialized or uninitialized).
* <p>
* If {@code resetSuppressed} parameter is {@code true} and
original exception
* has suppression enabled, then clone's suppressed exceptions are
reset
* to an empty list. If {@code resetSuppressed} parameter is {@code
false}
* (or original exception has suppression disabled) then clone's
* suppressed exceptions are inherited from original exception (or
clone's
* suppression is disabled too). In either case, clone's suppressed
* exceptions are independent of original exception's suppressed
* exceptions. Any further {@link #addSuppressed(Throwable)
additions} to
* the clone's suppressed exceptions do not affect original exception's
* suppressed exceptions and vice versa.
*
* @param exception the exception to clone.
* @param <T> the type of exception
* @param resetCause if {@code true}, clone's cause is reset to an
* uninitialized state.
* @param resetSuppressed if {@code true} and original exception
has suppression
* enabled, clone's suppressed exceptions
are cleared.
* @return shallow clone of given exception augmented according to
passed-in
* flags.
* @since 1.9
*/
@SuppressWarnings("unchecked")
public static <T extends Throwable> T clone(T exception,
boolean resetCause,
boolean resetSuppressed) {
try {
synchronized (exception) {
Throwable clone = (Throwable) exception.clone();
if (resetCause) {
// reset to uninitialized state
clone.cause = clone;
}
if (clone.suppressedExceptions != null &&
clone.suppressedExceptions != SUPPRESSED_SENTINEL) {
// suppressedExceptions has already been added to
// and suppression is enabled
clone.suppressedExceptions = resetSuppressed
? new ArrayList<>()
: new ArrayList<>(clone.suppressedExceptions);
}
return (T) clone;
}
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
In ForkJoinTask.getThrowableException you could then replace the following:
private Throwable getThrowableException() {
int h = System.identityHashCode(this);
ExceptionNode e;
final ReentrantLock lock = exceptionTableLock;
lock.lock();
try {
expungeStaleExceptions();
ExceptionNode[] t = exceptionTable;
e = t[h & (t.length - 1)];
while (e != null && e.get() != this)
e = e.next;
} finally {
lock.unlock();
}
Throwable ex;
if (e == null || (ex = e.ex) == null)
return null;
if (e.thrower != Thread.currentThread().getId()) {
Class<? extends Throwable> ec = ex.getClass();
try {
Constructor<?> noArgCtor = null;
Constructor<?>[] cs = ec.getConstructors();// public
ctors only
for (int i = 0; i < cs.length; ++i) {
Constructor<?> c = cs[i];
Class<?>[] ps = c.getParameterTypes();
if (ps.length == 0)
noArgCtor = c;
else if (ps.length == 1 && ps[0] == Throwable.class) {
Throwable wx = (Throwable)c.newInstance(ex);
return (wx == null) ? ex : wx;
}
}
if (noArgCtor != null) {
Throwable wx = (Throwable)(noArgCtor.newInstance());
if (wx != null) {
wx.initCause(ex);
return wx;
}
}
} catch (Exception ignore) {
}
}
return ex;
}
with the following:
private Throwable getThrowableException() {
int h = System.identityHashCode(this);
ExceptionNode e;
final ReentrantLock lock = exceptionTableLock;
lock.lock();
try {
expungeStaleExceptions();
ExceptionNode[] t = exceptionTable;
e = t[h & (t.length - 1)];
while (e != null && e.get() != this)
e = e.next;
} finally {
lock.unlock();
}
Throwable ex;
if (e == null || (ex = e.ex) == null)
return null;
if (e.thrower != Thread.currentThread().getId()) {
ex = Throwable.clone(ex, true, true)
.fillInStackTrace()
.initCause(ex);
}
return ex;
}
Regards, Peter
>
> In the mean time though, I think using addSuppressed in
> CompletableFuture.whenComplete is the best we can do, and
> surely better than not doing it.
>
> -Doug
>
>>
>> /**
>> * {@link Object#clone() Clones} given {@code exception} and
>> returns it's
>> clone which
>> * shares all state with original exception (shallow clone)
>> except for the
>> possible list of already
>> * {@link #addSuppressed(Throwable) added} {@link
>> #getSuppressed() suppressed}
>> * exceptions. The suppressed exception instances are not
>> cloned, just the
>> * list containing them. Further {@link
>> #addSuppressed(Throwable) additions}
>> * to the suppressed exceptions of the returned clone instance
>> * don't affect the suppressed exceptions of original exception
>> and vice versa.
>> *
>> * @param exception the exception to clone.
>> * @param <T> the type of exception
>> * @return shallow clone of given exception with suppressed
>> exception
>> * list shallow-cloned
>> * @since 1.9
>> */
>> @SuppressWarnings("unchecked")
>> public static <T extends Throwable> T clone(T exception) {
>> try {
>> Throwable clone = (Throwable) exception.clone();
>> if (clone.suppressedExceptions != null &&
>> clone.suppressedExceptions != SUPPRESSED_SENTINEL) {
>> clone.suppressedExceptions = new
>> ArrayList<>(clone.suppressedExceptions);
>> }
>> return (T) clone;
>> } catch (CloneNotSupportedException e) {
>> throw new InternalError(e);
>> }
>> }
>>
>>
>> ...then would you prefer using it or would you nevertheless prefer
>> swapping the
>> roles of exceptions in whenComplete?
>>
>> Regards, Peter
>>
>
More information about the core-libs-dev
mailing list