[cpp-sp] branch main updated: Start implementing session cache base class.

Scott Cantor cantor.2 at osu.edu
Thu May 8 19:28:58 UTC 2025


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository cpp-sp.

View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=0ddcc0a73df5f93d2c2c6c1534e1ffcf1d30acd9

The following commit(s) were added to refs/heads/main by this push:
     new 0ddcc0a7 Start implementing session cache base class.
0ddcc0a7 is described below

commit 0ddcc0a73df5f93d2c2c6c1534e1ffcf1d30acd9
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu May 8 15:28:53 2025 -0400

    Start implementing session cache base class.
---
 shibsp/session/AbstractSessionCache.h          |  61 +++++++++--
 shibsp/session/impl/AbstractSessionCache.cpp   | 140 ++++++++++++++++++++++++-
 shibsp/session/impl/FilesystemSessionCache.cpp |   2 +-
 3 files changed, 194 insertions(+), 9 deletions(-)

diff --git a/shibsp/session/AbstractSessionCache.h b/shibsp/session/AbstractSessionCache.h
index e5968c10..69917480 100644
--- a/shibsp/session/AbstractSessionCache.h
+++ b/shibsp/session/AbstractSessionCache.h
@@ -24,8 +24,15 @@
 #include <logging/Category.h>
 #include <remoting/ddf.h>
 #include <session/SessionCache.h>
+#include <util/BoostPropertySet.h>
 
+#include <condition_variable>
 #include <mutex>
