blob: ef13be3b0f71bc7c4655dbc1c2a0b1a0bef1739e [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/WinIncludes.h"
#include "dxc/Support/WinFunctions.h"
#include "llvm/Support/ThreadLocal.h"
#include <memory>
static llvm::sys::ThreadLocal<IMalloc> *g_ThreadMallocTls;
static IMalloc *g_pDefaultMalloc;
HRESULT DxcInitThreadMalloc() throw() {
DXASSERT(g_pDefaultMalloc == nullptr, "else InitThreadMalloc already called");
// We capture the default malloc early to avoid potential failures later on.
HRESULT hrMalloc = CoGetMalloc(1, &g_pDefaultMalloc);
if (FAILED(hrMalloc)) return hrMalloc;
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->llvm::sys::ThreadLocal<IMalloc>::~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() {
DXASSERT(g_ThreadMallocTls != nullptr, "else prior to DxcInitThreadMalloc or after DxcCleanupThreadMalloc");
IMalloc *pMalloc = DxcGetThreadMallocNoRef();
g_ThreadMallocTls->erase();
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);
}