#include "ep.h"
#include "epmacro.h"
#ifndef DMALLOC
#undef malloc
#undef realloc
#undef strdup
#undef free
#endif
HV * pProviders ;
HV * pCacheItems ;
tCacheItem * * pCachesToRelease = NULL ;
int
Cache_AddProviderClass (
const
char
* sName,
tProviderClass * pClass)
{
SetHashValueInt (NULL, pProviders, sName, (IV)pClass) ;
return
ok ;
}
int
Cache_Init (
tApp * a)
{
epaTHX_
pProviders = newHV () ;
pCacheItems = newHV () ;
ArrayNew (a, &pCachesToRelease, 16,
sizeof
(tCacheItem *)) ;
return
ok ;
}
int
Cache_CleanupRequest (req * r)
{
if
(pCachesToRelease)
{
int
n = ArrayGetSize (r -> pApp, pCachesToRelease) ;
int
i ;
for
(i = 0; i < n; i++)
Cache_FreeContent (r, pCachesToRelease[i]) ;
ArraySetSize(r -> pApp, &pCachesToRelease, 0) ;
}
return
ok ;
}
int
Cache_ParamUpdate (
req * r,
HV * pProviderParam,
bool
bTopLevel,
char
* sLogText,
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)
{
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 ;
}
int
Cache_New (
req * r,
SV * pParam,
IV nParamNdx,
bool
bTopLevel,
tCacheItem * * pItem)
{
epTHX_
int
rc ;
HV * pProviderParam ;
char
* sProvider ;
tProviderClass * pProviderClass ;
tCacheItem * pNew = NULL ;
SV * pKey = NULL ;
const
char
* sKey =
""
;
STRLEN len ;
if
(SvROK(pParam))
pParam = SvRV (pParam) ;
if
(SvTYPE(pParam) == SVt_PV)
{
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 ;
}
int
Cache_AppendKey (
req * r,
HV * pProviderParam,
const
char
* sSubProvider,
SV * pParam,
IV nParamIndex,
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)
{
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 ;
}
tCacheItem * Cache_GetByKey (
req * r,
const
char
* sKey)
{
tCacheItem * pItem ;
pItem = (tCacheItem *)GetHashValuePtr (r, pCacheItems, sKey, NULL) ;
return
pItem ;
}
int
Cache_AddDependency (
req * r,
tCacheItem * pItem,
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 ;
}
tCacheItem * Cache_GetDependency (
req * r,
tCacheItem * pItem,
int
n)
{
if
(!pItem -> pDependsOn || ArrayGetSize (r -> pApp, pItem -> pDependsOn) < n || n < 0)
return
NULL ;
return
pItem -> pDependsOn[n] ;
}
int
Cache_IsExpired (
req * r,
tCacheItem * pItem,
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 ;
pItem -> nLastChecked = r -> nRequestCount ;
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))
{
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 ;
}
int
Cache_SetNotExpired (
req * r,
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 ;
}
int
Cache_GetContentSV (
req * r,
tCacheItem *pItem,
SV * * pData,
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 ;
}
int
Cache_GetContentPtr (
req * r,
tCacheItem *pItem,
void
* * pData,
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 ;
}
int
Cache_GetContentIndex (
req * r,
tCacheItem *pItem,
tIndex * pData,
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 ;
}
int
Cache_GetContentSvIndex (
req * r,
tCacheItem *pItem,
SV * * pSVData,
tIndex * pData,
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 ;
}
int
Cache_ReleaseContent (
req * r,
tCacheItem *pItem)
{
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 ;
}
int
Cache_FreeContent (
req * r,
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 ;
}