From 1adc5f72c535ba9790115d06e91f9681e9575493 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=B2=20Caruso?= <niccolo.caruso@skywarder.eu>
Date: Sat, 6 Apr 2024 09:34:04 +0200
Subject: [PATCH] [Buffered-flash-backend] Created the buffered flash backend
 class

BufferedFlashBackend is a class to manage the concurrency and decouple the backend and frontend.
It uses buffer in such way that while writing to the backend, the write function is not blocking and can be called while there is a write to the backend.
---
 .../utils/Registry/BufferedFlashBackend.cpp   |  88 +++++++++++++
 .../utils/Registry/BufferedFlashBackend.h     | 123 ++++++++++++++++++
 2 files changed, 211 insertions(+)
 create mode 100644 src/shared/utils/Registry/BufferedFlashBackend.cpp
 create mode 100644 src/shared/utils/Registry/BufferedFlashBackend.h

diff --git a/src/shared/utils/Registry/BufferedFlashBackend.cpp b/src/shared/utils/Registry/BufferedFlashBackend.cpp
new file mode 100644
index 000000000..dbec5885e
--- /dev/null
+++ b/src/shared/utils/Registry/BufferedFlashBackend.cpp
@@ -0,0 +1,88 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Nicolò Caruso
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#pragma once
+#include "BufferedFlashBackend.h"
+
+namespace Boardcore
+{
+
+void BufferedFlashBackend::write(std::vector<uint8_t> &vector)
+{
+    std::lock_guard<std::mutex> lockBuffers(mutexBuffers);
+    std::lock_guard<std::mutex> lockCurrent(currentBuffer->mutex);
+
+    // Copy the vector to be then written and tell there is a needed write
+    currentBuffer->vector = vector;
+    currentBuffer->write  = true;
+}
+
+void BufferedFlashBackend::swap()
+{
+    std::lock_guard<std::mutex> lockBuffers(mutexBuffers);
+
+    WriteBuffer *tmp;
+    tmp           = busyBuffer;
+    busyBuffer    = currentBuffer;
+    currentBuffer = tmp;
+}
+
+void BufferedFlashBackend::run()
+{
+    while (true)
+    {
+        // Writes the busy buffer when it needs to be written
+        {
+            std::unique_lock<std::mutex> lockBusy(busyBuffer->mutex);
+
+            // Waits until the busy buffer not needs to be written
+            while (!busyBuffer->write)
+                writeCondition.wait(lockBusy);
+
+            // TODO: write to backend the busy buffer
+            busyBuffer->write = false;
+        }
+
+        // Swaps the buffer to update and the one to be written
+        swap();
+    }
+}
+
+bool BufferedFlashBackend::load(std::vector<uint8_t> &vector)
+{
+    // TODO: will call the backend for load the configuration to the vector
+    return false;
+}
+
+void BufferedFlashBackend::clear()
+{
+    std::lock_guard<std::mutex> lockBuffers(mutexBuffers);
+    std::lock_guard<std::mutex> lockBusy(busyBuffer->mutex);
+    std::lock_guard<std::mutex> lockCurrent(currentBuffer->mutex);
+
+    busyBuffer->vector.clear();
+    currentBuffer->vector.clear();
+
+    busyBuffer->write    = false;
+    currentBuffer->write = false;
+}
+
+}  // namespace Boardcore
\ No newline at end of file
diff --git a/src/shared/utils/Registry/BufferedFlashBackend.h b/src/shared/utils/Registry/BufferedFlashBackend.h
new file mode 100644
index 000000000..558f99c55
--- /dev/null
+++ b/src/shared/utils/Registry/BufferedFlashBackend.h
@@ -0,0 +1,123 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Nicolò Caruso
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#pragma once
+
+#include <ActiveObject.h>
+#include <stdint.h>
+#include <utils/Debug.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+namespace
+{
+constexpr uint32_t entriesReserve =
+    40;  ///< The nr. of configuration entries to reserve in the vectors
+constexpr uint32_t address = 0;  ///< The backend's address for load/write
+}  // namespace
+
+namespace Boardcore
+{
+/**
+ * @brief Write buffer structs wraps an std::vector<uint8_t> with also its mutex
+ * and a flag for specify if it changed from last write.
+ */
+struct WriteBuffer
+{
+    std::vector<uint8_t> vector;  ///< vector with serialized data
+    std::mutex mutex;
+    bool write;  ///< True if it is needed a write to the backend
+};
+
+/**
+ * @brief Buffered backend class that uses buffers to hide the write time,
+ * making it asynchronous. It decouples frontend and backend such that the
+ * frontend is blocked just while loading and while copying the vector to be
+ * written. This make the write asynchronous, while the blocking call is made by
+ * the Buffered backend.
+ */
+class BufferedFlashBackend : public ActiveObject
+{
+public:
+    /**
+     * @brief Construct a new BufferedBackend object
+     */
+    BufferedFlashBackend()
+        : ActiveObject(miosix::STACK_DEFAULT_FOR_PTHREAD, miosix::MAIN_PRIORITY)
+    {
+        // Allocates and initializes the two buffers
+        busyBuffer = static_cast<WriteBuffer *>(malloc(sizeof(WriteBuffer)));
+        busyBuffer->vector = std::vector<uint8_t>();
+        busyBuffer->vector.reserve(entriesReserve);
+        busyBuffer->write = false;
+        busyBuffer->mutex.unlock();
+
+        currentBuffer = static_cast<WriteBuffer *>(malloc(sizeof(WriteBuffer)));
+        currentBuffer->vector = std::vector<uint8_t>();
+        currentBuffer->vector.reserve(entriesReserve);
+        currentBuffer->write = false;
+        currentBuffer->mutex.unlock();
+    }
+
+    /**
+     * @brief Copies the vector to a buffer for write and then writes it
+     * asynchronously to the backend
+     *
+     * @param vector The vector to be written to backend
+     */
+    virtual void write(std::vector<uint8_t> &vector) = 0;
+
+    /**
+     * @brief Loads the configuration into the vector, if any configuration
+     * exists in the underlying backend.
+     *
+     * @param vector The vector where the configuration is loaded
+     * @return true If the vector has been loaded with a configuration
+     * @return false Otherwise
+     */
+    virtual bool load(std::vector<uint8_t> &vector) = 0;
+
+    /**
+     * @brief Clears the buffers
+     */
+    virtual void clear() = 0;
+
+    /**
+     * @brief Executed by the internal thread for write the buffers to backend
+     */
+    virtual void run() override;
+
+private:
+    WriteBuffer *busyBuffer,  ///< The buffer that will be written
+        *currentBuffer;       ///< The buffer that will be used for updates
+    std::mutex mutexBuffers;
+    std::thread workerThread;
+    std::condition_variable writeCondition;
+
+    /**
+     * @brief Swaps the two buffers.
+     */
+    void swap();
+};
+}  // namespace Boardcore
\ No newline at end of file
-- 
GitLab