diff --git a/test/stream_test.cc b/test/stream_test.cc index 42fac2f..5b622a8 100644 --- a/test/stream_test.cc +++ b/test/stream_test.cc @@ -87,6 +87,18 @@ TEST(TtyTest, closeWhileReading) { run_loop(setup); } +TEST(PipeTest, danglingReadDies) { + auto f = [](const Loop &loop) -> uvco::Promise> { + auto [read, write] = pipe(loop); + auto _ = write.write("Hello"); + return read.read(); + }; + auto setup = [&f](const Loop &loop) -> uvco::Promise { + co_await f(loop); + }; + EXPECT_DEATH(run_loop(setup), R"(stream must outlive reader coroutine)"); +} + TEST(PipeTest, pipePingPong) { auto setup = [&](const Loop &loop) -> uvco::Promise { auto [read, write] = pipe(loop); diff --git a/uvco/stream.cc b/uvco/stream.cc index 588ba96..68f345f 100644 --- a/uvco/stream.cc +++ b/uvco/stream.cc @@ -35,6 +35,12 @@ StreamBase::~StreamBase() { // descriptors. closeHandle(stream_.release()); } + BOOST_ASSERT_MSG( + !reader_, + "StreamBase::~StreamBase(): stream must outlive reader coroutines."); + BOOST_ASSERT_MSG( + !writer_, + "StreamBase::~StreamBase(): stream must outlive writer coroutines."); } TtyStream TtyStream::tty(const Loop &loop, int fd) { diff --git a/uvco/stream.h b/uvco/stream.h index c55a1f2..11035b2 100644 --- a/uvco/stream.h +++ b/uvco/stream.h @@ -57,7 +57,9 @@ class StreamBase { /// Read available data (up to `buffer.size()` bytes) from stream. Returns /// the number of bytes read, or 0 on EOF or closed handle (`close()`). /// - /// Only one read() coroutine may be active at a time. + /// Only one read() coroutine may be active at a time. The stream must outlive + /// the coroutine, i.e. live until `co_await stream.read(...)` returns a + /// result. /// /// Throws `UvcoException` on error. Promise read(std::span buffer);