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