Added thread safety
This commit is contained in:
86
main.cpp
86
main.cpp
@@ -1,5 +1,3 @@
|
||||
// Start using chrono when C++20 becomes available
|
||||
// #include <chrono>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
@@ -7,6 +5,7 @@
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -15,7 +14,6 @@
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
// Stop using these when C++20 becomes available
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -34,7 +32,21 @@ constexpr int API_KEY_LENGHT = 16;
|
||||
constexpr char DEFAULT_IMAGE_URL[] = "https://trmnl.com/images/setup/setup-logo.bmp";
|
||||
constexpr char DEFAULT_IMAGE_FNAME[] = "2024-09-20T00:00:00";
|
||||
|
||||
string generate_api_key(int len = API_KEY_LENGHT)
|
||||
// Generates a random api key with specified length
|
||||
string generate_api_key(int len = API_KEY_LENGHT);
|
||||
|
||||
// Reloads devices from specified filename
|
||||
// If forced is false - checks last modification time to determine whether to actually do it
|
||||
bool reload_container(TRMNLContainer& container, shared_mutex& mut_container, const string& filename, bool forced = false);
|
||||
|
||||
// Returns the last modification time down to seconds in ISO format - 'YYYY-MM-DDThh:mm:ss'
|
||||
string get_timestamp_from_filename(const string& filepath);
|
||||
|
||||
// First string is image filename for URL
|
||||
// Second string is timestamp for TRMNL filename
|
||||
pair<string, string> find_image_for_friendly(const string& folder_images, const string& friendly_id);
|
||||
|
||||
string generate_api_key(int len)
|
||||
{
|
||||
string result = "";
|
||||
std::random_device rand;
|
||||
@@ -82,14 +94,32 @@ string generate_api_key(int len = API_KEY_LENGHT)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool reload_container(TRMNLContainer& container, const string& filename)
|
||||
bool reload_container(TRMNLContainer& container, shared_mutex& mut_container, const string& filename, bool forced)
|
||||
{
|
||||
static mutex mut_mod_time;
|
||||
static string last_mod_time = "";
|
||||
json j;
|
||||
bool ok = read_file_json(j, filename, &cout);
|
||||
bool ok;
|
||||
lock_guard guard(mut_mod_time);
|
||||
|
||||
// Check if file mod time has changed since last reload
|
||||
if (!forced)
|
||||
{
|
||||
if (last_mod_time == get_timestamp_from_filename(filename))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ok = read_file_json(j, filename, &cout);
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lock Write (exclusive) Mutex & automatically unlock on scope exit
|
||||
unique_lock l(mut_container);
|
||||
|
||||
container.clear();
|
||||
if (j.is_array())
|
||||
{
|
||||
@@ -98,6 +128,7 @@ bool reload_container(TRMNLContainer& container, const string& filename)
|
||||
container.add_device(j[i]);
|
||||
}
|
||||
}
|
||||
last_mod_time = get_timestamp_from_filename(filename);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -127,31 +158,6 @@ string get_timestamp_from_filename(const string& filepath)
|
||||
return result;
|
||||
}
|
||||
|
||||
// Use this when C++20 becomes available
|
||||
#if 0
|
||||
string get_timestamp_from_file_time(const fs::file_time_type& ftime)
|
||||
{
|
||||
char result[128] = "";
|
||||
time_t file_time = std::chrono::system_clock::to_time_t(std::chrono::file_clock::to_sys(ftime));
|
||||
|
||||
tm* file_tm;
|
||||
file_tm = gmtime(&file_time);
|
||||
|
||||
snprintf(result, sizeof(result) - 1, "%04d-%02d-%02dT%02d:%02d:%02d",
|
||||
file_tm->tm_year + 1900,
|
||||
file_tm->tm_mon + 1,
|
||||
file_tm->tm_mday,
|
||||
file_tm->tm_hour,
|
||||
file_tm->tm_min,
|
||||
file_tm->tm_sec
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
// First string is image filename for URL
|
||||
// Second string is timestamp for TRMNL filename
|
||||
pair<string, string> find_image_for_friendly(const string& folder_images, const string& friendly_id)
|
||||
{
|
||||
pair<string, string> result = {"", ""};
|
||||
@@ -169,8 +175,6 @@ pair<string, string> find_image_for_friendly(const string& folder_images, const
|
||||
{
|
||||
result.first = entry.path().filename();
|
||||
result.second = get_timestamp_from_filename(entry.path());
|
||||
// Change to this when C++20 becomes available
|
||||
// result.second = get_timestamp_from_file_time(entry.last_write_time());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +203,7 @@ int main(int argc, char **argv)
|
||||
json cfg;
|
||||
json devs;
|
||||
TRMNLContainer container;
|
||||
shared_mutex mut_container;
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
@@ -264,14 +269,15 @@ int main(int argc, char **argv)
|
||||
server = make_shared<httplib::Server>();
|
||||
}
|
||||
|
||||
ok = reload_container(container, devices_filename);
|
||||
ok = reload_container(container, mut_container, devices_filename);
|
||||
if (!ok)
|
||||
{
|
||||
cout << "Could not read devices file" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto setup_handler = [&container, &devices_filename](const httplib::Request& req, httplib::Response& res)
|
||||
auto setup_handler = [&container, &mut_container, &devices_filename]
|
||||
(const httplib::Request& req, httplib::Response& res)
|
||||
{
|
||||
json response;
|
||||
|
||||
@@ -291,8 +297,10 @@ int main(int argc, char **argv)
|
||||
|
||||
// Refresh data from file
|
||||
// Someone might have put a new device in
|
||||
reload_container(container, devices_filename);
|
||||
reload_container(container, mut_container, devices_filename);
|
||||
|
||||
// Write Lock - setup does not happen often enough, but 2 setups can lead to race condition on swapping read/write locks
|
||||
unique_lock l(mut_container);
|
||||
string id = req.get_header_value("ID");
|
||||
TRMNL* trmnl = container.get_device_by_id(id);
|
||||
|
||||
@@ -341,7 +349,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
};
|
||||
|
||||
auto display_handler = [&container, &devices_filename, &folder_images, &base_url, refresh_min, refresh_offset]
|
||||
auto display_handler = [&container, &mut_container, &devices_filename, &folder_images, &base_url, refresh_min, refresh_offset]
|
||||
(const httplib::Request& req, httplib::Response& res)
|
||||
{
|
||||
json response;
|
||||
@@ -361,8 +369,10 @@ int main(int argc, char **argv)
|
||||
|
||||
// Refresh data from file
|
||||
// Someone might have put a new device in
|
||||
reload_container(container, devices_filename);
|
||||
reload_container(container, mut_container, devices_filename);
|
||||
|
||||
// Lock for reading
|
||||
shared_lock l(mut_container);
|
||||
string id = req.get_header_value("ID");
|
||||
string api_key = req.get_header_value("Access-Token");
|
||||
TRMNL* trmnl = container.get_device_by_id(id);
|
||||
|
||||
Reference in New Issue
Block a user