You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
5.7 KiB
266 lines
5.7 KiB
#ifndef _GENCACHE_H_
|
|
#define _GENCACHE_H_
|
|
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// GENCACHE.H
|
|
//
|
|
// Header for generic cache classes.
|
|
//
|
|
// Copyright 1997 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
|
|
#ifdef _DAVCDATA_
|
|
#error "buffer.h uses throwing allocators"
|
|
#endif
|
|
|
|
// Include the non-exdav-safe/throwing allocators
|
|
#include <mem.h>
|
|
#include <autoptr.h>
|
|
#include <synchro.h>
|
|
|
|
// Include exdav-safe CCache definition header
|
|
#include <ex\gencache.h>
|
|
|
|
// ========================================================================
|
|
//
|
|
// TEMPLATE CLASS CMTCache
|
|
//
|
|
// Multithread-safe generic cache.
|
|
//
|
|
template<class _K, class _Ty>
|
|
class CMTCache
|
|
{
|
|
typedef CCache<_K, _Ty> CBaseCache;
|
|
|
|
//
|
|
// The cache
|
|
//
|
|
CBaseCache m_cache;
|
|
|
|
//
|
|
// Multi-reader/single-writer lock to protect the cache
|
|
//
|
|
mutable CMRWLock m_mrw;
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CMTCache& operator=( const CMTCache& );
|
|
CMTCache( const CMTCache& );
|
|
|
|
public:
|
|
typedef typename CBaseCache::IOp IOp;
|
|
|
|
// CREATORS
|
|
//
|
|
CMTCache()
|
|
{
|
|
if ( !m_mrw.FInitialize() )
|
|
throw CLastErrorException();
|
|
// If this fails, our allocators will throw for us.
|
|
m_cache.FInit();
|
|
}
|
|
|
|
// ACCESSORS
|
|
//
|
|
BOOL FFetch( const _K& key, _Ty * pValueRet ) const
|
|
{
|
|
CSynchronizedReadBlock blk(m_mrw);
|
|
|
|
return m_cache.FFetch(key, pValueRet);
|
|
}
|
|
|
|
void ForEach( IOp& op ) const
|
|
{
|
|
CSynchronizedReadBlock blk(m_mrw);
|
|
|
|
m_cache.ForEach(op);
|
|
}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
void Set( const _K& key, const _Ty& value )
|
|
{
|
|
CSynchronizedWriteBlock blk(m_mrw);
|
|
|
|
// If this fails, our allocators will throw for us.
|
|
(void)m_cache.FSet(key, value);
|
|
}
|
|
|
|
void Add( const _K& key, const _Ty& value )
|
|
{
|
|
CSynchronizedWriteBlock blk(m_mrw);
|
|
|
|
// If this fails, our allocators will throw for us.
|
|
(void)m_cache.FAdd(key, value);
|
|
}
|
|
|
|
void Remove( const _K& key )
|
|
{
|
|
CSynchronizedWriteBlock blk(m_mrw);
|
|
|
|
m_cache.Remove(key);
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
CSynchronizedWriteBlock blk(m_mrw);
|
|
|
|
m_cache.Clear();
|
|
}
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS CAccInv
|
|
//
|
|
// Access/Invalidate synchronization logic.
|
|
// This class encapsulates the logic needed to safely read
|
|
// (access) from a datasource that may be invalidated (invalidate)
|
|
// by an asynchronous, external event. (IN-ternal events
|
|
// should ALWAYS use the synch mechanisms we provide DIRECTLY.)
|
|
//
|
|
class IEcb;
|
|
class CAccInv
|
|
{
|
|
//
|
|
// Multi-reader/single-writer lock to synchronize
|
|
// access and invalidation functions
|
|
//
|
|
CMRWLock m_mrw;
|
|
|
|
//
|
|
// Flag to indicate whether the object is invalid.
|
|
// If 0, the object is invalid and and will
|
|
// be refreshed the next time it is accessed.
|
|
//
|
|
LONG m_lValid;
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CAccInv& operator=( const CAccInv& );
|
|
CAccInv( const CAccInv& );
|
|
|
|
public:
|
|
|
|
// Forward declaration
|
|
//
|
|
class IAccCtx;
|
|
|
|
protected:
|
|
//
|
|
// Refresh operation to be provided by derived class
|
|
//
|
|
virtual void RefreshOp( const IEcb& ecb ) = 0;
|
|
|
|
void Access( const IEcb& ecb, IAccCtx& context )
|
|
{
|
|
//
|
|
// Repeat the following validity check, refresh, and
|
|
// access, and recheck sequence until the access succeeds
|
|
// and the object is valid from start to finish.
|
|
//
|
|
for (;;)
|
|
{
|
|
//
|
|
// Check validity, and refresh if invalid.
|
|
//
|
|
while ( !m_lValid )
|
|
{
|
|
CTryWriteBlock blk(m_mrw);
|
|
|
|
//
|
|
// Only one thread should refresh the object.
|
|
// Other threads detecting that the object is invalid
|
|
// periodically retry checking validity (spin waiting)
|
|
// until the object becomes valid.
|
|
//
|
|
if ( blk.FTryEnter() )
|
|
{
|
|
//
|
|
// By being the first to enter the write lock,
|
|
// this thread gets to refresh the object.
|
|
//
|
|
|
|
//
|
|
// Mark the object as valid BEFORE actually
|
|
// refreshing it so that it is possible to
|
|
// tell if the object gets marked invalid by
|
|
// another thread while it is being refreshed.
|
|
//
|
|
InterlockedExchange( &m_lValid, 1 );
|
|
|
|
//
|
|
// Refresh the object
|
|
//
|
|
RefreshOp(ecb);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Give up the rest of this thread's time slice so
|
|
// that the thread holding the write lock may finish
|
|
// as soon as possible.
|
|
//
|
|
Sleep(0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The object is valid (or at least it was a tiny instant
|
|
// ago) so go ahead and access it. Apply a read lock
|
|
// to prevent other threads from refreshing it during
|
|
// access (if the object is marked invalid during access).
|
|
//
|
|
{
|
|
CSynchronizedReadBlock blk(m_mrw);
|
|
|
|
context.AccessOp( *this );
|
|
|
|
//
|
|
// Test whether the object is still valid after access.
|
|
// (Do this while holding the read lock to prevent other
|
|
// threads from marking the object as invalid and refreshing
|
|
// it since it was accessed on this thread.) If the
|
|
// object is still valid now, then it was valid for
|
|
// the entire operation, so we're done.
|
|
//
|
|
if ( m_lValid )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
class IAccCtx
|
|
{
|
|
|
|
public:
|
|
|
|
//
|
|
// Method on the cache context to perform the access operation.
|
|
// This allows for caches to support multiple access methods for
|
|
// both ::Lookup() and ::ForEach() mechanisms
|
|
//
|
|
virtual void AccessOp( CAccInv& cache ) = 0;
|
|
};
|
|
|
|
// The object is initially considered invalid. It will be refreshed
|
|
// the first time it is accessed.
|
|
//
|
|
CAccInv() :
|
|
m_lValid(0)
|
|
{
|
|
if ( !m_mrw.FInitialize() )
|
|
throw CLastErrorException();
|
|
}
|
|
|
|
void Invalidate()
|
|
{
|
|
InterlockedExchange( &m_lValid, 0 );
|
|
}
|
|
};
|
|
|
|
#endif // !_GENCACHE_H_
|