shared_memory_unittest.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2006-2008 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/basictypes.h" 6#include "base/platform_thread.h" 7#include "base/scoped_nsautorelease_pool.h" 8#include "base/shared_memory.h" 9#include "base/scoped_ptr.h" 10#include "base/test/multiprocess_test.h" 11#include "testing/gtest/include/gtest/gtest.h" 12#include "testing/multiprocess_func_list.h" 13 14static const int kNumThreads = 5; 15static const int kNumTasks = 5; 16 17namespace base { 18 19namespace { 20 21// Each thread will open the shared memory. Each thread will take a different 4 22// byte int pointer, and keep changing it, with some small pauses in between. 23// Verify that each thread's value in the shared memory is always correct. 24class MultipleThreadMain : public PlatformThread::Delegate { 25 public: 26 explicit MultipleThreadMain(int16 id) : id_(id) {} 27 ~MultipleThreadMain() {} 28 29 static void CleanUp() { 30 SharedMemory memory; 31 memory.Delete(s_test_name_); 32 } 33 34 // PlatformThread::Delegate interface. 35 void ThreadMain() { 36 ScopedNSAutoreleasePool pool; // noop if not OSX 37 const uint32 kDataSize = 1024; 38 SharedMemory memory; 39 bool rv = memory.Create(s_test_name_, false, true, kDataSize); 40 EXPECT_TRUE(rv); 41 rv = memory.Map(kDataSize); 42 EXPECT_TRUE(rv); 43 int *ptr = static_cast<int*>(memory.memory()) + id_; 44 EXPECT_EQ(*ptr, 0); 45 46 for (int idx = 0; idx < 100; idx++) { 47 *ptr = idx; 48 PlatformThread::Sleep(1); // Short wait. 49 EXPECT_EQ(*ptr, idx); 50 } 51 52 memory.Close(); 53 } 54 55 private: 56 int16 id_; 57 58 static const char* const s_test_name_; 59 60 DISALLOW_COPY_AND_ASSIGN(MultipleThreadMain); 61}; 62 63const char* const MultipleThreadMain::s_test_name_ = 64 "SharedMemoryOpenThreadTest"; 65 66// TODO(port): 67// This test requires the ability to pass file descriptors between processes. 68// We haven't done that yet in Chrome for POSIX. 69#if defined(OS_WIN) 70// Each thread will open the shared memory. Each thread will take the memory, 71// and keep changing it while trying to lock it, with some small pauses in 72// between. Verify that each thread's value in the shared memory is always 73// correct. 74class MultipleLockThread : public PlatformThread::Delegate { 75 public: 76 explicit MultipleLockThread(int id) : id_(id) {} 77 ~MultipleLockThread() {} 78 79 // PlatformThread::Delegate interface. 80 void ThreadMain() { 81 const uint32 kDataSize = sizeof(int); 82 SharedMemoryHandle handle = NULL; 83 { 84 SharedMemory memory1; 85 EXPECT_TRUE(memory1.Create("SharedMemoryMultipleLockThreadTest", 86 false, true, kDataSize)); 87 EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle)); 88 // TODO(paulg): Implement this once we have a posix version of 89 // SharedMemory::ShareToProcess. 90 EXPECT_TRUE(true); 91 } 92 93 SharedMemory memory2(handle, false); 94 EXPECT_TRUE(memory2.Map(kDataSize)); 95 volatile int* const ptr = static_cast<int*>(memory2.memory()); 96 97 for (int idx = 0; idx < 20; idx++) { 98 memory2.Lock(); 99 int i = (id_ << 16) + idx; 100 *ptr = i; 101 PlatformThread::Sleep(1); // Short wait. 102 EXPECT_EQ(*ptr, i); 103 memory2.Unlock(); 104 } 105 106 memory2.Close(); 107 } 108 109 private: 110 int id_; 111 112 DISALLOW_COPY_AND_ASSIGN(MultipleLockThread); 113}; 114#endif 115 116} // namespace 117 118TEST(SharedMemoryTest, OpenClose) { 119 const uint32 kDataSize = 1024; 120 std::string test_name = "SharedMemoryOpenCloseTest"; 121 122 // Open two handles to a memory segment, confirm that they are mapped 123 // separately yet point to the same space. 124 SharedMemory memory1; 125 bool rv = memory1.Delete(test_name); 126 EXPECT_TRUE(rv); 127 rv = memory1.Delete(test_name); 128 EXPECT_TRUE(rv); 129 rv = memory1.Open(test_name, false); 130 EXPECT_FALSE(rv); 131 rv = memory1.Create(test_name, false, false, kDataSize); 132 EXPECT_TRUE(rv); 133 rv = memory1.Map(kDataSize); 134 EXPECT_TRUE(rv); 135 SharedMemory memory2; 136 rv = memory2.Open(test_name, false); 137 EXPECT_TRUE(rv); 138 rv = memory2.Map(kDataSize); 139 EXPECT_TRUE(rv); 140 EXPECT_NE(memory1.memory(), memory2.memory()); // Compare the pointers. 141 142 // Make sure we don't segfault. (it actually happened!) 143 ASSERT_NE(memory1.memory(), static_cast<void*>(NULL)); 144 ASSERT_NE(memory2.memory(), static_cast<void*>(NULL)); 145 146 // Write data to the first memory segment, verify contents of second. 147 memset(memory1.memory(), '1', kDataSize); 148 EXPECT_EQ(memcmp(memory1.memory(), memory2.memory(), kDataSize), 0); 149 150 // Close the first memory segment, and verify the second has the right data. 151 memory1.Close(); 152 char *start_ptr = static_cast<char *>(memory2.memory()); 153 char *end_ptr = start_ptr + kDataSize; 154 for (char* ptr = start_ptr; ptr < end_ptr; ptr++) 155 EXPECT_EQ(*ptr, '1'); 156 157 // Close the second memory segment. 158 memory2.Close(); 159 160 rv = memory1.Delete(test_name); 161 EXPECT_TRUE(rv); 162 rv = memory2.Delete(test_name); 163 EXPECT_TRUE(rv); 164} 165 166// Create a set of N threads to each open a shared memory segment and write to 167// it. Verify that they are always reading/writing consistent data. 168TEST(SharedMemoryTest, MultipleThreads) { 169 MultipleThreadMain::CleanUp(); 170 // On POSIX we have a problem when 2 threads try to create the shmem 171 // (a file) at exactly the same time, since create both creates the 172 // file and zerofills it. We solve the problem for this unit test 173 // (make it not flaky) by starting with 1 thread, then 174 // intentionally don't clean up its shmem before running with 175 // kNumThreads. 176 177 int threadcounts[] = { 1, kNumThreads }; 178 for (size_t i = 0; i < sizeof(threadcounts) / sizeof(threadcounts); i++) { 179 int numthreads = threadcounts[i]; 180 scoped_array<PlatformThreadHandle> thread_handles; 181 scoped_array<MultipleThreadMain*> thread_delegates; 182 183 thread_handles.reset(new PlatformThreadHandle[numthreads]); 184 thread_delegates.reset(new MultipleThreadMain*[numthreads]); 185 186 // Spawn the threads. 187 for (int16 index = 0; index < numthreads; index++) { 188 PlatformThreadHandle pth; 189 thread_delegates[index] = new MultipleThreadMain(index); 190 EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth)); 191 thread_handles[index] = pth; 192 } 193 194 // Wait for the threads to finish. 195 for (int index = 0; index < numthreads; index++) { 196 PlatformThread::Join(thread_handles[index]); 197 delete thread_delegates[index]; 198 } 199 } 200 MultipleThreadMain::CleanUp(); 201} 202 203// TODO(port): this test requires the MultipleLockThread class 204// (defined above), which requires the ability to pass file 205// descriptors between processes. We haven't done that yet in Chrome 206// for POSIX. 207#if defined(OS_WIN) 208// Create a set of threads to each open a shared memory segment and write to it 209// with the lock held. Verify that they are always reading/writing consistent 210// data. 211TEST(SharedMemoryTest, Lock) { 212 PlatformThreadHandle thread_handles[kNumThreads]; 213 MultipleLockThread* thread_delegates[kNumThreads]; 214 215 // Spawn the threads. 216 for (int index = 0; index < kNumThreads; ++index) { 217 PlatformThreadHandle pth; 218 thread_delegates[index] = new MultipleLockThread(index); 219 EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth)); 220 thread_handles[index] = pth; 221 } 222 223 // Wait for the threads to finish. 224 for (int index = 0; index < kNumThreads; ++index) { 225 PlatformThread::Join(thread_handles[index]); 226 delete thread_delegates[index]; 227 } 228} 229#endif 230 231// Allocate private (unique) shared memory with an empty string for a 232// name. Make sure several of them don't point to the same thing as 233// we might expect if the names are equal. 234TEST(SharedMemoryTest, AnonymousPrivate) { 235 int i, j; 236 int count = 4; 237 bool rv; 238 const uint32 kDataSize = 8192; 239 240 scoped_array<SharedMemory> memories(new SharedMemory[count]); 241 scoped_array<int*> pointers(new int*[count]); 242 ASSERT_TRUE(memories.get()); 243 ASSERT_TRUE(pointers.get()); 244 245 for (i = 0; i < count; i++) { 246 rv = memories[i].Create("", false, true, kDataSize); 247 EXPECT_TRUE(rv); 248 rv = memories[i].Map(kDataSize); 249 EXPECT_TRUE(rv); 250 int *ptr = static_cast<int*>(memories[i].memory()); 251 EXPECT_TRUE(ptr); 252 pointers[i] = ptr; 253 } 254 255 for (i = 0; i < count; i++) { 256 // zero out the first int in each except for i; for that one, make it 100. 257 for (j = 0; j < count; j++) { 258 if (i == j) 259 pointers[j][0] = 100; 260 else 261 pointers[j][0] = 0; 262 } 263 // make sure there is no bleeding of the 100 into the other pointers 264 for (j = 0; j < count; j++) { 265 if (i == j) 266 EXPECT_EQ(100, pointers[j][0]); 267 else 268 EXPECT_EQ(0, pointers[j][0]); 269 } 270 } 271 272 for (int i = 0; i < count; i++) { 273 memories[i].Close(); 274 } 275} 276 277// On POSIX it is especially important we test shmem across processes, 278// not just across threads. But the test is enabled on all platforms. 279class SharedMemoryProcessTest : public base::MultiProcessTest { 280 public: 281 282 static void CleanUp() { 283 SharedMemory memory; 284 memory.Delete(s_test_name_); 285 } 286 287 static int TaskTestMain() { 288 int errors = 0; 289 ScopedNSAutoreleasePool pool; // noop if not OSX 290 const uint32 kDataSize = 1024; 291 SharedMemory memory; 292 bool rv = memory.Create(s_test_name_, false, true, kDataSize); 293 EXPECT_TRUE(rv); 294 if (rv != true) 295 errors++; 296 rv = memory.Map(kDataSize); 297 EXPECT_TRUE(rv); 298 if (rv != true) 299 errors++; 300 int *ptr = static_cast<int*>(memory.memory()); 301 302 for (int idx = 0; idx < 20; idx++) { 303 memory.Lock(); 304 int i = (1 << 16) + idx; 305 *ptr = i; 306 PlatformThread::Sleep(10); // Short wait. 307 if (*ptr != i) 308 errors++; 309 memory.Unlock(); 310 } 311 312 memory.Close(); 313 return errors; 314 } 315 316 private: 317 static const char* const s_test_name_; 318}; 319 320const char* const SharedMemoryProcessTest::s_test_name_ = "MPMem"; 321 322 323TEST_F(SharedMemoryProcessTest, Tasks) { 324 SharedMemoryProcessTest::CleanUp(); 325 326 base::ProcessHandle handles[kNumTasks]; 327 for (int index = 0; index < kNumTasks; ++index) { 328 handles[index] = SpawnChild("SharedMemoryTestMain", false); 329 } 330 331 int exit_code = 0; 332 for (int index = 0; index < kNumTasks; ++index) { 333 EXPECT_TRUE(base::WaitForExitCode(handles[index], &exit_code)); 334 EXPECT_TRUE(exit_code == 0); 335 } 336 337 SharedMemoryProcessTest::CleanUp(); 338} 339 340MULTIPROCESS_TEST_MAIN(SharedMemoryTestMain) { 341 return SharedMemoryProcessTest::TaskTestMain(); 342} 343 344} // namespace base 345