1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5#include "common.h"
6
7#include "utilcode.h"
8#include "corjit.h"
9#include "jithost.h"
10
11void* JitHost::allocateMemory(size_t size)
12{
13 WRAPPER_NO_CONTRACT;
14
15 return ClrAllocInProcessHeap(0, S_SIZE_T(size));
16}
17
18void JitHost::freeMemory(void* block)
19{
20 WRAPPER_NO_CONTRACT;
21
22 ClrFreeInProcessHeap(0, block);
23}
24
25int JitHost::getIntConfigValue(const wchar_t* name, int defaultValue)
26{
27 WRAPPER_NO_CONTRACT;
28
29 // Translate JIT call into runtime configuration query
30 CLRConfig::ConfigDWORDInfo info{ name, defaultValue, CLRConfig::EEConfig_default };
31
32 // Perform a CLRConfig look up on behalf of the JIT.
33 return CLRConfig::GetConfigValue(info);
34}
35
36const wchar_t* JitHost::getStringConfigValue(const wchar_t* name)
37{
38 WRAPPER_NO_CONTRACT;
39
40 // Translate JIT call into runtime configuration query
41 CLRConfig::ConfigStringInfo info{ name, CLRConfig::EEConfig_default };
42
43 // Perform a CLRConfig look up on behalf of the JIT.
44 return CLRConfig::GetConfigValue(info);
45}
46
47void JitHost::freeStringConfigValue(const wchar_t* value)
48{
49 WRAPPER_NO_CONTRACT;
50
51 CLRConfig::FreeConfigString(const_cast<wchar_t*>(value));
52}
53
54//
55// Pool memory blocks for JIT to avoid frequent commit/decommit. The frequent commit/decommit has been
56// shown to slow down the JIT significantly (10% or more). The memory blocks used by the JIT tend to be too big
57// to be covered by pooling done by the default malloc.
58//
59// - Keep up to some limit worth of memory, with loose affinization of memory blocks to threads.
60// - On finalizer thread, release the extra memory that was not used recently.
61//
62
63void* JitHost::allocateSlab(size_t size, size_t* pActualSize)
64{
65 size = max(size, sizeof(Slab));
66
67 Thread* pCurrentThread = GetThread();
68 if (m_pCurrentCachedList != NULL || m_pPreviousCachedList != NULL)
69 {
70 CrstHolder lock(&m_jitSlabAllocatorCrst);
71 Slab** ppCandidate = NULL;
72
73 for (Slab ** ppList = &m_pCurrentCachedList; *ppList != NULL; ppList = &(*ppList)->pNext)
74 {
75 Slab* p = *ppList;
76 if (p->size >= size && p->size <= 4 * size) // Avoid wasting more than 4x memory
77 {
78 ppCandidate = ppList;
79 if (p->affinity == pCurrentThread)
80 break;
81 }
82 }
83
84 if (ppCandidate == NULL)
85 {
86 for (Slab ** ppList = &m_pPreviousCachedList; *ppList != NULL; ppList = &(*ppList)->pNext)
87 {
88 Slab* p = *ppList;
89 if (p->size == size) // Allocation from previous list requires exact match
90 {
91 ppCandidate = ppList;
92 if (p->affinity == pCurrentThread)
93 break;
94 }
95 }
96 }
97
98 if (ppCandidate != NULL)
99 {
100 Slab* p = *ppCandidate;
101 *ppCandidate = p->pNext;
102
103 m_totalCached -= p->size;
104 *pActualSize = p->size;
105
106 return p;
107 }
108 }
109
110 *pActualSize = size;
111 return ClrAllocInProcessHeap(0, S_SIZE_T(size));
112}
113
114void JitHost::freeSlab(void* slab, size_t actualSize)
115{
116 _ASSERTE(actualSize >= sizeof(Slab));
117
118 if (actualSize < 0x100000) // Do not cache blocks that are more than 1MB
119 {
120 CrstHolder lock(&m_jitSlabAllocatorCrst);
121
122 if (m_totalCached < 0x1000000) // Do not cache more than 16MB
123 {
124 m_totalCached += actualSize;
125
126 Slab* pSlab = (Slab*)slab;
127 pSlab->size = actualSize;
128 pSlab->affinity = GetThread();
129 pSlab->pNext = m_pCurrentCachedList;
130 m_pCurrentCachedList = pSlab;
131 return;
132 }
133 }
134
135 ClrFreeInProcessHeap(0, slab);
136}
137
138void JitHost::init()
139{
140 m_jitSlabAllocatorCrst.Init(CrstLeafLock);
141}
142
143void JitHost::reclaim()
144{
145 if (m_pCurrentCachedList != NULL || m_pPreviousCachedList != NULL)
146 {
147 DWORD ticks = ::GetTickCount();
148
149 if (m_lastFlush == 0) // Just update m_lastFlush first time around
150 {
151 m_lastFlush = ticks;
152 return;
153 }
154
155 if ((DWORD)(ticks - m_lastFlush) < 2000) // Flush the free lists every 2 seconds
156 return;
157 m_lastFlush = ticks;
158
159 // Flush all slabs in m_pPreviousCachedList
160 for (;;)
161 {
162 Slab* slabToDelete = NULL;
163
164 {
165 CrstHolder lock(&m_jitSlabAllocatorCrst);
166 slabToDelete = m_pPreviousCachedList;
167 if (slabToDelete == NULL)
168 {
169 m_pPreviousCachedList = m_pCurrentCachedList;
170 m_pCurrentCachedList = NULL;
171 break;
172 }
173 m_totalCached -= slabToDelete->size;
174 m_pPreviousCachedList = slabToDelete->pNext;
175 }
176
177 ClrFreeInProcessHeap(0, slabToDelete);
178 }
179 }
180}
181
182JitHost JitHost::s_theJitHost;
183