Index: services/blamer/shared_memory_heap.h |
diff --git a/services/blamer/shared_memory_heap.h b/services/blamer/shared_memory_heap.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e38142cf104c0b8fd29eaf301fea8146c02a340b |
--- /dev/null |
+++ b/services/blamer/shared_memory_heap.h |
@@ -0,0 +1,155 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+// Defines a shared memory heap. One of these is created by each process |
+// using the blame service. This heap takes care of registering all of its slabs |
+// with the central blamer service, allowing it to iterate over all blame |
+// information across all processes. |
+ |
+#ifndef SERVICES_BLAMER_SHARED_MEMORY_HEAP_H_ |
+#define SERVICES_BLAMER_SHARED_MEMORY_HEAP_H_ |
+ |
+#include "base/metrics/persistent_memory_allocator.h" |
+#include "mojo/public/cpp/system/buffer.h" |
+#include "services/blamer/heap_object_types.h" |
+#include "services/blamer/public/interfaces/shared_memory_heap_registry.mojom.h" |
+ |
+namespace blamer { |
+ |
+// This class is thread safe. Most of the operations will actually be performed |
+// without using any locks, however occasionally a lock will need to be |
+// acquired in support of some operations. |
+class SharedMemoryHeap { |
+ public: |
+ static constexpr size_t kSlabSizeBits = 22; |
+ static constexpr size_t kSlabSize = 1 << kSlabSizeBits; |
+ |
+ // Represents a pointer in a process-portable way. This is what would be sent |
+ // over IPC to communicate pointers between processes. Knowing the ID of the |
+ // process owning the memory and the |raw_value| of the PortablePointer is |
+ // sufficient to extract an actual pointer to the object in the process |
+ // hosting the service. |
+ struct PortablePointer { |
+ union { |
+ uint32_t raw_value; |
+ struct { |
+ unsigned int slab_id : 32 - kSlabSizeBits; |
+ unsigned int slab_offset : kSlabSizeBits; |
+ }; |
+ }; |
+ }; |
+ static_assert(sizeof(PortablePointer) == 4, |
+ "unexpected PortablePointer size"); |
+ |
+ // Encapsulates pointers as they are doled out by this shared memory |
+ // allocator. Dereferencing has a non-trivial cost so the result should |
+ // ideally be cached by clients that need to do this often. |
+ template<typename ObjectType> |
+ struct TypedLocalPointer { |
+ TypedLocalPointer(void* slab, PortablePointer pointer) |
+ : slab(slab), pointer(pointer) { |
+ } |
+ |
+ ObjectType* Get() const { |
+ auto* allocator = reinterpret_cast<base::PersistentMemoryAllocator*>( |
+ slab); |
+ base::PersistentMemoryAllocator::Reference ref = pointer.slab_offset; |
+ return allocator->GetAsObject<ObjectType>(ref); |
+ } |
+ ObjectType* operator->() const { return Get(); } |
+ ObjectType& operator*() const { return *Get(); } |
+ |
+ void* slab; |
+ PortablePointer pointer; |
+ }; |
+ |
+ SharedMemoryHeap(mojom::SharedMemoryHeapRegistryPtr heap_registry); |
+ ~SharedMemoryHeap(); |
+ |
+ // Allocates an object on the shared memory heap. Uses the actual object size. |
+ template<typename ObjectType> |
+ TypedLocalPointer<ObjectType> Allocate() { |
+ return MakePointer<ObjectType>(Allocate( |
+ ObjectType::kPersistentTypeId, sizeof(ObjectType))); |
+ } |
+ |
+ // Allocates an object on the shared memory heap. Uses the request object |
+ // size. |
+ template<typename ObjectType> |
+ TypedLocalPointer<ObjectType> Allocate(size_t object_size) { |
+ return MakePointer<ObjectType>(Allocate( |
+ ObjectType::kPersistentTypeId, object_size)); |
+ } |
+ |
+ // Frees an object. Once freed the object will no longer be visible to |
+ // remote processes. |
+ template<typename ObjectType> |
+ void Free(const TypedLocalPointer<ObjectType>& pointer) { |
+ Free(ObjectType::kPersistentTypeId, MakePointer(pointer)); |
+ } |
+ |
+ private: |
+ struct LocalPointer { |
+ void* slab; |
+ PortablePointer pointer; |
+ }; |
+ |
+ struct Slab { |
+ Slab(); |
+ Slab(Slab&& other); |
+ ~Slab(); |
+ |
+ mojo::ScopedSharedBufferHandle buffer; |
+ mojo::ScopedSharedBufferMapping mapping; |
+ std::unique_ptr<base::PersistentMemoryAllocator> allocator; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(Slab); |
+ }; |
+ |
+ // Returns the current slab, creating one if none exists. |
+ base::PersistentMemoryAllocator* GetCurrentSlab(); |
+ |
+ // Creates a new slab, and updates |current_slab_|. Returns a pointer to the |
+ // newly created slab. |
+ base::PersistentMemoryAllocator* CreateNewSlab( |
+ base::PersistentMemoryAllocator* current_slab); |
+ |
+ // Creates a TypedLocalPointer from an untyped LocalPointer. |
+ template<typename ObjectType> |
+ TypedLocalPointer<ObjectType> MakePointer(const LocalPointer& pointer) { |
+ return TypedLocalPointer<ObjectType>(pointer.slab, pointer.pointer); |
+ } |
+ |
+ // Creates an untyped LocalPointer from a TypedLocalPointer. |
+ template<typename ObjectType> |
+ LocalPointer MakePointer(const TypedLocalPointer<ObjectType>& pointer) { |
+ LocalPointer lp = {}; |
+ lp.slab = pointer.slab; |
+ lp.slab = pointer.pointer = pointer.pointer; |
+ return lp; |
+ } |
+ |
+ // Allocates an object from the current slab. Creates a new slab if the |
+ // current slab fails to perform the allocation. |
+ LocalPointer Allocate(HeapObjectType object_type, size_t object_size); |
+ |
+ // Frees the provided object. |
+ void Free(HeapObjectType object_type, const LocalPointer& pointer); |
+ |
+ // Pointer to the heap registry to be notified of new slabs. |
+ mojom::SharedMemoryHeapRegistryPtr heap_registry_; |
+ |
+ // The current persistent memory allocator. This is updated in a lock-free |
+ // manner. The common use case is to grab the current allocator and make an |
+ // allocation, which is very fast. |
+ std::atomic<base::PersistentMemoryAllocator*> current_slab_; |
+ |
+ // |current_slab_| is used as a lock for modifying this safely. |
+ std::vector<Slab> slabs_; |
+}; |
+ |
+} // namespace blamer |
+ |
+#endif // SERVICES_BLAMER_SHARED_MEMORY_HEAP_H_ |