/*################################################################################### # # Embperl - Copyright (c) 1997-2008 Gerald Richter / ecos gmbh www.ecos.de # Embperl - Copyright (c) 2008-2015 Gerald Richter # Embperl - Copyright (c) 2015-2023 actevy.io # # You may distribute under the terms of either the GNU General Public # License or the Artistic License, as specified in the Perl README file. # # THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED # WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. # ###################################################################################*/ #include "ep.h" #include "epmacro.h" /* --- don't use Perl's memory management here --- */ #ifndef DMALLOC #undef malloc #undef realloc #undef strdup #undef free #endif HV * pProviders ; /**< global hash that holds all known providers classes */ HV * pCacheItems ; /**< hash which contains all CacheItems by Key */ tCacheItem * * pCachesToRelease = NULL ; /* ------------------------------------------------------------------------ */ /* */ /* Cache_AddProviderClass */ /* */ /*! * \_en * Add a provider class to list of known providers * @param sName Name of the Providerclass * @param pProviderClass Provider class record * @return error code * * \endif * * \_de * Fügt eine Providerklasse den der Liste der bekannten Providern hinzu * @param sName Name der Providerklasse * @param pProviderClass Provider class record * @return Fehlercode * * \endif * * ------------------------------------------------------------------------ */ int Cache_AddProviderClass (/*in*/ const char * sName, /*in*/ tProviderClass * pClass) { SetHashValueInt (NULL, pProviders, sName, (IV)pClass) ; return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_Init */ /* */ /*! * \_en * Do global initialization of cache system * @return error code * * \endif * * \_de * Führt die globale Initialisierung des Cachesystems durch * @return Fehlercode * * \endif * * ------------------------------------------------------------------------ */ int Cache_Init (/*in*/ tApp * a) { epaTHX_ pProviders = newHV () ; pCacheItems = newHV () ; ArrayNew (a, &pCachesToRelease, 16, sizeof (tCacheItem *)) ; /* lprintf (a, "XXXXX Cache_Init [%d/%d] pProviders=%x pCacheItems=%x pCachesToRelease=%x", _getpid(), GetCurrentThreadId(), pProviders, pCacheItems, pCachesToRelease) ; */ return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_CleanupRequest */ /* */ /*! * \_en * Do cleanup at end of request * @param r Embperl request record * @return error code * * \endif * * \_de * Führt die Aufräumarbeiten am Requestende aus * @param r Embperl request record * @return Fehlercode * * \endif * * ------------------------------------------------------------------------ */ int Cache_CleanupRequest (req * r) { if (pCachesToRelease) { int n = ArrayGetSize (r -> pApp, pCachesToRelease) ; int i ; /* lprintf (r -> pApp, "XXXXX Cache_CleanupRequest [%d/%d] pProviders=%x pCacheItems=%x pCachesToRelease=%x", _getpid(), GetCurrentThreadId(), pProviders, pCacheItems, pCachesToRelease) ; */ for (i = 0; i < n; i++) Cache_FreeContent (r, pCachesToRelease[i]) ; ArraySetSize(r -> pApp, &pCachesToRelease, 0) ; } return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_ParamUpdate */ /* */ int Cache_ParamUpdate (/*in*/ req * r, /*in*/ HV * pProviderParam, /*in*/ bool bTopLevel, /*in*/ char * sLogText, /*in*/ tCacheItem * pNew) { epTHX_ char * exfn ; int rc ; pNew -> nExpiresInTime = GetHashValueInt (aTHX_ pProviderParam, "expires_in", bTopLevel?r -> Component.Config.nExpiresIn:0) ; if (pNew -> pExpiresCV) SvREFCNT_dec (pNew -> pExpiresCV) ; if ((rc = GetHashValueCREF (r, pProviderParam, "expires_func", &pNew -> pExpiresCV)) != ok) return rc ; if (!pNew -> pExpiresCV && bTopLevel) pNew -> pExpiresCV = (CV *)SvREFCNT_inc((SV *)r -> Component.Config.pExpiredFunc) ; exfn = GetHashValueStrDupA (aTHX_ pProviderParam, "expires_filename", bTopLevel?r -> Component.Config.sExpiresFilename:NULL) ; if (pNew -> sExpiresFilename) { if (exfn) { /* lprintf (r -> pApp, "exfn=%s\n", exfn) ; */ free ((void *)pNew -> sExpiresFilename) ; pNew -> sExpiresFilename = exfn ; } } else pNew -> sExpiresFilename = exfn ; pNew -> bCache = (bool)GetHashValueInt (aTHX_ pProviderParam, "cache", exfn || pNew -> pExpiresCV || pNew -> nExpiresInTime?1:0) ; if (sLogText && (r -> Component.Config.bDebug & dbgCache)) lprintf (r -> pApp, "[%d]CACHE: %s CacheItem %s; expires_in=%d expires_func=%s expires_filename=%s cache=%s\n", r -> pThread -> nPid, sLogText, pNew -> sKey, pNew -> nExpiresInTime, pNew -> pExpiresCV?"yes":"no", pNew -> sExpiresFilename?pNew -> sExpiresFilename:"", pNew -> bCache?"yes":"no") ; return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_New */ /* */ /*! * \_en * Checks if a CacheItem which matches the parameters already exists, if * not it creates a new CacheItem and fills it with data from the hash * pParam * * @param r Embperl request record * @param pParam Parameter (PV,HV,AV) * expires_in number of seconds when the item * expires, 0 = expires never * expires_func Perl Function (coderef) that * is called and item is expired * if it returns TRUE * expires_filename item expires when modification * time of file changes * cache set to zero to not cache the content * provider parameter for the provider * @param nParamNdx If pParam is a AV, this parameter gives the index into the Array * @param bTopLevel True if last elemet before output. In this case the cache parameters * defaults to the ones from Componet.Config * @param pItem Return of the new Items * @return error code * \endif * * \_de * Prüft ob ein passendes CacheItem bereits vorhanden ist, wenn nicht * erzeugt die Funktion ein neues CacheItem und füllte es mit den Daten aus * pParam * * @param r Embperl request record * @param pParam Parameter (PV,HV,AV) * expires_in anzahl der Sekunden wenn Item * abläuft; 0 = nie * expires_func Perl Funktion (coderef) die * aufgerufen wird. Wenn sie wahr * zurückgibt ist das Item abgelaufen * expires_filename Item ist abgelaufen wenn * Dateidatum sich ändert * cache Auf Null setzen damit Inhalt * nicht gecacht wird * provider parameter für Provider * @param nParamNdx Wenn pParam ein AV ist, gibt dieser Parameter den Index an * @param bTopLevel Wahr wenn letztes Element vor der Ausgabe, dann werden * die Cache Parameter aus Componet.Config herangezogen * @param pItem Rückgabe des neuen Items * @return Fehlercode * \endif * * ------------------------------------------------------------------------ */ int Cache_New (/*in*/ req * r, /*in*/ SV * pParam, /*in*/ IV nParamNdx, /*in*/ bool bTopLevel, /*in*/ tCacheItem * * pItem) { epTHX_ int rc ; HV * pProviderParam ; char * sProvider ; tProviderClass * pProviderClass ; tCacheItem * pNew = NULL ; SV * pKey = NULL ; const char * sKey = "" ; STRLEN len ; /* lprintf (r -> pApp, "XXXXX Cache_New [%d/%d] pProviders=%x %s pCacheItems=%x %s pCachesToRelease=%x %s\n", _getpid(), GetCurrentThreadId(), pProviders, IsBadReadPtr (pProviders,4 )?"bad":"ok", pCacheItems, IsBadReadPtr (pCacheItems, 4)?"bad":"ok", pCachesToRelease, IsBadReadPtr (pCachesToRelease, 4)?"bad":"ok") ; */ if (SvROK(pParam)) pParam = SvRV (pParam) ; if (SvTYPE(pParam) == SVt_PV) { /* change this to auto match later on ... */ pProviderParam = (HV *)SvRV(sv_2mortal (CreateHashRef (r, "type", hashtstr, "file", "filename", hashtsv, pParam, NULL) )) ; } else if (SvTYPE(pParam) == SVt_PVAV) { SV * * ppRV = av_fetch ((AV *)pParam, nParamNdx, 0) ; if (!ppRV || !*ppRV) { strncpy (r -> errdat1, "<provider missing>", sizeof(r -> errdat1) - 1) ; return rcUnknownProvider ; } if (!SvROK (*ppRV) || SvTYPE(pProviderParam = (HV *)SvRV (*ppRV)) != SVt_PVHV) { strncpy (r -> errdat1, "<provider missing, element is no hashref>", sizeof(r -> errdat1) - 1) ; return rcUnknownProvider ; } } else if (SvTYPE(pParam) == SVt_PVHV) { pProviderParam = (HV *)pParam ; } else { strncpy (r -> errdat1, "<provider missing, no description found>", sizeof(r -> errdat1) - 1) ; return rcUnknownProvider ; } sProvider = GetHashValueStr (aTHX_ pProviderParam, "type", "") ; pProviderClass = (tProviderClass *)GetHashValuePtr (r, pProviders, sProvider, NULL) ; if (!pProviderClass) { if (*sProvider) strncpy (r -> errdat1, sProvider, sizeof(r -> errdat1) - 1) ; else strncpy (r -> errdat1, "<provider missing>", sizeof(r -> errdat1) - 1) ; return rcUnknownProvider ; } pKey = newSVpv ("", 0) ; if (pProviderClass -> fAppendKey) if ((rc = (*pProviderClass -> fAppendKey)(r, pProviderClass, pProviderParam, pParam, nParamNdx - 1, pKey)) != ok) return rc ; sKey = SvPV(pKey, len) ; if ((pNew = Cache_GetByKey (r, sKey))) { Cache_ParamUpdate (r, pProviderParam, bTopLevel, "Update", pNew) ; if (pProviderClass -> fUpdateParam) if ((rc = (*pProviderClass -> fUpdateParam)(r, pNew -> pProvider, pProviderParam)) != ok) return rc ; } if (!pNew) { pNew = cache_malloc (r, sizeof(tCacheItem)) ; if (!pNew) { if (pKey) SvREFCNT_dec (pKey) ; return rcOutOfMemory ; } *pItem = NULL ; memset (pNew, 0, sizeof (tCacheItem)) ; Cache_ParamUpdate (r, pProviderParam, bTopLevel, NULL, pNew) ; pNew -> sKey = strdup (sKey) ; if (pProviderParam) { if ((rc = (*pProviderClass -> fNew)(r, pNew, pProviderClass, pProviderParam, pParam, nParamNdx - 1)) != ok) { if (pKey) SvREFCNT_dec (pKey) ; cache_free (r, pNew) ; return rc ; } if (pProviderClass -> fUpdateParam) if ((rc = (*pProviderClass -> fUpdateParam)(r, pNew -> pProvider, pProviderParam)) != ok) return rc ; } if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: Created new CacheItem %s; expires_in=%d expires_func=%s expires_filename=%s cache=%s\n", r -> pThread -> nPid, sKey, pNew -> nExpiresInTime, pNew -> pExpiresCV?"yes":"no", pNew -> sExpiresFilename?pNew -> sExpiresFilename:"", pNew -> bCache?"yes":"no") ; SetHashValueInt (r, pCacheItems, sKey, (IV)pNew) ; } if (pKey) SvREFCNT_dec (pKey) ; *pItem = pNew ; return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_AppendKey */ /* */ /*! * \_en * Append it's key to the keystring. If it depends on anything it must * call Cache_AppendKey for any dependency. * The file provider appends the filename * * @param r Embperl request record * @param pParam Parameter Hash * @param sSubProvider sub provider parameter * @param pKey Key to which string should be appended * @return error code * \endif * * \_de * Hängt ein eigenen Schlüssel an den Schlüsselstring an. Wenn irgednwelche * Abhänigkeiten bestehen, muß Cache_AppendKey für alle Abhänigkeiten aufgerufen * werden. * Der File Provider hängt den Dateinamen an. * * @param r Embperl request record * @param pParam Parameter Hash * @param sSubProvider sub provider parameter * @param pKey Schlüssel zu welchem hinzugefügt wird * @return Fehlercode * \endif * * ------------------------------------------------------------------------ */ int Cache_AppendKey (/*in*/ req * r, /*in*/ HV * pProviderParam, /*in*/ const char * sSubProvider, /*in*/ SV * pParam, /*in*/ IV nParamIndex, /*i/o*/ SV * pKey) { epTHX_ int rc ; char * sProvider ; tProviderClass * pProviderClass ; STRLEN len ; tCacheItem * pItem ; SV * pSubParam = GetHashValueSV (r, pProviderParam, sSubProvider) ; if (pSubParam) pParam = pSubParam ; if (!pParam) { strncpy (r -> errdat1, sSubProvider, sizeof (r -> errdat1) - 1) ; return rcMissingParam ; } if (SvROK(pParam)) pParam = SvRV (pParam) ; if (SvTYPE(pParam) == SVt_PV) { /* change this to auto match later on ... */ pProviderParam = (HV *)SvRV(sv_2mortal (CreateHashRef (r, "type", hashtstr, "file", "filename", hashtsv, pParam, NULL) )) ; } else if (SvTYPE(pParam) == SVt_PVAV) { SV * * ppRV = av_fetch ((AV *)pParam, nParamIndex, 0) ; if (!ppRV || !*ppRV) { strncpy (r -> errdat1, "<provider missing>", sizeof(r -> errdat1) - 1) ; return rcUnknownProvider ; } if (!SvROK (*ppRV) || SvTYPE(pProviderParam = (HV *)SvRV (*ppRV)) != SVt_PVHV) { strncpy (r -> errdat1, "<provider missing, element is no hashref>", sizeof(r -> errdat1) - 1) ; return rcUnknownProvider ; } } else if (SvTYPE(pParam) == SVt_PVHV) { pProviderParam = (HV *)pParam ; } else { strncpy (r -> errdat1, "<provider missing, no description found>", sizeof(r -> errdat1) - 1) ; return rcUnknownProvider ; } sProvider = GetHashValueStr (aTHX_ pProviderParam, "type", "") ; pProviderClass = (tProviderClass *)GetHashValuePtr (r, pProviders, sProvider, NULL) ; if (!pProviderClass) { if (*sProvider) strncpy (r -> errdat1, sProvider, sizeof(r -> errdat1) - 1) ; else strncpy (r -> errdat1, "<provider missing>", sizeof(r -> errdat1) - 1) ; return rcUnknownProvider ; } if (pProviderClass -> fAppendKey) if ((rc = (*pProviderClass -> fAppendKey)(r, pProviderClass, pProviderParam, pParam, nParamIndex - 1, pKey)) != ok) { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: Error in Update CacheItem provider=%s\n", r -> pThread -> nPid, sProvider) ; return rc ; } if ((pItem = Cache_GetByKey (r, SvPV(pKey, len)))) { int bCache = pItem -> bCache ; Cache_ParamUpdate (r, pProviderParam, 0, "Update", pItem) ; if (!pItem -> bCache && bCache) Cache_FreeContent (r, pItem) ; if (pProviderClass -> fUpdateParam) if ((rc = (*pProviderClass -> fUpdateParam)(r, pItem -> pProvider, pProviderParam)) != ok) return rc ; } return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_GetByKey */ /* */ /*! * \_en * Gets an CacheItem by it's key. * * @param r Embperl request record * @param sKey Key * @return Returns the cache item specified by the key if found * \endif * * \_de * Liefert das durch den Schlüssel angegeben CacheItem zurück. * * @param r Embperl request record * @param sKey Key * @return Liefert das CacheItem welches durch den Schlüssel * angegeben wird, soweit gefunden. * \endif * * ------------------------------------------------------------------------ */ tCacheItem * Cache_GetByKey (/*in*/ req * r, /*in*/ const char * sKey) { tCacheItem * pItem ; pItem = (tCacheItem *)GetHashValuePtr (r, pCacheItems, sKey, NULL) ; return pItem ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_AddDependency */ /* */ /*! * \_en * Adds a CacheItem on which this cache items depends * * @param r Embperl request record * @param pItem CacheItem which depends on pDependsOn * @param pDependsOn CacheItem on which pItem depends * @return 0 on success * \endif * * \_de * Fügt ein CacheItem von welches Adds a CacheItem on which this cache items depends * * @param r Embperl request record * @param pItem CacheItem welches von pDependsOn anhängt * @param pDependsOn CacheItem von welchem pItem abhängt * @return 0 wenn fehlerfrei ausgeführt * \endif * * ------------------------------------------------------------------------ */ int Cache_AddDependency (/*in*/ req * r, /*in*/ tCacheItem * pItem, /*in*/ tCacheItem * pDependsOn) { int n ; if (!pItem -> pDependsOn) ArrayNew (r -> pApp, &pItem -> pDependsOn, 2, sizeof (tCacheItem *)) ; n = ArrayAdd (r -> pApp, &pItem -> pDependsOn, 1) ; pItem -> pDependsOn[n] = pDependsOn ; if (!pDependsOn -> pNeededFor) ArrayNew (r -> pApp, &pDependsOn -> pNeededFor, 2, sizeof (tCacheItem *)) ; n = ArrayAdd (r -> pApp, &pDependsOn -> pNeededFor, 1) ; pDependsOn -> pNeededFor[n] = pItem ; return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_GetDependency */ /* */ /*! * \_en * Get the Nth CacheItem on which this cache depends * * @param r Embperl request record * @param pItem CacheItem * @param n Dependency number * @return Nth Dependency CacheItem * \endif * * \_de * Gibt das Nte CacheItem von dem pItem abhängt zurück * * @param r Embperl request record * @param pItem CacheItem * @param n Number der Abhänigkeit * @return Ntes CacheItem von welchem pItem abhängt * \endif * * ------------------------------------------------------------------------ */ tCacheItem * Cache_GetDependency (/*in*/ req * r, /*in*/ tCacheItem * pItem, /*in*/ int n) { if (!pItem -> pDependsOn || ArrayGetSize (r -> pApp, pItem -> pDependsOn) < n || n < 0) return NULL ; return pItem -> pDependsOn[n] ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_IsExpired */ /* */ /*! * \_en * Checks if the cache item or a cache item on which this one depends is * expired * * @param r Embperl request record * @param pItem CacheItem which should be checked * @param nLastUpdated When a item on which this one depends, was * updated after the given request count, then * this item is expired * @return TRUE if expired, otherwise FALSE * \endif * * \_de * Prüft ob das CacheItem oder eines von welchem dieses abhängt nihct * mehr gültig ist * * @param r Embperl request record * @param pItem CacheItem welches überprüft werden soll * @param nLastUpdated Wenn ein Item von welchem dieses Item abhängt * nach dem angegebenen Request Count geändert * wurde ist diese Item nicht mehr gültig * @return wahr wenn ungültig, ansonsten falsch * \endif * * ------------------------------------------------------------------------ */ int Cache_IsExpired (/*in*/ req * r, /*in*/ tCacheItem * pItem, /*in*/ int nLastUpdated) { epTHX_ int rc ; tCacheItem * pSubItem ; int i ; int numItems = pItem -> pDependsOn?ArrayGetSize (r -> pApp, pItem -> pDependsOn):0 ; if (nLastUpdated < pItem -> nLastUpdated) return TRUE ; if (pItem -> pProvider -> pProviderClass -> fExpires) { if ((*pItem -> pProvider -> pProviderClass -> fExpires)(r, pItem -> pProvider)) { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s expired because provider C sub returned TRUE\n", r -> pThread -> nPid, pItem -> sKey) ; Cache_FreeContent (r, pItem) ; return pItem -> bExpired = TRUE ; } } if (pItem -> bExpired || pItem -> nLastChecked == r -> nRequestCount) return pItem -> bExpired ; /* we already have checked this or know that is it expired */ pItem -> nLastChecked = r -> nRequestCount ; /* first check dependency */ for (i = 0; i < numItems; i++) { pSubItem = pItem -> pDependsOn[i] ; if (Cache_IsExpired (r, pSubItem, pItem -> nLastUpdated)) { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s expired because dependencies is expired or newer\n", r -> pThread -> nPid, pItem -> sKey) ; Cache_FreeContent (r, pItem) ; return pItem -> bExpired = TRUE ; } } if (pItem -> nExpiresInTime && pItem -> nLastModified + pItem -> nExpiresInTime < r -> nRequestTime) { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s expired because of timeout (%d sec)\n", r -> pThread -> nPid, pItem -> sKey, pItem -> nExpiresInTime) ; Cache_FreeContent (r, pItem) ; return pItem -> bExpired = TRUE ; } if (pItem -> sExpiresFilename) { #ifdef WIN32 if (_stat (pItem -> sExpiresFilename, &pItem -> FileStat)) #else if (stat (pItem -> sExpiresFilename, &pItem -> FileStat)) #endif { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s expired because cannot stat file %s\n", r -> pThread -> nPid, pItem -> sKey, pItem -> sExpiresFilename) ; Cache_FreeContent (r, pItem) ; return pItem -> bExpired = TRUE ; } if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s stat file %s mtime=%d size=%d\n", r -> pThread -> nPid, pItem -> sKey, pItem -> sExpiresFilename, pItem -> FileStat.st_mtime, pItem -> FileStat.st_size) ; if (pItem -> nFileModified != pItem -> FileStat.st_mtime) { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s expired because file %s changed\n", r -> pThread -> nPid, pItem -> sKey, pItem -> sExpiresFilename) ; pItem -> nFileModified = pItem -> FileStat.st_mtime ; Cache_FreeContent (r, pItem) ; return pItem -> bExpired = TRUE ; } } if (pItem -> pExpiresCV) { SV * pRet ; if ((rc = CallCV (r, "Expired?", pItem -> pExpiresCV, 0, &pRet)) != ok) { LogError (r, rc) ; Cache_FreeContent (r, pItem) ; return pItem -> bExpired = TRUE ; } if (pRet && SvTRUE(pRet)) { /* Expire the entry */ if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s expired because expirey Perl sub returned TRUE\n", r -> pThread -> nPid, pItem -> sKey) ; Cache_FreeContent (r, pItem) ; return pItem -> bExpired = TRUE ; } } if (pItem -> fExpires) { if ((*pItem -> fExpires)(pItem)) { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s expired because expirey C sub returned TRUE\n", r -> pThread -> nPid, pItem -> sKey) ; Cache_FreeContent (r, pItem) ; return pItem -> bExpired = TRUE ; } } if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s NOT expired\n", r -> pThread -> nPid, pItem -> sKey) ; return FALSE ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_SetNotExpired */ /* */ /*! * \_en * Reset expired flag and last modification time * * @param r Embperl request record * @param pItem CacheItem which should be checked * @return TRUE if expired, otherwise FALSE * \endif * * \_de * Abgelaufen Flag zurücksetzen und Zeitpunkt der letzten Änderung setzen * * @param r Embperl request record * @param pItem CacheItem welches überprüft werden soll * @return wahr wenn ungültig, ansonsten falsch * \endif * * ------------------------------------------------------------------------ */ int Cache_SetNotExpired (/*in*/ req * r, /*in*/ tCacheItem * pItem) { pItem -> nLastChecked = r -> nRequestCount ; pItem -> nLastUpdated = r -> nRequestCount ; pItem -> nLastModified = r -> nRequestTime ; pItem -> bExpired = FALSE ; if (!pItem -> bCache) { int n = ArrayAdd(r -> pApp, &pCachesToRelease, 1) ; pCachesToRelease[n] = pItem ; } return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_GetContentSV */ /* */ /*! * \_en * Get the whole content as SV, if not expired from the cache, otherwise ask * the provider to fetch it. This will also put a read lock on the * Cacheitem. When you are done with the content call ReleaseContent * * @param r Embperl request record * @param pItem CacheItem which should be checked * @param pData Returns the content * @param bUseCache Set if the content should not recomputed * @return error code * \endif * * \_de * Holt den gesamt Inhalt als SV soweit nich abgelaufen aus dem Cache, ansonsten * wird der Provider beauftragt ihn einzulesen. Zusätzlich wird ein * Read Lock gesetzt. Nach der Bearbeitetung des Inhalts sollte deshalb * ReleaseLock zum freigeben aufgerufen werden. * * @param r Embperl request record * @param pItem CacheItem welches überprüft werden soll * @param pData Liefert den Inhalt * @param bUseCache Gesetzt wenn der Inhalt nicht neu berechnet werden soll * @return Fehlercode * \endif * * ------------------------------------------------------------------------ */ int Cache_GetContentSV (/*in*/ req * r, /*in*/ tCacheItem *pItem, /*in*/ SV * * pData, /*in*/ bool bUseCache) { epTHX_ int rc ; if (!bUseCache && (Cache_IsExpired (r, pItem, pItem -> nLastUpdated) || !pItem -> pSVData)) { if (pItem -> pProvider -> pProviderClass -> fGetContentSV) if ((rc = ((*pItem -> pProvider -> pProviderClass -> fGetContentSV) (r, pItem -> pProvider, pData, FALSE))) != ok) { Cache_FreeContent (r, pItem) ; return rc ; } Cache_SetNotExpired (r, pItem) ; if (pItem -> pSVData) SvREFCNT_dec (pItem -> pSVData) ; pItem -> pSVData = *pData ; } else { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s take from cache\n", r -> pThread -> nPid, pItem -> sKey) ; *pData = pItem -> pSVData ; if (pItem -> pProvider -> pProviderClass -> fGetContentSV) if ((rc = ((*pItem -> pProvider -> pProviderClass -> fGetContentSV) (r, pItem -> pProvider, pData, TRUE))) != ok) { Cache_FreeContent (r, pItem) ; return rc ; } } return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_GetContentPtr */ /* */ /*! * \_en * Get the whole content as pointer, if not expired from the cache, otherwise ask * the provider to fetch it. This will also put a read lock on the * Cacheitem. When you are done with the content call ReleaseContent * * @param r Embperl request record * @param pItem CacheItem which should be checked * @param pData Returns the content * @param bUseCache Set if the content should not recomputed * @return error code * \endif * * \_de * Holt den gesamt Inhalt als Zeiger soweit nich abgelaufen aus dem Cache, ansonsten * wird der Provider beauftragt ihn einzulesen. Zusätzlich wird ein * Read Lock gesetzt. Nach der Bearbeitetung des Inhalts sollte deshalb * ReleaseLock zum freigeben aufgerufen werden. * * @param r Embperl request record * @param pItem CacheItem welches überprüft werden soll * @param pData Liefert den Inhalt * @param bUseCache Gesetzt wenn der Inhalt nicht neu berechnet werden soll * @return Fehlercode * \endif * * ------------------------------------------------------------------------ */ int Cache_GetContentPtr (/*in*/ req * r, /*in*/ tCacheItem *pItem, /*in*/ void * * pData, /*in*/ bool bUseCache) { int rc ; if (!bUseCache && (Cache_IsExpired (r, pItem, pItem -> nLastUpdated) || !pItem -> pData)) { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s get from provider\n", r -> pThread -> nPid, pItem -> sKey) ; if (pItem -> pProvider -> pProviderClass -> fGetContentPtr) if ((rc = (*pItem -> pProvider -> pProviderClass -> fGetContentPtr) (r, pItem -> pProvider, pData, FALSE)) != ok) { Cache_FreeContent (r, pItem) ; return rc ; } pItem -> pData = *pData ; Cache_SetNotExpired (r, pItem) ; } else { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s take from cache\n", r -> pThread -> nPid, pItem -> sKey) ; *pData = pItem -> pData ; if (pItem -> pProvider -> pProviderClass -> fGetContentPtr) if ((rc = (*pItem -> pProvider -> pProviderClass -> fGetContentPtr) (r, pItem -> pProvider, pData, TRUE)) != ok) { Cache_FreeContent (r, pItem) ; return rc ; } } return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_GetContentIndex */ /* */ /*! * \_en * Get the whole content as pointer, if not expired from the cache, otherwise ask * the provider to fetch it. This will also put a read lock on the * Cacheitem. When you are done with the content call ReleaseContent * * @param r Embperl request record * @param pItem CacheItem which should be checked * @param pData Returns the content * @param bUseCache Set if the content should not recomputed * @return error code * \endif * * \_de * Holt den gesamt Inhalt als Zeiger soweit nich abgelaufen aus dem Cache, ansonsten * wird der Provider beauftragt ihn einzulesen. Zusätzlich wird ein * Read Lock gesetzt. Nach der Bearbeitetung des Inhalts sollte deshalb * ReleaseLock zum freigeben aufgerufen werden. * * @param r Embperl request record * @param pItem CacheItem welches überprüft werden soll * @param pData Liefert den Inhalt * @param bUseCache Gesetzt wenn der Inhalt nicht neu berechnet werden soll * @return Fehlercode * \endif * * ------------------------------------------------------------------------ */ int Cache_GetContentIndex (/*in*/ req * r, /*in*/ tCacheItem *pItem, /*in*/ tIndex * pData, /*in*/ bool bUseCache) { int rc ; if (!bUseCache && (Cache_IsExpired (r, pItem, pItem -> nLastUpdated) || !pItem -> xData)) { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s get from provider\n", r -> pThread -> nPid, pItem -> sKey) ; if (pItem -> pProvider -> pProviderClass -> fGetContentIndex) if ((rc = (*pItem -> pProvider -> pProviderClass -> fGetContentIndex) (r, pItem -> pProvider, pData, FALSE)) != ok) { Cache_FreeContent (r, pItem) ; return rc ; } pItem -> xData = *pData ; Cache_SetNotExpired (r, pItem) ; } else { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s take from cache\n", r -> pThread -> nPid, pItem -> sKey) ; *pData = pItem -> xData ; if (pItem -> pProvider -> pProviderClass -> fGetContentIndex) if ((rc = (*pItem -> pProvider -> pProviderClass -> fGetContentIndex) (r, pItem -> pProvider, pData, TRUE)) != ok) { Cache_FreeContent (r, pItem) ; return rc ; } } return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_GetContentSvIndex */ /* */ /*! * \_en * Get the whole content as pointer, if not expired from the cache, otherwise ask * the provider to fetch it. This will also put a read lock on the * Cacheitem. When you are done with the content call ReleaseContent * * @param r Embperl request record * @param pItem CacheItem which should be checked * @param pData Returns the content * @param bUseCache Set if the content should not recomputed * @return error code * \endif * * \_de * Holt den gesamt Inhalt als Zeiger soweit nich abgelaufen aus dem Cache, ansonsten * wird der Provider beauftragt ihn einzulesen. Zusätzlich wird ein * Read Lock gesetzt. Nach der Bearbeitetung des Inhalts sollte deshalb * ReleaseLock zum freigeben aufgerufen werden. * * @param r Embperl request record * @param pItem CacheItem welches überprüft werden soll * @param pData Liefert den Inhalt * @param bUseCache Gesetzt wenn der Inhalt nicht neu berechnet werden soll * @return Fehlercode * \endif * * ------------------------------------------------------------------------ */ int Cache_GetContentSvIndex (/*in*/ req * r, /*in*/ tCacheItem *pItem, /*in*/ SV * * pSVData, /*in*/ tIndex * pData, /*in*/ bool bUseCache) { int rc ; bool bUpdate = FALSE ; if (!bUseCache && (Cache_IsExpired (r, pItem, pItem -> nLastUpdated))) { pItem -> xData = 0 ; pItem -> pSVData = NULL ; } if (!pItem -> xData) { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s get from provider\n", r -> pThread -> nPid, pItem -> sKey) ; if (pItem -> pProvider -> pProviderClass -> fGetContentIndex) if ((rc = (*pItem -> pProvider -> pProviderClass -> fGetContentIndex) (r, pItem -> pProvider, pData, FALSE)) != ok) { Cache_FreeContent (r, pItem) ; return rc ; } pItem -> xData = *pData ; bUpdate = TRUE ; } else { *pData = pItem -> xData ; if (pItem -> pProvider -> pProviderClass -> fGetContentIndex) if ((rc = (*pItem -> pProvider -> pProviderClass -> fGetContentIndex) (r, pItem -> pProvider, pData, TRUE)) != ok) { Cache_FreeContent (r, pItem) ; return rc ; } } if (!pItem -> pSVData) { if ((r -> Component.Config.bDebug & dbgCache) && !bUpdate) lprintf (r -> pApp, "[%d]CACHE: %s get from provider\n", r -> pThread -> nPid, pItem -> sKey) ; if (pItem -> pProvider -> pProviderClass -> fGetContentSV) if ((rc = (*pItem -> pProvider -> pProviderClass -> fGetContentSV) (r, pItem -> pProvider, pSVData, FALSE)) != ok) { Cache_FreeContent (r, pItem) ; return rc ; } pItem -> pSVData = *pSVData ; bUpdate = TRUE ; } else *pSVData = pItem -> pSVData ; if (bUpdate) { Cache_SetNotExpired (r, pItem) ; } else { if (r -> Component.Config.bDebug & dbgCache) lprintf (r -> pApp, "[%d]CACHE: %s taken from cache\n", r -> pThread -> nPid, pItem -> sKey) ; } return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_ReleaseContent */ /* */ /*! * \_en * Removes the read and/or write lock from the content. * * @param r Embperl request record * @param pItem CacheItem which should be checked * @return error code * \endif * * \_de * Gibt den Read und/oder Write Lock wieder frei. * * @param r Embperl request record * @param pItem CacheItem welches überprüft werden soll * @return Fehlercode * \endif * * ------------------------------------------------------------------------ */ int Cache_ReleaseContent (/*in*/ req * r, /*in*/ tCacheItem *pItem) { /* locking not yet implemented */ tCacheItem * pSubItem ; int i ; int numItems = pItem -> pDependsOn?ArrayGetSize (r -> pApp, pItem -> pDependsOn):0 ; if (!pItem -> bCache) Cache_FreeContent (r, pItem) ; for (i = 0; i < numItems; i++) { pSubItem = pItem -> pDependsOn[i] ; Cache_ReleaseContent (r, pSubItem) ; } return ok ; } /* ------------------------------------------------------------------------ */ /* */ /* Cache_FreeContent */ /* */ /*! * \_en * Free the cached data * * @param r Embperl request record * @param pItem CacheItem * @return error code * \endif * * \_de * Gibt die gecachten Daten frei * * @param r Embperl request record * @param pItem CacheItem * @return Fehlercode * \endif * * ------------------------------------------------------------------------ */ int Cache_FreeContent (/*in*/ req * r, /*in*/ tCacheItem *pItem) { epTHX_ int rc ; if ((r -> Component.Config.bDebug & dbgCache) && (pItem -> pSVData || pItem -> pData || pItem -> xData)) lprintf (r -> pApp, "[%d]CACHE: Free content for %s\n", r -> pThread -> nPid, pItem -> sKey) ; if (pItem -> pProvider -> pProviderClass -> fFreeContent) if ((rc = (*pItem -> pProvider -> pProviderClass -> fFreeContent) (r, pItem)) != ok) return rc ; if (pItem -> pSVData) { SvREFCNT_dec (pItem -> pSVData) ; pItem -> pSVData = NULL ; } pItem -> pData = NULL ; pItem -> xData = 0 ; return ok ; }