Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(156)

Side by Side Diff: base/metrics/persistent_memory_allocator.h

Issue 1410213004: Create "persistent memory allocator" for persisting and sharing objects. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addessed more comments by Alexander Potapenko Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_
6 #define BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_
7
8 #include <stdint.h>
9 #include <atomic>
10 #include <string>
11
12 #include "base/atomicops.h"
13 #include "base/base_export.h"
14 #include "base/gtest_prod_util.h"
15 #include "base/macros.h"
16 #include "base/memory/scoped_ptr.h"
17
18 namespace base {
19
20 class HistogramBase;
21 class MemoryMappedFile;
22
23 // Simple allocator for pieces of a memory block that may be persistent
24 // to some storage or shared across multiple processes. This class resides
25 // under base/metrics because it was written for that purpose. It is,
26 // however, fully general-purpose and can be freely moved to base/memory
27 // if other uses are found.
28 //
29 // This class provides for thread-secure (i.e. safe against other threads
30 // or processes that may be compromised and thus have malicious intent)
31 // allocation of memory within a designated block and also a mechanism by
32 // which other threads can learn of these allocations.
33 //
34 // There is (currently) no way to release an allocated block of data because
35 // doing so would risk invalidating pointers held by other processes and
36 // greatly complicate the allocation algorithm.
37 //
38 // Construction of this object can accept new, clean (i.e. zeroed) memory
39 // or previously initialized memory. In the first case, construction must
40 // be allowed to complete before letting other allocators attach to the same
41 // segment. In other words, don't share the segment until at least one
42 // allocator has been attached to it.
43 //
44 // Note that memory not in active use is not accessed so it is possible to
45 // use virtual memory, including memory-mapped files, as backing storage with
46 // the OS "pinning" new (zeroed) physical RAM pages only as they are needed.
47 class BASE_EXPORT PersistentMemoryAllocator {
48 public:
49 typedef uint32_t Reference;
50
51 // Internal state information when iterating over memory allocations.
52 class Iterator {
53 public:
54 Iterator() : last(0) {}
55
56 bool operator==(const Iterator& rhs) const { return last == rhs.last; }
57 bool operator!=(const Iterator& rhs) const { return last != rhs.last; }
58
59 void clear() { last = 0; }
60 bool is_clear() const { return last == 0; }
61
62 private:
63 friend class PersistentMemoryAllocator;
64
65 Reference last;
66 uint32_t niter;
67 };
68
69 // Returned information about the internal state of the heap.
70 struct MemoryInfo {
71 size_t total;
72 size_t free;
73 };
74
75 enum : uint32_t {
76 kTypeIdAny = 0 // Match any type-id inside GetAsObject().
77 };
78
79 // The allocator operates on any arbitrary block of memory. Creation and
80 // persisting or sharing of that block with another process is the
81 // responsibility of the caller. The allocator needs to know only the
82 // block's |base| address, the total |size| of the block, and any internal
83 // |page| size (zero if not paged) across which allocations should not span.
84 // The |id| is an arbitrary value the caller can use to identify a
85 // particular memory segment. It will only be loaded during the initial
86 // creation of the segment and can be checked by the caller for consistency.
87 // The |name|, if provided, is used to distinguish histograms for this
88 // allocator. Only the primary owner of the segment should define this value;
89 // other processes can learn it from the shared state. If the underlying
90 // memory is |readonly| then no changes will be made to it. The resulting
91 // object should be stored as a "const" pointer.
92 //
93 // PersistentMemoryAllocator does NOT take ownership of the memory block.
94 // The caller must manage it and ensure it stays available throughout the
95 // lifetime of this object.
96 //
97 // Memory segments for sharing must have had an allocator attached to them
98 // before actually being shared. If the memory segment was just created, it
99 // should be zeroed before being passed here. If it was an existing segment,
100 // the values here will be compared to copies stored in the shared segment
101 // as a guard against corruption.
102 //
103 // Make sure that the memory segment is acceptable (see IsMemoryAcceptable()
104 // method below) before construction if the definition of the segment can
105 // vary in any way at run-time. Invalid memory segments will cause a crash.
106 PersistentMemoryAllocator(void* base, size_t size, size_t page_size,
107 uint32_t id, const std::string& name,
108 bool readonly);
109 virtual ~PersistentMemoryAllocator();
110
111 // Check if memory segment is acceptable for creation of an Allocator. This
112 // doesn't do any analysis of the data and so doesn't guarantee that the
113 // contents are valid, just that the pramaters won't cause the program to
chrisha 2016/01/18 22:40:28 parameters*
bcwhite 2016/01/19 19:49:40 Done.
114 // abort. The IsCorrupt() method will report detection of data problems
115 // found during construction and general operation.
116 static bool IsMemoryAcceptable(const void* data, size_t size,
117 size_t page_size, bool readonly);
118
119 // Get the internal identifier for this persistent memory segment.
120 uint32_t Id() const;
121
122 // Get the internal name of this allocator (possibly an empty string).
123 const char* Name() const;
124
125 // Is this segment open only for read?
126 bool IsReadonly() { return readonly_; }
127
128 // Create histograms for tracking memory use and allocation sizes. This
129 // is done seperately from construction for situations such as when the
130 // histograms will be backed my memory provided by this very allocator.
chrisha 2016/01/18 22:40:28 backed by*
bcwhite 2016/01/19 19:49:40 Done.
131 void CreateHistograms(const std::string& name);
chrisha 2016/01/18 22:40:28 I'm not sure I follow what this does? CreateHistog
bcwhite 2016/01/19 19:49:40 Done.
132
133 // Direct access to underlying memory segment. If the segment is shared
134 // across threads or processes, reading data through these values does
135 // not guarantee consistency. Use with care. Do not write.
136 const void* data() const { return const_cast<const char*>(mem_base_); }
137 size_t length() const { return mem_size_; }
138 size_t used() const;
139
140 // Get an object referenced by a |ref|. For safety reasons, the |type_id|
141 // code and size-of(|T|) are compared to ensure the reference is valid
142 // and cannot return an object outside of the memory segment. A |type_id| of
143 // zero will match any though the size is still checked. NULL is returned
chrisha 2016/01/18 22:40:28 kTypeIdAny
bcwhite 2016/01/19 19:49:40 Done.
144 // if any problem is detected, such as corrupted storage or incorrect
145 // parameters. Callers MUST check that the returned value is not-null EVERY
146 // TIME before accessing it or risk crashing! Once dereferenced, the pointer
chrisha 2016/01/18 22:40:28 Remove double space after .
bcwhite 2016/01/19 19:49:40 Done.
147 // is safe to reuse forever.
148 //
149 // NOTE: Though this method will guarantee that an object of the specified
150 // type can be accessed without going outside the bounds of the memory
151 // segment, it makes no guarantees of the validity of the data within the
152 // object itself. If it is expected that the contents of the segment could
153 // be compromised with malicious intent, the object must be hardened as well.
154 //
155 // Though the persistent data may be "volatile" if it is shared with
156 // other processes, such is not necessarily the case. The internal
157 // "volatile" designation is discarded so as to not propagate the viral
158 // nature of that keyword to the caller. It can add it back, if necessary,
159 // based on knowledge of how the allocator is being used.
160 template <typename T>
161 T* GetAsObject(Reference ref, uint32_t type_id) {
162 static_assert(!std::is_polymorphic<T>::value, "no polymorphic objects");
163 return const_cast<T*>(
164 reinterpret_cast<volatile T*>(GetBlockData(ref, type_id, sizeof(T))));
165 }
166 template <typename T>
167 const T* GetAsObject(Reference ref, uint32_t type_id) const {
168 static_assert(!std::is_polymorphic<T>::value, "no polymorphic objects");
169 return const_cast<const T*>(
170 reinterpret_cast<const volatile T*>(GetBlockData(
171 ref, type_id, sizeof(T))));
172 }
173
174 // Get the number of bytes allocated to a block. This is useful when storing
175 // arrays in order to validate the ending boundary. The returned value will
176 // include any padding added to achieve the required alignment and so could
177 // be larger than given in the original Allocate() request.
178 size_t GetAllocSize(Reference ref) const;
179
180 // Access the internal "type" of an object. This generally isn't necessary
181 // but can be used to "clear" the type and so effectively mark it as deleted
182 // even though the memory stays valid and allocated.
183 uint32_t GetType(Reference ref) const;
184 void SetType(Reference ref, uint32_t type_id);
185
186 // Reserve space in the memory segment of the desired |size| and |type_id|.
187 // A return value of zero indicates the allocation failed, otherwise the
188 // returned reference can be used by any process to get a real pointer via
189 // the GetAsObject() call.
190 Reference Allocate(size_t size, uint32_t type_id);
191
192 // Allocated objects can be added to an internal list that can then be
193 // iterated over by other processes. If an allocated object can be found
194 // another way, such as by having its reference within a different object
195 // that will be made iterable, then this call is not necessary. This always
196 // succeeds unless corruption is detected; check IsCorrupted() to find out.
197 // Once an object is made iterable, its position in iteration can never
198 // change; new iterable objects will always be added after it in the series.
199 void MakeIterable(Reference ref);
200
201 // Get the information about the amount of free space in the allocator. The
202 // amount of free space should be treated as approximate due to extras from
203 // alignment and metadata. Concurrent allocations from other threads will
204 // also make the true amount less than what is reported.
205 void GetMemoryInfo(MemoryInfo* meminfo) const;
206
207 // Iterating uses a |state| structure (initialized by CreateIterator) and
208 // returns both the reference to the object as well as the |type_id| of
209 // that object. A zero return value indicates there are currently no more
210 // objects to be found but future attempts can be made without having to
211 // reset the iterator to "first". Creating an iterator |starting_after|
212 // a known iterable object allows "resume" from that point.
213 void CreateIterator(Iterator* state) const { CreateIterator(state, 0); };
214 void CreateIterator(Iterator* state, Reference starting_after) const;
chrisha 2016/01/18 22:40:28 Not clear if the object pointer to by |starting_af
bcwhite 2016/01/19 19:49:40 Done.
215 Reference GetNextIterable(Iterator* state, uint32_t* type_id) const;
216
217 // If there is some indication that the memory has become corrupted,
218 // calling this will attempt to prevent further damage by indicating to
219 // all processes that something is not as expected.
220 void SetCorrupt() const;
221
222 // This can be called to determine if corruption has been detected in the
223 // segment, possibly my a malicious actor. Once detected, future allocations
224 // will fail and iteration may not locate all objects.
225 bool IsCorrupt() const;
226
227 // Flag set if an allocation has failed because the memory segment was full.
228 bool IsFull() const;
229
230 // Update static-state histograms. This should be called on a periodic basis
231 // to record such things as how much of the total space is used.
232 void UpdateStaticHistograms();
chrisha 2016/01/18 22:40:28 Maybe rename CreateHistograms above to CreateStati
bcwhite 2016/01/19 19:49:40 Static referred to those that don't get updated au
233
234 protected:
235 volatile char* const mem_base_; // Memory base. (char so sizeof guaranteed 1)
236 const uint32_t mem_size_; // Size of entire memory segment.
237 const uint32_t mem_page_; // Page size allocations shouldn't cross.
238
239 private:
240 struct SharedMetadata;
241 struct BlockHeader;
242 static const Reference kReferenceQueue;
243 static const Reference kReferenceNull;
244
245 // The shared metadata is always located at the top of the memory segment.
246 // These convenience functions eliminate constant casting of the base
247 // pointer within the code.
248 const volatile SharedMetadata* shared_meta() const {
249 return reinterpret_cast<const volatile SharedMetadata*>(mem_base_);
250 }
251 volatile SharedMetadata* shared_meta() {
252 return reinterpret_cast<volatile SharedMetadata*>(mem_base_);
253 }
254
255 // Actual method for doing the allocation.
256 Reference AllocateImpl(size_t size, uint32_t type_id);
257
258 // Get the block header associated with a specific reference.
259 const volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id,
260 uint32_t size, bool queue_ok,
261 bool free_ok) const;
262 volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id, uint32_t size,
263 bool queue_ok, bool free_ok) {
264 return const_cast<volatile BlockHeader*>(
265 const_cast<const PersistentMemoryAllocator*>(this)->GetBlock(
266 ref, type_id, size, queue_ok, free_ok));
267 }
268
269 // Get the actual data within a block associated with a specific reference.
270 const volatile void* GetBlockData(Reference ref, uint32_t type_id,
271 uint32_t size) const;
272 volatile void* GetBlockData(Reference ref, uint32_t type_id,
273 uint32_t size) {
274 return const_cast<volatile void*>(
275 const_cast<const PersistentMemoryAllocator*>(this)->GetBlockData(
276 ref, type_id, size));
277 }
278
279 const bool readonly_; // Indicates access to read-only memory.
280 std::atomic<bool> corrupt_; // Local version of "corrupted" flag.
281
282 HistogramBase* allocs_histogram_; // Histogram recording allocs.
283 HistogramBase* used_histogram_; // Histogram recording used space.
284
285 FRIEND_TEST_ALL_PREFIXES(PersistentMemoryAllocatorTest, AllocateAndIterate);
286 DISALLOW_COPY_AND_ASSIGN(PersistentMemoryAllocator);
287 };
288
289
290 // This allocator uses a local memory block it allocates from the general
291 // heap. It is generally used when some kind of "death rattle" handler will
292 // save the contents to persistent storage during process shutdown. It is
293 // also useful for testing.
294 class BASE_EXPORT LocalPersistentMemoryAllocator
295 : public PersistentMemoryAllocator {
296 public:
297 LocalPersistentMemoryAllocator(size_t size, uint32_t id,
298 const std::string& name);
299 ~LocalPersistentMemoryAllocator() override;
300
301 private:
302 DISALLOW_COPY_AND_ASSIGN(LocalPersistentMemoryAllocator);
303 };
304
305
306 // This allocator takes a memory-mapped file object and performs allocation
307 // from it. The allocator takes ownership of the file object. Only read access
308 // is provided due to limitions of the MemoryMappedFile class.
309 class BASE_EXPORT FilePersistentMemoryAllocator
chrisha 2016/01/18 22:40:28 Oh? There's no way to have a writable FilePersiste
bcwhite 2016/01/19 19:49:40 Agreed but right now the underlying MemoryMappedFi
310 : public PersistentMemoryAllocator {
311 public:
312 FilePersistentMemoryAllocator(MemoryMappedFile* file, uint32_t id,
313 const std::string& name);
314 ~FilePersistentMemoryAllocator() override;
315
316 // Ensure that the file isn't so invalid that it won't crash when passing it
317 // to the allocator. This doesn't guarantee the file is valid, just that it
318 // won't cause program to abort. The existing IsCorrupt() call will handle
319 // the rest.
320 static bool IsFileAcceptable(const MemoryMappedFile& file);
321
322 private:
323 scoped_ptr<MemoryMappedFile> mapped_file_;
324 };
325
326 } // namespace base
327
328 #endif // BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698