From a39163bba7f0487b6b8a616a8811ca86bd703942 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Fri, 22 Sep 2006 19:28:38 +0000 Subject: [PATCH] Simplify storage context mgmt. --- xmltooling/impl/MemoryStorageService.cpp | 166 +++++++++++++----------------- xmltooling/util/StorageService.h | 85 +++++++-------- xmltoolingtest/MemoryStorageServiceTest.h | 18 ++-- 3 files changed, 115 insertions(+), 154 deletions(-) diff --git a/xmltooling/impl/MemoryStorageService.cpp b/xmltooling/impl/MemoryStorageService.cpp index 4acd7c3..0179897 100644 --- a/xmltooling/impl/MemoryStorageService.cpp +++ b/xmltooling/impl/MemoryStorageService.cpp @@ -40,29 +40,29 @@ namespace xmltooling { MemoryStorageService(const DOMElement* e); virtual ~MemoryStorageService(); - StorageHandle* createHandle(); + void createString(const char* context, const char* key, const char* value, time_t expiration); + bool readString(const char* context, const char* key, string& value, time_t modifiedSince=0); + bool updateString(const char* context, const char* key, const char* value=NULL, time_t expiration=0); + bool deleteString(const char* context, const char* key); - void createString(StorageHandle* handle, const char* key, const char* value, time_t expiration); - bool readString(StorageHandle* handle, const char* key, string& value, time_t modifiedSince=0); - bool updateString(StorageHandle* handle, const char* key, const char* value=NULL, time_t expiration=0); - bool deleteString(StorageHandle* handle, const char* key); - - void createText(StorageHandle* handle, const char* key, const char* value, time_t expiration) { - return createString(handle, key, value, expiration); + void createText(const char* context, const char* key, const char* value, time_t expiration) { + return createString(context, key, value, expiration); } - bool readText(StorageHandle* handle, const char* key, string& value, time_t modifiedSince=0) { - return readString(handle, key, value, modifiedSince); + bool readText(const char* context, const char* key, string& value, time_t modifiedSince=0) { + return readString(context, key, value, modifiedSince); } - bool updateText(StorageHandle* handle, const char* key, const char* value=NULL, time_t expiration=0) { - return updateString(handle, key, value, expiration); + bool updateText(const char* context, const char* key, const char* value=NULL, time_t expiration=0) { + return updateString(context, key, value, expiration); } - bool deleteText(StorageHandle* handle, const char* key) { - return deleteString(handle, key); + bool deleteText(const char* context, const char* key) { + return deleteString(context, key); } - void reap(StorageHandle* handle); - - void removeHandle(StorageHandle* handle); + void reap(const char* context); + void deleteContext(const char* context) { + Lock wrapper(contextLock); + m_contextMap.erase(context); + } private: void cleanup(); @@ -74,20 +74,27 @@ namespace xmltooling { time_t modified, expiration; }; - struct XMLTOOL_DLLLOCAL MemoryHandle : public StorageHandle { - MemoryHandle(StorageService* storage) : StorageHandle(storage), m_lock(RWLock::create()) {} - virtual ~MemoryHandle() { - delete m_lock; - static_cast(m_storage)->removeHandle(this); + struct XMLTOOL_DLLLOCAL Context { + Context() : m_lock(RWLock::create()) {} + Context(const Context& src) { + m_dataMap = src.m_dataMap; + m_expMap = src.m_expMap; + m_lock = RWLock::create(); } + ~Context() { delete m_lock; } map m_dataMap; multimap m_expMap; RWLock* m_lock; unsigned long reap(); }; - - vector m_handles; - Mutex* mutex; + + Context& getContext(const char* context) { + Lock wrapper(contextLock); + return m_contextMap[context]; + } + + map m_contextMap; + Mutex* contextLock; CondWait* shutdown_wait; Thread* cleanup_thread; static void* cleanup_fn(void*); @@ -106,10 +113,10 @@ namespace xmltooling { 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) - : mutex(NULL), shutdown_wait(NULL), cleanup_thread(NULL), shutdown(false), m_cleanupInterval(0), + : contextLock(NULL), shutdown_wait(NULL), cleanup_thread(NULL), shutdown(false), m_cleanupInterval(0), m_log(Category::getInstance(XMLTOOLING_LOGCAT".StorageService")) { - mutex = Mutex::create(); + contextLock = Mutex::create(); shutdown_wait = CondWait::create(); cleanup_thread = Thread::create(&cleanup_fn, (void*)this); @@ -129,27 +136,7 @@ MemoryStorageService::~MemoryStorageService() cleanup_thread->join(NULL); delete shutdown_wait; - delete mutex; - for_each(m_handles.begin(), m_handles.end(), xmltooling::cleanup()); -} - -StorageService::StorageHandle* MemoryStorageService::createHandle() -{ - Lock wrapper(mutex); - MemoryHandle* ret = new MemoryHandle(this); - m_handles.push_back(ret); - return ret; -} - -void MemoryStorageService::removeHandle(StorageHandle* handle) -{ - Lock wrapper(mutex); - for (vector::iterator i=m_handles.begin(); i!=m_handles.end(); ++i) { - if (*i == handle) { - m_handles.erase(i); - return; - } - } + delete contextLock; } void* MemoryStorageService::cleanup_fn(void* cache_p) @@ -184,8 +171,9 @@ void MemoryStorageService::cleanup() break; unsigned long count=0; - for (vector::iterator i=m_handles.begin(); i!=m_handles.end(); ++i) - count += (*i)->reap(); + Lock wrapper(contextLock); + for (map::iterator i=m_contextMap.begin(); i!=m_contextMap.end(); ++i) + count += i->second.reap(); if (count) m_log.info("purged %d record(s) from storage", count); @@ -198,14 +186,12 @@ void MemoryStorageService::cleanup() Thread::exit(NULL); } -void MemoryStorageService::reap(StorageHandle* handle) +void MemoryStorageService::reap(const char* context) { - if (!isValid(handle)) - throw IOException("Invalid storage handle."); - static_cast(handle)->reap(); + getContext(context).reap(); } -unsigned long MemoryStorageService::MemoryHandle::reap() +unsigned long MemoryStorageService::Context::reap() { // Lock the "database". m_lock->wrlock(); @@ -223,36 +209,32 @@ unsigned long MemoryStorageService::MemoryHandle::reap() return count; } -void MemoryStorageService::createString(StorageHandle* handle, const char* key, const char* value, time_t expiration) +void MemoryStorageService::createString(const char* context, const char* key, const char* value, time_t expiration) { - if (!isValid(handle)) - throw IOException("Invalid storage handle."); - MemoryHandle* h = static_cast(handle); + Context& ctx = getContext(context); // Lock the maps. - h->m_lock->wrlock(); - SharedLock wrapper(h->m_lock, false); + ctx.m_lock->wrlock(); + SharedLock wrapper(ctx.m_lock, false); // Check for a duplicate. - map::iterator i=h->m_dataMap.find(key); - if (i!=h->m_dataMap.end()) + map::iterator i=ctx.m_dataMap.find(key); + if (i!=ctx.m_dataMap.end()) throw IOException("attempted to insert a record with duplicate key ($1)", params(1,key)); - h->m_dataMap[key]=Record(value,time(NULL),expiration); - h->m_expMap.insert(multimap::value_type(expiration,key)); + ctx.m_dataMap[key]=Record(value,time(NULL),expiration); + ctx.m_expMap.insert(multimap::value_type(expiration,key)); m_log.debug("inserted record (%s)", key); } -bool MemoryStorageService::readString(StorageHandle* handle, const char* key, string& value, time_t modifiedSince) +bool MemoryStorageService::readString(const char* context, const char* key, string& value, time_t modifiedSince) { - if (!isValid(handle)) - throw IOException("Invalid storage handle."); - MemoryHandle* h = static_cast(handle); + Context& ctx = getContext(context); - SharedLock wrapper(h->m_lock); - map::iterator i=h->m_dataMap.find(key); - if (i==h->m_dataMap.end()) + SharedLock wrapper(ctx.m_lock); + map::iterator i=ctx.m_dataMap.find(key); + if (i==ctx.m_dataMap.end()) return false; else if (modifiedSince >= i->second.modified) return false; @@ -260,18 +242,16 @@ bool MemoryStorageService::readString(StorageHandle* handle, const char* key, st return true; } -bool MemoryStorageService::updateString(StorageHandle* handle, const char* key, const char* value, time_t expiration) +bool MemoryStorageService::updateString(const char* context, const char* key, const char* value, time_t expiration) { - if (!isValid(handle)) - throw IOException("Invalid storage handle."); - MemoryHandle* h = static_cast(handle); + Context& ctx = getContext(context); // Lock the maps. - h->m_lock->wrlock(); - SharedLock wrapper(h->m_lock, false); + ctx.m_lock->wrlock(); + SharedLock wrapper(ctx.m_lock, false); - map::iterator i=h->m_dataMap.find(key); - if (i==h->m_dataMap.end()) + map::iterator i=ctx.m_dataMap.find(key); + if (i==ctx.m_dataMap.end()) return false; if (value) @@ -280,15 +260,15 @@ bool MemoryStorageService::updateString(StorageHandle* handle, const char* key, if (expiration && expiration != i->second.expiration) { // Update secondary map. pair::iterator,multimap::iterator> range = - h->m_expMap.equal_range(i->second.expiration); + ctx.m_expMap.equal_range(i->second.expiration); for (; range.first != range.second; ++range.first) { if (range.first->second == i->first) { - h->m_expMap.erase(range.first); + ctx.m_expMap.erase(range.first); break; } } i->second.expiration = expiration; - h->m_expMap.insert(multimap::value_type(expiration,key)); + ctx.m_expMap.insert(multimap::value_type(expiration,key)); } i->second.modified = time(NULL); @@ -296,30 +276,28 @@ bool MemoryStorageService::updateString(StorageHandle* handle, const char* key, return true; } -bool MemoryStorageService::deleteString(StorageHandle* handle, const char* key) +bool MemoryStorageService::deleteString(const char* context, const char* key) { - if (!isValid(handle)) - throw IOException("Invalid storage handle."); - MemoryHandle* h = static_cast(handle); + Context& ctx = getContext(context); // Lock the maps. - h->m_lock->wrlock(); - SharedLock wrapper(h->m_lock, false); + ctx.m_lock->wrlock(); + SharedLock wrapper(ctx.m_lock, false); // Find the record. - map::iterator i=h->m_dataMap.find(key); - if (i!=h->m_dataMap.end()) { + map::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::iterator,multimap::iterator> range = - h->m_expMap.equal_range(i->second.expiration); + ctx.m_expMap.equal_range(i->second.expiration); for (; range.first != range.second; ++range.first) { if (range.first->second == i->first) { - h->m_expMap.erase(range.first); + ctx.m_expMap.erase(range.first); break; } } // And finally delete the record itself. - h->m_dataMap.erase(i); + ctx.m_dataMap.erase(i); m_log.debug("deleted record (%s)", key); return true; } diff --git a/xmltooling/util/StorageService.h b/xmltooling/util/StorageService.h index 7a2a707..fe6318f 100644 --- a/xmltooling/util/StorageService.h +++ b/xmltooling/util/StorageService.h @@ -17,7 +17,7 @@ /** * @file xmltooling/util/StorageService.h * - * Generic data storage facility for use by services that require persistence. + * Generic data storage interface */ #ifndef __xmltooling_storage_h__ @@ -30,7 +30,14 @@ namespace xmltooling { /** - * Generic data storage facility for use by services that require persistence. + * Generic data storage facility for use by services that require + * some degree of persistence. Implementations will vary in how much + * persistence they can supply. + * + *

Storage is divided into "contexts" identified by a string label. + * Keys need to be unique only within a given context, so multiple + * components can share a single storage service safely as long as they + * use different labels. */ class XMLTOOL_API StorageService { @@ -39,47 +46,21 @@ namespace xmltooling { virtual ~StorageService() {} /** - * A "context" for accessing a StorageService instance. - * Handles are created and freed using the StorageService interface, - * and can be kept for the life of the service, and shared by multiple - * threads. - */ - class XMLTOOL_API StorageHandle { - MAKE_NONCOPYABLE(StorageHandle); - friend class XMLTOOL_API StorageService; - public: - virtual ~StorageHandle() {} - protected: - StorageHandle(StorageService* storage) : m_storage(storage) {} - StorageService* m_storage; - }; - - - /** - * Returns a new handle for the storage service. - * The caller MUST delete the handle - * before freeing the StorageService itself. - * - * @return a new handle - */ - virtual StorageHandle* createHandle()=0; - - /** * Creates a new "short" record in the storage service. * - * @param handle a valid storage handle + * @param context a storage context label * @param key null-terminated unique key of up to 255 bytes * @param value null-terminated value of up to 255 bytes to store * @param expiration an expiration timestamp, after which the record can be purged * * @throws IOException raised if errors occur in the insertion process */ - virtual void createString(StorageHandle* handle, const char* key, const char* value, time_t expiration)=0; + virtual void createString(const char* context, const char* key, const char* value, time_t expiration)=0; /** * Returns an existing "short" record from the storage service. * - * @param handle a valid storage handle + * @param context a storage context label * @param key null-terminated unique key of up to 255 bytes * @param value location in which to return the record value * @param modifiedSince the record should not be returned if unmodified since this time, @@ -88,12 +69,12 @@ namespace xmltooling { * * @throws IOException raised if errors occur in the read process */ - virtual bool readString(StorageHandle* handle, const char* key, std::string& value, time_t modifiedSince=0)=0; + virtual bool readString(const char* context, const char* key, std::string& value, time_t modifiedSince=0)=0; /** * Updates an existing "short" record in the storage service. * - * @param handle a valid storage handle + * @param context a storage context label * @param key null-terminated unique key of up to 255 bytes * @param value null-terminated value of up to 255 bytes to store, or NULL to leave alone * @param expiration a new expiration timestamp, or 0 to leave alone @@ -101,35 +82,35 @@ namespace xmltooling { * * @throws IOException raised if errors occur in the update process */ - virtual bool updateString(StorageHandle* handle, const char* key, const char* value=NULL, time_t expiration=0)=0; + virtual bool updateString(const char* context, const char* key, const char* value=NULL, time_t expiration=0)=0; /** * Deletes an existing "short" record from the storage service. * - * @param handle a valid storage handle + * @param context a storage context label * @param key null-terminated unique key of up to 255 bytes * @return true iff the record existed and was deleted * * @throws IOException raised if errors occur in the deletion process */ - virtual bool deleteString(StorageHandle* handle, const char* key)=0; + virtual bool deleteString(const char* context, const char* key)=0; /** * Creates a new "long" record in the storage service. * - * @param handle a valid storage handle + * @param context a storage context label * @param key null-terminated unique key of up to 255 bytes * @param value null-terminated value of arbitrary length * @param expiration an expiration timestamp, after which the record can be purged * * @throws IOException raised if errors occur in the insertion process */ - virtual void createText(StorageHandle* handle, const char* key, const char* value, time_t expiration)=0; + virtual void createText(const char* context, const char* key, const char* value, time_t expiration)=0; /** * Returns an existing "long" record from the storage service. * - * @param handle a valid storage handle + * @param context a storage context label * @param key null-terminated unique key of up to 255 bytes * @param value location in which to return the record value * @param modifiedSince the record should not be returned if unmodified since this time, @@ -138,12 +119,12 @@ namespace xmltooling { * * @throws IOException raised if errors occur in the read process */ - virtual bool readText(StorageHandle* handle, const char* key, std::string& value, time_t modifiedSince=0)=0; + virtual bool readText(const char* context, const char* key, std::string& value, time_t modifiedSince=0)=0; /** * Updates an existing "long" record in the storage service. * - * @param handle a valid storage handle + * @param context a storage context label * @param key null-terminated unique key of up to 255 bytes * @param value null-terminated value of arbitrary length to store, or NULL to leave alone * @param expiration a new expiration timestamp, or 0 to leave alone @@ -151,34 +132,38 @@ namespace xmltooling { * * @throws IOException raised if errors occur in the update process */ - virtual bool updateText(StorageHandle* handle, const char* key, const char* value=NULL, time_t expiration=0)=0; + virtual bool updateText(const char* context, const char* key, const char* value=NULL, time_t expiration=0)=0; /** * Deletes an existing "long" record from the storage service. * - * @param handle a valid storage handle + * @param context a storage context label * @param key null-terminated unique key of up to 255 bytes * @return true iff the record existed and was deleted * * @throws IOException raised if errors occur in the deletion process */ - virtual bool deleteText(StorageHandle* handle, const char* key)=0; + virtual bool deleteText(const char* context, const char* key)=0; /** * Manually trigger a cleanup of expired records. * The method MAY return without guaranteeing that * cleanup has already occurred. * - * @param handle a valid storage handle + * @param context a storage context label + */ + virtual void reap(const char* context)=0; + + /** + * Forcibly removes all records in a given context along with any + * associated resources devoted to maintaining the context. + * + * @param context a storage context label */ - virtual void reap(StorageHandle* handle)=0; + virtual void deleteContext(const char* context)=0; protected: StorageService() {} - - virtual bool isValid(StorageHandle* handle) { - return this == (handle ? handle->m_storage : NULL); - } }; /** diff --git a/xmltoolingtest/MemoryStorageServiceTest.h b/xmltoolingtest/MemoryStorageServiceTest.h index 99e2e0e..a673e9a 100644 --- a/xmltoolingtest/MemoryStorageServiceTest.h +++ b/xmltoolingtest/MemoryStorageServiceTest.h @@ -30,19 +30,17 @@ public: auto_ptr storage( XMLToolingConfig::getConfig().StorageServiceManager.newPlugin(MEMORY_STORAGE_SERVICE,NULL) ); - auto_ptr handle(storage->createHandle()); string data; - TSM_ASSERT("Record found in storage.", !storage->readString(handle.get(), "foo1", data)); - storage->createString(handle.get(), "foo1", "bar1", time(NULL) - 300); - storage->createString(handle.get(), "foo2", "bar2", time(NULL)); - TSM_ASSERT("Record not found in storage.", storage->readString(handle.get(), "foo1", data)); + TSM_ASSERT("Record found in storage.", !storage->readString("context", "foo1", data)); + storage->createString("context", "foo1", "bar1", time(NULL) - 300); + storage->createString("context", "foo2", "bar2", time(NULL)); + TSM_ASSERT("Record not found in storage.", storage->readString("context", "foo1", data)); TSM_ASSERT_EQUALS("Record value doesn't match.", data, "bar1"); - TSM_ASSERT("Update failed.", storage->updateString(handle.get(), "foo2", "bar1")); - TSM_ASSERT("Record not found in storage.", storage->readString(handle.get(), "foo2", data)); + TSM_ASSERT("Update failed.", storage->updateString("context", "foo2", "bar1")); + TSM_ASSERT("Record not found in storage.", storage->readString("context", "foo2", data)); TSM_ASSERT_EQUALS("Record value doesn't match.", data, "bar1"); - TSM_ASSERT("Delete failed.", storage->deleteString(handle.get(), "foo2")); - storage->reap(handle.get()); - Thread::sleep(1); + TSM_ASSERT("Delete failed.", storage->deleteString("context", "foo2")); + storage->reap("context"); } }; -- 2.1.4