Correct eol-style property.
[shibboleth/cpp-xmltooling.git] / xmltooling / impl / MemoryStorageService.cpp
index 4e5bef8..b0b304b 100644 (file)
@@ -1,6 +1,6 @@
 /*
- *  Copyright 2001-2005 Internet2
- * 
+ *  Copyright 2001-2007 Internet2
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
 
 /**
  * MemoryStorageService.cpp
- * 
+ *
  * In-memory "persistent" storage, suitable for simple applications.
  */
 
 #include "internal.h"
+#include "logging.h"
 #include "util/NDC.h"
 #include "util/StorageService.h"
 #include "util/Threads.h"
 #include "util/XMLHelper.h"
 
-#include <log4cpp/Category.hh>
+#include <memory>
 #include <xercesc/util/XMLUniDefs.hpp>
 
+using namespace xmltooling::logging;
 using namespace xmltooling;
-using namespace log4cpp;
 using namespace std;
 
+using xercesc::DOMElement;
+
 namespace xmltooling {
     class XMLTOOL_DLLLOCAL MemoryStorageService : public StorageService
     {
     public:
         MemoryStorageService(const DOMElement* e);
         virtual ~MemoryStorageService();
-        
-        void createString(const char* context, const char* key, const char* value, time_t expiration);
+
+        bool createString(const char* context, const char* key, const char* value, time_t expiration);
         int readString(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0);
         int updateString(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0);
         bool deleteString(const char* context, const char* key);
-        
-        void createText(const char* context, const char* key, const char* value, time_t expiration) {
+
+        bool createText(const char* context, const char* key, const char* value, time_t expiration) {
             return createString(context, key, value, expiration);
         }
         int readText(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0) {
@@ -57,16 +60,18 @@ namespace xmltooling {
         bool deleteText(const char* context, const char* key) {
             return deleteString(context, key);
         }
-        
+
         void reap(const char* context);
+        void updateContext(const char* context, time_t expiration);
         void deleteContext(const char* context) {
-            Lock wrapper(contextLock);
+            m_lock->wrlock();
             m_contextMap.erase(context);
+            m_lock->unlock();
         }
 
     private:
         void cleanup();
-    
+
         struct XMLTOOL_DLLLOCAL Record {
             Record() : expiration(0), version(1) {}
             Record(const string& s, time_t t) : data(s), expiration(t), version(1) {}
@@ -74,28 +79,33 @@ namespace xmltooling {
             time_t expiration;
             int version;
         };
-        
+
         struct XMLTOOL_DLLLOCAL Context {
-            Context() : m_lock(RWLock::create()) {}
+            Context() {}
             Context(const Context& src) {
                 m_dataMap = src.m_dataMap;
-                m_expMap = src.m_expMap;
-                m_lock = RWLock::create();
             }
-            ~Context() { delete m_lock; }
             map<string,Record> m_dataMap;
-            multimap<time_t,string> m_expMap;
-            RWLock* m_lock;
-            unsigned long reap();
+            unsigned long reap(time_t exp);
         };
 
-        Context& getContext(const char* context) {
-            Lock wrapper(contextLock);
+        Context& readContext(const char* context) {
+            m_lock->rdlock();
+            map<string,Context>::iterator i = m_contextMap.find(context);
+            if (i != m_contextMap.end())
+                return i->second;
+            m_lock->unlock();
+            m_lock->wrlock();
+            return m_contextMap[context];
+        }
+
+        Context& writeContext(const char* context) {
+            m_lock->wrlock();
             return m_contextMap[context];
         }
 
         map<string,Context> m_contextMap;
-        Mutex* contextLock;
+        RWLock* m_lock;
         CondWait* shutdown_wait;
         Thread* cleanup_thread;
         static void* cleanup_fn(void*);
@@ -108,13 +118,12 @@ namespace xmltooling {
     {
         return new MemoryStorageService(e);
     }
-
 };
 
 static const XMLCh cleanupInterval[] = UNICODE_LITERAL_15(c,l,e,a,n,u,p,I,n,t,e,r,v,a,l);
 
 MemoryStorageService::MemoryStorageService(const DOMElement* e)
-    : contextLock(NULL), shutdown_wait(NULL), cleanup_thread(NULL), shutdown(false), m_cleanupInterval(0),
+    : m_lock(NULL), shutdown_wait(NULL), cleanup_thread(NULL), shutdown(false), m_cleanupInterval(0),
         m_log(Category::getInstance(XMLTOOLING_LOGCAT".StorageService"))
 {
     const XMLCh* tag=e ? e->getAttributeNS(NULL,cleanupInterval) : NULL;
@@ -122,9 +131,9 @@ MemoryStorageService::MemoryStorageService(const DOMElement* e)
         m_cleanupInterval = XMLString::parseInt(tag);
     }
     if (!m_cleanupInterval)
-        m_cleanupInterval=300;
+        m_cleanupInterval=900;
 
-    contextLock = Mutex::create();
+    m_lock = RWLock::create();
     shutdown_wait = CondWait::create();
     cleanup_thread = Thread::create(&cleanup_fn, (void*)this);
 }
@@ -136,8 +145,9 @@ MemoryStorageService::~MemoryStorageService()
     shutdown_wait->signal();
     cleanup_thread->join(NULL);
 
+    delete cleanup_thread;
     delete shutdown_wait;
-    delete contextLock;
+    delete m_lock;
 }
 
 void* MemoryStorageService::cleanup_fn(void* cache_p)
@@ -145,7 +155,7 @@ void* MemoryStorageService::cleanup_fn(void* cache_p)
     MemoryStorageService* cache = reinterpret_cast<MemoryStorageService*>(cache_p);
 
 #ifndef WIN32
-    // First, let's block all signals 
+    // First, let's block all signals
     Thread::mask_all_signals();
 #endif
 
@@ -169,14 +179,16 @@ void MemoryStorageService::cleanup()
         shutdown_wait->timedwait(mutex.get(), m_cleanupInterval);
         if (shutdown)
             break;
-        
+
         unsigned long count=0;
-        Lock wrapper(contextLock);
+        time_t now = time(NULL);
+        m_lock->wrlock();
+        SharedLock locker(m_lock, false);
         for (map<string,Context>::iterator i=m_contextMap.begin(); i!=m_contextMap.end(); ++i)
-            count += i->second.reap();
-        
+            count += i->second.reap(now);
+
         if (count)
-            m_log.info("purged %d record(s) from storage", count);
+            m_log.info("purged %d expired record(s) from storage", count);
     }
 
     m_log.info("cleanup thread finished");
@@ -187,93 +199,81 @@ void MemoryStorageService::cleanup()
 
 void MemoryStorageService::reap(const char* context)
 {
-    getContext(context).reap();
+    Context& ctx = writeContext(context);
+    SharedLock locker(m_lock, false);
+    ctx.reap(time(NULL));
 }
 
-unsigned long MemoryStorageService::Context::reap()
+unsigned long MemoryStorageService::Context::reap(time_t exp)
 {
-    // Lock the "database".
-    m_lock->wrlock();
-    SharedLock wrapper(m_lock, false);
-    
     // Garbage collect any expired entries.
     unsigned long count=0;
-    multimap<time_t,string>::iterator stop=m_expMap.upper_bound(time(NULL));
-    for (multimap<time_t,string>::iterator i=m_expMap.begin(); i!=stop; m_expMap.erase(i++)) {
-        m_dataMap.erase(i->second);
-        ++count;
+    map<string,Record>::iterator cur = m_dataMap.begin();
+    map<string,Record>::iterator stop = m_dataMap.end();
+    while (cur != stop) {
+        if (cur->second.expiration <= exp) {
+            map<string,Record>::iterator tmp = cur++;
+            m_dataMap.erase(tmp);
+            ++count;
+        }
+        else {
+            cur++;
+        }
     }
-
     return count;
 }
 
-void MemoryStorageService::createString(const char* context, const char* key, const char* value, time_t expiration)
+bool MemoryStorageService::createString(const char* context, const char* key, const char* value, time_t expiration)
 {
-    Context& ctx = getContext(context);
+    Context& ctx = writeContext(context);
+    SharedLock locker(m_lock, false);
 
-    // Lock the maps.
-    ctx.m_lock->wrlock();
-    SharedLock wrapper(ctx.m_lock, false);
-    
     // Check for a duplicate.
     map<string,Record>::iterator i=ctx.m_dataMap.find(key);
     if (i!=ctx.m_dataMap.end()) {
         // Not yet expired?
         if (time(NULL) < i->second.expiration)
-            throw IOException("attempted to insert a record with duplicate key ($1)", params(1,key));
+            return false;
         // It's dead, so we can just remove it now and create the new record.
-        // Now find the reversed index of expiration to key, so we can clear it.
-        pair<multimap<time_t,string>::iterator,multimap<time_t,string>::iterator> range =
-            ctx.m_expMap.equal_range(i->second.expiration);
-        for (; range.first != range.second; ++range.first) {
-            if (range.first->second == i->first) {
-                ctx.m_expMap.erase(range.first);
-                break;
-            }
-        }
-        // And finally delete the record itself.
         ctx.m_dataMap.erase(i);
     }
-    
+
     ctx.m_dataMap[key]=Record(value,expiration);
-    ctx.m_expMap.insert(multimap<time_t,string>::value_type(expiration,key));
-    
+
     m_log.debug("inserted record (%s) in context (%s)", key, context);
+    return true;
 }
 
 int MemoryStorageService::readString(const char* context, const char* key, string* pvalue, time_t* pexpiration, int version)
 {
-    Context& ctx = getContext(context);
+    Context& ctx = readContext(context);
+    SharedLock locker(m_lock, false);
 
-    SharedLock wrapper(ctx.m_lock);
     map<string,Record>::iterator i=ctx.m_dataMap.find(key);
     if (i==ctx.m_dataMap.end())
         return 0;
     else if (time(NULL) >= i->second.expiration)
         return 0;
+    if (pexpiration)
+        *pexpiration = i->second.expiration;
     if (i->second.version == version)
         return version; // nothing's changed, so just echo back the version
     if (pvalue)
         *pvalue = i->second.data;
-    if (pexpiration)
-        *pexpiration = i->second.expiration;
     return i->second.version;
 }
 
 int MemoryStorageService::updateString(const char* context, const char* key, const char* value, time_t expiration, int version)
 {
-    Context& ctx = getContext(context);
-
-    // Lock the maps.
-    ctx.m_lock->wrlock();
-    SharedLock wrapper(ctx.m_lock, false);
+    Context& ctx = writeContext(context);
+    SharedLock locker(m_lock, false);
 
     map<string,Record>::iterator i=ctx.m_dataMap.find(key);
     if (i==ctx.m_dataMap.end())
         return 0;
     else if (time(NULL) >= i->second.expiration)
         return 0;
-    
+
     if (version > 0 && version != i->second.version)
         return -1;  // caller's out of sync
 
@@ -281,20 +281,9 @@ int MemoryStorageService::updateString(const char* context, const char* key, con
         i->second.data = value;
         ++(i->second.version);
     }
-        
-    if (expiration && expiration != i->second.expiration) {
-        // Update secondary map.
-        pair<multimap<time_t,string>::iterator,multimap<time_t,string>::iterator> range =
-            ctx.m_expMap.equal_range(i->second.expiration);
-        for (; range.first != range.second; ++range.first) {
-            if (range.first->second == i->first) {
-                ctx.m_expMap.erase(range.first);
-                break;
-            }
-        }
+
+    if (expiration && expiration != i->second.expiration)
         i->second.expiration = expiration;
-        ctx.m_expMap.insert(multimap<time_t,string>::value_type(expiration,key));
-    }
 
     m_log.debug("updated record (%s) in context (%s)", key, context);
     return i->second.version;
@@ -302,25 +291,12 @@ int MemoryStorageService::updateString(const char* context, const char* key, con
 
 bool MemoryStorageService::deleteString(const char* context, const char* key)
 {
-    Context& ctx = getContext(context);
+    Context& ctx = writeContext(context);
+    SharedLock locker(m_lock, false);
 
-    // Lock the maps.
-    ctx.m_lock->wrlock();
-    SharedLock wrapper(ctx.m_lock, false);
-    
     // Find the record.
     map<string,Record>::iterator i=ctx.m_dataMap.find(key);
     if (i!=ctx.m_dataMap.end()) {
-        // Now find the reversed index of expiration to key, so we can clear it.
-        pair<multimap<time_t,string>::iterator,multimap<time_t,string>::iterator> range =
-            ctx.m_expMap.equal_range(i->second.expiration);
-        for (; range.first != range.second; ++range.first) {
-            if (range.first->second == i->first) {
-                ctx.m_expMap.erase(range.first);
-                break;
-            }
-        }
-        // And finally delete the record itself.
         ctx.m_dataMap.erase(i);
         m_log.debug("deleted record (%s) in context (%s)", key, context);
         return true;
@@ -329,3 +305,18 @@ bool MemoryStorageService::deleteString(const char* context, const char* key)
     m_log.debug("deleting record (%s) in context (%s)....not found", key, context);
     return false;
 }
+
+void MemoryStorageService::updateContext(const char* context, time_t expiration)
+{
+    Context& ctx = writeContext(context);
+    SharedLock locker(m_lock, false);
+
+    time_t now = time(NULL);
+    map<string,Record>::iterator stop=ctx.m_dataMap.end();
+    for (map<string,Record>::iterator i = ctx.m_dataMap.begin(); i!=stop; ++i) {
+        if (now < i->second.expiration)
+            i->second.expiration = expiration;
+    }
+
+    m_log.debug("updated expiration of valid records in context (%s)", context);
+}