From e2688eec0aa8d41b7e1a6beffea8bbf3aa5f55e5 Mon Sep 17 00:00:00 2001 From: jules Date: Tue, 16 Jan 2018 12:38:19 +0000 Subject: [PATCH] Added methods AbstractFifo::read() and AbstractFifo::write() which return a scope-based object to make it easy to hold, release and iterate blocks of items. --- .../containers/juce_AbstractFifo.cpp | 140 +++++++++++++----- .../juce_core/containers/juce_AbstractFifo.h | 103 ++++++++++++- 2 files changed, 202 insertions(+), 41 deletions(-) diff --git a/modules/juce_core/containers/juce_AbstractFifo.cpp b/modules/juce_core/containers/juce_AbstractFifo.cpp index 22e7ded67a..46af05e282 100644 --- a/modules/juce_core/containers/juce_AbstractFifo.cpp +++ b/modules/juce_core/containers/juce_AbstractFifo.cpp @@ -23,8 +23,7 @@ namespace juce { -AbstractFifo::AbstractFifo (const int capacity) noexcept - : bufferSize (capacity) +AbstractFifo::AbstractFifo (int capacity) noexcept : bufferSize (capacity) { jassert (bufferSize > 0); } @@ -36,8 +35,8 @@ int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - int AbstractFifo::getNumReady() const noexcept { - const int vs = validStart.get(); - const int ve = validEnd.get(); + auto vs = validStart.get(); + auto ve = validEnd.get(); return ve >= vs ? (ve - vs) : (bufferSize - (vs - ve)); } @@ -57,10 +56,10 @@ void AbstractFifo::setTotalSize (int newSize) noexcept //============================================================================== void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept { - const int vs = validStart.get(); - const int ve = validEnd.get(); + auto vs = validStart.get(); + auto ve = validEnd.get(); - const int freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve); + auto freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve); numToWrite = jmin (numToWrite, freeSpace - 1); if (numToWrite <= 0) @@ -84,7 +83,7 @@ void AbstractFifo::finishedWrite (int numWritten) noexcept { jassert (numWritten >= 0 && numWritten < bufferSize); - int newEnd = validEnd.get() + numWritten; + auto newEnd = validEnd.get() + numWritten; if (newEnd >= bufferSize) newEnd -= bufferSize; @@ -94,10 +93,10 @@ void AbstractFifo::finishedWrite (int numWritten) noexcept void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept { - const int vs = validStart.get(); - const int ve = validEnd.get(); + auto vs = validStart.get(); + auto ve = validEnd.get(); - const int numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve)); + auto numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve)); numWanted = jmin (numWanted, numReady); if (numWanted <= 0) @@ -121,7 +120,7 @@ void AbstractFifo::finishedRead (int numRead) noexcept { jassert (numRead >= 0 && numRead <= bufferSize); - int newStart = validStart.get() + numRead; + auto newStart = validStart.get() + numRead; if (newStart >= bufferSize) newStart -= bufferSize; @@ -129,6 +128,76 @@ void AbstractFifo::finishedRead (int numRead) noexcept validStart = newStart; } +//============================================================================== +template +AbstractFifo::ScopedReadWrite::ScopedReadWrite (AbstractFifo& f, int num) noexcept + : fifo (&f) +{ + prepare (*fifo, num); +} + +template +AbstractFifo::ScopedReadWrite::ScopedReadWrite (ScopedReadWrite&& other) noexcept + : startIndex1 (other.startIndex1), + blockSize1 (other.blockSize1), + startIndex2 (other.startIndex2), + blockSize2 (other.blockSize2) +{ + swap (other); +} + +template +AbstractFifo::ScopedReadWrite& +AbstractFifo::ScopedReadWrite::operator= (ScopedReadWrite&& other) noexcept +{ + swap (other); + return *this; +} + +template +AbstractFifo::ScopedReadWrite::~ScopedReadWrite() noexcept +{ + if (fifo != nullptr) + finish (*fifo, blockSize1 + blockSize2); +} + +template +void AbstractFifo::ScopedReadWrite::swap (ScopedReadWrite& other) noexcept +{ + std::swap (other.fifo, fifo); + std::swap (other.startIndex1, startIndex1); + std::swap (other.blockSize1, blockSize1); + std::swap (other.startIndex2, startIndex2); + std::swap (other.blockSize2, blockSize2); +} + +template<> +void AbstractFifo::ScopedReadWrite::prepare (AbstractFifo& f, int num) noexcept +{ + f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2); +} + +template<> +void AbstractFifo::ScopedReadWrite::prepare (AbstractFifo& f, int num) noexcept +{ + f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2); +} + +template<> +void AbstractFifo::ScopedReadWrite::finish (AbstractFifo& f, int num) noexcept +{ + f.finishedRead (num); +} + +template<> +void AbstractFifo::ScopedReadWrite::finish (AbstractFifo& f, int num) noexcept +{ + f.finishedWrite (num); +} + +template class AbstractFifo::ScopedReadWrite; +template class AbstractFifo::ScopedReadWrite; + //============================================================================== #if JUCE_UNIT_TESTS @@ -137,9 +206,8 @@ class AbstractFifoTests : public UnitTest public: AbstractFifoTests() : UnitTest ("Abstract Fifo", "Containers") {} - class WriteThread : public Thread + struct WriteThread : public Thread { - public: WriteThread (AbstractFifo& f, int* b, Random rng) : Thread ("fifo writer"), fifo (f), buffer (b), random (rng) { @@ -159,24 +227,18 @@ public: { int num = random.nextInt (2000) + 1; - int start1, size1, start2, size2; - fifo.prepareToWrite (num, start1, size1, start2, size2); - - jassert (size1 >= 0 && size2 >= 0); - jassert (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize())); - jassert (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize())); + auto writer = fifo.write (num); - for (int i = 0; i < size1; ++i) - buffer [start1 + i] = n++; + jassert (writer.blockSize1 >= 0 && writer.blockSize2 >= 0); + jassert (writer.blockSize1 == 0 + || (writer.startIndex1 >= 0 && writer.startIndex1 < fifo.getTotalSize())); + jassert (writer.blockSize2 == 0 + || (writer.startIndex2 >= 0 && writer.startIndex2 < fifo.getTotalSize())); - for (int i = 0; i < size2; ++i) - buffer [start2 + i] = n++; - - fifo.finishedWrite (size1 + size2); + writer.forEach ([this, &n] (int index) { this->buffer[index] = n++; }); } } - private: AbstractFifo& fifo; int* buffer; Random random; @@ -186,7 +248,7 @@ public: { beginTest ("AbstractFifo"); - int buffer [5000]; + int buffer[5000]; AbstractFifo fifo (numElementsInArray (buffer)); WriteThread writer (fifo, buffer, getRandom()); @@ -199,12 +261,13 @@ public: { int num = r.nextInt (6000) + 1; - int start1, size1, start2, size2; - fifo.prepareToRead (num, start1, size1, start2, size2); + auto reader = fifo.read (num); - if (! (size1 >= 0 && size2 >= 0) - && (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize())) - && (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize()))) + if (! (reader.blockSize1 >= 0 && reader.blockSize2 >= 0) + && (reader.blockSize1 == 0 + || (reader.startIndex1 >= 0 && reader.startIndex1 < fifo.getTotalSize())) + && (reader.blockSize2 == 0 + || (reader.startIndex2 >= 0 && reader.startIndex2 < fifo.getTotalSize()))) { expect (false, "prepareToRead returned -ve values"); break; @@ -212,19 +275,16 @@ public: bool failed = false; - for (int i = 0; i < size1; ++i) - failed = (buffer [start1 + i] != n++) || failed; - - for (int i = 0; i < size2; ++i) - failed = (buffer [start2 + i] != n++) || failed; + reader.forEach ([this, &failed, &buffer, &n] (int index) + { + failed = (buffer[index] != n++) || failed; + }); if (failed) { expect (false, "read values were incorrect"); break; } - - fifo.finishedRead (size1 + size2); } } }; diff --git a/modules/juce_core/containers/juce_AbstractFifo.h b/modules/juce_core/containers/juce_AbstractFifo.h index 0d41d6d9b9..c9b24aacfc 100644 --- a/modules/juce_core/containers/juce_AbstractFifo.h +++ b/modules/juce_core/containers/juce_AbstractFifo.h @@ -200,11 +200,112 @@ public: */ void finishedRead (int numRead) noexcept; + //============================================================================== + +private: + enum class ReadOrWrite + { + read, + write + }; + +public: + template + class ScopedReadWrite final + { + public: + /** Construct an unassigned reader/writer. Doesn't do anything upon destruction. */ + ScopedReadWrite() = default; + + /** Construct a reader/writer and immediately call prepareRead/prepareWrite + on the abstractFifo which was passed in. + This object will hold a pointer back to the fifo, so make sure that + the fifo outlives this object. + */ + ScopedReadWrite (AbstractFifo&, int num) noexcept; + + ScopedReadWrite (const ScopedReadWrite&) = delete; + ScopedReadWrite (ScopedReadWrite&&) noexcept; + + ScopedReadWrite& operator= (const ScopedReadWrite&) = delete; + ScopedReadWrite& operator= (ScopedReadWrite&&) noexcept; + + /** Calls finishedRead or finishedWrite if this is a non-null scoped + reader/writer. + */ + ~ScopedReadWrite() noexcept; + + /** Calls the passed function with each index that was deemed valid + for the current read/write operation. + */ + template + void forEach (FunctionToApply&& func) const + { + for (auto i = startIndex1, e = startIndex1 + blockSize1; i != e; ++i) func (i); + for (auto i = startIndex2, e = startIndex2 + blockSize2; i != e; ++i) func (i); + } + + int startIndex1, blockSize1, startIndex2, blockSize2; + + private: + void prepare (AbstractFifo&, int) noexcept; + static void finish (AbstractFifo&, int) noexcept; + void swap (ScopedReadWrite&) noexcept; + + AbstractFifo* fifo = nullptr; + }; + + using ScopedRead = ScopedReadWrite; + using ScopedWrite = ScopedReadWrite; + + /** Replaces prepareToRead/finishedRead with a single function. + This function returns an object which contains the start indices and + block sizes, and also automatically finishes the read operation when + it goes out of scope. + @code + { + auto readHandle = fifo.read (4); + + for (auto i = 0; i != readHandle.blockSize1; ++i) + { + // read the item at index readHandle.startIndex1 + i + } + + for (auto i = 0; i != readHandle.blockSize2; ++i) + { + // read the item at index readHandle.startIndex2 + i + } + } // readHandle goes out of scope here, finishing the read operation + @endcode + */ + ScopedRead read (int numToRead) noexcept { return { *this, numToRead }; } + + /** Replaces prepareToWrite/finishedWrite with a single function. + This function returns an object which contains the start indices and + block sizes, and also automatically finishes the write operation when + it goes out of scope. + @code + { + auto writeHandle = fifo.write (5); + + for (auto i = 0; i != writeHandle.blockSize1; ++i) + { + // write the item at index writeHandle.startIndex1 + i + } + + for (auto i = 0; i != writeHandle.blockSize2; ++i) + { + // write the item at index writeHandle.startIndex2 + i + } + } // writeHandle goes out of scope here, finishing the write operation + @endcode + */ + ScopedWrite write (int numToWrite) noexcept { return { *this, numToWrite }; } private: //============================================================================== int bufferSize; - Atomic validStart, validEnd; + Atomic validStart, validEnd; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo) };