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