1 /* Copyright (C) 2017 Wildfire Games.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining
4 * a copy of this software and associated documentation files (the
5 * "Software"), to deal in the Software without restriction, including
6 * without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sublicense, and/or sell copies of the Software, and to
8 * permit persons to whom the Software is furnished to do so, subject to
9 * the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 #include "precompiled.h"
24 #include "lib/allocators/unique_range.h"
25
26 #include "lib/bits.h" // is_pow2, round_up
27 #include "lib/sysdep/cpu.h" // cpu_AtomicAdd
28 #include "lib/sysdep/rtl.h" // rtl_FreeAligned
29
30
FreeNone(void * UNUSED (pointer),size_t UNUSED (size))31 static void FreeNone(void* UNUSED(pointer), size_t UNUSED(size))
32 {
33 // (providing a deleter function for idxDeleterNone avoids
34 // having to check whether deleters[idxDeleter] == 0)
35 }
36
FreeAligned(void * pointer,size_t UNUSED (size))37 static void FreeAligned(void* pointer, size_t UNUSED(size))
38 {
39 return rtl_FreeAligned(pointer);
40 }
41
42
43 static UniqueRangeDeleter deleters[allocationAlignment] = { FreeNone, FreeAligned };
44
45 static IdxDeleter numDeleters = 2;
46
47
48 // NB: callers should skip this if *idxDeleterOut != 0 (avoids the overhead
49 // of an unnecessary indirect function call)
RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter,volatile IdxDeleter * idxDeleterOut)50 void RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter, volatile IdxDeleter* idxDeleterOut)
51 {
52 ENSURE(deleter);
53
54 if(!cpu_CAS(idxDeleterOut, idxDeleterNone, -1)) // not the first call for this deleter
55 {
56 // wait until an index has been assigned
57 while(*idxDeleterOut <= 0)
58 cpu_Pause();
59 return;
60 }
61
62 const IdxDeleter idxDeleter = cpu_AtomicAdd(&numDeleters, 1);
63 ENSURE(idxDeleter < (IdxDeleter)ARRAY_SIZE(deleters));
64 deleters[idxDeleter] = deleter;
65 COMPILER_FENCE;
66 *idxDeleterOut = idxDeleter;
67 }
68
69
CallUniqueRangeDeleter(void * pointer,size_t size,IdxDeleter idxDeleter)70 NOTHROW_DEFINE void CallUniqueRangeDeleter(void* pointer, size_t size, IdxDeleter idxDeleter)
71 {
72 ASSERT(idxDeleter < numDeleters);
73 // (some deleters do not tolerate null pointers)
74 if(pointer)
75 deleters[idxDeleter](pointer, size);
76 }
77
78
AllocateAligned(size_t size,size_t alignment)79 UniqueRange AllocateAligned(size_t size, size_t alignment)
80 {
81 ENSURE(is_pow2(alignment));
82 alignment = std::max(alignment, allocationAlignment);
83
84 const size_t alignedSize = round_up(size, alignment);
85 const UniqueRange::pointer p = rtl_AllocateAligned(alignedSize, alignment);
86
87 static volatile IdxDeleter idxDeleterAligned;
88 if(idxDeleterAligned == 0) // (optional optimization)
89 RegisterUniqueRangeDeleter(FreeAligned, &idxDeleterAligned);
90
91 return UniqueRange(p, size, idxDeleterAligned);
92 }
93
94
AllocateVM(size_t size,vm::PageType pageType,int prot)95 UniqueRange AllocateVM(size_t size, vm::PageType pageType, int prot)
96 {
97 const UniqueRange::pointer p = vm::Allocate(size, pageType, prot);
98
99 static volatile IdxDeleter idxDeleter;
100 if(idxDeleter == 0) // (optional optimization)
101 RegisterUniqueRangeDeleter(vm::Free, &idxDeleter);
102
103 return UniqueRange(p, size, idxDeleter);
104 }
105