This patch drops the separate cache structure, preferring pointers in the ITypeLibImpl struct instead. It uses a critical section (grrr, i'll get into the habit of thread safety eventually, bear with me). I tested it a bit here, but haven't included any test cases. ChangeLog: Implement typelib loader cache
? typelib.patch ? dlls/kernel/system.spec.c Index: dlls/oleaut32/typelib.c =================================================================== RCS file: /home/wine/wine/dlls/oleaut32/typelib.c,v retrieving revision 1.97 diff -u -r1.97 typelib.c --- dlls/oleaut32/typelib.c 27 Jun 2003 19:40:03 -0000 1.97 +++ dlls/oleaut32/typelib.c 21 Aug 2003 12:42:04 -0000 @@ -286,6 +286,8 @@ INT index = 1; TRACE("(%s,%d,%p)\n",debugstr_w(szFile), regkind, pptLib); + + *pptLib = NULL; if(!SearchPathW(NULL,szFile,NULL,sizeof(szPath)/sizeof(WCHAR),szPath, NULL)) { @@ -652,6 +654,10 @@ TYPEDESC * pTypeDesc; /* array of TypeDescriptions found in the libary. Only used while read MSFT typelibs */ + + /* typelibs are cached, keyed by path, so store the linked list info within them */ + struct tagITypeLibImpl *next, *prev; + WCHAR *path; } ITypeLibImpl; static struct ICOM_VTABLE(ITypeLib2) tlbvt; @@ -1707,6 +1713,7 @@ recoffset += reclength; } } + static void MSFT_DoVars(TLBContext *pcx, ITypeInfoImpl *pTI, int cFuncs, int cVars, int offset, TLBVarDesc ** pptvd) { @@ -1964,6 +1971,22 @@ return ptiRet; } +/* Because type library parsing has some degree of overhead, and some apps repeatedly load the same + * typelibs over and over, we cache them here. According to MSDN Microsoft have a similar scheme in + * place. This will cause a deliberate memory leak, but generally losing RAM for cycles is an acceptable + * tradeoff here. + */ +ITypeLibImpl *tlb_cache_tail = NULL; +static CRITICAL_SECTION cache_section; +static CRITICAL_SECTION_DEBUG cache_section_debug = +{ + 0, 0, &cache_section, + { &cache_section_debug.ProcessLocksList, &cache_section_debug.ProcessLocksList }, + 0, 0, { 0, (DWORD)(__FILE__ ": typelib loader cache") } +}; +static CRITICAL_SECTION cache_section = { &cache_section_debug, -1, 0, 0, 0, 0 }; + + /**************************************************************************** * TLB_ReadTypeLib * @@ -1982,6 +2005,20 @@ *ppTypeLib = NULL; + /* We look the path up in the typelib cache. If found, we just addref it, and return the pointer. */ + EnterCriticalSection(&cache_section); + if (tlb_cache_tail) { + ITypeLibImpl *entry; + for (entry = tlb_cache_tail; entry != NULL; entry = entry->next) + if (!strcmpiW(entry->path, pszFileName)) { + TRACE("cache hit\n"); + *ppTypeLib = (ITypeLib2*)entry; + ITypeLib_AddRef(*ppTypeLib); + return S_OK; + } + } + LeaveCriticalSection(&cache_section); + /* check the signature of the file */ hFile = CreateFileW( pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); if (INVALID_HANDLE_VALUE != hFile) @@ -2053,11 +2090,34 @@ } } - if(*ppTypeLib) + if(*ppTypeLib) { + ITypeLibImpl *impl = (ITypeLibImpl*)*ppTypeLib; + + TRACE("adding to cache\n"); + EnterCriticalSection(&cache_section); + impl->path = HeapAlloc(GetProcessHeap(), 0, MAX_PATH); /* is MAX_PATH chars unicode safe? */ + lstrcpyW(impl->path, pszFileName); + /* We should really canonicalise the path here. */ + + if (!tlb_cache_tail) { + /* this is the first typelib loaded */ + tlb_cache_tail = (ITypeLibImpl*)*ppTypeLib; + tlb_cache_tail->next = NULL; + tlb_cache_tail->prev = NULL; + } else { + ITypeLibImpl *tlb_cache_head = tlb_cache_tail; + + /* locate the head of the list */ + while (tlb_cache_head->next != NULL) + tlb_cache_head = tlb_cache_head->next; + + tlb_cache_head->next = (ITypeLibImpl*)*ppTypeLib; + tlb_cache_head->next->prev = tlb_cache_head; + } + LeaveCriticalSection(&cache_section); ret = S_OK; - else - ERR("Loading of typelib %s failed with error %ld\n", - debugstr_w(pszFileName), GetLastError()); + } else + ERR("Loading of typelib %s failed with error %ld\n", debugstr_w(pszFileName), GetLastError()); return ret; } @@ -3196,7 +3256,7 @@ { ICOM_THIS( ITypeLibImpl, iface); - TRACE("(%p)->ref is %u\n",This, This->ref); + TRACE("(%p)->ref was %u\n",This, This->ref); return ++(This->ref); } @@ -3213,8 +3273,15 @@ if (!This->ref) { - /* FIXME destroy child objects */ + /* remove cache entry */ + TRACE("removing from cache list\n"); + EnterCriticalSection(&cache_section); + if (This->next) This->next->prev = This->prev; + if (This->prev) This->prev->next = This->next; + if (This == tlb_cache_tail) tlb_cache_tail = NULL; + LeaveCriticalSection(&cache_section); + /* FIXME destroy child objects */ TRACE(" destroying ITypeLib(%p)\n",This); if (This->Name) @@ -4447,6 +4514,7 @@ if (pFDesc->funcdesc.invkind & dwFlags) break; } + if (pFDesc) { if (TRACE_ON(typelib)) dump_TLBFuncDescOne(pFDesc); /* dump_FUNCDESC(&pFDesc->funcdesc);*/ @@ -4868,13 +4936,13 @@ /* If a pointer is null, we simply ignore it, the ATL in particular passes pIndex as 0 */ if (pIndex) { *pIndex=This->index; - TRACE("returning pIndex=%d", *pIndex); + TRACE("returning pIndex=%d\n", *pIndex); } if (ppTLib) { *ppTLib=(LPTYPELIB )(This->pTypeLib); ITypeLib2_AddRef(*ppTLib); - TRACE("returning ppTLib=%p", *ppTLib); + TRACE("returning ppTLib=%p\n", *ppTLib); } return S_OK;