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