• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #pragma once
15 
16 #ifdef _WIN32
17 #ifdef _MSC_VER
18 #include "base\msvc.h"
19 #include <windows.h>
20 #else
21 #include <windows.h>
22 #endif  // _MSC_VER
23 #endif  // _WIN32
24 
25 #include <string>
26 
27 namespace android {
28 
29 namespace base {
30 
31 // SharedMemory - A class to share memory between 2 process. The region can
32 // be shared in 2 modes:
33 //
34 // - As shared memory (/dev/shm, memory page file in windows)
35 // - Using memory mapped files backed by a file on the file system.
36 //
37 // To use memory mapped files prefix the handle with "file://" followed
38 // by an absolute path. For example: file:///c:/WINDOWS/shared.mem or
39 // file:///tmp/shared.mem
40 //
41 // Usage examples:
42 // Proc1: The owner
43 //    StringView message = "Hello world!";
44 //    SharedMemory writer("foo", 4096);
45 //    writer.create(0600);
46 //    memcpy(*writer, message.c_str(), message.size());
47 //
48 // Proc2: The observer
49 //    SharedMemory reader("foo", 4096);
50 //    reader.open(SharedMemory::AccessMode::READ_ONLY);
51 //    StringView read((const char*) *reader));
52 //
53 // Using file backed ram:
54 //
55 // Proc1: The owner
56 //    StringView message = "Hello world!";
57 //    SharedMemory writer("file:///abs/path/to/a/file", 4096);
58 //    writer.create(0600);
59 //    memcpy(*writer, message.c_str(), message.size());
60 //
61 // Proc2: The observer
62 //    SharedMemory reader("file::///abs/path/to/a/file", 4096);
63 //    reader.open(SharedMemory::AccessMode::READ_ONLY);
64 //    StringView read((const char*) *reader));
65 //
66 //   It is not possible to find out the size of an in memory shared region on
67 //   Win32 (boo!), there are undocumented workaround (See:
68 //   https://stackoverflow.com/questions/34860452/extracting-shared-memorys-size/47951175#47951175)
69 //   that we are not using.
70 //
71 //   For this reason the size has to be explicitly set in the
72 //   constructor. As a workaround you could write the region size in the first
73 //   few bytes of the region, or use a different channel to exchange region
74 //   size.
75 //
76 //   Shared memory behaves differently on Win32 vs Posix. You as a user must be
77 //   very well aware of these differences, or run into unexpected results on
78 //   different platforms:
79 //
80 // Posix (linux/macos):
81 //  - There is a notion of an OWNER of a SharedMemory region. The one to call
82 //    create will own the region. If this object goes out of scope the region
83 //    will be unlinked, meaning that mappings (calls to open) will fail. As
84 //    soon as all other references to the shared memory go away the handle will
85 //    disappear from /dev/shm as well.
86 //  - Multiple calls to create for the same region can cause undefined behavior
87 //    due to closing and potential resizing of the shared memory.
88 //  - Shared memory can outlive processes that are using it. So don't crash
89 //    while a shared object is still alive.
90 //  - Access control is observed by mode permissions
91 // Mac:
92 //  - The name cannot exceed 30 chars.
93 // Win32:
94 //   - Names are prefixed with SHM_ to prevent collision with other objects
95 //     in windows;
96 //   - There is no notion of an owner. The OS will release the region as
97 //     soon as all references to a region disappear.
98 //   - The first call to create will determine the size of the region.
99 //     According to msdn regions cannot grow. Multiple calls to create
100 //     have no effect, and behave like open. (Note, you can grow size according
101 //     to https://blogs.msdn.microsoft.com/oldnewthing/20150130-00/?p=44793)
102 //   - Win32 does not officially support determining the size of a shared
103 //     region.
104 //   - The access control lists (ACL) in the default security descriptor for
105 //     a file mapping object come from the primary or impersonation token of
106 //     the creator.
107 //
108 // If the shared memory region is backed by a file you must keep the following
109 // things in mind:
110 //
111 // Win32:
112 //  - If an application specifies a size for the file mapping object that
113 //    is larger than the size of the actual named file on disk and if the page
114 //    protection allows write access (that is, the flProtect parameter specifies
115 //    PAGE_READWRITE or PAGE_EXECUTE_READWRITE), then the file on disk is
116 //    increased to match the specified size of the file mapping object. If the
117 //    file is extended, the contents of the file between the old end of the file
118 //    and the new end of the file are not guaranteed to be zero; the behavior is
119 //    defined by the file system. If the file on disk cannot be increased,
120 //    CreateFileMapping fails and GetLastError returns ERROR_DISK_FULL.
121 //    In practice this means that the shared memory region will not contain
122 //    useful data upon creation.
123 //  - A sharing object will be created for each view on the file.
124 //  - The memory mapped file will be deleted by the owner upond destruction.
125 //    this is however not guaranteed!
126 // Posix:
127 //  - The notion of ownership is handled at the filesystem level. Usually this
128 //    means that a memory mapped file will be deleted once all references have
129 //    been removed.
130 //  - The memory mapped file will be deleted by the owner upond destruction.
131 //    this is however not guaranteed!
132 class SharedMemory {
133 public:
134 #ifdef _WIN32
135     using memory_type = void*;
136     using handle_type = HANDLE;
invalidHandle()137     constexpr static handle_type invalidHandle() {
138         // This is the invalid return value for
139         // CreateFileMappingW; INVALID_HANDLE_VALUE
140         // could mean the paging file on Windows.
141         return NULL;
142     }
unmappedMemory()143     constexpr static memory_type unmappedMemory() { return nullptr; }
144 #else
145     using memory_type = void*;
146     using handle_type = int;
147     constexpr static handle_type invalidHandle() { return -1; }
148     static memory_type unmappedMemory() {
149         return reinterpret_cast<memory_type>(-1);
150     }
151 #endif
152     enum class AccessMode { READ_ONLY, READ_WRITE };
153     enum class ShareType { SHARED_MEMORY, FILE_BACKED };
154 
155     // Creates a SharedMemory region either backed by a shared memory handle
156     // or by a file. If the string uriOrHandle starts with `file://` it will be
157     // file backed otherwise it will be a named shared memory region.
158     // |uriHandle| A file:// uri or handle
159     // |size| Size of the desired shared memory region. Cannot change after
160     // creation.
161     SharedMemory(const std::string& uriOrHandle, size_t size);
~SharedMemory()162     ~SharedMemory() { close(); }
163 
SharedMemory(SharedMemory && other)164     SharedMemory(SharedMemory&& other) {
165         mName = std::move(other.mName);
166         mSize = other.mSize;
167         mAddr = other.mAddr;
168         mFd = other.mFd;
169         mCreate = other.mCreate;
170         mShareType = other.mShareType;
171         other.clear();
172     }
173 
174     SharedMemory& operator=(SharedMemory&& other) {
175         mName = std::move(other.mName);
176         mSize = other.mSize;
177         mAddr = other.mAddr;
178         mShareType = other.mShareType;
179         mFd = other.mFd;
180         mCreate = other.mCreate;
181 
182         other.clear();
183         return *this;
184     }
185 
186     // Let's not have any weirdness due to double unlinks due to destructors.
187     SharedMemory(const SharedMemory&) = delete;
188     SharedMemory& operator=(const SharedMemory&) = delete;
189 
190     // Creates a shared region, you will be considered the owner, and will have
191     // write access. Returns 0 on success, or an negative error code otheriwse.
192     // The error code (errno) is platform dependent.
193     int create(mode_t mode);
194     // Creates a shared object in the same manner as create(), except for
195     // performing actual mapping.
196     int createNoMapping(mode_t mode);
197 
198     // Opens the shared memory object, returns 0 on success
199     // or the negative error code.
200     // The shared memory object will be mapped.
201     int open(AccessMode access);
202 
203     bool isOpen() const;
204     void close(bool forceDestroy = false);
205 
size()206     size_t size() const { return mSize; }
name()207     std::string name() const { return mName; }
get()208     memory_type get() const { return mAddr; }
209     memory_type operator*() const { return get(); }
type()210     ShareType type() const { return mShareType; }
getFd()211     handle_type getFd() { return mFd; }
isMapped()212     bool isMapped() const { return mAddr != unmappedMemory(); }
213 
214 private:
215 #ifdef _WIN32
216     int openInternal(AccessMode access, bool doMapping = true);
217 #else
218     int openInternal(int oflag, int mode, bool doMapping = true);
219 #endif
220 
clear()221     void clear() {
222         mSize = 0;
223         mName = "";
224         mCreate = false;
225         mFd = invalidHandle();
226         mAddr = unmappedMemory();
227     }
228 
229     memory_type mAddr = unmappedMemory();
230     handle_type mFd = invalidHandle();
231     handle_type mFile = invalidHandle();
232     bool mCreate = false;
233 
234     std::string mName;
235     size_t mSize;
236     ShareType mShareType;
237 };
238 }  // namespace base
239 }  // namespace android
240