8075939: Stream.flatMap() causes breaking of short-circuiting of terminal operations

Reviewed-by: forax, smarks
This commit is contained in:
Paul Sandoz 2017-12-21 13:52:20 -08:00
parent 8c39e16731
commit e1e9023545
6 changed files with 262 additions and 51 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -298,6 +298,12 @@ abstract class IntPipeline<E_IN>
@Override
Sink<Integer> opWrapSink(int flags, Sink<Integer> sink) {
return new Sink.ChainedInt<Integer>(sink) {
// true if cancellationRequested() has been called
boolean cancellationRequestedCalled;
// cache the consumer to avoid creation on every accepted element
IntConsumer downstreamAsInt = downstream::accept;
@Override
public void begin(long size) {
downstream.begin(-1);
@ -306,11 +312,27 @@ abstract class IntPipeline<E_IN>
@Override
public void accept(int t) {
try (IntStream result = mapper.apply(t)) {
// We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
if (result != null)
result.sequential().forEach(i -> downstream.accept(i));
if (result != null) {
if (!cancellationRequestedCalled) {
result.sequential().forEach(downstreamAsInt);
}
else {
var s = result.sequential().spliterator();
do { } while (!downstream.cancellationRequested() && s.tryAdvance(downstreamAsInt));
}
}
}
}
@Override
public boolean cancellationRequested() {
// If this method is called then an operation within the stream
// pipeline is short-circuiting (see AbstractPipeline.copyInto).
// Note that we cannot differentiate between an upstream or
// downstream operation
cancellationRequestedCalled = true;
return downstream.cancellationRequested();
}
};
}
};