aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/stream_executor/stream_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tensorflow/stream_executor/stream_test.cc')
-rw-r--r--tensorflow/stream_executor/stream_test.cc90
1 files changed, 77 insertions, 13 deletions
diff --git a/tensorflow/stream_executor/stream_test.cc b/tensorflow/stream_executor/stream_test.cc
index 47dd675834..cfc051fd09 100644
--- a/tensorflow/stream_executor/stream_test.cc
+++ b/tensorflow/stream_executor/stream_test.cc
@@ -95,18 +95,18 @@ TEST_F(StreamTest, TwoSubStreams) {
EXPECT_NE(sub_stream3, sub_stream4);
}
-TEST_F(StreamTest, FailedSubStreamNotReused) {
+TEST_F(StreamTest, FailedSubStreamBeforeReturnNotReused) {
std::unique_ptr<StreamExecutor> executor = NewStreamExecutor();
Stream stream(executor.get());
stream.Init();
EXPECT_TRUE(stream.ok());
- // Get a sub-stream.
+ // Get sub_stream1.
Stream* sub_stream1 = stream.GetOrCreateSubStream();
EXPECT_TRUE(sub_stream1->ok());
- // Force an error on the stream; here we call a method that requires
- // DNN support, which we know the Host platform doesn't support.
+ // Force an error on sub_stream1; here we call a method that requires DNN
+ // support, which we know the Host platform doesn't support.
sub_stream1->ThenDepthConcatenate({}, {}, nullptr);
EXPECT_FALSE(sub_stream1->ok());
@@ -115,20 +115,84 @@ TEST_F(StreamTest, FailedSubStreamNotReused) {
Stream* sub_stream2 = stream.GetOrCreateSubStream();
EXPECT_TRUE(sub_stream2->ok());
- // The underlying streams should be different. They would have been
- // the same, but since we forced an error on sub_stream1, it will
- // not be re-used. Sadly we can't just check:
+ // The underlying sub_streams should be different. They would have been the
+ // same, but since we forced an error on sub_stream1, it will not be
+ // re-used. Sadly we can't just check:
// EXPECT_NE(sub_stream1, sub_stream2);
//
- // The above should hold logically, but it may fail if the new
- // stream instance allocated for sub_stream2 happens to reside in
- // the same memory address as sub_stream1.
+ // The above should hold logically, but it may fail if the new Stream instance
+ // allocated for sub_stream2 happens to reside in the same memory address as
+ // sub_stream1.
//
// The check that sub_stream2->ok() serves as a good-enough check.
- // Return sub_stream2 and get sub_stream3. The previous error on
- // sub_stream1 has no effect on these streams, and they are the
- // same.
+ // Return sub_stream2 and get sub_stream3. The previous error on sub_stream1
+ // has no effect on these streams, and they are the same.
+ stream.ReturnSubStream(sub_stream2);
+ Stream* sub_stream3 = stream.GetOrCreateSubStream();
+ EXPECT_TRUE(sub_stream3->ok());
+ EXPECT_EQ(sub_stream2, sub_stream3);
+}
+
+TEST_F(StreamTest, FailedSubStreamAfterReturnNotReused) {
+ std::unique_ptr<StreamExecutor> executor = NewStreamExecutor();
+ Stream stream(executor.get());
+ stream.Init();
+ EXPECT_TRUE(stream.ok());
+
+ // Get and return sub_stream1.
+ Stream* sub_stream1 = stream.GetOrCreateSubStream();
+ EXPECT_TRUE(sub_stream1->ok());
+ stream.ReturnSubStream(sub_stream1);
+
+ // Force an error on sub_stream1; here we call a method that requires DNN
+ // support, which we know the Host platform doesn't support.
+ //
+ // It is a bit weird to use sub_stream1 after it has already been returned. By
+ // doing this, we're simulating an asynchronous error that occurs during
+ // execution of the sub_stream, that occurs after the sub_stream is returned.
+ //
+ // E.g. the following is a common pattern of usage, where the execution of the
+ // operations enqueued onto the sub streams may occur after the streams have
+ // already been returned.
+ //
+ // void EnqueueOnSubStreams(Stream* stream) {
+ // Stream* sub_stream1 = stream.GetOrCreateSubStream();
+ // Stream* sub_stream2 = stream.GetOrCreateSubStream();
+ // // ... enqueue some operations on the sub streams ...
+ // stream.ThenWaitFor(sub_stream1).ThenWaitFor(sub_stream2);
+ // stream.ReturnSubStream(sub_stream1);
+ // stream.ReturnSubStream(sub_stream2);
+ // }
+ //
+ // Stream* main_stream = ...;
+ // EnqueueOnSubStreams(main_stream);
+ // main_stream.BlockHostUntilDone();
+ //
+ // TODO(b/112196569): The semantics of failed sub-streams is error-prone;
+ // GetOrCreateSubStream can still return a sub-stream that has not encountered
+ // an error yet, but will encounter one in the future, based on previously
+ // enqueued operations.
+ sub_stream1->ThenDepthConcatenate({}, {}, nullptr);
+ EXPECT_FALSE(sub_stream1->ok());
+
+ // Get and return sub_stream2.
+ Stream* sub_stream2 = stream.GetOrCreateSubStream();
+ EXPECT_TRUE(sub_stream2->ok());
+
+ // The underlying streams should be different. They would have been the same,
+ // but since we forced an error on sub_stream1, it will not be re-used. Sadly
+ // we can't just check:
+ // EXPECT_NE(sub_stream1, sub_stream2);
+ //
+ // The above should hold logically, but it may fail if the new stream instance
+ // allocated for sub_stream2 happens to reside in the same memory address as
+ // sub_stream1.
+ //
+ // The check that sub_stream2->ok() serves as a good-enough check.
+
+ // Return sub_stream2 and get sub_stream3. The previous error on sub_stream1
+ // has no effect on these streams, and they are the same.
stream.ReturnSubStream(sub_stream2);
Stream* sub_stream3 = stream.GetOrCreateSubStream();
EXPECT_TRUE(sub_stream3->ok());