Return memory to OS right after free (not in the async thread).
[platform/upstream/linaro-gcc.git] / libsanitizer / sanitizer_common / sanitizer_allocator_combined.h
1 //===-- sanitizer_allocator_combined.h --------------------------*- C++ -*-===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // Part of the Sanitizer Allocator.
9 //
10 //===----------------------------------------------------------------------===//
11 #ifndef SANITIZER_ALLOCATOR_H
12 #error This file must be included inside sanitizer_allocator.h
13 #endif
14
15 // This class implements a complete memory allocator by using two
16 // internal allocators:
17 // PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
18 //  When allocating 2^x bytes it should return 2^x aligned chunk.
19 // PrimaryAllocator is used via a local AllocatorCache.
20 // SecondaryAllocator can allocate anything, but is not efficient.
21 template <class PrimaryAllocator, class AllocatorCache,
22           class SecondaryAllocator>  // NOLINT
23 class CombinedAllocator {
24  public:
25   void InitCommon(bool may_return_null, s32 release_to_os_interval_ms) {
26     primary_.Init(release_to_os_interval_ms);
27     atomic_store(&may_return_null_, may_return_null, memory_order_relaxed);
28   }
29
30   void InitLinkerInitialized(
31       bool may_return_null, s32 release_to_os_interval_ms) {
32     secondary_.InitLinkerInitialized(may_return_null);
33     stats_.InitLinkerInitialized();
34     InitCommon(may_return_null, release_to_os_interval_ms);
35   }
36
37   void Init(bool may_return_null, s32 release_to_os_interval_ms) {
38     secondary_.Init(may_return_null);
39     stats_.Init();
40     InitCommon(may_return_null, release_to_os_interval_ms);
41   }
42
43   void *Allocate(AllocatorCache *cache, uptr size, uptr alignment,
44                  bool cleared = false, bool check_rss_limit = false) {
45     // Returning 0 on malloc(0) may break a lot of code.
46     if (size == 0)
47       size = 1;
48     if (size + alignment < size) return ReturnNullOrDieOnBadRequest();
49     if (check_rss_limit && RssLimitIsExceeded()) return ReturnNullOrDieOnOOM();
50     if (alignment > 8)
51       size = RoundUpTo(size, alignment);
52     void *res;
53     bool from_primary = primary_.CanAllocate(size, alignment);
54     if (from_primary)
55       res = cache->Allocate(&primary_, primary_.ClassID(size));
56     else
57       res = secondary_.Allocate(&stats_, size, alignment);
58     if (alignment > 8)
59       CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
60     if (cleared && res && from_primary)
61       internal_bzero_aligned16(res, RoundUpTo(size, 16));
62     return res;
63   }
64
65   bool MayReturnNull() const {
66     return atomic_load(&may_return_null_, memory_order_acquire);
67   }
68
69   void *ReturnNullOrDieOnBadRequest() {
70     if (MayReturnNull())
71       return nullptr;
72     ReportAllocatorCannotReturnNull(false);
73   }
74
75   void *ReturnNullOrDieOnOOM() {
76     if (MayReturnNull()) return nullptr;
77     ReportAllocatorCannotReturnNull(true);
78   }
79
80   void SetMayReturnNull(bool may_return_null) {
81     secondary_.SetMayReturnNull(may_return_null);
82     atomic_store(&may_return_null_, may_return_null, memory_order_release);
83   }
84
85   s32 ReleaseToOSIntervalMs() const {
86     return primary_.ReleaseToOSIntervalMs();
87   }
88
89   void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) {
90     primary_.SetReleaseToOSIntervalMs(release_to_os_interval_ms);
91   }
92
93   bool RssLimitIsExceeded() {
94     return atomic_load(&rss_limit_is_exceeded_, memory_order_acquire);
95   }
96
97   void SetRssLimitIsExceeded(bool rss_limit_is_exceeded) {
98     atomic_store(&rss_limit_is_exceeded_, rss_limit_is_exceeded,
99                  memory_order_release);
100   }
101
102   void Deallocate(AllocatorCache *cache, void *p) {
103     if (!p) return;
104     if (primary_.PointerIsMine(p))
105       cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
106     else
107       secondary_.Deallocate(&stats_, p);
108   }
109
110   void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
111                    uptr alignment) {
112     if (!p)
113       return Allocate(cache, new_size, alignment);
114     if (!new_size) {
115       Deallocate(cache, p);
116       return nullptr;
117     }
118     CHECK(PointerIsMine(p));
119     uptr old_size = GetActuallyAllocatedSize(p);
120     uptr memcpy_size = Min(new_size, old_size);
121     void *new_p = Allocate(cache, new_size, alignment);
122     if (new_p)
123       internal_memcpy(new_p, p, memcpy_size);
124     Deallocate(cache, p);
125     return new_p;
126   }
127
128   bool PointerIsMine(void *p) {
129     if (primary_.PointerIsMine(p))
130       return true;
131     return secondary_.PointerIsMine(p);
132   }
133
134   bool FromPrimary(void *p) {
135     return primary_.PointerIsMine(p);
136   }
137
138   void *GetMetaData(const void *p) {
139     if (primary_.PointerIsMine(p))
140       return primary_.GetMetaData(p);
141     return secondary_.GetMetaData(p);
142   }
143
144   void *GetBlockBegin(const void *p) {
145     if (primary_.PointerIsMine(p))
146       return primary_.GetBlockBegin(p);
147     return secondary_.GetBlockBegin(p);
148   }
149
150   // This function does the same as GetBlockBegin, but is much faster.
151   // Must be called with the allocator locked.
152   void *GetBlockBeginFastLocked(void *p) {
153     if (primary_.PointerIsMine(p))
154       return primary_.GetBlockBegin(p);
155     return secondary_.GetBlockBeginFastLocked(p);
156   }
157
158   uptr GetActuallyAllocatedSize(void *p) {
159     if (primary_.PointerIsMine(p))
160       return primary_.GetActuallyAllocatedSize(p);
161     return secondary_.GetActuallyAllocatedSize(p);
162   }
163
164   uptr TotalMemoryUsed() {
165     return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
166   }
167
168   void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
169
170   void InitCache(AllocatorCache *cache) {
171     cache->Init(&stats_);
172   }
173
174   void DestroyCache(AllocatorCache *cache) {
175     cache->Destroy(&primary_, &stats_);
176   }
177
178   void SwallowCache(AllocatorCache *cache) {
179     cache->Drain(&primary_);
180   }
181
182   void GetStats(AllocatorStatCounters s) const {
183     stats_.Get(s);
184   }
185
186   void PrintStats() {
187     primary_.PrintStats();
188     secondary_.PrintStats();
189   }
190
191   // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
192   // introspection API.
193   void ForceLock() {
194     primary_.ForceLock();
195     secondary_.ForceLock();
196   }
197
198   void ForceUnlock() {
199     secondary_.ForceUnlock();
200     primary_.ForceUnlock();
201   }
202
203   bool PointsIntoChunk(const void *p) {
204     if (primary_.PointerIsMine(p))
205       return primary_.PointsIntoChunk(p);
206     return true;
207   }
208
209   // Iterate over all existing chunks.
210   // The allocator must be locked when calling this function.
211   void ForEachChunk(ForEachChunkCallback callback, void *arg) {
212     primary_.ForEachChunk(callback, arg);
213     secondary_.ForEachChunk(callback, arg);
214   }
215
216  private:
217   PrimaryAllocator primary_;
218   SecondaryAllocator secondary_;
219   AllocatorGlobalStats stats_;
220   atomic_uint8_t may_return_null_;
221   atomic_uint8_t rss_limit_is_exceeded_;
222 };