• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
27 namespace berberis {
28 
29 // Lock-free allocator for memory that is never freed.
30 // Can be linker-initialized.
31 // Should only be used for object smaller than memory page.
32 class ForeverAllocator {
33  public:
ForeverAllocator()34   constexpr ForeverAllocator() : curr_(0) {}
35 
36   template <typename T>
Allocate()37   T* Allocate() {
38     static_assert(sizeof(T) < kPageSize, "ForeverAllocator: bad type");
39     return reinterpret_cast<T*>(AllocateImpl(sizeof(T), alignof(T)));
40   }
41 
Allocate(size_t size,size_t align)42   void* Allocate(size_t size, size_t align) {
43     CHECK_GT(size, 0);
44     CHECK_LT(size, kPageSize);
45     CHECK(IsPowerOf2(align));
46     return reinterpret_cast<void*>(AllocateImpl(size, align));
47   }
48 
49  private:
AllocatePage()50   uintptr_t AllocatePage() {
51     void* ptr = MmapOrDie(kPageSize);
52     uintptr_t page = reinterpret_cast<uintptr_t>(ptr);
53     CHECK(IsAlignedPageSize(page));
54 
55     uintptr_t curr = 0;
56     if (!curr_.compare_exchange_strong(curr, page, std::memory_order_acq_rel)) {
57       MunmapOrDie(ptr, kPageSize);
58       return curr;
59     }
60     return page;
61   }
62 
AllocateImpl(size_t size,size_t align)63   uintptr_t AllocateImpl(size_t size, size_t align) {
64     uintptr_t curr = curr_.load(std::memory_order_acquire);
65     for (;;) {
66       if (!curr) {
67         curr = AllocatePage();
68       }
69 
70       uintptr_t res = AlignUp(curr, align);
71       uintptr_t next = res + size;
72       uintptr_t end = AlignDownPageSize(curr) + kPageSize;
73 
74       if (end < next) {
75         curr_.compare_exchange_weak(curr, 0, std::memory_order_acquire);
76         continue;
77       }
78       if (end == next) {
79         next = 0;
80       }
81       if (curr_.compare_exchange_weak(curr, next, std::memory_order_acquire)) {
82         return res;
83       }
84     }
85   }
86 
87   std::atomic_uintptr_t curr_;
88 
89   DISALLOW_COPY_AND_ASSIGN(ForeverAllocator);
90 };
91 
92 // Allocate from common ForeverAllocator.
93 // Thread-safe, signal-safe, reentrant.
AllocateForever(size_t size,size_t align)94 inline void* AllocateForever(size_t size, size_t align) {
95   static ForeverAllocator g_forever_allocator;
96   return g_forever_allocator.Allocate(size, align);
97 }
98 
99 }  // namespace berberis
100 
101 #endif  // BERBERIS_BASE_FOREVER_ALLOC_H_
102