1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef BERBERIS_BASE_FOREVER_ALLOC_H_
18 #define BERBERIS_BASE_FOREVER_ALLOC_H_
19
20 #include <atomic>
21 #include <cstdint>
22
23 #include "berberis/base/checks.h"
24 #include "berberis/base/macros.h"
25 #include "berberis/base/mmap.h"
26 #include "berberis/base/page_size.h"
27
28 namespace berberis {
29
30 // Lock-free allocator for memory that is never freed.
31 // Can be linker-initialized.
32 // Should only be used for object smaller than memory page.
33 class ForeverAllocator {
34 public:
ForeverAllocator()35 constexpr ForeverAllocator() : curr_(0) {}
36
37 template <typename T>
Allocate()38 T* Allocate() {
39 static_assert(sizeof(T) < kPageSize, "ForeverAllocator: bad type");
40 return reinterpret_cast<T*>(AllocateImpl(sizeof(T), alignof(T)));
41 }
42
Allocate(size_t size,size_t align)43 void* Allocate(size_t size, size_t align) {
44 CHECK_GT(size, 0);
45 CHECK_LT(size, static_cast<size_t>(kPageSize));
46 CHECK(IsPowerOf2(align));
47 return reinterpret_cast<void*>(AllocateImpl(size, align));
48 }
49
50 private:
AllocatePage()51 uintptr_t AllocatePage() {
52 void* ptr = MmapOrDie(kPageSize);
53 uintptr_t page = reinterpret_cast<uintptr_t>(ptr);
54 CHECK(IsAlignedPageSize(page));
55
56 uintptr_t curr = 0;
57 if (!curr_.compare_exchange_strong(curr, page, std::memory_order_acq_rel)) {
58 MunmapOrDie(ptr, kPageSize);
59 return curr;
60 }
61 return page;
62 }
63
AllocateImpl(size_t size,size_t align)64 uintptr_t AllocateImpl(size_t size, size_t align) {
65 uintptr_t curr = curr_.load(std::memory_order_acquire);
66 for (;;) {
67 if (!curr) {
68 curr = AllocatePage();
69 }
70
71 uintptr_t res = AlignUp(curr, align);
72 uintptr_t next = res + size;
73 uintptr_t end = AlignDownPageSize(curr) + kPageSize;
74
75 if (end < next) {
76 curr_.compare_exchange_weak(curr, 0, std::memory_order_acquire);
77 continue;
78 }
79 if (end == next) {
80 next = 0;
81 }
82 if (curr_.compare_exchange_weak(curr, next, std::memory_order_acquire)) {
83 return res;
84 }
85 }
86 }
87
88 std::atomic_uintptr_t curr_;
89
90 DISALLOW_COPY_AND_ASSIGN(ForeverAllocator);
91 };
92
93 // Allocate from common ForeverAllocator.
94 // Thread-safe, signal-safe, reentrant.
AllocateForever(size_t size,size_t align)95 inline void* AllocateForever(size_t size, size_t align) {
96 static ForeverAllocator g_forever_allocator;
97 return g_forever_allocator.Allocate(size, align);
98 }
99
100 } // namespace berberis
101
102 #endif // BERBERIS_BASE_FOREVER_ALLOC_H_
103