//%LICENSE//////////////////////////////////////////////////////////////// // // Licensed to The Open Group (TOG) under one or more contributor license // agreements. Refer to the OpenPegasusNOTICE.txt file distributed with // this work for additional information regarding copyright ownership. // Each contributor licenses this file to you under the OpenPegasus Open // Source License; you may not use this file except in compliance with the // License. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ////////////////////////////////////////////////////////////////////////// // //%///////////////////////////////////////////////////////////////////////////// #include #include PEGASUS_NAMESPACE_BEGIN PEGASUS_USING_STD; SCMOClassCache* SCMOClassCache::_theInstance = 0; void SCMOClassCache::destroy() { delete _theInstance; _theInstance=0; } SCMOClassCache* SCMOClassCache::getInstance() { if(_theInstance == NULL) { _theInstance = new SCMOClassCache(); } return _theInstance; } #ifdef PEGASUS_USE_SCMO_CLASS_CACHE SCMOClassCache::~SCMOClassCache() { // Signal to all callers and work in progress that the SMOClassCache // will be destroyed soon. // As from now, no other caller can get the the lock. They are blocked out. _dying = true; // Cleanup the class cache for (Uint32 i = 0 ; i < PEGASUS_SCMO_CLASS_CACHE_SIZE; i++) { delete _theCache[i].data; } } Uint64 SCMOClassCache::_generateKey( const char* className, Uint32 classNameLen, const char* nameSpaceName, Uint32 nameSpaceNameLen) { Uint64 key = 0; key = (Uint64(classNameLen) << 48 ) | (Uint64(className[0]) << 40 ) | (Uint64(className[classNameLen-1]) << 32 ) | (Uint64(nameSpaceNameLen) << 16 ) | (Uint64(nameSpaceName[0]) << 8 ) | Uint64(nameSpaceName[nameSpaceNameLen-1]); /* fprintf(stderr,"Class Name(%s) \'%04X%02X%02X\' " "Name Space(%s) \'%04X%02X%02X\' " "Key = %016llX\n", className,classNameLen,className[0],className[classNameLen-1], nameSpaceName,nameSpaceNameLen,nameSpaceName[0], nameSpaceName[nameSpaceNameLen-1],key); */ return key; } inline Boolean SCMOClassCache::_lockEntry(Uint32 index) { // The lock is implemented as a spin loop, since the action to // verify for the right cache entry is very short. if ( _dying ) { // The cache is going to be destroyed. // The caller will never get the lock. return false; } while ( true ) { if ( _dying ) { // The cache is going to be destroyed. // The caller will never get the lock. return false; } // If the lock counter not 1,an other caller is reading the entry. if ( _theCache[index].lock.get() == 1 ) { // Decrement the atomic lock counter and test if we do have lock: // _theCache[index].lock == 0 if ( _theCache[index].lock.decAndTestIfZero() ) { // We do have lock! return true; } } // I did not get the lock. So signal the scheduer to change the active // thread to allow other threads to proceed. This also prevents from // looping in a tight loop that causes a dead look due to the // lock obtaining thread does not get any time ot finsh his work. #ifdef PEGASUS_DEBUG _contentionCount.inc(); #endif Threads::yield(); } return false; } inline Boolean SCMOClassCache::_sameSCMOClass( const char* nsName, Uint32 nsNameLen, const char* className, Uint32 classNameLen, SCMOClass* theClass) { if (_equalNoCaseUTF8Strings( theClass->cls.hdr->className, theClass->cls.base, className, classNameLen)) { return _equalNoCaseUTF8Strings( theClass->cls.hdr->nameSpace, theClass->cls.base, nsName, nsNameLen); } return false; } SCMOClass SCMOClassCache::_addClassToCache( const char* nsName, Uint32 nsNameLen, const char* className, Uint32 classNameLen, Uint64 theKey) { WriteLock modifyLock(_modifyCacheLock); if ( _dying ) { // The cache is going to be destroyed. return SCMOClass(); } Uint32 startIndex =_lastSuccessIndex % PEGASUS_SCMO_CLASS_CACHE_SIZE; Uint32 nextIndex = startIndex; // The number of used entries is form 0 to PEGASUS_SCMO_CLASS_CACHE_SIZE Uint32 usedEntries = _fillingLevel % (PEGASUS_SCMO_CLASS_CACHE_SIZE + 1); // This constallation would cause an infitloop below. // A miss read of global variables has happen if (nextIndex > usedEntries) { // start from the beginning startIndex = 0; nextIndex = 0; } // Check the cache if the class was already added while waiting for the // modifyLock. // // Note: The lock for each cache entry must not be obtained, // because it is used to signal a modify operation, that some // body is reading it. Due to we are the modify task, we know // that we are reading the cache entries! // for (Uint32 i = 0; i < usedEntries; i++) { // Does the key match for the entry and the requested class ? if (0 != _theCache[nextIndex].key && theKey == _theCache[nextIndex].key) { // To get sure we found the right class, compare name space // and class name. if (_sameSCMOClass(nsName,nsNameLen,className,classNameLen, _theCache[nextIndex].data)) { // The entry was added while waiting for the modify lock. // The modify lock is destroyed automaticaly ! #ifdef PEGASUS_DEBUG _cacheReadHit++; #endif _lastSuccessIndex = nextIndex; return SCMOClass(*_theCache[nextIndex].data); } } // go to the next used entries. nextIndex = (nextIndex + 1) % usedEntries; } PEGASUS_ASSERT(_resolveCallBack); #ifdef PEGASUS_DEBUG _cacheReadMiss++; #endif SCMOClass tmp = _resolveCallBack( CIMNamespaceNameCast(String(nsName,nsNameLen)), CIMNameCast(String(className,classNameLen))); if (tmp.isEmpty()) { // The requested class was not found ! // The modify lock is destroyed automaticaly ! return SCMOClass(); } SCMOClass* scmoClass = new SCMOClass(tmp); _lastWrittenIndex = (_lastWrittenIndex + 1)%PEGASUS_SCMO_CLASS_CACHE_SIZE; // Ensure that nobody is reading the enty, so I can write. if (_lockEntry(_lastWrittenIndex)) { _theCache[_lastWrittenIndex].key = theKey; // If the entry was reused, release old object form the cache. if (0 != _theCache[_lastWrittenIndex].data ) { #ifdef PEGASUS_DEBUG _cacheRemoveLRU++; #endif delete _theCache[_lastWrittenIndex].data; } _theCache[_lastWrittenIndex].data = scmoClass; if (_fillingLevel < PEGASUS_SCMO_CLASS_CACHE_SIZE) { _fillingLevel ++; } _lastSuccessIndex = _lastWrittenIndex; _unlockEntry(_lastWrittenIndex); } else { // The cache is going to be destroyed. // The lock can not be obtained. delete scmoClass; return SCMOClass(); } // The modify lock is destroyed automaticaly ! return SCMOClass(*scmoClass); } SCMOClass SCMOClassCache::getSCMOClass( const char* nsName, Uint32 nsNameLen, const char* className, Uint32 classNameLen) { Uint64 theKey; // Due to the _lastSuccessIndex may contain an invalid value, // use the modulo to ensure it the index is in a valid range. Uint32 startIndex =_lastSuccessIndex % PEGASUS_SCMO_CLASS_CACHE_SIZE; Uint32 nextIndex = startIndex; // The number of used entries form 0 to PEGASUS_SCMO_CLASS_CACHE_SIZE Uint32 usedEntries = _fillingLevel % (PEGASUS_SCMO_CLASS_CACHE_SIZE + 1); // This constallation would cause an infitloop below. // A miss read of global variables has happen if (nextIndex > usedEntries) { // start from the beginning startIndex = 0; nextIndex = 0; } if (nsName && className && nsNameLen && classNameLen) { theKey = _generateKey(className,classNameLen,nsName,nsNameLen); // The counter i is never used as index. // It is used to ensure to look through all used entries. for (Uint32 i = 0; i < usedEntries; i++) { if(_lockEntry(nextIndex)) { // does the key match for the entry and the requested class ? if ( 0 != _theCache[nextIndex].key && theKey == _theCache[nextIndex].key) { // To get sure we found the right class, compare name space // and class name. if (_sameSCMOClass(nsName,nsNameLen,className,classNameLen, _theCache[nextIndex].data)) { // Yes, we got it ! SCMOClass theClass(*_theCache[nextIndex].data); _lastSuccessIndex = nextIndex; #ifdef PEGASUS_DEBUG _cacheReadHit++; #endif _unlockEntry(nextIndex); return theClass; } } // It was the wrong entry, go to the next. _unlockEntry(nextIndex); } else { // The cache is going to be destroyed. // The lock can not be obtained. Give up. return SCMOClass(); } // It was the wrong entry, go to the next used entry.. nextIndex = (nextIndex + 1) % usedEntries; } // If we end up here, the class is not in the cache ! // We have to get it from the repositroy and add it into the cache. return _addClassToCache(nsName,nsNameLen,className,classNameLen,theKey); } return SCMOClass(); } void SCMOClassCache::removeSCMOClass( CIMNamespaceName cimNameSpace, CIMName cimClassName) { if (cimClassName.isNull() || cimNameSpace.isNull()) { return ; } CString nsName = cimNameSpace.getString().getCString(); Uint32 nsNameLen = strlen(nsName); CString clsName = cimClassName.getString().getCString(); Uint32 clsNameLen = strlen(clsName); // The number of used entries form 0 to PEGASUS_SCMO_CLASS_CACHE_SIZE Uint32 usedEntries = _fillingLevel % (PEGASUS_SCMO_CLASS_CACHE_SIZE + 1); Uint64 theKey = _generateKey(clsName,clsNameLen,nsName,nsNameLen); // A straight forward loop through all used entries, // ignoring the last success. for (Uint32 i = 0; i < usedEntries; i++) { if(_lockEntry(i)) { // does the key match for the entry and the requested class ? if ( 0 != _theCache[i].key && theKey == _theCache[i].key) { // To get sure we found the right class, compare name space // and class name. if (_sameSCMOClass(nsName,nsNameLen,clsName,clsNameLen, _theCache[i].data)) { // Yes, we got it ! _theCache[i].key = 0; delete _theCache[i].data; _theCache[i].data = 0; _unlockEntry(i); return; } } // It was the wrong entry, go to the next. _unlockEntry(i); } else { // We do not get the lock, if the cache is going to be destroyed. return; } } } void SCMOClassCache::clear() { WriteLock modifyLock(_modifyCacheLock); if ( _dying ) { // The cache is going to be destroyed. return ; } // The number of used entries form 0 to PEGASUS_SCMO_CLASS_CACHE_SIZE Uint32 usedEntries = _fillingLevel % (PEGASUS_SCMO_CLASS_CACHE_SIZE + 1); // A straight forwar loop through all used entries, // ignoring the last success. for (Uint32 i = 0; i < usedEntries; i++) { if(_lockEntry(i)) { _theCache[i].key = 0; delete _theCache[i].data; _theCache[i].data = 0; _unlockEntry(i); } else { // We do not get the lock, if the cache is going to be destroyed. return; } } // Reset all controll data _fillingLevel = 0; _lastSuccessIndex = 0; _lastWrittenIndex = PEGASUS_SCMO_CLASS_CACHE_SIZE-1; } #ifdef PEGASUS_DEBUG void SCMOClassCache::DisplayCacheStatistics() { PEGASUS_STD(cout) << "SCMOClass Cache Statistics:" << PEGASUS_STD(endl); PEGASUS_STD(cout) << " Size (current/max): " << _fillingLevel << "/" << PEGASUS_SCMO_CLASS_CACHE_SIZE << PEGASUS_STD(endl); PEGASUS_STD(cout) << " Requests satisfied from cache: " << _cacheReadHit << PEGASUS_STD(endl); PEGASUS_STD(cout) << " Requests *not* satisfied from cache: " << _cacheReadMiss << " (implies write to cache)" << PEGASUS_STD(endl); PEGASUS_STD(cout) << " Cache entries \"aged out\" due to cache size constraints: " << _cacheRemoveLRU << PEGASUS_STD(endl); PEGASUS_STD(cout) << " Number of lock contentions: " << _contentionCount.get() << PEGASUS_STD(endl); } #endif #else // PEGASUS_USE_SCMO_CLASS_CACHE SCMOClass SCMOClassCache::getSCMOClass( const char* nsName, Uint32 nsNameLen, const char* className, Uint32 classNameLen) { if (nsName && className && nsNameLen && classNameLen) { PEGASUS_ASSERT(_resolveCallBack); SCMOClass tmp = _resolveCallBack( CIMNamespaceNameCast(String(nsName,nsNameLen)), CIMNameCast(String(className,classNameLen))); if (tmp->isEpmpty()) { // The requested class was not found ! // The modify lock is destroyed automaticaly ! return SCMOClass(); } return SCMOClass(tmp); } return SCMOClass(); } void SCMOClassCache::removeSCMOClass( CIMNamespaceName cimNameSpace, CIMName cimClassName) { } void SCMOClassCache::clear() { } # ifdef PEGASUS_DEBUG void SCMOClassCache::DisplayCacheStatistics(){} # endif #endif PEGASUS_NAMESPACE_END