From 37ad77c7015a7e2f55249454f6e607414ad5fbbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sun, 16 Jun 2019 22:33:15 +0200 Subject: [PATCH] Let empty block to make sure the following consumeOne succeeds. This change ensures that a return value of false guarantees the next call to consumeOne to succeed, meaning that the combination of empty/consumeOne is sound in a single-consumer scenario. This also updates the documentation to stress that tryConsumeOne is still the preferred API. --- source/vibe/core/channel.d | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/source/vibe/core/channel.d b/source/vibe/core/channel.d index e6741f8..10f389b 100644 --- a/source/vibe/core/channel.d +++ b/source/vibe/core/channel.d @@ -41,7 +41,7 @@ struct Channel(T, size_t buffer_size = 100) { private shared ChannelImpl!(T, buffer_size) m_impl; - /** Determines whether there is more data to read. + /** Determines whether there is more data to read in a single-reader scenario. This property is empty $(I iff) no more elements are in the internal buffer and `close()` has been called. Once the channel is empty, @@ -49,8 +49,8 @@ struct Channel(T, size_t buffer_size = 100) { exception. Note that relying on the return value to determine whether another - element can be read is only safe in a single-reader scenario. Use - `tryConsumeOne` in a multiple-reader scenario instead. + element can be read is only safe in a single-reader scenario. It is + generally recommended to use `tryConsumeOne` instead. */ @property bool empty() { return m_impl.empty; } /// ditto @@ -78,6 +78,10 @@ struct Channel(T, size_t buffer_size = 100) { This function will block if no elements are available. If the `empty` property is `true`, an exception will be thrown. + + Note that it is recommended to use `tryConsumeOne` instead of a + combination of `empty` and `consumeOne` due to being more efficient and + also being reliable in a multiple-reader scenario. */ T consumeOne() { return m_impl.consumeOne(); } /// ditto @@ -143,6 +147,12 @@ private final class ChannelImpl(T, size_t buffer_size) { scope (exit) m_mutex.unlock_nothrow(); auto thisus = () @trusted { return cast(ChannelImpl)this; } (); + + // ensure that in a single-reader scenario !empty guarantees a + // successful call to consumeOne + while (!thisus.m_closed && thisus.m_items.empty) + thisus.m_condition.wait(); + return thisus.m_closed && thisus.m_items.empty; } }