RFR 8147505: Clarification of BaseStream.onClose() behavior

Paul Sandoz paul.sandoz at oracle.com
Tue Jan 26 10:52:30 UTC 2016


Hi Tagir,

StreamCloseTest.java
—

 181         try(Stream<Integer> s = countTo(100).stream()) {
 182             s.map(x -> x).forEach(i -> {});
 183             checkISE(() -> s.onClose(() -> fail("2")));
 184         }

We don’t need this one, it’s redundant. The other performing the s.close() is i think useful because i don’t recall it being tested in other places like this, plus it also tests the idempotent behaviour, perhaps worth a comment on this in the test.

Otherwise, +1

Again, i will handle in bulk with your other fixes.

Many thanks,
Paul.

> On 23 Jan 2016, at 11:38, Tagir F. Valeev <amaembo at gmail.com> wrote:
> 
> Hello!
> 
> Here's a webrev:
> http://cr.openjdk.java.net/~tvaleev/webrev/8147505/r1/
> 
> I just added a check in AbstractPipeline.onClose and the corresponding
> unit-test. To me it seems that no documentation corrections are
> necessary, because "onClose" is already documented that it's
> an intermediate operation:
> 
> http://download.java.net/jdk9/docs/api/java/util/stream/BaseStream.html#onClose-java.lang.Runnable-
> 
> Also it's already documented:
> 
>> A stream should be operated on (invoking an intermediate or terminal
>> stream operation) only once. This rules out, for example, "forked"
>> streams, where the same source feeds two or more pipelines, or
>> multiple traversals of the same stream. A stream implementation may
>> throw IllegalStateException if it detects that the stream is being
>> reused.
> 
> http://download.java.net/jdk9/docs/api/java/util/stream/Stream.html
> 
> So this already implies that invoking intermediate operation "onClose"
> after stream terminal operation is not allowed and may throw
> IllegalStateException.
> 
> Please review!
> 
> With best regards,
> Tagir Valeev.
> 
> TFV> Seems that my original letter sounds confusing due to my bad English.
> TFV> Every occurrence of "idempotent" should be read as "not idempotent".
> TFV> Sorry for this!
> 
> PS>> We did not bother to throw ISEs from parallel/sequence/onClose
> PS>> primarily because they return “this” and they are kind of
> PS>> harmless, but the undefined behaviour you point out could be shut
> PS>> down without much concern for backwards compatibility. We can
> PS>> modify them to check whether they have been linked or consumed
> PS>> (while not updating the previous stage).
> 
> PS>> Wanna log an issue?
> 
> TFV> Sure:
> TFV> https://bugs.openjdk.java.net/browse/JDK-8147505
> 
> TFV> Thanks,
> TFV> Tagir Valeev.
> 
> PS>> Thanks,
> PS>> Paul.
> 
> 
>>>> On 15 Jan 2016, at 05:35, Tagir F. Valeev <amaembo at gmail.com> wrote:
>>>> 
>>>> Hello!
>>>> 
>>>> Current documentation for BaseStream.onClose() does not specify
>>>> explicitly how the stream would behave if additional close handlers
>>>> are registered after the stream is consumed or even closed. The
>>>> implementation actually allows registering the additional close
>>>> handlers after consumption or closure and close() method is
>>>> idempotent: newly registered handlers are perfectly called:
>>>> 
>>>> Stream<String> s = Stream.of("content");
>>>> s = s.onClose(() -> System.out.println("A"));
>>>> s.forEach(System.out::println);
>>>> s = s.onClose(() -> System.out.println("B"));
>>>> s.close(); // prints A and B
>>>> s = s.onClose(() -> System.out.println("C"));
>>>> s.close(); // prints C
>>>> 
>>>> (removing "s =" produces the same result).
>>>> 
>>>> I think if such behavior is intended, it should be explicitly noted in
>>>> the specification, as third-party implementations of BaseStream
>>>> interface should provide consistent behavior. Or at least it should be
>>>> noted that some BaseStream implementations may have idempotent close()
>>>> method, so the derived AutoCloseable objects (which own the BaseStream
>>>> object) should be aware about this behavior and provide idempotent
>>>> close() method as well. Without knowing this one may write:
>>>> 
>>>> class MyObject implements AutoCloseable {
>>>> private BaseStream<?, ?> s;
>>>> 
>>>> public MyObject(BaseStream<?, ?> s) {
>>>>   this.s = s;
>>>> }
>>>> 
>>>> @Override
>>>> public void close() throws Exception {
>>>>   if(s != null) {
>>>>     s.close();
>>>>     s = null;
>>>>   }
>>>> }
>>>> }
>>>> 
>>>> Such code closes the stream only once, so newly registered handlers
>>>> will not be called if MyObject::close is called the second time.
>>>> 
>>>> However, to my opinion the current behavior is weird and should be
>>>> changed in order not to allow registering new close handles (throwing
>>>> IllegalStateException) when the stream is already closed, or even
>>>> better when the stream is linked/consumed. As far as I know currently
>>>> in JDK close handlers are registered only for non-consumed stream, so
>>>> such change would not break existing JDK code (though may break some
>>>> strange third party code). It should be noted that AutoCloseable
>>>> interface discourages idempotent close() methods (though not forbids
>>>> them).
>>>> 
>>>> What do you think?
>>>> 
>>>> With best regards,
>>>> Tagir Valeev.
>>>> 
> 




More information about the core-libs-dev mailing list