1 // Copyright (c) 2011 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.
5 #include "base/shared_memory.h"
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"
20 #if defined(OS_MACOSX)
21 #include "base/mac/foundation_util.h"
24 #if defined(OS_ANDROID)
25 #include "base/os_compat_android.h"
26 #include "third_party/ashmem/ashmem.h"
33 // Paranoia. Semaphores and shared memory segments should live in different
34 // namespaces, but who knows what's out there.
35 const char kSemaphoreSuffix
[] = "-sem";
39 SharedMemory::SharedMemory()
48 SharedMemory::SharedMemory(SharedMemoryHandle handle
, bool read_only
)
49 : mapped_file_(handle
.fd
),
53 read_only_(read_only
),
56 if (fstat(handle
.fd
, &st
) == 0) {
57 // If fstat fails, then the file descriptor is invalid and we'll learn this
58 // fact when Map() fails.
63 SharedMemory::SharedMemory(SharedMemoryHandle handle
, bool read_only
,
64 ProcessHandle process
)
65 : mapped_file_(handle
.fd
),
69 read_only_(read_only
),
71 // We don't handle this case yet (note the ignored parameter); let's die if
72 // someone comes calling.
76 SharedMemory::~SharedMemory() {
81 bool SharedMemory::IsHandleValid(const SharedMemoryHandle
& handle
) {
82 return handle
.fd
>= 0;
86 SharedMemoryHandle
SharedMemory::NULLHandle() {
87 return SharedMemoryHandle();
91 void SharedMemory::CloseHandle(const SharedMemoryHandle
& handle
) {
92 DCHECK_GE(handle
.fd
, 0);
93 if (HANDLE_EINTR(close(handle
.fd
)) < 0)
94 DPLOG(ERROR
) << "close";
97 bool SharedMemory::CreateAndMapAnonymous(uint32 size
) {
98 return CreateAnonymous(size
) && Map(size
);
101 bool SharedMemory::CreateAnonymous(uint32 size
) {
102 return CreateNamed("", false, size
);
105 #if !defined(OS_ANDROID)
106 // Chromium mostly only uses the unique/private shmem as specified by
107 // "name == L"". The exception is in the StatsTable.
108 // TODO(jrg): there is no way to "clean up" all unused named shmem if
109 // we restart from a crash. (That isn't a new problem, but it is a problem.)
110 // In case we want to delete it later, it may be useful to save the value
111 // of mem_filename after FilePathForMemoryName().
112 bool SharedMemory::CreateNamed(const std::string
& name
,
113 bool open_existing
, uint32 size
) {
114 DCHECK_EQ(-1, mapped_file_
);
115 if (size
== 0) return false;
117 // This function theoretically can block on the disk, but realistically
118 // the temporary files we create will just go into the buffer cache
119 // and be deleted before they ever make it out to disk.
120 base::ThreadRestrictions::ScopedAllowIO allow_io
;
123 bool fix_size
= true;
127 // It doesn't make sense to have a open-existing private piece of shmem
128 DCHECK(!open_existing
);
129 // Q: Why not use the shm_open() etc. APIs?
130 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
131 fp
= file_util::CreateAndOpenTemporaryShmemFile(&path
);
133 // Deleting the file prevents anyone else from mapping it in
134 // (making it private), and prevents the need for cleanup (once
135 // the last fd is closed, it is truly freed).
137 file_util::Delete(path
, false);
140 if (!FilePathForMemoryName(name
, &path
))
143 fp
= file_util::OpenFile(path
, "w+x");
144 if (fp
== NULL
&& open_existing
) {
145 // "w+" will truncate if it already exists.
146 fp
= file_util::OpenFile(path
, "a+");
150 if (fp
&& fix_size
) {
153 if (fstat(fileno(fp
), &stat
) != 0) {
154 file_util::CloseFile(fp
);
157 const uint32 current_size
= stat
.st_size
;
158 if (current_size
!= size
) {
159 if (HANDLE_EINTR(ftruncate(fileno(fp
), size
)) != 0) {
160 file_util::CloseFile(fp
);
163 if (fseeko(fp
, size
, SEEK_SET
) != 0) {
164 file_util::CloseFile(fp
);
168 created_size_
= size
;
171 #if !defined(OS_MACOSX)
172 PLOG(ERROR
) << "Creating shared memory in " << path
.value() << " failed";
173 FilePath dir
= path
.DirName();
174 if (access(dir
.value().c_str(), W_OK
| X_OK
) < 0) {
175 PLOG(ERROR
) << "Unable to access(W_OK|X_OK) " << dir
.value();
176 if (dir
.value() == "/dev/shm") {
177 LOG(FATAL
) << "This is frequently caused by incorrect permissions on "
178 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix.";
182 PLOG(ERROR
) << "Creating shared memory in " << path
.value() << " failed";
187 return PrepareMapFile(fp
);
190 // Our current implementation of shmem is with mmap()ing of files.
191 // These files need to be deleted explicitly.
192 // In practice this call is only needed for unit tests.
193 bool SharedMemory::Delete(const std::string
& name
) {
195 if (!FilePathForMemoryName(name
, &path
))
198 if (file_util::PathExists(path
)) {
199 return file_util::Delete(path
, false);
202 // Doesn't exist, so success.
206 bool SharedMemory::Open(const std::string
& name
, bool read_only
) {
208 if (!FilePathForMemoryName(name
, &path
))
211 read_only_
= read_only
;
213 const char *mode
= read_only
? "r" : "r+";
214 FILE *fp
= file_util::OpenFile(path
, mode
);
215 return PrepareMapFile(fp
);
218 #endif // !defined(OS_ANDROID)
220 bool SharedMemory::Map(uint32 bytes
) {
221 if (mapped_file_
== -1)
224 #if defined(OS_ANDROID)
226 int ashmem_bytes
= ashmem_get_size_region(mapped_file_
);
227 if (ashmem_bytes
< 0)
230 DCHECK_GE(static_cast<uint32
>(ashmem_bytes
), bytes
);
231 // The caller wants to determine the map region size from ashmem.
232 bytes
= ashmem_bytes
;
233 // TODO(port): we set the created size here so that it is available in
234 // transport_dib_android.cc. We should use ashmem_get_size_region()
235 // in transport_dib_android.cc.
236 created_size_
= bytes
;
240 memory_
= mmap(NULL
, bytes
, PROT_READ
| (read_only_
? 0 : PROT_WRITE
),
241 MAP_SHARED
, mapped_file_
, 0);
244 mapped_size_
= bytes
;
246 bool mmap_succeeded
= (memory_
!= (void*)-1);
247 DCHECK(mmap_succeeded
) << "Call to mmap failed, errno=" << errno
;
248 return mmap_succeeded
;
251 bool SharedMemory::Unmap() {
255 munmap(memory_
, mapped_size_
);
261 SharedMemoryHandle
SharedMemory::handle() const {
262 return FileDescriptor(mapped_file_
, false);
265 void SharedMemory::Close() {
268 if (mapped_file_
> 0) {
269 if (HANDLE_EINTR(close(mapped_file_
)) < 0)
270 PLOG(ERROR
) << "close";
275 void SharedMemory::Lock() {
276 LockOrUnlockCommon(F_LOCK
);
279 void SharedMemory::Unlock() {
280 LockOrUnlockCommon(F_ULOCK
);
283 #if !defined(OS_ANDROID)
284 bool SharedMemory::PrepareMapFile(FILE *fp
) {
285 DCHECK_EQ(-1, mapped_file_
);
286 if (fp
== NULL
) return false;
288 // This function theoretically can block on the disk, but realistically
289 // the temporary files we create will just go into the buffer cache
290 // and be deleted before they ever make it out to disk.
291 base::ThreadRestrictions::ScopedAllowIO allow_io
;
293 file_util::ScopedFILE
file_closer(fp
);
295 mapped_file_
= dup(fileno(fp
));
296 if (mapped_file_
== -1) {
297 if (errno
== EMFILE
) {
298 LOG(WARNING
) << "Shared memory creation failed; out of file descriptors";
301 NOTREACHED() << "Call to dup failed, errno=" << errno
;
306 if (fstat(mapped_file_
, &st
))
314 // For the given shmem named |mem_name|, return a filename to mmap()
315 // (and possibly create). Modifies |filename|. Return false on
316 // error, or true of we are happy.
317 bool SharedMemory::FilePathForMemoryName(const std::string
& mem_name
,
319 // mem_name will be used for a filename; make sure it doesn't
320 // contain anything which will confuse us.
321 DCHECK_EQ(std::string::npos
, mem_name
.find('/'));
322 DCHECK_EQ(std::string::npos
, mem_name
.find('\0'));
325 if (!file_util::GetShmemTempDir(&temp_dir
))
328 #if !defined(OS_MACOSX)
329 #if defined(GOOGLE_CHROME_BUILD)
330 std::string name_base
= std::string("com.google.Chrome");
332 std::string name_base
= std::string("org.chromium.Chromium");
335 std::string name_base
= std::string(base::mac::BaseBundleID());
337 *path
= temp_dir
.AppendASCII(name_base
+ ".shmem." + mem_name
);
341 void SharedMemory::LockOrUnlockCommon(int function
) {
342 DCHECK_GE(mapped_file_
, 0);
343 while (lockf(mapped_file_
, function
, 0) < 0) {
344 if (errno
== EINTR
) {
346 } else if (errno
== ENOLCK
) {
347 // temporary kernel resource exaustion
348 base::PlatformThread::Sleep(500);
351 NOTREACHED() << "lockf() failed."
352 << " function:" << function
353 << " fd:" << mapped_file_
354 << " errno:" << errno
355 << " msg:" << safe_strerror(errno
);
360 bool SharedMemory::ShareToProcessCommon(ProcessHandle process
,
361 SharedMemoryHandle
*new_handle
,
363 const int new_fd
= dup(mapped_file_
);
364 DCHECK_GE(new_fd
, 0);
365 new_handle
->fd
= new_fd
;
366 new_handle
->auto_close
= true;