• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors
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_MEMORY_DISCARDABLE_SHARED_MEMORY_H_
6 #define BASE_MEMORY_DISCARDABLE_SHARED_MEMORY_H_
7 
8 #include <stddef.h>
9 
10 #include "base/base_export.h"
11 #include "base/compiler_specific.h"
12 #include "base/containers/span.h"
13 #include "base/dcheck_is_on.h"
14 #include "base/memory/shared_memory_mapping.h"
15 #include "base/memory/unsafe_shared_memory_region.h"
16 #include "base/threading/thread_collision_warner.h"
17 #include "base/time/time.h"
18 #include "build/build_config.h"
19 
20 #if DCHECK_IS_ON()
21 #include <set>
22 #endif
23 
24 // Linux (including Android) support the MADV_REMOVE argument with madvise()
25 // which has the behavior of reliably causing zero-fill-on-demand pages to
26 // be returned after a call. Here we define
27 // DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE on Linux
28 // and Android to indicate that this type of behavior can be expected on
29 // those platforms. Note that madvise() will still be used on other POSIX
30 // platforms but doesn't provide the zero-fill-on-demand pages guarantee.
31 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
32 #define DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE
33 #endif
34 
35 namespace base {
36 
37 namespace trace_event {
38 class MemoryAllocatorDump;
39 class ProcessMemoryDump;
40 }  // namespace trace_event
41 
42 // Platform abstraction for discardable shared memory.
43 //
44 // This class is not thread-safe. Clients are responsible for synchronizing
45 // access to an instance of this class.
46 class BASE_EXPORT DiscardableSharedMemory {
47  public:
48   enum LockResult { SUCCESS, PURGED, FAILED };
49 
50   DiscardableSharedMemory();
51 
52   // Create a new DiscardableSharedMemory object from an existing, open shared
53   // memory file. Memory must be locked.
54   explicit DiscardableSharedMemory(UnsafeSharedMemoryRegion region);
55 
56   DiscardableSharedMemory(const DiscardableSharedMemory&) = delete;
57   DiscardableSharedMemory& operator=(const DiscardableSharedMemory&) = delete;
58 
59   // Closes any open files.
60   virtual ~DiscardableSharedMemory();
61 
62   // Creates and maps a locked DiscardableSharedMemory object with |size|.
63   // Returns true on success and false on failure.
64   bool CreateAndMap(size_t size);
65 
66   // Maps the locked discardable memory into the caller's address space.
67   // Returns true on success, false otherwise.
68   bool Map(size_t size);
69 
70   // Unmaps the discardable shared memory from the caller's address space.
71   // Unmapping won't unlock previously locked range.
72   // Returns true if successful; returns false on error or if the memory is
73   // not mapped.
74   bool Unmap();
75 
76   // The actual size of the mapped memory (may be larger than requested). It is
77   // 0 when the memory has not been mapped via `Map()`.
mapped_size()78   size_t mapped_size() const {
79     if (shared_memory_mapping_.IsValid()) {
80       return mapped_memory().size();
81     } else {
82       return 0u;
83     }
84   }
85 
86   // Returns a duplicated shared memory region for this DiscardableSharedMemory
87   // object.
DuplicateRegion()88   UnsafeSharedMemoryRegion DuplicateRegion() const {
89     return shared_memory_region_.Duplicate();
90   }
91 
92   // Returns an ID for the shared memory region. This is ID of the mapped region
93   // consistent across all processes and is valid as long as the region is not
94   // unmapped.
mapped_id()95   const UnguessableToken& mapped_id() const LIFETIME_BOUND {
96     return shared_memory_mapping_.guid();
97   }
98 
99   // Locks a range of memory so that it will not be purged by the system.
100   // The range of memory must be unlocked. The result of trying to lock an
101   // already locked range is undefined. |offset| and |length| must both be
102   // a multiple of the page size as returned by GetPageSize().
103   // Passing 0 for |length| means "everything onward".
104   // Returns SUCCESS if range was successfully locked and the memory is still
105   // resident, PURGED if range was successfully locked but has been purged
106   // since last time it was locked and FAILED if range could not be locked.
107   // Locking can fail for two reasons; object might have been purged, our
108   // last known usage timestamp might be out of date. Last known usage time
109   // is updated to the actual last usage timestamp if memory is still resident
110   // or 0 if not.
111   LockResult Lock(size_t offset, size_t length);
112 
113   // Unlock a previously successfully locked range of memory. The range of
114   // memory must be locked. The result of trying to unlock a not
115   // previously locked range is undefined.
116   // |offset| and |length| must both be a multiple of the page size as returned
117   // by GetPageSize().
118   // Passing 0 for |length| means "everything onward".
119   void Unlock(size_t offset, size_t length);
120 
121   // Gets a span over the opened discardable memory space. Discardable memory
122   // must have been mapped via Map().
123   //
124   // This gives the logical memory region, matching the size of what was
125   // requested. The actual mapped memory may be larger due to system alignment
126   // requirements. See `SharedMemoryMapping::size()` vs
127   // `SharedMemoryMapping::mapped_size()`.
128   span<uint8_t> memory() const;
129 
130   // Returns the last known usage time for DiscardableSharedMemory object. This
131   // may be earlier than the "true" usage time when memory has been used by a
132   // different process. Returns NULL time if purged.
last_known_usage()133   Time last_known_usage() const { return last_known_usage_; }
134 
135   // This returns true and sets |last_known_usage_| to 0 if
136   // DiscardableSharedMemory object was successfully purged. Purging can fail
137   // for two reasons; object might be locked or our last known usage timestamp
138   // might be out of date. Last known usage time is updated to |current_time|
139   // if locked or the actual last usage timestamp if unlocked. It is often
140   // necessary to call this function twice for the object to successfully be
141   // purged. First call, updates |last_known_usage_|. Second call, successfully
142   // purges the object using the updated |last_known_usage_|.
143   // Note: there is no guarantee that multiple calls to this function will
144   // successfully purge object. DiscardableSharedMemory object might be locked
145   // or another thread/process might be able to lock and unlock it in between
146   // each call.
147   bool Purge(Time current_time);
148 
149   // Returns true if memory is still resident.
150   bool IsMemoryResident() const;
151 
152   // Returns true if memory is locked.
153   bool IsMemoryLocked() const;
154 
155   // Closes the open discardable memory segment.
156   // It is safe to call Close repeatedly.
157   void Close();
158 
159   // For tracing: Creates ownership edge to the underlying shared memory dump
160   // which is cross process in the given |pmd|. |local_segment_dump| is the dump
161   // associated with the local discardable shared memory segment and |is_owned|
162   // is true when the current process owns the segment and the effective memory
163   // is assigned to the current process.
164   void CreateSharedMemoryOwnershipEdge(
165       trace_event::MemoryAllocatorDump* local_segment_dump,
166       trace_event::ProcessMemoryDump* pmd,
167       bool is_owned) const;
168 
169 #if BUILDFLAG(IS_ANDROID)
170   // Returns true if the Ashmem device is supported on this system.
171   // Only use this for unit-testing.
172   static bool IsAshmemDeviceSupportedForTesting();
173 #endif
174 
175  private:
176   // Returns the full mapped memory region after the internal bookkeeping
177   // header. This may be larger than the region exposed through `memory()` due
178   // to platform alignment requirements. Discardable memory must have been
179   // mapped via Map().
180   span<uint8_t> mapped_memory() const;
181 
182   // LockPages/UnlockPages are platform-native discardable page management
183   // helper functions. Both expect |offset| to be specified relative to the
184   // base address at which |memory| is mapped, and that |offset| and |length|
185   // are page-aligned by the caller.
186   // Returns SUCCESS on platforms which do not support discardable pages.
187   static LockResult LockPages(const UnsafeSharedMemoryRegion& region,
188                               size_t offset,
189                               size_t length);
190   // UnlockPages() is a no-op on platforms not supporting discardable pages.
191   static void UnlockPages(const UnsafeSharedMemoryRegion& region,
192                           size_t offset,
193                           size_t length);
194 
195   // Virtual for tests.
196   virtual Time Now() const;
197 
198   UnsafeSharedMemoryRegion shared_memory_region_;
199   WritableSharedMemoryMapping shared_memory_mapping_;
200   size_t locked_page_count_ = 0u;
201 #if DCHECK_IS_ON()
202   std::set<size_t> locked_pages_;
203 #endif
204   // Implementation is not thread-safe but still usable if clients are
205   // synchronized somehow. Use a collision warner to detect incorrect usage.
206   DFAKE_MUTEX(thread_collision_warner_);
207   Time last_known_usage_;
208 };
209 
210 }  // namespace base
211 
212 #endif  // BASE_MEMORY_DISCARDABLE_SHARED_MEMORY_H_
213