summaryrefslogtreecommitdiff
path: root/src/database/db_task.cpp
blob: ce1cd98ab93f0b7b14e0c102d8c51fd7b7771759 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "db_task.hpp"

#include <functional>

#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
#include "freertos/queue.h"
#include "freertos/task.h"

namespace database {

static const std::size_t kDbStackSize = 256 * 1024;
static StaticTask_t sDbStaticTask;
static StackType_t* sDbStack = nullptr;

static std::atomic<bool> sTaskRunning(false);
static QueueHandle_t sWorkQueue;

struct WorkItem {
  std::function<void(void)>* fn;
  bool quit;
};

auto SendToDbTask(std::function<void(void)> fn) -> void {
  WorkItem item{
      .fn = new std::function<void(void)>(fn),
      .quit = false,
  };
  xQueueSend(sWorkQueue, &item, portMAX_DELAY);
}

template <>
auto RunOnDbTask(std::function<void(void)> fn) -> std::future<void> {
  std::shared_ptr<std::promise<void>> promise =
      std::make_shared<std::promise<void>>();
  SendToDbTask([=]() {
    std::invoke(fn);
    promise->set_value();
  });
  return promise->get_future();
}

void DatabaseTaskMain(void* args) {
  while (true) {
    WorkItem item;
    if (xQueueReceive(sWorkQueue, &item, portMAX_DELAY)) {
      if (item.quit) {
        break;
      }
      if (item.fn != nullptr) {
        std::invoke(*item.fn);
        delete item.fn;
      }
    }
  }
  vQueueDelete(sWorkQueue);
  sTaskRunning.store(false);
  vTaskDelete(NULL);
}

auto StartDbTask() -> bool {
  if (sTaskRunning.exchange(true)) {
    return false;
  }
  if (sDbStack == nullptr) {
    sDbStack = reinterpret_cast<StackType_t*>(
        heap_caps_malloc(kDbStackSize, MALLOC_CAP_SPIRAM));
  }
  sWorkQueue = xQueueCreate(8, sizeof(std::function<void(void)>*));
  xTaskCreateStatic(&DatabaseTaskMain, "DB", kDbStackSize, NULL, 1, sDbStack,
                    &sDbStaticTask);
  return true;
}

auto QuitDbTask() -> void {
  if (!sTaskRunning.load()) {
    return;
  }
  WorkItem item{
      .fn = nullptr,
      .quit = true,
  };
  xQueueSend(sWorkQueue, &item, portMAX_DELAY);
  while (sTaskRunning.load()) {
    vTaskDelay(pdMS_TO_TICKS(1));
  }
}

}  // namespace database