Switch to side-by-side view

--- a/upmpd/ohmetacache.cxx
+++ b/upmpd/ohmetacache.cxx
@@ -23,8 +23,20 @@
 
 #include "ohmetacache.hxx"
 #include "conftree.hxx"
+#include "libupnpp/workqueue.hxx"
 #include "libupnpp/log.hxx"
 #include "base64.hxx"
+
+class SaveCacheTask {
+public:
+    SaveCacheTask(const string& fn, const mcache_type& cache)
+        : m_fn(fn), m_cache(cache)
+        {}
+
+    string m_fn;
+    mcache_type m_cache;
+};
+static WorkQueue<SaveCacheTask*> saveQueue("SaveQueue");
 
 // We use base64 to encode names and value, and need to replace the '='
 static const char eqesc = '*';
@@ -43,33 +55,72 @@
     }
 }
 
-bool dmcacheSave(const char *fn, const mcache_type& cache)
+bool dmcacheSave(const string& fn, const mcache_type& cache)
 {
-    ConfSimple cs(fn);
-    if (cs.getStatus() != ConfSimple::STATUS_RW) {
-        LOGERR("dmcacheSave: could not open " << fn << " for writing" << endl);
-        return false;
-    }
-    cs.clear();
-    cs.holdWrites(true);
-    for (mcache_type::const_iterator it = cache.begin();
-         it != cache.end(); it++) {
-        //LOGDEB("dmcacheSave: " << it->first << " -> " << it->second << endl);
-        string name = base64_encode(it->first);
-        eqtoggle(name);
-        cs.set(name, base64_encode(it->second));
-    }
-    
-    if (!cs.holdWrites(false)) {
-        LOGERR("dmcacheSave: write error while saving to " << fn << endl);
+    SaveCacheTask *tsk = new SaveCacheTask(fn, cache);
+
+    // Use the flush option to put() so that only the latest version
+    // stays on the queue, possibly saving writes.
+    if (!saveQueue.put(tsk, true)) {
+        LOGERR("dmcacheSave: can't queue save task" << endl);
         return false;
     }
     return true;
 }
 
-bool dmcacheRestore(const char *fn, mcache_type& cache)
+static void *dmcacheSaveWorker(void *)
 {
-    ConfSimple cs(fn, 1);
+    for (;;) {
+        SaveCacheTask *tsk = 0;
+        size_t qsz;
+        if (!saveQueue.take(&tsk, &qsz)) {
+            LOGERR("dmcacheSaveWorker: can't get task from queue" << endl);
+            saveQueue.workerExit();
+            return (void*)1;
+        }
+        LOGDEB("dmcacheSave: got save task: " << tsk->m_cache.size() << 
+               " entries to " << tsk->m_fn << endl);
+
+        // Beware that calling the constructor with a std::string
+        // would result in a memory-based object...
+        ConfSimple cs(tsk->m_fn.c_str());
+        cs.clear();
+        if (cs.getStatus() != ConfSimple::STATUS_RW) {
+            LOGERR("dmcacheSave: could not open " << tsk->m_fn 
+                   << " for writing" << endl);
+            delete tsk;
+            continue;
+        }
+        cs.holdWrites(true);
+        for (mcache_type::const_iterator it = tsk->m_cache.begin();
+             it != tsk->m_cache.end(); it++) {
+            string name = base64_encode(it->first);
+            eqtoggle(name);
+            string value = base64_encode(it->second);
+            //LOGDEB("dmcacheSave: " << name << " -> " << value << endl);
+            cs.set(name, value);
+        }
+    
+        if (!cs.holdWrites(false)) {
+            LOGERR("dmcacheSave: write error while saving to " << 
+                   tsk->m_fn << endl);
+        }
+        delete tsk;
+    }
+}
+
+bool dmcacheRestore(const string& fn, mcache_type& cache)
+{
+    // Restore is called once at startup, so seize the opportunity to start the
+    // save thread
+    if (!saveQueue.start(1, dmcacheSaveWorker, 0)) {
+        LOGERR("dmcacheRestore: could not start save thread" << endl);
+        return false;
+    }
+
+    // Beware that calling the constructor with a std::string
+    // would result in a memory-based object...
+    ConfSimple cs(fn.c_str(), 1);
     if (!cs.ok()) {
         LOGINF("dmcacheRestore: could not read metadata from " << fn << endl);
         return false;