Add /flushcache for cleaning up all caches

Use a higher performance implementation of cache read/write locks.
This commit is contained in:
Tindy X
2020-09-07 20:03:42 +08:00
parent cb27e583da
commit 10c3d2f35e
4 changed files with 120 additions and 4 deletions

View File

@@ -213,6 +213,17 @@ int main(int argc, char *argv[])
return "done\n";
});
append_response("GET", "/flushcache", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
if(getUrlArg(request.argument, "token") != gAccessToken)
{
response.status_code = 403;
return "Forbidden";
}
flushCache();
return "done";
});
append_response("GET", "/sub", "text/plain;charset=utf-8", subconverter);
append_response("GET", "/sub2clashr", "text/plain;charset=utf-8", simpleToClashR);

View File

@@ -5,6 +5,8 @@
#include <vector>
#include <sstream>
#include <algorithm>
#include <sys/types.h>
#include <dirent.h>
#include <yaml-cpp/yaml.h>
@@ -90,6 +92,25 @@ bool fileCopy(const std::string &source, const std::string &dest);
std::string fileToBase64(const std::string &filepath);
std::string fileGetMD5(const std::string &filepath);
template<typename F>
int operateFiles(const std::string &path, F &&op)
{
DIR* dir = opendir(path.data());
if(!dir)
return -1;
struct dirent* dp;
while((dp = readdir(dir)) != NULL)
{
if(strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
{
if(op(dp->d_name))
break;
}
}
closedir(dir);
return 0;
}
static inline bool strFind(const std::string &str, const std::string &target)
{
return str.find(target) != str.npos;

View File

@@ -1,7 +1,8 @@
#include <iostream>
#include <unistd.h>
#include <sys/stat.h>
#include <mutex>
//#include <mutex>
#include <atomic>
#include <curl/curl.h>
@@ -19,8 +20,76 @@
extern bool gPrintDbgInfo, gServeCacheOnFetchFail;
extern int gLogLevel;
/*
typedef std::lock_guard<std::mutex> guarded_mutex;
std::mutex cache_rw_lock;
*/
class RWLock
{
#define WRITE_LOCK_STATUS -1
#define FREE_STATUS 0
private:
const std::thread::id NULL_THREAD;
const bool WRITE_FIRST;
std::thread::id m_write_thread_id;
std::atomic_int m_lockCount;
std::atomic_uint m_writeWaitCount;
public:
RWLock(const RWLock&) = delete;
RWLock& operator=(const RWLock&) = delete;
RWLock(bool writeFirst = true): WRITE_FIRST(writeFirst), m_write_thread_id(), m_lockCount(0), m_writeWaitCount(0) {}
virtual ~RWLock() = default;
int readLock()
{
if (std::this_thread::get_id() != m_write_thread_id)
{
int count;
if (WRITE_FIRST)
do {
while ((count = m_lockCount) == WRITE_LOCK_STATUS || m_writeWaitCount > 0);
} while (!m_lockCount.compare_exchange_weak(count, count + 1));
else
do {
while ((count = m_lockCount) == WRITE_LOCK_STATUS);
} while (!m_lockCount.compare_exchange_weak(count, count + 1));
}
return m_lockCount;
}
int readUnlock()
{
if (std::this_thread::get_id() != m_write_thread_id)
--m_lockCount;
return m_lockCount;
}
int writeLock()
{
if (std::this_thread::get_id() != m_write_thread_id)
{
++m_writeWaitCount;
for (int zero = FREE_STATUS; !m_lockCount.compare_exchange_weak(zero, WRITE_LOCK_STATUS); zero = FREE_STATUS);
--m_writeWaitCount;
m_write_thread_id = std::this_thread::get_id();
}
return m_lockCount;
}
int writeUnlock()
{
if (std::this_thread::get_id() != m_write_thread_id)
{
throw std::runtime_error("writeLock/Unlock mismatch");
}
if (WRITE_LOCK_STATUS != m_lockCount)
{
throw std::runtime_error("RWLock internal error");
}
m_write_thread_id = NULL_THREAD;
m_lockCount.store(FREE_STATUS);
return m_lockCount;
}
};
RWLock cache_rw_lock;
long gMaxAllowedDownloadSize = 1048576L;
@@ -216,7 +285,9 @@ std::string webGet(const std::string &url, const std::string &proxy, unsigned in
if(difftime(now, mtime) <= cache_ttl) // within TTL
{
writeLog(0, "CACHE HIT: '" + url + "', using local cache.");
guarded_mutex guard(cache_rw_lock);
//guarded_mutex guard(cache_rw_lock);
cache_rw_lock.readLock();
defer(cache_rw_lock.readUnlock();)
if(response_headers)
*response_headers = fileGet(path_header, true);
return fileGet(path, true);
@@ -229,7 +300,9 @@ std::string webGet(const std::string &url, const std::string &proxy, unsigned in
curlGet(argument, fetch_res);
if(return_code == CURLE_OK) // success, save new cache
{
guarded_mutex guard(cache_rw_lock);
//guarded_mutex guard(cache_rw_lock);
cache_rw_lock.writeLock();
defer(cache_rw_lock.writeUnlock();)
fileWrite(path, content, true);
if(response_headers)
fileWrite(path_header, *response_headers, true);
@@ -239,7 +312,9 @@ std::string webGet(const std::string &url, const std::string &proxy, unsigned in
if(fileExist(path) && gServeCacheOnFetchFail) // failed, check if cache exist
{
writeLog(0, "Fetch failed. Serving cached content."); // cache exist, serving cache
guarded_mutex guard(cache_rw_lock);
//guarded_mutex guard(cache_rw_lock);
cache_rw_lock.readLock();
defer(cache_rw_lock.readUnlock();)
content = fileGet(path, true);
if(response_headers)
*response_headers = fileGet(path_header, true);
@@ -254,6 +329,14 @@ std::string webGet(const std::string &url, const std::string &proxy, unsigned in
return content;
}
void flushCache()
{
//guarded_mutex guard(cache_rw_lock);
cache_rw_lock.writeLock();
defer(cache_rw_lock.writeUnlock();)
operateFiles("cache", [](std::string file){ remove(("cache/" + file).data()); return 0; });
}
int curlPost(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData)
{
CURL *curl_handle;

View File

@@ -22,6 +22,7 @@ struct FetchResult
};
std::string webGet(const std::string &url, const std::string &proxy = "", unsigned int cache_ttl = 0, std::string *response_headers = NULL, string_map *request_headers = NULL);
void flushCache();
int webPost(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData);
int webPatch(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData);
std::string buildSocks5ProxyString(const std::string &addr, int port, const std::string &username, const std::string &password);