diff options
Diffstat (limited to 'src/kmk/w32/imagecache.c')
-rw-r--r-- | src/kmk/w32/imagecache.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/src/kmk/w32/imagecache.c b/src/kmk/w32/imagecache.c new file mode 100644 index 0000000..2b26dac --- /dev/null +++ b/src/kmk/w32/imagecache.c @@ -0,0 +1,219 @@ +/* $Id: imagecache.c 3195 2018-03-27 18:09:23Z bird $ */ +/** @file + * kBuild specific executable image cache for Windows. + */ + +/* + * Copyright (c) 2012 knut st. osmundsen <bird-kBuild-spamx@anduin.net> + * + * This file is part of kBuild. + * + * kBuild is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * kBuild is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kBuild. If not, see <http://www.gnu.org/licenses/> + * + */ + +/* No GNU coding style here! */ + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "makeint.h" + +#include <Windows.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct EXECCACHEENTRY +{ + /** The name hash value. */ + unsigned uHash; + /** The name length. */ + unsigned cwcName; + /** Pointer to the next name with the same hash. */ + struct EXECCACHEENTRY *pNext; + /** When it was last referenced. */ + unsigned uLastRef; + /** The module handle, LOAD_LIBRARY_AS_DATAFILE. */ + HMODULE hmod1; + /** The module handle, DONT_RESOLVE_DLL_REFERENCES. */ + HMODULE hmod2; + /** The executable path. */ + wchar_t wszName[1]; +} EXECCACHEENTRY; +typedef EXECCACHEENTRY *PEXECCACHEENTRY; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Critical section serializing all access. */ +static CRITICAL_SECTION g_CritSect; +/** Set if initialized. */ +static int volatile g_fInitialized = 0; +/** The number of cached images. */ +static unsigned g_cCached; +/** Used noting when entries was last used. + * Increased on each kmk_cache_exec_image call. */ +static unsigned g_uNow; + +/** The size of the hash table. */ +#define EXECCACHE_HASHTAB_SIZE 128 +/** The hash table. */ +static PEXECCACHEENTRY g_apHashTab[EXECCACHE_HASHTAB_SIZE]; + + +/** A sleepy approach to do-once. */ +static void kmk_cache_lazy_init(void) +{ + if (_InterlockedCompareExchange(&g_fInitialized, -1, 0) == 0) + { + InitializeCriticalSection(&g_CritSect); + _InterlockedExchange(&g_fInitialized, 1); + } + else + while (g_fInitialized != 1) + Sleep(1); +} + + +/* sdbm: + This algorithm was created for sdbm (a public-domain reimplementation of + ndbm) database library. it was found to do well in scrambling bits, + causing better distribution of the keys and fewer splits. it also happens + to be a good general hashing function with good distribution. the actual + function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below + is the faster version used in gawk. [there is even a faster, duff-device + version] the magic constant 65599 was picked out of thin air while + experimenting with different constants, and turns out to be a prime. + this is one of the algorithms used in berkeley db (see sleepycat) and + elsewhere. */ + +static unsigned execcache_calc_hash(const wchar_t *pwsz, size_t *pcch) +{ + wchar_t const * const pwszStart = pwsz; + unsigned hash = 0; + int ch; + + while ((ch = *pwsz++) != L'\0') + hash = ch + (hash << 6) + (hash << 16) - hash; + + *pcch = (size_t)(pwsz - pwszStart - 1); + return hash; +} + +/** + * Caches two memory mappings of the specified image so that it isn't flushed + * from the kernel's cache mananger. + * + * Not sure exactly how much this actually helps, but whatever... + * + * @param pwszExec The executable. + */ +extern void kmk_cache_exec_image_w(const wchar_t *pwszExec) +{ + /* + * Prepare name lookup and to lazy init. + */ + size_t cwcName; + const unsigned uHash = execcache_calc_hash(pwszExec, &cwcName); + PEXECCACHEENTRY *ppCur = &g_apHashTab[uHash % EXECCACHE_HASHTAB_SIZE]; + PEXECCACHEENTRY pCur; + + if (g_fInitialized != 1) + kmk_cache_lazy_init(); + + /* + * Do the lookup. + */ + EnterCriticalSection(&g_CritSect); + pCur = *ppCur; + while (pCur) + { + if ( pCur->uHash == uHash + && pCur->cwcName == cwcName + && !memcmp(pCur->wszName, pwszExec, cwcName * sizeof(wchar_t))) + { + pCur->uLastRef = ++g_uNow; + LeaveCriticalSection(&g_CritSect); + return; + } + ppCur = &pCur->pNext; + pCur = pCur->pNext; + } + LeaveCriticalSection(&g_CritSect); + + /* + * Not found, create a new entry. + */ + pCur = xmalloc(sizeof(*pCur) + cwcName * sizeof(wchar_t)); + pCur->uHash = uHash; + pCur->cwcName = (unsigned)cwcName; + pCur->pNext = NULL; + pCur->uLastRef = ++g_uNow; + memcpy(pCur->wszName, pwszExec, (cwcName + 1) * sizeof(wchar_t)); + pCur->hmod1 = LoadLibraryExW(pwszExec, NULL, LOAD_LIBRARY_AS_DATAFILE); + if (pCur->hmod1 != NULL) + pCur->hmod2 = LoadLibraryExW(pwszExec, NULL, DONT_RESOLVE_DLL_REFERENCES); + else + pCur->hmod2 = NULL; + + /* + * Insert it. + * Take into account that we might've been racing other threads, + * fortunately we don't evict anything from the cache. + */ + EnterCriticalSection(&g_CritSect); + if (*ppCur != NULL) + { + /* Find new end of chain and check for duplicate. */ + PEXECCACHEENTRY pCur2 = *ppCur; + while (pCur2) + { + if ( pCur->uHash == uHash + && pCur->cwcName == cwcName + && !memcmp(pCur->wszName, pwszExec, cwcName * sizeof(wchar_t))) + break; + ppCur = &pCur->pNext; + pCur = pCur->pNext; + } + + } + if (*ppCur == NULL) + { + *ppCur = pCur; + g_cCached++; + LeaveCriticalSection(&g_CritSect); + } + else + { + LeaveCriticalSection(&g_CritSect); + + if (pCur->hmod1 != NULL) + FreeLibrary(pCur->hmod1); + if (pCur->hmod2 != NULL) + FreeLibrary(pCur->hmod2); + free(pCur); + } +} + +extern void kmk_cache_exec_image_a(const char *pszExec) +{ + wchar_t wszExec[260]; + int cwc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszExec, strlen(pszExec) + 1, wszExec, 260); + if (cwc > 0) + kmk_cache_exec_image_w(wszExec); +} + |