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

Side by Side Diff: base/memory/shared_memory_mac.cc

Issue 2555483002: Add POSIX shared memory support for Mac (Closed)
Patch Set: Remove GetType(). Created 4 years 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/memory/shared_memory.h" 5 #include "base/memory/shared_memory.h"
6 6
7 #include <errno.h>
8 #include <fcntl.h>
7 #include <mach/mach_vm.h> 9 #include <mach/mach_vm.h>
10 #include <stddef.h>
11 #include <sys/mman.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
8 14
9 #include "base/files/file_util.h" 15 #include "base/files/file_util.h"
10 #include "base/files/scoped_file.h" 16 #include "base/files/scoped_file.h"
11 #include "base/logging.h" 17 #include "base/logging.h"
12 #include "base/mac/foundation_util.h"
13 #include "base/mac/mac_util.h" 18 #include "base/mac/mac_util.h"
14 #include "base/mac/scoped_mach_vm.h" 19 #include "base/mac/scoped_mach_vm.h"
15 #include "base/metrics/field_trial.h" 20 #include "base/metrics/field_trial.h"
16 #include "base/metrics/histogram_macros.h" 21 #include "base/metrics/histogram_macros.h"
22 #include "base/posix/eintr_wrapper.h"
23 #include "base/posix/safe_strerror.h"
17 #include "base/process/process_metrics.h" 24 #include "base/process/process_metrics.h"
18 #include "base/profiler/scoped_tracker.h" 25 #include "base/profiler/scoped_tracker.h"
19 #include "base/scoped_generic.h" 26 #include "base/scoped_generic.h"
20 #include "base/strings/utf_string_conversions.h" 27 #include "base/strings/utf_string_conversions.h"
21 #include "build/build_config.h" 28 #include "build/build_config.h"
22 29
30 #if defined(OS_MACOSX)
31 #include "base/mac/foundation_util.h"
32 #endif // OS_MACOSX
33
23 namespace base { 34 namespace base {
24 35
25 namespace { 36 namespace {
26 37
27 // Returns whether the operation succeeded. 38 // Returns whether the operation succeeded.
28 // |new_handle| is an output variable, populated on success. The caller takes 39 // |new_handle| is an output variable, populated on success. The caller takes
29 // ownership of the underlying memory object. 40 // ownership of the underlying memory object.
30 // |handle| is the handle to copy. 41 // |handle| is the handle to copy.
31 // If |handle| is already mapped, |mapped_addr| is its mapped location. 42 // If |handle| is already mapped, |mapped_addr| is its mapped location.
32 // Otherwise, |mapped_addr| should be |nullptr|. 43 // Otherwise, |mapped_addr| should be |nullptr|.
(...skipping 27 matching lines...) Expand all
60 mach_task_self(), reinterpret_cast<memory_object_size_t*>(&size), 71 mach_task_self(), reinterpret_cast<memory_object_size_t*>(&size),
61 reinterpret_cast<memory_object_offset_t>(temp_addr), VM_PROT_READ, 72 reinterpret_cast<memory_object_offset_t>(temp_addr), VM_PROT_READ,
62 &named_right, MACH_PORT_NULL); 73 &named_right, MACH_PORT_NULL);
63 if (kr != KERN_SUCCESS) 74 if (kr != KERN_SUCCESS)
64 return false; 75 return false;
65 76
66 *new_handle = SharedMemoryHandle(named_right, size, base::GetCurrentProcId()); 77 *new_handle = SharedMemoryHandle(named_right, size, base::GetCurrentProcId());
67 return true; 78 return true;
68 } 79 }
69 80
81 struct ScopedPathUnlinkerTraits {
82 static FilePath* InvalidValue() { return nullptr; }
83
84 static void Free(FilePath* path) {
85 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
86 // is fixed.
87 tracked_objects::ScopedTracker tracking_profile(
Robert Sesek 2016/12/06 21:45:55 Do we still need the ScopedTrackers in this file?
lawrencewu 2016/12/06 22:33:24 Probably not, now that the bug is fixed. erikchen@
lawrencewu 2016/12/07 19:44:26 Removed all instances of ScopedTracker.
88 FROM_HERE_WITH_EXPLICIT_FUNCTION(
89 "466437 SharedMemory::Create::Unlink"));
90 if (unlink(path->value().c_str()))
91 PLOG(WARNING) << "unlink";
92 }
93 };
94
95 // Unlinks the FilePath when the object is destroyed.
96 typedef ScopedGeneric<FilePath*, ScopedPathUnlinkerTraits> ScopedPathUnlinker;
97
98 // Makes a temporary file, fdopens it, and then unlinks it. |fp| is populated
99 // with the fdopened FILE. |readonly_fd| is populated with the opened fd if
100 // options.share_read_only is true. |path| is populated with the location of
101 // the file before it was unlinked.
102 // Returns false if there's an unhandled failure.
103 bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options,
104 ScopedFILE* fp,
105 ScopedFD* readonly_fd,
106 FilePath* path) {
107 // Q: Why not use the shm_open() etc. APIs?
108 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
Robert Sesek 2016/12/06 21:45:55 Hmmmm. I wonder if we could use shm_open for this
lawrencewu 2016/12/06 22:33:24 I'd like to punt on this, since we can just reuse
109 FilePath directory;
110 ScopedPathUnlinker path_unlinker;
111 if (GetShmemTempDir(options.executable, &directory)) {
112 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
113 // is fixed.
114 tracked_objects::ScopedTracker tracking_profile(
115 FROM_HERE_WITH_EXPLICIT_FUNCTION(
116 "466437 SharedMemory::Create::OpenTemporaryFile"));
117 fp->reset(CreateAndOpenTemporaryFileInDir(directory, path));
118
119 // Deleting the file prevents anyone else from mapping it in (making it
120 // private), and prevents the need for cleanup (once the last fd is
121 // closed, it is truly freed).
122 if (*fp)
123 path_unlinker.reset(path);
124 }
125
126 if (*fp) {
127 if (options.share_read_only) {
128 // TODO(erikchen): Remove ScopedTracker below once
129 // http://crbug.com/466437 is fixed.
130 tracked_objects::ScopedTracker tracking_profile(
131 FROM_HERE_WITH_EXPLICIT_FUNCTION(
132 "466437 SharedMemory::Create::OpenReadonly"));
133 // Also open as readonly so that we can ShareReadOnlyToProcess.
134 readonly_fd->reset(HANDLE_EINTR(open(path->value().c_str(), O_RDONLY)));
135 if (!readonly_fd->is_valid()) {
136 DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed";
137 fp->reset();
138 return false;
139 }
140 }
141 }
142 return true;
143 }
144
70 } // namespace 145 } // namespace
71 146
72 SharedMemory::SharedMemory() 147 SharedMemory::SharedMemory()
73 : mapped_size_(0), memory_(NULL), read_only_(false), requested_size_(0) {} 148 : mapped_memory_mechanism_(SharedMemoryHandle::MACH),
149 readonly_mapped_file_(-1),
150 mapped_size_(0),
151 memory_(NULL),
152 read_only_(false),
153 requested_size_(0) {}
74 154
75 SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) 155 SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
76 : shm_(handle), 156 : shm_(handle),
157 mapped_memory_mechanism_(SharedMemoryHandle::POSIX),
158 readonly_mapped_file_(-1),
77 mapped_size_(0), 159 mapped_size_(0),
78 memory_(NULL), 160 memory_(NULL),
79 read_only_(read_only), 161 read_only_(read_only),
80 requested_size_(0) {} 162 requested_size_(0) {}
81 163
82 SharedMemory::~SharedMemory() { 164 SharedMemory::~SharedMemory() {
83 Unmap(); 165 Unmap();
84 Close(); 166 Close();
85 } 167 }
86 168
87 // static 169 // static
88 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { 170 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
89 return handle.IsValid(); 171 return handle.IsValid();
90 } 172 }
91 173
92 // static 174 // static
93 SharedMemoryHandle SharedMemory::NULLHandle() { 175 SharedMemoryHandle SharedMemory::NULLHandle() {
94 return SharedMemoryHandle(); 176 return SharedMemoryHandle();
95 } 177 }
96 178
97 // static 179 // static
98 void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { 180 void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
99 handle.Close(); 181 handle.Close();
100 } 182 }
101 183
102 // static 184 // static
103 size_t SharedMemory::GetHandleLimit() { 185 size_t SharedMemory::GetHandleLimit() {
104 // This should be effectively unlimited on OS X. 186 return GetMaxFds();
105 return 10000;
106 } 187 }
107 188
108 // static 189 // static
109 SharedMemoryHandle SharedMemory::DuplicateHandle( 190 SharedMemoryHandle SharedMemory::DuplicateHandle(
110 const SharedMemoryHandle& handle) { 191 const SharedMemoryHandle& handle) {
111 return handle.Duplicate(); 192 return handle.Duplicate();
112 } 193 }
113 194
195 // static
196 int SharedMemory::GetFdFromSharedMemoryHandle(
197 const SharedMemoryHandle& handle) {
198 return handle.GetFileDescriptor().fd;
199 }
200
114 bool SharedMemory::CreateAndMapAnonymous(size_t size) { 201 bool SharedMemory::CreateAndMapAnonymous(size_t size) {
115 return CreateAnonymous(size) && Map(size); 202 return CreateAnonymous(size) && Map(size);
116 } 203 }
117 204
118 // static 205 // static
119 bool SharedMemory::GetSizeFromSharedMemoryHandle( 206 bool SharedMemory::GetSizeFromSharedMemoryHandle(
120 const SharedMemoryHandle& handle, 207 const SharedMemoryHandle& handle,
121 size_t* size) { 208 size_t* size) {
122 return handle.GetSize(size); 209 return handle.GetSize(size);
123 } 210 }
124 211
125 // Chromium mostly only uses the unique/private shmem as specified by 212 // Chromium mostly only uses the unique/private shmem as specified by
126 // "name == L"". The exception is in the StatsTable. 213 // "name == L"". The exception is in the StatsTable.
127 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { 214 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
128 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437 215 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
129 // is fixed. 216 // is fixed.
130 tracked_objects::ScopedTracker tracking_profile1( 217 tracked_objects::ScopedTracker tracking_profile1(
131 FROM_HERE_WITH_EXPLICIT_FUNCTION( 218 FROM_HERE_WITH_EXPLICIT_FUNCTION(
132 "466437 SharedMemory::Create::Start")); 219 "466437 SharedMemory::Create::Start"));
133 DCHECK(!shm_.IsValid()); 220 DCHECK(!shm_.IsValid());
134 if (options.size == 0) return false; 221 if (options.size == 0) return false;
135 222
136 if (options.size > static_cast<size_t>(std::numeric_limits<int>::max())) 223 if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
137 return false; 224 return false;
138 225
139 shm_ = SharedMemoryHandle(options.size); 226 if (options.type == SharedMemoryHandle::MACH) {
227 shm_ = SharedMemoryHandle(options.size);
228 requested_size_ = options.size;
229 return shm_.IsValid();
230 }
231
232 // This function theoretically can block on the disk. Both profiling of real
233 // users and local instrumentation shows that this is a real problem.
234 // https://code.google.com/p/chromium/issues/detail?id=466437
235 base::ThreadRestrictions::ScopedAllowIO allow_io;
236
237 ScopedFILE fp;
238 ScopedFD readonly_fd;
239
240 FilePath path;
241 bool result = CreateAnonymousSharedMemory(options, &fp, &readonly_fd, &path);
242 if (!result)
243 return false;
244
245 if (!fp) {
246 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
247 return false;
248 }
249
250 // Get current size.
251 struct stat stat;
252 if (fstat(fileno(fp.get()), &stat) != 0)
253 return false;
254 const size_t current_size = stat.st_size;
255 if (current_size != options.size) {
256 if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0)
257 return false;
258 }
140 requested_size_ = options.size; 259 requested_size_ = options.size;
141 return shm_.IsValid(); 260
261 return PrepareMapFile(std::move(fp), std::move(readonly_fd));
142 } 262 }
143 263
144 bool SharedMemory::MapAt(off_t offset, size_t bytes) { 264 bool SharedMemory::MapAt(off_t offset, size_t bytes) {
145 if (!shm_.IsValid()) 265 if (!shm_.IsValid())
146 return false; 266 return false;
147 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) 267 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
148 return false; 268 return false;
149 if (memory_) 269 if (memory_)
150 return false; 270 return false;
151 271
152 bool success = shm_.MapAt(offset, bytes, &memory_, read_only_); 272 bool success = shm_.MapAt(offset, bytes, &memory_, read_only_);
153 if (success) { 273 if (success) {
154 mapped_size_ = bytes; 274 mapped_size_ = bytes;
155 DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) & 275 DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
156 (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1)); 276 (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
277 mapped_memory_mechanism_ = shm_.type_;
157 } else { 278 } else {
158 memory_ = NULL; 279 memory_ = NULL;
159 } 280 }
160 281
161 return success; 282 return success;
162 } 283 }
163 284
164 bool SharedMemory::Unmap() { 285 bool SharedMemory::Unmap() {
165 if (memory_ == NULL) 286 if (memory_ == NULL)
166 return false; 287 return false;
167 288
168 mach_vm_deallocate(mach_task_self(), 289 switch (mapped_memory_mechanism_) {
169 reinterpret_cast<mach_vm_address_t>(memory_), 290 case SharedMemoryHandle::POSIX:
170 mapped_size_); 291 munmap(memory_, mapped_size_);
292 break;
293 case SharedMemoryHandle::MACH:
294 mach_vm_deallocate(mach_task_self(),
295 reinterpret_cast<mach_vm_address_t>(memory_),
296 mapped_size_);
297 break;
298 }
299
171 memory_ = NULL; 300 memory_ = NULL;
172 mapped_size_ = 0; 301 mapped_size_ = 0;
173 return true; 302 return true;
174 } 303 }
175 304
176 SharedMemoryHandle SharedMemory::handle() const { 305 SharedMemoryHandle SharedMemory::handle() const {
177 return shm_; 306 switch (shm_.type_) {
307 case SharedMemoryHandle::POSIX:
308 return SharedMemoryHandle(shm_.GetFileDescriptor().fd);
309 case SharedMemoryHandle::MACH:
310 return shm_;
311 }
178 } 312 }
179 313
180 SharedMemoryHandle SharedMemory::TakeHandle() { 314 SharedMemoryHandle SharedMemory::TakeHandle() {
181 SharedMemoryHandle dup = DuplicateHandle(handle()); 315 SharedMemoryHandle dup = DuplicateHandle(handle());
182 Close(); 316 Close();
183 return dup; 317 return dup;
184 } 318 }
185 319
186 void SharedMemory::Close() { 320 void SharedMemory::Close() {
187 shm_.Close(); 321 shm_.Close();
188 shm_ = SharedMemoryHandle(); 322 shm_ = SharedMemoryHandle();
323 if (shm_.type_ == SharedMemoryHandle::POSIX) {
324 if (readonly_mapped_file_ > 0) {
325 if (IGNORE_EINTR(close(readonly_mapped_file_)) < 0)
326 PLOG(ERROR) << "close";
327 readonly_mapped_file_ = -1;
328 }
329 }
330 }
331
332 bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
333 DCHECK(!shm_.IsValid());
334 DCHECK_EQ(-1, readonly_mapped_file_);
335 if (fp == NULL)
336 return false;
337
338 // This function theoretically can block on the disk, but realistically
339 // the temporary files we create will just go into the buffer cache
340 // and be deleted before they ever make it out to disk.
341 base::ThreadRestrictions::ScopedAllowIO allow_io;
342
343 struct stat st = {};
344 if (fstat(fileno(fp.get()), &st))
345 NOTREACHED();
346 if (readonly_fd.is_valid()) {
347 struct stat readonly_st = {};
348 if (fstat(readonly_fd.get(), &readonly_st))
349 NOTREACHED();
350 if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) {
351 LOG(ERROR) << "writable and read-only inodes don't match; bailing";
352 return false;
353 }
354 }
355
356 int mapped_file = HANDLE_EINTR(dup(fileno(fp.get())));
357 if (mapped_file == -1) {
358 if (errno == EMFILE) {
359 LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
360 return false;
361 } else {
362 NOTREACHED() << "Call to dup failed, errno=" << errno;
363 }
364 }
365 shm_ = SharedMemoryHandle(mapped_file);
366 readonly_mapped_file_ = readonly_fd.release();
367
368 return true;
189 } 369 }
190 370
191 bool SharedMemory::ShareToProcessCommon(ProcessHandle process, 371 bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
192 SharedMemoryHandle* new_handle, 372 SharedMemoryHandle* new_handle,
193 bool close_self, 373 bool close_self,
194 ShareMode share_mode) { 374 ShareMode share_mode) {
195 DCHECK(shm_.IsValid()); 375 if (shm_.type_ == SharedMemoryHandle::MACH) {
376 DCHECK(shm_.IsValid());
196 377
197 bool success = false; 378 bool success = false;
379 switch (share_mode) {
380 case SHARE_CURRENT_MODE:
381 *new_handle = shm_.Duplicate();
382 success = true;
383 break;
384 case SHARE_READONLY:
385 success = MakeMachSharedMemoryHandleReadOnly(new_handle, shm_, memory_);
386 break;
387 }
388
389 if (success)
390 new_handle->SetOwnershipPassesToIPC(true);
391
392 if (close_self) {
393 Unmap();
394 Close();
395 }
396
397 return success;
398 }
399
400 int handle_to_dup = -1;
198 switch (share_mode) { 401 switch (share_mode) {
199 case SHARE_CURRENT_MODE: 402 case SHARE_CURRENT_MODE:
200 *new_handle = shm_.Duplicate(); 403 handle_to_dup = shm_.GetFileDescriptor().fd;
201 success = true;
202 break; 404 break;
203 case SHARE_READONLY: 405 case SHARE_READONLY:
204 success = MakeMachSharedMemoryHandleReadOnly(new_handle, shm_, memory_); 406 // We could imagine re-opening the file from /dev/fd, but that can't make
407 // it readonly on Mac: https://codereview.chromium.org/27265002/#msg10
408 CHECK_GE(readonly_mapped_file_, 0);
409 handle_to_dup = readonly_mapped_file_;
205 break; 410 break;
206 } 411 }
207 412
208 if (success) 413 const int new_fd = HANDLE_EINTR(dup(handle_to_dup));
209 new_handle->SetOwnershipPassesToIPC(true); 414 if (new_fd < 0) {
415 if (close_self) {
416 Unmap();
417 Close();
418 }
419 DPLOG(ERROR) << "dup() failed.";
420 return false;
421 }
422
423 new_handle->SetFileHandle(new_fd, true);
210 424
211 if (close_self) { 425 if (close_self) {
212 Unmap(); 426 Unmap();
213 Close(); 427 Close();
214 } 428 }
215 429
216 return success; 430 return true;
217 } 431 }
218 432
219 } // namespace base 433 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698