blob: 4002902e786fdffaec4b7348dc3c9c59fc8d27d0 [file] [log] [blame] [edit]
///////////////////////////////////////////////////////////////////////////////
// //
// dxcmem.cpp //
// Copyright (C) Microsoft Corporation. All rights reserved. //
// This file is distributed under the University of Illinois Open Source //
// License. See LICENSE.TXT for details. //
// //
// Provides support for a thread-local allocator. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "dxc/Support/Global.h"
#ifdef _WIN32
#include <specstrings.h>
#endif
#include "dxc/Support/WinFunctions.h"
#include "dxc/Support/WinIncludes.h"
#include "llvm/Support/ThreadLocal.h"
#include <memory>
static llvm::sys::ThreadLocal<IMalloc> *g_ThreadMallocTls;
static IMalloc *g_pDefaultMalloc;
HRESULT DxcInitThreadMalloc() throw() {
// Allow a default malloc from a previous call to Init.
// This will not be cleaned up in the call to Cleanup because
// it can still be referenced after Cleanup is called.
if (g_pDefaultMalloc) {
g_pDefaultMalloc->AddRef();
} else {
// We capture the default malloc early to avoid potential failures later on.
HRESULT hrMalloc = DxcCoGetMalloc(1, &g_pDefaultMalloc);
if (FAILED(hrMalloc))
return hrMalloc;
}
DXASSERT(g_ThreadMallocTls == nullptr,
"else InitThreadMalloc already called");
g_ThreadMallocTls =
(llvm::sys::ThreadLocal<IMalloc> *)g_pDefaultMalloc->Alloc(
sizeof(llvm::sys::ThreadLocal<IMalloc>));
if (g_ThreadMallocTls == nullptr) {
g_pDefaultMalloc->Release();
g_pDefaultMalloc = nullptr;
return E_OUTOFMEMORY;
}
g_ThreadMallocTls = new (g_ThreadMallocTls) llvm::sys::ThreadLocal<IMalloc>;
return S_OK;
}
void DxcCleanupThreadMalloc() throw() {
if (g_ThreadMallocTls) {
DXASSERT(g_pDefaultMalloc,
"else DxcInitThreadMalloc didn't work/fail atomically");
g_ThreadMallocTls->~ThreadLocal();
g_pDefaultMalloc->Free(g_ThreadMallocTls);
g_ThreadMallocTls = nullptr;
}
}
IMalloc *DxcGetThreadMallocNoRef() throw() {
if (g_ThreadMallocTls == nullptr) {
return g_pDefaultMalloc;
}
return g_ThreadMallocTls->get();
}
void DxcClearThreadMalloc() throw() {
if (g_ThreadMallocTls != nullptr) {
IMalloc *pMalloc = DxcGetThreadMallocNoRef();
g_ThreadMallocTls->erase();
if (pMalloc != nullptr) {
pMalloc->Release();
}
}
}
void DxcSetThreadMallocToDefault() throw() {
DXASSERT(g_ThreadMallocTls != nullptr,
"else prior to DxcInitThreadMalloc or after DxcCleanupThreadMalloc");
DXASSERT(DxcGetThreadMallocNoRef() == nullptr,
"else nested allocation invoked");
g_ThreadMallocTls->set(g_pDefaultMalloc);
g_pDefaultMalloc->AddRef();
}
static IMalloc *DxcSwapThreadMalloc(IMalloc *pMalloc,
IMalloc **ppPrior) throw() {
DXASSERT(g_ThreadMallocTls != nullptr,
"else prior to DxcInitThreadMalloc or after DxcCleanupThreadMalloc");
IMalloc *pPrior = DxcGetThreadMallocNoRef();
if (ppPrior) {
*ppPrior = pPrior;
}
g_ThreadMallocTls->set(pMalloc);
return pMalloc;
}
DxcThreadMalloc::DxcThreadMalloc(IMalloc *pMallocOrNull) throw() {
p = DxcSwapThreadMalloc(pMallocOrNull ? pMallocOrNull : g_pDefaultMalloc,
&pPrior);
}
DxcThreadMalloc::~DxcThreadMalloc() { DxcSwapThreadMalloc(pPrior, nullptr); }
void *DxcNew(std::size_t size) throw() {
void *ptr;
IMalloc *iMalloc = DxcGetThreadMallocNoRef();
if (iMalloc != nullptr) {
ptr = iMalloc->Alloc(size);
} else {
// DxcGetThreadMallocNoRef() returning null means the operator is called
// before DllMain where the g_pDefaultMalloc is initialized, for example
// from CRT libraries when static linking is enabled. In that case fallback
// to the standard allocator and use CoTaskMemAlloc directly instead of
// CoGetMalloc, Alloc & Release for better perf.
ptr = CoTaskMemAlloc(size);
}
return ptr;
}
void DxcDelete(void *ptr) throw() {
IMalloc *iMalloc = DxcGetThreadMallocNoRef();
if (iMalloc != nullptr) {
iMalloc->Free(ptr);
} else {
// DxcGetThreadMallocNoRef() returning null means the operator is called
// before DllMain where the g_pDefaultMalloc is initialized, for example
// from CRT libraries when static linking is enabled. In that case fallback
// to the standard allocator and use CoTaskMemFree directly instead of
// CoGetMalloc, Free & Release for better perf.
CoTaskMemFree(ptr);
}
}