+#ifdef HAVE_CXX14
+# include <shared_mutex>
+#endif
+#include <thread>
+#include <boost/property_tree/ptree_fwd.hpp>
 
 namespace shibsp {
 
@@ -68,17 +75,57 @@ namespace shibsp {
         std::mutex m_lock;
     };
 
-    class SHIBSP_API AbstractSessionCache : public virtual SessionCache {
+    class SHIBSP_API AbstractSessionCache : public virtual SessionCache, public virtual BoostPropertySet {
+        public:
+            /**
+             * Starts background cleanup thread for in-memory hashtable of sessions.
+             * 
+             * @return true iff the thread was successfully started
+             */
+            bool start();
+
+            static const char CLEANUP_INTERVAL_PROP_NAME[];
+            static const char INPROC_TIMEOUT_PROP_NAME[];
+
+            static unsigned int CLEANUP_INTERVAL_PROP_DEFAULT;
+            static unsigned int INPROC_TIMEOUT_PROP_DEFAULT;
+
         protected:
-            /** Constructor. */
-            AbstractSessionCache();
+            /**
+             * Constructor.
+             * 
+             * @param pt root of property tree to load
+             */
+            AbstractSessionCache(const boost::property_tree::ptree& pt);
+
+            /** Destructor. */
             virtual ~AbstractSessionCache();
     
-            Category& m_log;
-    
-        public:
-            bool start();
+            /**
+             * Get logging object.
+             * 
+             * @return logging object
+             */
+            Category& log() const;
 
+        private:
+            static void* cleanup_fn(void*);
+            void dormant(const std::string& key);
+
+            Category& m_log;
+#if defined(HAVE_CXX17)
+            std::shared_mutex m_lock;
+#elif defined(HAVE_CXX14)
+            std::shared_timed_mutex m_lock;
+#else
+            std::mutex m_lock;
+#endif
+            std::map<std::string,std::unique_ptr<BasicSession>> m_hashtable;
+
+            bool m_shutdown;
+            std::condition_variable m_shutdown_wait;
+            std::thread m_cleanup_thread;
+                
         friend class BasicSession;
         };    
 };
diff --git a/shibsp/session/impl/AbstractSessionCache.cpp b/shibsp/session/impl/AbstractSessionCache.cpp
index a7f2a1e6..4edacffc 100644
--- a/shibsp/session/impl/AbstractSessionCache.cpp
+++ b/shibsp/session/impl/AbstractSessionCache.cpp
@@ -24,8 +24,18 @@
 #include "session/AbstractSessionCache.h"
 #include "logging/Category.h"
 
+#include <chrono>
 #include <boost/property_tree/ptree.hpp>
 
+#ifndef WIN32
+# include <signal.h>
+# ifdef HAVE_PTHREAD
+#  include <pthread.h>
+# else
+#  error "This implementation is for POSIX platforms."
+# endif
+#endif
+
 using namespace shibsp;
 using namespace boost::property_tree;
 using namespace std;
@@ -57,19 +67,147 @@ SessionCache::~SessionCache()
 {
 }
 
-AbstractSessionCache::AbstractSessionCache() : m_log(Category::getInstance(SHIBSP_LOGCAT ".SessionCache"))
+const char AbstractSessionCache::CLEANUP_INTERVAL_PROP_NAME[] = "cleanupInterval";
+const char AbstractSessionCache::INPROC_TIMEOUT_PROP_NAME[] = "inprocTimeout";
+
+unsigned int AbstractSessionCache::CLEANUP_INTERVAL_PROP_DEFAULT = 900;
+unsigned int AbstractSessionCache::INPROC_TIMEOUT_PROP_DEFAULT = 900;
+
+AbstractSessionCache::AbstractSessionCache(const ptree& pt) : m_log(Category::getInstance(SHIBSP_LOGCAT ".SessionCache"))
 {
+    load(pt);
 }
 
 AbstractSessionCache::~AbstractSessionCache()
 {
+    // Notify and join with the cleanup thread.
+    m_shutdown = true;
+    m_shutdown_wait.notify_all();
+    if (m_cleanup_thread.joinable()) {
+        m_cleanup_thread.join();
+    }
+}
+
+Category& AbstractSessionCache::log() const
+{
+    return m_log;
 }
 
 bool AbstractSessionCache::start()
 {
+    try {
+        m_cleanup_thread = thread(cleanup_fn, this);
+        return true;
+    }
+    catch (const system_error& e) {
+        m_log.error("error starting cleanup thread: %s", e.what());
+    }
     return false;
 }
 
+void AbstractSessionCache::dormant(const string& key)
+{
+    m_log.debug("deleting local copy of session (%s)", key.c_str());
+
+    // lock the cache for writing, which means we know nobody is sitting in a lookup.
+    m_lock.lock();
+
+    // grab the entry from the table
+    const auto& i = m_hashtable.find(key);
+    if (i == m_hashtable.end()) {
+        m_lock.unlock();
+        return;
+    }
+
+    // ok, swap ownership of the entry, remove from cache
+    unique_ptr<BasicSession> session;
+    session.swap(i->second);
+    m_hashtable.erase(key);
+
+    // lock the entry, ensuring nobody else has a copy
+    session->lock();
+
+    // unlock the cache
+    m_lock.unlock();
+
+    // we can release the cache entry lock because we know we're not in the cache anymore
+    session->unlock();
+}
+
+void* AbstractSessionCache::cleanup_fn(void* p)
+{
+    AbstractSessionCache* pcache = reinterpret_cast<AbstractSessionCache*>(p);
+
+#ifndef WIN32
+    // Bblock all signals.
+    sigset_t sigmask;
+    sigfillset(&sigmask);
+    pthread_sigmask(SIG_BLOCK, &sigmask, nullptr);
+#endif
+
+    mutex internal_mutex;
+
+    // Load our configuration details...
+    unsigned int cleanupInterval = pcache->getUnsignedInt(CLEANUP_INTERVAL_PROP_NAME, CLEANUP_INTERVAL_PROP_DEFAULT);
+    unsigned int inprocTimeout = pcache->getUnsignedInt(INPROC_TIMEOUT_PROP_NAME, INPROC_TIMEOUT_PROP_DEFAULT);
+
+    unique_lock lock(internal_mutex);
+
+    pcache->m_log.info("cleanup thread started...run every %u secs; timeout after %u secs", cleanupInterval, inprocTimeout);
+
+    while (!pcache->m_shutdown) {
+        pcache->m_shutdown_wait.wait_for(lock, chrono::seconds(cleanupInterval));
+        
+        if (pcache->m_shutdown) {
+            break;
+        }
+
+        // Ok, let's run through the cleanup process and clean out
+        // really old sessions.  This is a two-pass process.  The
+        // first pass is done holding a read-lock while we iterate over
+        // the cache.  The second pass doesn't need a lock because
+        // the 'deletes' will lock the cache.
+
+        // Pass 1: iterate over the map and find all entries that have not been
+        // used in the allotted timeout.
+        vector<string> stale_keys;
+        time_t stale = time(nullptr) - inprocTimeout;
+
+        pcache->m_log.debug("cleanup thread running");
+
+#ifdef HAVE_CXX14
+        pcache->m_lock.lock_shared();
+#else
+        pcache->m_lock.lock();
+#endif
+        for (const auto& session : pcache->m_hashtable) {
+            // If the last access was BEFORE the stale timeout...
+            session.second->lock();
+            time_t last = session.second->getLastAccess();
+            session.second->unlock();
+            if (last < stale)
+                stale_keys.push_back(session.first);
+        }
+
+        pcache->m_lock.unlock();
+
+        if (!stale_keys.empty()) {
+            pcache->m_log.info("purging %u old sessions", stale_keys.size());
+
+            // Pass 2: walk through the list of stale entries and remove them from the cache
+            for (const string& key : stale_keys) {
+                pcache->dormant(key.c_str());
+            }
+        }
+
+        pcache->m_log.debug("cleanup thread completed");
+    }
+
+    pcache->m_log.info("cleanup thread exiting");
+
+    return nullptr;
+}
+
 BasicSession::BasicSession(AbstractSessionCache& cache, DDF& obj)
     : m_obj(obj), m_cache(cache), m_creation(0), m_lastAccess(time(nullptr))
 {
diff --git a/shibsp/session/impl/FilesystemSessionCache.cpp b/shibsp/session/impl/FilesystemSessionCache.cpp
index c2932274..d8efd00a 100644
--- a/shibsp/session/impl/FilesystemSessionCache.cpp
+++ b/shibsp/session/impl/FilesystemSessionCache.cpp
@@ -51,7 +51,7 @@ namespace shibsp {
     }
 }
 
-FilesystemSessionCache::FilesystemSessionCache(const ptree& pt)
+FilesystemSessionCache::FilesystemSessionCache(const ptree& pt) : AbstractSessionCache(pt)
 {
 }
 

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list