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