shared_memory_posix.cc revision a873c8ac8e3c7252f191b1774a9137a91b8ae80d
1// Copyright (c) 2010 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#include "base/shared_memory.h" 6 7#include <errno.h> 8#include <fcntl.h> 9#include <sys/mman.h> 10#include <sys/stat.h> 11#include <unistd.h> 12 13#include "base/file_util.h" 14#include "base/logging.h" 15#include "base/threading/platform_thread.h" 16#include "base/safe_strerror_posix.h" 17#include "base/threading/thread_restrictions.h" 18#include "base/utf_string_conversions.h" 19 20namespace base { 21 22namespace { 23// Paranoia. Semaphores and shared memory segments should live in different 24// namespaces, but who knows what's out there. 25const char kSemaphoreSuffix[] = "-sem"; 26} 27 28SharedMemory::SharedMemory() 29 : mapped_file_(-1), 30 mapped_size_(0), 31 inode_(0), 32 memory_(NULL), 33 read_only_(false), 34 created_size_(0) { 35} 36 37SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) 38 : mapped_file_(handle.fd), 39 mapped_size_(0), 40 inode_(0), 41 memory_(NULL), 42 read_only_(read_only), 43 created_size_(0) { 44 struct stat st; 45 if (fstat(handle.fd, &st) == 0) { 46 // If fstat fails, then the file descriptor is invalid and we'll learn this 47 // fact when Map() fails. 48 inode_ = st.st_ino; 49 } 50} 51 52SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, 53 ProcessHandle process) 54 : mapped_file_(handle.fd), 55 mapped_size_(0), 56 inode_(0), 57 memory_(NULL), 58 read_only_(read_only), 59 created_size_(0) { 60 // We don't handle this case yet (note the ignored parameter); let's die if 61 // someone comes calling. 62 NOTREACHED(); 63} 64 65SharedMemory::~SharedMemory() { 66 Close(); 67} 68 69// static 70bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { 71 return handle.fd >= 0; 72} 73 74// static 75SharedMemoryHandle SharedMemory::NULLHandle() { 76 return SharedMemoryHandle(); 77} 78 79// static 80void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { 81 DCHECK(handle.fd >= 0); 82 close(handle.fd); 83} 84 85bool SharedMemory::CreateAndMapAnonymous(uint32 size) { 86 return CreateAnonymous(size) && Map(size); 87} 88 89bool SharedMemory::CreateAnonymous(uint32 size) { 90 return CreateNamed("", false, size); 91} 92 93// Chromium mostly only uses the unique/private shmem as specified by 94// "name == L"". The exception is in the StatsTable. 95// TODO(jrg): there is no way to "clean up" all unused named shmem if 96// we restart from a crash. (That isn't a new problem, but it is a problem.) 97// In case we want to delete it later, it may be useful to save the value 98// of mem_filename after FilePathForMemoryName(). 99bool SharedMemory::CreateNamed(const std::string& name, 100 bool open_existing, uint32 size) { 101 DCHECK(mapped_file_ == -1); 102 if (size == 0) return false; 103 104 // This function theoretically can block on the disk, but realistically 105 // the temporary files we create will just go into the buffer cache 106 // and be deleted before they ever make it out to disk. 107 base::ThreadRestrictions::ScopedAllowIO allow_io; 108 109 FILE *fp; 110 bool fix_size = true; 111 112 FilePath path; 113 if (name.empty()) { 114 // It doesn't make sense to have a open-existing private piece of shmem 115 DCHECK(!open_existing); 116 // Q: Why not use the shm_open() etc. APIs? 117 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU 118 fp = file_util::CreateAndOpenTemporaryShmemFile(&path); 119 120 // Deleting the file prevents anyone else from mapping it in 121 // (making it private), and prevents the need for cleanup (once 122 // the last fd is closed, it is truly freed). 123 if (fp) 124 file_util::Delete(path, false); 125 126 } else { 127 if (!FilePathForMemoryName(name, &path)) 128 return false; 129 130 fp = file_util::OpenFile(path, "w+x"); 131 if (fp == NULL && open_existing) { 132 // "w+" will truncate if it already exists. 133 fp = file_util::OpenFile(path, "a+"); 134 fix_size = false; 135 } 136 } 137 if (fp && fix_size) { 138 // Get current size. 139 struct stat stat; 140 if (fstat(fileno(fp), &stat) != 0) 141 return false; 142 const uint32 current_size = stat.st_size; 143 if (current_size != size) { 144 if (ftruncate(fileno(fp), size) != 0) 145 return false; 146 if (fseeko(fp, size, SEEK_SET) != 0) 147 return false; 148 } 149 created_size_ = size; 150 } 151 if (fp == NULL) { 152#if !defined(OS_MACOSX) 153 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; 154 FilePath dir = path.DirName(); 155 if (access(dir.value().c_str(), W_OK | X_OK) < 0) { 156 PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); 157 if (dir.value() == "/dev/shm") { 158 LOG(FATAL) << "This is frequently caused by incorrect permissions on " 159 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; 160 } 161 } 162#else 163 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; 164#endif 165 return false; 166 } 167 168 return PrepareMapFile(fp); 169} 170 171// Our current implementation of shmem is with mmap()ing of files. 172// These files need to be deleted explicitly. 173// In practice this call is only needed for unit tests. 174bool SharedMemory::Delete(const std::string& name) { 175 FilePath path; 176 if (!FilePathForMemoryName(name, &path)) 177 return false; 178 179 if (file_util::PathExists(path)) { 180 return file_util::Delete(path, false); 181 } 182 183 // Doesn't exist, so success. 184 return true; 185} 186 187bool SharedMemory::Open(const std::string& name, bool read_only) { 188 FilePath path; 189 if (!FilePathForMemoryName(name, &path)) 190 return false; 191 192 read_only_ = read_only; 193 194 const char *mode = read_only ? "r" : "r+"; 195 FILE *fp = file_util::OpenFile(path, mode); 196 return PrepareMapFile(fp); 197} 198 199bool SharedMemory::Map(uint32 bytes) { 200 if (mapped_file_ == -1) 201 return false; 202 203 memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), 204 MAP_SHARED, mapped_file_, 0); 205 206 if (memory_) 207 mapped_size_ = bytes; 208 209 bool mmap_succeeded = (memory_ != (void*)-1); 210 DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno; 211 return mmap_succeeded; 212} 213 214bool SharedMemory::Unmap() { 215 if (memory_ == NULL) 216 return false; 217 218 munmap(memory_, mapped_size_); 219 memory_ = NULL; 220 mapped_size_ = 0; 221 return true; 222} 223 224SharedMemoryHandle SharedMemory::handle() const { 225 return FileDescriptor(mapped_file_, false); 226} 227 228void SharedMemory::Close() { 229 Unmap(); 230 231 if (mapped_file_ > 0) { 232 close(mapped_file_); 233 mapped_file_ = -1; 234 } 235} 236 237void SharedMemory::Lock() { 238#if !defined(ANDROID) 239 LockOrUnlockCommon(F_LOCK); 240#endif 241} 242 243void SharedMemory::Unlock() { 244#if !defined(ANDROID) 245 LockOrUnlockCommon(F_ULOCK); 246#endif 247} 248 249bool SharedMemory::PrepareMapFile(FILE *fp) { 250 DCHECK(mapped_file_ == -1); 251 if (fp == NULL) return false; 252 253 // This function theoretically can block on the disk, but realistically 254 // the temporary files we create will just go into the buffer cache 255 // and be deleted before they ever make it out to disk. 256 base::ThreadRestrictions::ScopedAllowIO allow_io; 257 258 file_util::ScopedFILE file_closer(fp); 259 260 mapped_file_ = dup(fileno(fp)); 261 if (mapped_file_ == -1) { 262 if (errno == EMFILE) { 263 LOG(WARNING) << "Shared memory creation failed; out of file descriptors"; 264 return false; 265 } else { 266 NOTREACHED() << "Call to dup failed, errno=" << errno; 267 } 268 } 269 270 struct stat st; 271 if (fstat(mapped_file_, &st)) 272 NOTREACHED(); 273 inode_ = st.st_ino; 274 275 return true; 276} 277 278// For the given shmem named |mem_name|, return a filename to mmap() 279// (and possibly create). Modifies |filename|. Return false on 280// error, or true of we are happy. 281bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, 282 FilePath* path) { 283 // mem_name will be used for a filename; make sure it doesn't 284 // contain anything which will confuse us. 285 DCHECK(mem_name.find('/') == std::string::npos); 286 DCHECK(mem_name.find('\0') == std::string::npos); 287 288 FilePath temp_dir; 289 if (!file_util::GetShmemTempDir(&temp_dir)) 290 return false; 291 292 *path = temp_dir.AppendASCII("com.google.chrome.shmem." + mem_name); 293 return true; 294} 295 296void SharedMemory::LockOrUnlockCommon(int function) { 297 DCHECK(mapped_file_ >= 0); 298#if !defined(ANDROID) 299 while (lockf(mapped_file_, function, 0) < 0) { 300 if (errno == EINTR) { 301 continue; 302 } else if (errno == ENOLCK) { 303 // temporary kernel resource exaustion 304 base::PlatformThread::Sleep(500); 305 continue; 306 } else { 307 NOTREACHED() << "lockf() failed." 308 << " function:" << function 309 << " fd:" << mapped_file_ 310 << " errno:" << errno 311 << " msg:" << safe_strerror(errno); 312 } 313 } 314#endif 315} 316 317bool SharedMemory::ShareToProcessCommon(ProcessHandle process, 318 SharedMemoryHandle *new_handle, 319 bool close_self) { 320 const int new_fd = dup(mapped_file_); 321 DCHECK(new_fd >= 0); 322 new_handle->fd = new_fd; 323 new_handle->auto_close = true; 324 325 if (close_self) 326 Close(); 327 328 return true; 329} 330 331} // namespace base 332