Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / alloc.cpp
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 "jitpch.h"
6
7 #if defined(_MSC_VER)
8 #pragma hdrstop
9 #endif // defined(_MSC_VER)
10
11 //------------------------------------------------------------------------
12 // PooledAllocator:
13 //    This subclass of `ArenaAllocator` is a singleton that always keeps
14 //    a single default-sized page allocated. We try to use the singleton
15 //    allocator as often as possible (i.e. for all non-concurrent
16 //    method compilations).
17 class PooledAllocator : public ArenaAllocator
18 {
19 private:
20     enum
21     {
22         POOLED_ALLOCATOR_NOTINITIALIZED = 0,
23         POOLED_ALLOCATOR_IN_USE         = 1,
24         POOLED_ALLOCATOR_AVAILABLE      = 2,
25         POOLED_ALLOCATOR_SHUTDOWN       = 3,
26     };
27
28     static PooledAllocator s_pooledAllocator;
29     static LONG            s_pooledAllocatorState;
30
31     PooledAllocator() : ArenaAllocator()
32     {
33     }
34     PooledAllocator(IEEMemoryManager* memoryManager);
35
36     PooledAllocator(const PooledAllocator& other) = delete;
37     PooledAllocator& operator=(const PooledAllocator& other) = delete;
38
39 public:
40     PooledAllocator& operator=(PooledAllocator&& other);
41
42     void destroy() override;
43
44     static void shutdown();
45
46     static ArenaAllocator* getPooledAllocator(IEEMemoryManager* memoryManager);
47 };
48
49 size_t ArenaAllocator::s_defaultPageSize = 0;
50
51 //------------------------------------------------------------------------
52 // ArenaAllocator::bypassHostAllocator:
53 //    Indicates whether or not the ArenaAllocator should bypass the JIT
54 //    host when allocating memory for arena pages.
55 //
56 // Return Value:
57 //    True if the JIT should bypass the JIT host; false otherwise.
58 bool ArenaAllocator::bypassHostAllocator()
59 {
60 #if defined(DEBUG)
61     // When JitDirectAlloc is set, all JIT allocations requests are forwarded
62     // directly to the OS. This allows taking advantage of pageheap and other gflag
63     // knobs for ensuring that we do not have buffer overruns in the JIT.
64
65     return JitConfig.JitDirectAlloc() != 0;
66 #else  // defined(DEBUG)
67     return false;
68 #endif // !defined(DEBUG)
69 }
70
71 //------------------------------------------------------------------------
72 // ArenaAllocator::getDefaultPageSize:
73 //    Returns the default size of an arena page.
74 //
75 // Return Value:
76 //    The default size of an arena page.
77 size_t ArenaAllocator::getDefaultPageSize()
78 {
79     return s_defaultPageSize;
80 }
81
82 //------------------------------------------------------------------------
83 // ArenaAllocator::ArenaAllocator:
84 //    Default-constructs an arena allocator.
85 ArenaAllocator::ArenaAllocator()
86     : m_memoryManager(nullptr)
87     , m_firstPage(nullptr)
88     , m_lastPage(nullptr)
89     , m_nextFreeByte(nullptr)
90     , m_lastFreeByte(nullptr)
91 {
92 }
93
94 //------------------------------------------------------------------------
95 // ArenaAllocator::ArenaAllocator:
96 //    Constructs an arena allocator.
97 //
98 // Arguments:
99 //    memoryManager - The `IEEMemoryManager` instance that will be used to
100 //                    allocate memory for arena pages.
101 ArenaAllocator::ArenaAllocator(IEEMemoryManager* memoryManager)
102     : m_memoryManager(memoryManager)
103     , m_firstPage(nullptr)
104     , m_lastPage(nullptr)
105     , m_nextFreeByte(nullptr)
106     , m_lastFreeByte(nullptr)
107 {
108     assert(getDefaultPageSize() != 0);
109     assert(isInitialized());
110 }
111
112 //------------------------------------------------------------------------
113 // ArenaAllocator::operator=:
114 //    Move-assigns a `ArenaAllocator`.
115 ArenaAllocator& ArenaAllocator::operator=(ArenaAllocator&& other)
116 {
117     assert(!isInitialized());
118
119     m_memoryManager = other.m_memoryManager;
120     m_firstPage     = other.m_firstPage;
121     m_lastPage      = other.m_lastPage;
122     m_nextFreeByte  = other.m_nextFreeByte;
123     m_lastFreeByte  = other.m_lastFreeByte;
124
125     other.m_memoryManager = nullptr;
126     other.m_firstPage     = nullptr;
127     other.m_lastPage      = nullptr;
128     other.m_nextFreeByte  = nullptr;
129     other.m_lastFreeByte  = nullptr;
130
131     return *this;
132 }
133
134 bool ArenaAllocator::isInitialized()
135 {
136     return m_memoryManager != nullptr;
137 }
138
139 //------------------------------------------------------------------------
140 // ArenaAllocator::allocateNewPage:
141 //    Allocates a new arena page.
142 //
143 // Arguments:
144 //    size - The number of bytes that were requested by the allocation
145 //           that triggered this request to allocate a new arena page.
146 //
147 // Return Value:
148 //    A pointer to the first usable byte of the newly allocated page.
149 void* ArenaAllocator::allocateNewPage(size_t size, bool canThrow)
150 {
151     assert(isInitialized());
152
153     size_t pageSize = sizeof(PageDescriptor) + size;
154
155     // Check for integer overflow
156     if (pageSize < size)
157     {
158         if (canThrow)
159         {
160             NOMEM();
161         }
162
163         return nullptr;
164     }
165
166     // If the current page is now full, update a few statistics
167     if (m_lastPage != nullptr)
168     {
169         // Undo the "+=" done in allocateMemory()
170         m_nextFreeByte -= size;
171
172         // Save the actual used size of the page
173         m_lastPage->m_usedBytes = m_nextFreeByte - m_lastPage->m_contents;
174     }
175
176     // Round up to a default-sized page if necessary
177     if (pageSize <= s_defaultPageSize)
178     {
179         pageSize = s_defaultPageSize;
180     }
181
182     // Round to the nearest multiple of OS page size if necessary
183     if (!bypassHostAllocator())
184     {
185         pageSize = roundUp(pageSize, DEFAULT_PAGE_SIZE);
186     }
187
188     // Allocate the new page
189     PageDescriptor* newPage = (PageDescriptor*)allocateHostMemory(pageSize);
190     if (newPage == nullptr)
191     {
192         if (canThrow)
193         {
194             NOMEM();
195         }
196
197         return nullptr;
198     }
199
200     // Append the new page to the end of the list
201     newPage->m_next      = nullptr;
202     newPage->m_pageBytes = pageSize;
203     newPage->m_previous  = m_lastPage;
204     newPage->m_usedBytes = 0; // m_usedBytes is meaningless until a new page is allocated.
205                               // Instead of letting it contain garbage (so to confuse us),
206                               // set it to zero.
207
208     if (m_lastPage != nullptr)
209     {
210         m_lastPage->m_next = newPage;
211     }
212     else
213     {
214         m_firstPage = newPage;
215     }
216
217     m_lastPage = newPage;
218
219     // Adjust the next/last free byte pointers
220     m_nextFreeByte = newPage->m_contents + size;
221     m_lastFreeByte = (BYTE*)newPage + pageSize;
222     assert((m_lastFreeByte - m_nextFreeByte) >= 0);
223
224     return newPage->m_contents;
225 }
226
227 //------------------------------------------------------------------------
228 // ArenaAllocator::destroy:
229 //    Performs any necessary teardown for an `ArenaAllocator`.
230 void ArenaAllocator::destroy()
231 {
232     assert(isInitialized());
233
234     // Free all of the allocated pages
235     for (PageDescriptor *page = m_firstPage, *next; page != nullptr; page = next)
236     {
237         next = page->m_next;
238         freeHostMemory(page);
239     }
240
241     // Clear out the allocator's fields
242     m_memoryManager = nullptr;
243     m_firstPage     = nullptr;
244     m_lastPage      = nullptr;
245     m_nextFreeByte  = nullptr;
246     m_lastFreeByte  = nullptr;
247 }
248
249 // The debug version of the allocator may allocate directly from the
250 // OS rather than going through the hosting APIs. In order to do so,
251 // it must undef the macros that are usually in place to prevent
252 // accidental uses of the OS allocator.
253 #if defined(DEBUG)
254 #undef GetProcessHeap
255 #undef HeapAlloc
256 #undef HeapFree
257 #endif
258
259 //------------------------------------------------------------------------
260 // ArenaAllocator::allocateHostMemory:
261 //    Allocates memory from the host (or the OS if `bypassHostAllocator()`
262 //    returns `true`).
263 //
264 // Arguments:
265 //    size - The number of bytes to allocate.
266 //
267 // Return Value:
268 //    A pointer to the allocated memory.
269 void* ArenaAllocator::allocateHostMemory(size_t size)
270 {
271     assert(isInitialized());
272
273 #if defined(DEBUG)
274     if (bypassHostAllocator())
275     {
276         return ::HeapAlloc(GetProcessHeap(), 0, size);
277     }
278     else
279     {
280         return ClrAllocInProcessHeap(0, S_SIZE_T(size));
281     }
282 #else  // defined(DEBUG)
283     return m_memoryManager->ClrVirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
284 #endif // !defined(DEBUG)
285 }
286
287 //------------------------------------------------------------------------
288 // ArenaAllocator::freeHostMemory:
289 //    Frees memory allocated by a previous call to `allocateHostMemory`.
290 //
291 // Arguments:
292 //    block - A pointer to the memory to free.
293 void ArenaAllocator::freeHostMemory(void* block)
294 {
295     assert(isInitialized());
296
297 #if defined(DEBUG)
298     if (bypassHostAllocator())
299     {
300         ::HeapFree(GetProcessHeap(), 0, block);
301     }
302     else
303     {
304         ClrFreeInProcessHeap(0, block);
305     }
306 #else  // defined(DEBUG)
307     m_memoryManager->ClrVirtualFree(block, 0, MEM_RELEASE);
308 #endif // !defined(DEBUG)
309 }
310
311 //------------------------------------------------------------------------
312 // ArenaAllocator::getTotalBytesAllocated:
313 //    Gets the total number of bytes allocated for all of the arena pages
314 //    for an `ArenaAllocator`.
315 //
316 // Return Value:
317 //    See above.
318 size_t ArenaAllocator::getTotalBytesAllocated()
319 {
320     assert(isInitialized());
321
322     size_t bytes = 0;
323     for (PageDescriptor* page = m_firstPage; page != nullptr; page = page->m_next)
324     {
325         bytes += page->m_pageBytes;
326     }
327
328     return bytes;
329 }
330
331 //------------------------------------------------------------------------
332 // ArenaAllocator::getTotalBytesAllocated:
333 //    Gets the total number of bytes used in all of the arena pages for
334 //    an `ArenaAllocator`.
335 //
336 // Return Value:
337 //    See above.
338 //
339 // Notes:
340 //    An arena page may have unused space at the very end. This happens
341 //    when an allocation request comes in (via a call to `allocateMemory`)
342 //    that will not fit in the remaining bytes for the current page.
343 //    Another way to understand this method is as returning the total
344 //    number of bytes allocated for arena pages minus the number of bytes
345 //    that are unused across all area pages.
346 size_t ArenaAllocator::getTotalBytesUsed()
347 {
348     assert(isInitialized());
349
350     if (m_lastPage != nullptr)
351     {
352         m_lastPage->m_usedBytes = m_nextFreeByte - m_lastPage->m_contents;
353     }
354
355     size_t bytes = 0;
356     for (PageDescriptor* page = m_firstPage; page != nullptr; page = page->m_next)
357     {
358         bytes += page->m_usedBytes;
359     }
360
361     return bytes;
362 }
363
364 //------------------------------------------------------------------------
365 // ArenaAllocator::startup:
366 //    Performs any necessary initialization for the arena allocator
367 //    subsystem.
368 void ArenaAllocator::startup()
369 {
370     s_defaultPageSize = bypassHostAllocator() ? (size_t)MIN_PAGE_SIZE : (size_t)DEFAULT_PAGE_SIZE;
371 }
372
373 //------------------------------------------------------------------------
374 // ArenaAllocator::shutdown:
375 //    Performs any necessary teardown for the arena allocator subsystem.
376 void ArenaAllocator::shutdown()
377 {
378     PooledAllocator::shutdown();
379 }
380
381 PooledAllocator PooledAllocator::s_pooledAllocator;
382 LONG            PooledAllocator::s_pooledAllocatorState = POOLED_ALLOCATOR_NOTINITIALIZED;
383
384 //------------------------------------------------------------------------
385 // PooledAllocator::PooledAllocator:
386 //    Constructs a `PooledAllocator`.
387 PooledAllocator::PooledAllocator(IEEMemoryManager* memoryManager) : ArenaAllocator(memoryManager)
388 {
389 }
390
391 //------------------------------------------------------------------------
392 // PooledAllocator::operator=:
393 //    Move-assigns a `PooledAllocator`.
394 PooledAllocator& PooledAllocator::operator=(PooledAllocator&& other)
395 {
396     *((ArenaAllocator*)this) = std::move((ArenaAllocator &&)other);
397     return *this;
398 }
399
400 //------------------------------------------------------------------------
401 // PooledAllocator::shutdown:
402 //    Performs any necessary teardown for the pooled allocator.
403 //
404 // Notes:
405 //    If the allocator has been initialized and is in use when this method is called,
406 //    it is up to whatever is using the pooled allocator to call `destroy` in order
407 //    to free its memory.
408 void PooledAllocator::shutdown()
409 {
410     LONG oldState = InterlockedExchange(&s_pooledAllocatorState, POOLED_ALLOCATOR_SHUTDOWN);
411     switch (oldState)
412     {
413         case POOLED_ALLOCATOR_NOTINITIALIZED:
414         case POOLED_ALLOCATOR_SHUTDOWN:
415         case POOLED_ALLOCATOR_IN_USE:
416             return;
417
418         case POOLED_ALLOCATOR_AVAILABLE:
419             // The pooled allocator was initialized and not in use; we must destroy it.
420             s_pooledAllocator.destroy();
421             break;
422     }
423 }
424
425 //------------------------------------------------------------------------
426 // PooledAllocator::getPooledAllocator:
427 //    Returns the pooled allocator if it is not already in use.
428 //
429 // Arguments:
430 //    memoryManager: The `IEEMemoryManager` instance in use by the caller.
431 //
432 // Return Value:
433 //    A pointer to the pooled allocator if it is available or `nullptr`
434 //    if it is already in use.
435 //
436 // Notes:
437 //    Calling `destroy` on the returned allocator will return it to the
438 //    pool.
439 ArenaAllocator* PooledAllocator::getPooledAllocator(IEEMemoryManager* memoryManager)
440 {
441     LONG oldState = InterlockedExchange(&s_pooledAllocatorState, POOLED_ALLOCATOR_IN_USE);
442     switch (oldState)
443     {
444         case POOLED_ALLOCATOR_IN_USE:
445         case POOLED_ALLOCATOR_SHUTDOWN:
446             // Either the allocator is in use or this call raced with a call to `shutdown`.
447             // Return `nullptr`.
448             return nullptr;
449
450         case POOLED_ALLOCATOR_AVAILABLE:
451             if (s_pooledAllocator.m_memoryManager != memoryManager)
452             {
453                 // The allocator is available, but it was initialized with a different
454                 // memory manager. Release it and return `nullptr`.
455                 InterlockedExchange(&s_pooledAllocatorState, POOLED_ALLOCATOR_AVAILABLE);
456                 return nullptr;
457             }
458
459             return &s_pooledAllocator;
460
461         case POOLED_ALLOCATOR_NOTINITIALIZED:
462         {
463             PooledAllocator allocator(memoryManager);
464             if (allocator.allocateNewPage(0, false) == nullptr)
465             {
466                 // Failed to grab the initial memory page.
467                 InterlockedExchange(&s_pooledAllocatorState, POOLED_ALLOCATOR_NOTINITIALIZED);
468                 return nullptr;
469             }
470
471             s_pooledAllocator = std::move(allocator);
472         }
473
474             return &s_pooledAllocator;
475
476         default:
477             assert(!"Unknown pooled allocator state");
478             unreached();
479     }
480 }
481
482 //------------------------------------------------------------------------
483 // PooledAllocator::destroy:
484 //    Performs any necessary teardown for an `PooledAllocator` and returns the allocator
485 //    to the pool.
486 void PooledAllocator::destroy()
487 {
488     assert(isInitialized());
489     assert(this == &s_pooledAllocator);
490     assert(s_pooledAllocatorState == POOLED_ALLOCATOR_IN_USE || s_pooledAllocatorState == POOLED_ALLOCATOR_SHUTDOWN);
491     assert(m_firstPage != nullptr);
492
493     // Free all but the first allocated page
494     for (PageDescriptor *page = m_firstPage->m_next, *next; page != nullptr; page = next)
495     {
496         next = page->m_next;
497         freeHostMemory(page);
498     }
499
500     // Reset the relevant state to point back to the first byte of the first page
501     m_firstPage->m_next = nullptr;
502     m_lastPage          = m_firstPage;
503     m_nextFreeByte      = m_firstPage->m_contents;
504     m_lastFreeByte      = (BYTE*)m_firstPage + m_firstPage->m_pageBytes;
505
506     assert(getTotalBytesAllocated() == s_defaultPageSize);
507
508     // If we've already been shut down, free the first page. Otherwise, return the allocator to the pool.
509     if (s_pooledAllocatorState == POOLED_ALLOCATOR_SHUTDOWN)
510     {
511         ArenaAllocator::destroy();
512     }
513     else
514     {
515         InterlockedExchange(&s_pooledAllocatorState, POOLED_ALLOCATOR_AVAILABLE);
516     }
517 }
518
519 //------------------------------------------------------------------------
520 // ArenaAllocator::getPooledAllocator:
521 //    Returns the pooled allocator if it is not already in use.
522 //
523 // Arguments:
524 //    memoryManager: The `IEEMemoryManager` instance in use by the caller.
525 //
526 // Return Value:
527 //    A pointer to the pooled allocator if it is available or `nullptr`
528 //    if it is already in use.
529 //
530 // Notes:
531 //    Calling `destroy` on the returned allocator will return it to the
532 //    pool.
533 ArenaAllocator* ArenaAllocator::getPooledAllocator(IEEMemoryManager* memoryManager)
534 {
535     return PooledAllocator::getPooledAllocator(memoryManager);
536 }