IVGCVSW-3903 Create Counter Stream Buffer

 * Add implementation of PacketBuffer
 * Add implementation of BufferManager
 * Unit tests

Signed-off-by: Narumol Prangnawarat <narumol.prangnawarat@arm.com>
Change-Id: Icca3807149ff5a8ed31cc87de73c50b95bca39d4
diff --git a/src/profiling/BufferManager.cpp b/src/profiling/BufferManager.cpp
new file mode 100644
index 0000000..91ad1c5
--- /dev/null
+++ b/src/profiling/BufferManager.cpp
@@ -0,0 +1,93 @@
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "BufferManager.hpp"
+#include "PacketBuffer.hpp"
+#include "ProfilingUtils.hpp"
+
+#include <armnn/Exceptions.hpp>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+BufferManager::BufferManager(unsigned int numberOfBuffers, unsigned int maxPacketSize)
+    : m_MaxBufferSize(maxPacketSize)
+{
+    m_AvailableList.reserve(numberOfBuffers);
+    for (unsigned int i = 0; i < numberOfBuffers; ++i)
+    {
+        std::unique_ptr<IPacketBuffer> buffer = std::make_unique<PacketBuffer>(maxPacketSize);
+        m_AvailableList.emplace_back(std::move(buffer));
+    }
+    m_ReadableList.reserve(numberOfBuffers);
+}
+
+std::unique_ptr<IPacketBuffer> BufferManager::Reserve(unsigned int requestedSize, unsigned int& reservedSize)
+{
+    std::unique_lock<std::mutex> availableListLock(m_AvailableMutex, std::defer_lock);
+    if (requestedSize > m_MaxBufferSize)
+    {
+        throw armnn::RuntimeException("Maximum buffer size that can be requested is [" +
+            std::to_string(m_MaxBufferSize) + "] bytes");
+    }
+    availableListLock.lock();
+    if (m_AvailableList.empty())
+    {
+        throw armnn::profiling::BufferExhaustion("Buffer not available");
+    }
+    std::unique_ptr<IPacketBuffer> buffer = std::move(m_AvailableList.back());
+    m_AvailableList.pop_back();
+    availableListLock.unlock();
+    reservedSize = requestedSize;
+    return buffer;
+}
+
+void BufferManager::Commit(std::unique_ptr<IPacketBuffer>& packetBuffer, unsigned int size)
+{
+    std::unique_lock<std::mutex> readableListLock(m_ReadableMutex, std::defer_lock);
+    packetBuffer->Commit(size);
+    readableListLock.lock();
+    m_ReadableList.push_back(std::move(packetBuffer));
+    readableListLock.unlock();
+    m_ReadDataAvailable.notify_one();
+}
+
+void BufferManager::Release(std::unique_ptr<IPacketBuffer>& packetBuffer)
+{
+    std::unique_lock<std::mutex> availableListLock(m_AvailableMutex, std::defer_lock);
+    packetBuffer->Release();
+    availableListLock.lock();
+    m_AvailableList.push_back(std::move(packetBuffer));
+    availableListLock.unlock();
+}
+
+std::unique_ptr<IPacketBuffer> BufferManager::GetReadableBuffer()
+{
+    std::unique_lock<std::mutex> readableListLock(m_ReadableMutex);
+    if (!m_ReadableList.empty())
+    {
+        std::unique_ptr<IPacketBuffer> buffer = std::move(m_ReadableList.back());
+        m_ReadableList.pop_back();
+        readableListLock.unlock();
+        return buffer;
+    }
+    return nullptr;
+}
+
+void BufferManager::MarkRead(std::unique_ptr<IPacketBuffer>& packetBuffer)
+{
+    std::unique_lock<std::mutex> availableListLock(m_AvailableMutex, std::defer_lock);
+    packetBuffer->MarkRead();
+    availableListLock.lock();
+    m_AvailableList.push_back(std::move(packetBuffer));
+    availableListLock.unlock();
+}
+
+} // namespace profiling
+
+} // namespace armnn
diff --git a/src/profiling/BufferManager.hpp b/src/profiling/BufferManager.hpp
new file mode 100644
index 0000000..04a1507
--- /dev/null
+++ b/src/profiling/BufferManager.hpp
@@ -0,0 +1,59 @@
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "IBufferManager.hpp"
+
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+class BufferManager : public IBufferManager
+{
+public:
+    BufferManager(unsigned int numberOfBuffers = 5, unsigned int maxPacketSize = 4096);
+
+    ~BufferManager() {}
+
+    std::unique_ptr<IPacketBuffer> Reserve(unsigned int requestedSize, unsigned int& reservedSize) override;
+
+    void Commit(std::unique_ptr<IPacketBuffer>& packetBuffer, unsigned int size) override;
+
+    void Release(std::unique_ptr<IPacketBuffer>& packetBuffer) override;
+
+    std::unique_ptr<IPacketBuffer> GetReadableBuffer() override;
+
+    void MarkRead(std::unique_ptr<IPacketBuffer>& packetBuffer) override;
+
+private:
+    // Maximum buffer size
+    unsigned int m_MaxBufferSize;
+
+    // List of available packet buffers
+    std::vector<std::unique_ptr<IPacketBuffer>> m_AvailableList;
+
+    // List of readable packet buffers
+    std::vector<std::unique_ptr<IPacketBuffer>> m_ReadableList;
+
+    // Mutex for available packet buffer list
+    std::mutex m_AvailableMutex;
+
+    // Mutex for readable packet buffer list
+    std::mutex m_ReadableMutex;
+
+    // Condition to notify when data is availabe to be read
+    std::condition_variable m_ReadDataAvailable;
+};
+
+} // namespace profiling
+
+} // namespace armnn
diff --git a/src/profiling/PacketBuffer.cpp b/src/profiling/PacketBuffer.cpp
new file mode 100644
index 0000000..88133d7
--- /dev/null
+++ b/src/profiling/PacketBuffer.cpp
@@ -0,0 +1,60 @@
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "PacketBuffer.hpp"
+
+#include <armnn/Exceptions.hpp>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+PacketBuffer::PacketBuffer(unsigned int maxSize)
+    : m_MaxSize(maxSize)
+    , m_Size(0)
+{
+    m_Data = std::make_unique<unsigned char[]>(m_MaxSize);
+}
+
+const unsigned char* const PacketBuffer::GetReadableData() const
+{
+    return m_Data.get();
+}
+
+unsigned int PacketBuffer::GetSize() const
+{
+    return m_Size;
+}
+
+void PacketBuffer::MarkRead()
+{
+    m_Size = 0;
+}
+
+void PacketBuffer::Commit(unsigned int size)
+{
+    if (size > m_MaxSize)
+    {
+        throw armnn::RuntimeException("Cannot commit [" + std::to_string(size) +
+                "] bytes which is more than the maximum size of the buffer [" + std::to_string(m_MaxSize) + "]");
+    }
+    m_Size = size;
+}
+
+void PacketBuffer::Release()
+{
+    m_Size = 0;
+}
+
+unsigned char* PacketBuffer::GetWritableData()
+{
+    return m_Data.get();
+}
+
+} // namespace profiling
+
+} // namespace armnn
diff --git a/src/profiling/PacketBuffer.hpp b/src/profiling/PacketBuffer.hpp
new file mode 100644
index 0000000..a3d95d4
--- /dev/null
+++ b/src/profiling/PacketBuffer.hpp
@@ -0,0 +1,45 @@
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "IPacketBuffer.hpp"
+
+#include <memory>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+class PacketBuffer : public IPacketBuffer
+{
+public:
+    PacketBuffer(unsigned int maxSize);
+
+    ~PacketBuffer() {}
+
+    const unsigned char* const GetReadableData() const  override;
+
+    unsigned int GetSize() const  override;
+
+    void MarkRead() override;
+
+    void Commit(unsigned int size)  override;
+
+    void Release() override;
+
+    unsigned char* GetWritableData() override;
+
+private:
+    unsigned int m_MaxSize;
+    unsigned int m_Size;
+    std::unique_ptr<unsigned char[]> m_Data;
+};
+
+} // namespace profiling
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/profiling/test/BufferTests.cpp b/src/profiling/test/BufferTests.cpp
new file mode 100644
index 0000000..b678350
--- /dev/null
+++ b/src/profiling/test/BufferTests.cpp
@@ -0,0 +1,279 @@
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "BufferManager.hpp"
+#include "PacketBuffer.hpp"
+#include "ProfilingUtils.hpp"
+
+#include <armnn/Exceptions.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+using namespace armnn::profiling;
+
+BOOST_AUTO_TEST_SUITE(BufferTests)
+
+BOOST_AUTO_TEST_CASE(PacketBufferTest0)
+{
+    std::unique_ptr<IPacketBuffer> packetBuffer = std::make_unique<PacketBuffer>(512);
+
+    BOOST_TEST(packetBuffer->GetSize() == 0);
+
+    // Write data to the buffer
+    WriteUint32(packetBuffer, 0, 10);
+    WriteUint32(packetBuffer, 4, 20);
+    WriteUint32(packetBuffer, 8, 30);
+    WriteUint32(packetBuffer, 12, 40);
+
+    // Commit
+    packetBuffer->Commit(16);
+
+    // Size of buffer is equal to committed data
+    BOOST_TEST(packetBuffer->GetSize() == 16);
+
+    // Read data from the buffer
+    auto readBuffer = packetBuffer->GetReadableData();
+    uint32_t readData0 = ReadUint32(readBuffer, 0);
+    uint32_t readData1 = ReadUint32(readBuffer, 4);
+    uint32_t readData2 = ReadUint32(readBuffer, 8);
+    uint32_t readData3 = ReadUint32(readBuffer, 12);
+
+    // Check that data is correct
+    BOOST_TEST(readData0 == 10);
+    BOOST_TEST(readData1 == 20);
+    BOOST_TEST(readData2 == 30);
+    BOOST_TEST(readData3 == 40);
+
+    // Mark read
+    packetBuffer->MarkRead();
+
+    // Size of buffer become 0 after marked read
+    BOOST_TEST(packetBuffer->GetSize() == 0);
+}
+
+BOOST_AUTO_TEST_CASE(PacketBufferTest1)
+{
+    std::unique_ptr<IPacketBuffer> packetBuffer = std::make_unique<PacketBuffer>(512);
+
+    BOOST_TEST(packetBuffer->GetSize() == 0);
+
+    // Write data to the buffer using GetWritableData
+    auto writeBuffer = packetBuffer->GetWritableData();
+    WriteUint32(writeBuffer, 0, 10);
+    WriteUint32(writeBuffer, 4, 20);
+    WriteUint32(writeBuffer, 8, 30);
+    WriteUint32(writeBuffer, 12, 40);
+
+    packetBuffer->Commit(16);
+
+    BOOST_TEST(packetBuffer->GetSize() == 16);
+
+    // Read data from the buffer
+    auto readBuffer = packetBuffer->GetReadableData();
+    uint32_t readData0 = ReadUint32(readBuffer, 0);
+    uint32_t readData1 = ReadUint32(readBuffer, 4);
+    uint32_t readData2 = ReadUint32(readBuffer, 8);
+    uint32_t readData3 = ReadUint32(readBuffer, 12);
+
+    BOOST_TEST(readData0 == 10);
+    BOOST_TEST(readData1 == 20);
+    BOOST_TEST(readData2 == 30);
+    BOOST_TEST(readData3 == 40);
+
+    packetBuffer->MarkRead();
+
+    BOOST_TEST(packetBuffer->GetSize() == 0);
+}
+
+BOOST_AUTO_TEST_CASE(PacketBufferReleaseTest) {
+    std::unique_ptr<IPacketBuffer> packetBuffer = std::make_unique<PacketBuffer>(512);
+
+    BOOST_TEST(packetBuffer->GetSize() == 0);
+
+    auto writeBuffer = packetBuffer->GetWritableData();
+
+    WriteUint32(writeBuffer, 0, 10);
+    WriteUint32(writeBuffer, 4, 20);
+    WriteUint32(writeBuffer, 8, 30);
+    WriteUint32(writeBuffer, 12, 40);
+
+    packetBuffer->Release();
+
+    // Size of buffer become 0 after release
+    BOOST_TEST(packetBuffer->GetSize() == 0);
+}
+
+BOOST_AUTO_TEST_CASE(PacketBufferCommitErrorTest)
+{
+    std::unique_ptr<IPacketBuffer> packetBuffer = std::make_unique<PacketBuffer>(8);
+
+    // Cannot commit data bigger than the max size of the buffer
+    BOOST_CHECK_THROW(packetBuffer->Commit(16);, armnn::RuntimeException);
+}
+
+BOOST_AUTO_TEST_CASE(BufferReserveTest)
+{
+    BufferManager bufferManager(1, 512);
+    unsigned int reservedSize = 0;
+    auto packetBuffer = bufferManager.Reserve(512, reservedSize);
+
+    // Successfully reserved the buffer with requested size
+    BOOST_TEST(reservedSize == 512);
+    BOOST_TEST(packetBuffer.get());
+}
+
+BOOST_AUTO_TEST_CASE(BufferReserveExceedingSpaceTest)
+{
+    BufferManager bufferManager(1, 512);
+    unsigned int reservedSize = 0;
+
+    // Cannot reserve buffer bigger than maximum buffer size
+    BOOST_CHECK_THROW(bufferManager.Reserve(1024, reservedSize), armnn::RuntimeException);
+}
+
+BOOST_AUTO_TEST_CASE(BufferExhaustionTest)
+{
+    BufferManager bufferManager(1, 512);
+    unsigned int reservedSize = 0;
+    auto packetBuffer = bufferManager.Reserve(512, reservedSize);
+
+    // Successfully reserved the buffer with requested size
+    BOOST_TEST(reservedSize == 512);
+    BOOST_TEST(packetBuffer.get());
+
+    // Cannot reserve buffer when buffer is not available
+    BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize), BufferExhaustion);
+}
+
+BOOST_AUTO_TEST_CASE(BufferReserveMultipleTest)
+{
+    BufferManager bufferManager(3, 512);
+    unsigned int reservedSize0 = 0;
+    auto packetBuffer0 = bufferManager.Reserve(512, reservedSize0);
+
+    // Successfully reserved the buffer with requested size
+    BOOST_TEST(reservedSize0 == 512);
+    BOOST_TEST(packetBuffer0.get());
+
+    unsigned int reservedSize1 = 0;
+    auto packetBuffer1 = bufferManager.Reserve(128, reservedSize1);
+
+    // Successfully reserved the buffer with requested size
+    BOOST_TEST(reservedSize1 == 128);
+    BOOST_TEST(packetBuffer1.get());
+
+    unsigned int reservedSize2 = 0;
+    auto packetBuffer2 = bufferManager.Reserve(512, reservedSize2);
+
+    // Successfully reserved the buffer with requested size
+    BOOST_TEST(reservedSize2 == 512);
+    BOOST_TEST(packetBuffer2.get());
+
+    // Cannot reserve when buffer is not available
+    unsigned int reservedSize3 = 0;
+    BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize3), BufferExhaustion);
+}
+
+BOOST_AUTO_TEST_CASE(BufferReleaseTest)
+{
+    BufferManager bufferManager(2, 512);
+    unsigned int reservedSize0 = 0;
+    auto packetBuffer0 = bufferManager.Reserve(512, reservedSize0);
+
+    // Successfully reserved the buffer with requested size
+    BOOST_TEST(reservedSize0 == 512);
+    BOOST_TEST(packetBuffer0.get());
+
+    unsigned int reservedSize1 = 0;
+    auto packetBuffer1 = bufferManager.Reserve(128, reservedSize1);
+
+    // Successfully reserved the buffer with requested size
+    BOOST_TEST(reservedSize1 == 128);
+    BOOST_TEST(packetBuffer1.get());
+
+    // Cannot reserve when buffer is not available
+    unsigned int reservedSize2 = 0;
+    BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize2), BufferExhaustion);
+
+    bufferManager.Release(packetBuffer0);
+
+    // Buffer should become available after release
+    auto packetBuffer2 = bufferManager.Reserve(128, reservedSize2);
+
+    BOOST_TEST(reservedSize2 == 128);
+    BOOST_TEST(packetBuffer2.get());
+}
+
+BOOST_AUTO_TEST_CASE(BufferCommitTest)
+{
+    BufferManager bufferManager(2, 512);
+    unsigned int reservedSize0 = 0;
+    auto packetBuffer0 = bufferManager.Reserve(512, reservedSize0);
+
+    BOOST_TEST(reservedSize0 == 512);
+    BOOST_TEST(packetBuffer0.get());
+
+    unsigned int reservedSize1 = 0;
+    auto packetBuffer1 = bufferManager.Reserve(128, reservedSize1);
+
+    BOOST_TEST(reservedSize1 == 128);
+    BOOST_TEST(packetBuffer1.get());
+
+    unsigned int reservedSize2 = 0;
+    BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize2), BufferExhaustion);
+
+    bufferManager.Commit(packetBuffer0, 256);
+
+    // Buffer should become readable after commit
+    auto packetBuffer2 = bufferManager.GetReadableBuffer();
+    BOOST_TEST(packetBuffer2.get());
+    BOOST_TEST(packetBuffer2->GetSize() == 256);
+
+    // Buffer not set back to available list after commit
+    BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize2), BufferExhaustion);
+}
+
+BOOST_AUTO_TEST_CASE(BufferMarkReadTest)
+{
+    BufferManager bufferManager(2, 512);
+    unsigned int reservedSize0 = 0;
+    auto packetBuffer0 = bufferManager.Reserve(512, reservedSize0);
+
+    BOOST_TEST(reservedSize0 == 512);
+    BOOST_TEST(packetBuffer0.get());
+
+    unsigned int reservedSize1 = 0;
+    auto packetBuffer1 = bufferManager.Reserve(128, reservedSize1);
+
+    BOOST_TEST(reservedSize1 == 128);
+    BOOST_TEST(packetBuffer1.get());
+
+    // Cannot reserve when buffer is not available
+    unsigned int reservedSize2 = 0;
+    BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize2), BufferExhaustion);
+
+    bufferManager.Commit(packetBuffer0, 256);
+
+    // Buffer should become readable after commit
+    auto packetBuffer2 = bufferManager.GetReadableBuffer();
+    BOOST_TEST(packetBuffer2.get());
+    BOOST_TEST(packetBuffer2->GetSize() == 256);
+
+    // Buffer not set back to available list after commit
+    BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize2), BufferExhaustion);
+
+    bufferManager.MarkRead(packetBuffer2);
+
+    //Buffer should set back to available list after marked read and can be reserved
+    auto readBuffer = bufferManager.GetReadableBuffer();
+    BOOST_TEST(!readBuffer);
+    unsigned int reservedSize3 = 0;
+    auto packetBuffer3 = bufferManager.Reserve(56, reservedSize3);
+
+    BOOST_TEST(reservedSize3 == 56);
+    BOOST_TEST(packetBuffer3.get());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/profiling/test/SendCounterPacketTests.hpp b/src/profiling/test/SendCounterPacketTests.hpp
index c3d4715..243731c 100644
--- a/src/profiling/test/SendCounterPacketTests.hpp
+++ b/src/profiling/test/SendCounterPacketTests.hpp
@@ -147,10 +147,10 @@
     {
         std::unique_lock<std::mutex> availableListLock(m_AvailableMutex, std::defer_lock);
         if (requestedSize > m_MaxBufferSize)
-       {
+        {
             throw armnn::Exception("Maximum buffer size that can be requested is [" +
                 std::to_string(m_MaxBufferSize) + "] bytes");
-       }
+        }
         availableListLock.lock();
         if (m_AvailableList.empty())
         {