From 10c3d2f35ecb186847ab96b960826e673b563d13 Mon Sep 17 00:00:00 2001 From: Tindy X <49061470+tindy2013@users.noreply.github.com> Date: Mon, 7 Sep 2020 20:03:42 +0800 Subject: [PATCH] Add /flushcache for cleaning up all caches Use a higher performance implementation of cache read/write locks. --- src/main.cpp | 11 ++++++ src/misc.h | 21 ++++++++++++ src/webget.cpp | 91 +++++++++++++++++++++++++++++++++++++++++++++++--- src/webget.h | 1 + 4 files changed, 120 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 7790eaa..cc0df49 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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); diff --git a/src/misc.h b/src/misc.h index ee99fc6..b98cdc9 100644 --- a/src/misc.h +++ b/src/misc.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include @@ -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 +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; diff --git a/src/webget.cpp b/src/webget.cpp index 77c22b1..ba48204 100644 --- a/src/webget.cpp +++ b/src/webget.cpp @@ -1,7 +1,8 @@ #include #include #include -#include +//#include +#include #include @@ -19,8 +20,76 @@ extern bool gPrintDbgInfo, gServeCacheOnFetchFail; extern int gLogLevel; +/* typedef std::lock_guard 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; diff --git a/src/webget.h b/src/webget.h index c1cd60e..a2a0aec 100644 --- a/src/webget.h +++ b/src/webget.h @@ -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);