shared_memory_posix.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
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 199// For the given shmem named |mem_name|, return a filename to mmap() 200// (and possibly create). Modifies |filename|. Return false on 201// error, or true of we are happy. 202bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, 203 FilePath* path) { 204 // mem_name will be used for a filename; make sure it doesn't 205 // contain anything which will confuse us. 206 DCHECK(mem_name.find('/') == std::string::npos); 207 DCHECK(mem_name.find('\0') == std::string::npos); 208 209 FilePath temp_dir; 210 if (!file_util::GetShmemTempDir(&temp_dir)) 211 return false; 212 213 *path = temp_dir.AppendASCII("com.google.chrome.shmem." + mem_name); 214 return true; 215} 216 217bool SharedMemory::PrepareMapFile(FILE *fp) { 218 DCHECK(mapped_file_ == -1); 219 if (fp == NULL) return false; 220 221 // This function theoretically can block on the disk, but realistically 222 // the temporary files we create will just go into the buffer cache 223 // and be deleted before they ever make it out to disk. 224 base::ThreadRestrictions::ScopedAllowIO allow_io; 225 226 file_util::ScopedFILE file_closer(fp); 227 228 mapped_file_ = dup(fileno(fp)); 229 if (mapped_file_ == -1) { 230 if (errno == EMFILE) { 231 LOG(WARNING) << "Shared memory creation failed; out of file descriptors"; 232 return false; 233 } else { 234 NOTREACHED() << "Call to dup failed, errno=" << errno; 235 } 236 } 237 238 struct stat st; 239 if (fstat(mapped_file_, &st)) 240 NOTREACHED(); 241 inode_ = st.st_ino; 242 243 return true; 244} 245 246bool SharedMemory::Map(uint32 bytes) { 247 if (mapped_file_ == -1) 248 return false; 249 250 memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), 251 MAP_SHARED, mapped_file_, 0); 252 253 if (memory_) 254 mapped_size_ = bytes; 255 256 bool mmap_succeeded = (memory_ != (void*)-1); 257 DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno; 258 return mmap_succeeded; 259} 260 261bool SharedMemory::Unmap() { 262 if (memory_ == NULL) 263 return false; 264 265 munmap(memory_, mapped_size_); 266 memory_ = NULL; 267 mapped_size_ = 0; 268 return true; 269} 270 271bool SharedMemory::ShareToProcessCommon(ProcessHandle process, 272 SharedMemoryHandle *new_handle, 273 bool close_self) { 274 const int new_fd = dup(mapped_file_); 275 DCHECK(new_fd >= 0); 276 new_handle->fd = new_fd; 277 new_handle->auto_close = true; 278 279 if (close_self) 280 Close(); 281 282 return true; 283} 284 285 286void SharedMemory::Close() { 287 Unmap(); 288 289 if (mapped_file_ > 0) { 290 close(mapped_file_); 291 mapped_file_ = -1; 292 } 293} 294 295void SharedMemory::LockOrUnlockCommon(int function) { 296 DCHECK(mapped_file_ >= 0); 297#if !defined(ANDROID) 298 while (lockf(mapped_file_, function, 0) < 0) { 299 if (errno == EINTR) { 300 continue; 301 } else if (errno == ENOLCK) { 302 // temporary kernel resource exaustion 303 base::PlatformThread::Sleep(500); 304 continue; 305 } else { 306 NOTREACHED() << "lockf() failed." 307 << " function:" << function 308 << " fd:" << mapped_file_ 309 << " errno:" << errno 310 << " msg:" << safe_strerror(errno); 311 } 312 } 313#endif 314} 315 316void SharedMemory::Lock() { 317#if !defined(ANDROID) 318 LockOrUnlockCommon(F_LOCK); 319#endif 320} 321 322void SharedMemory::Unlock() { 323#if !defined(ANDROID) 324 LockOrUnlockCommon(F_ULOCK); 325#endif 326} 327 328SharedMemoryHandle SharedMemory::handle() const { 329 return FileDescriptor(mapped_file_, false); 330} 331 332} // namespace base 333