// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/basictypes.h" #include "base/platform_thread.h" #include "base/shared_memory.h" #include "testing/gtest/include/gtest/gtest.h" static const int kNumThreads = 5; namespace base { namespace { // Each thread will open the shared memory. Each thread will take a different 4 // byte int pointer, and keep changing it, with some small pauses in between. // Verify that each thread's value in the shared memory is always correct. class MultipleThreadMain : public PlatformThread::Delegate { public: explicit MultipleThreadMain(int16 id) : id_(id) {} ~MultipleThreadMain() {} // PlatformThread::Delegate interface. void ThreadMain() { const int kDataSize = 1024; std::wstring test_name = L"SharedMemoryOpenThreadTest"; SharedMemory memory; bool rv = memory.Create(test_name, false, true, kDataSize); EXPECT_TRUE(rv); rv = memory.Map(kDataSize); EXPECT_TRUE(rv); int *ptr = static_cast(memory.memory()) + id_; EXPECT_EQ(*ptr, 0); for (int idx = 0; idx < 100; idx++) { *ptr = idx; PlatformThread::Sleep(1); // Short wait. EXPECT_EQ(*ptr, idx); } memory.Close(); } private: int16 id_; DISALLOW_COPY_AND_ASSIGN(MultipleThreadMain); }; #if defined(OS_WIN) // Each thread will open the shared memory. Each thread will take the memory, // and keep changing it while trying to lock it, with some small pauses in // between. Verify that each thread's value in the shared memory is always // correct. class MultipleLockThread : public PlatformThread::Delegate { public: explicit MultipleLockThread(int id) : id_(id) {} ~MultipleLockThread() {} // PlatformThread::Delegate interface. void ThreadMain() { const int kDataSize = sizeof(int); SharedMemoryHandle handle = NULL; { SharedMemory memory1; EXPECT_TRUE(memory1.Create(L"SharedMemoryMultipleLockThreadTest", false, true, kDataSize)); EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle)); // TODO(paulg): Implement this once we have a posix version of // SharedMemory::ShareToProcess. EXPECT_TRUE(true); } SharedMemory memory2(handle, false); EXPECT_TRUE(memory2.Map(kDataSize)); volatile int* const ptr = static_cast(memory2.memory()); for (int idx = 0; idx < 20; idx++) { memory2.Lock(); int i = (id_ << 16) + idx; *ptr = i; PlatformThread::Sleep(1); // Short wait. EXPECT_EQ(*ptr, i); memory2.Unlock(); } memory2.Close(); } private: int id_; DISALLOW_COPY_AND_ASSIGN(MultipleLockThread); }; #endif } // namespace TEST(SharedMemoryTest, OpenClose) { const int kDataSize = 1024; std::wstring test_name = L"SharedMemoryOpenCloseTest"; // Open two handles to a memory segment, confirm that they are mapped // separately yet point to the same space. SharedMemory memory1; bool rv = memory1.Open(test_name, false); EXPECT_FALSE(rv); rv = memory1.Create(test_name, false, false, kDataSize); EXPECT_TRUE(rv); rv = memory1.Map(kDataSize); EXPECT_TRUE(rv); SharedMemory memory2; rv = memory2.Open(test_name, false); EXPECT_TRUE(rv); rv = memory2.Map(kDataSize); EXPECT_TRUE(rv); EXPECT_NE(memory1.memory(), memory2.memory()); // Compare the pointers. // Make sure we don't segfault. (it actually happened!) ASSERT_NE(memory1.memory(), static_cast(NULL)); ASSERT_NE(memory2.memory(), static_cast(NULL)); // Write data to the first memory segment, verify contents of second. memset(memory1.memory(), '1', kDataSize); EXPECT_EQ(memcmp(memory1.memory(), memory2.memory(), kDataSize), 0); // Close the first memory segment, and verify the second has the right data. memory1.Close(); char *start_ptr = static_cast(memory2.memory()); char *end_ptr = start_ptr + kDataSize; for (char* ptr = start_ptr; ptr < end_ptr; ptr++) EXPECT_EQ(*ptr, '1'); // Close the second memory segment. memory2.Close(); } #if defined(OS_WIN) // Create a set of 5 threads to each open a shared memory segment and write to // it. Verify that they are always reading/writing consistent data. TEST(SharedMemoryTest, MultipleThreads) { PlatformThreadHandle thread_handles[kNumThreads]; MultipleThreadMain* thread_delegates[kNumThreads]; // Spawn the threads. for (int16 index = 0; index < kNumThreads; index++) { PlatformThreadHandle pth; thread_delegates[index] = new MultipleThreadMain(index); EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth)); thread_handles[index] = pth; } // Wait for the threads to finish. for (int index = 0; index < kNumThreads; index++) { PlatformThread::Join(thread_handles[index]); delete thread_delegates[index]; } } // Create a set of threads to each open a shared memory segment and write to it // with the lock held. Verify that they are always reading/writing consistent // data. TEST(SharedMemoryTest, Lock) { PlatformThreadHandle thread_handles[kNumThreads]; MultipleLockThread* thread_delegates[kNumThreads]; // Spawn the threads. for (int index = 0; index < kNumThreads; ++index) { PlatformThreadHandle pth; thread_delegates[index] = new MultipleLockThread(index); EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth)); thread_handles[index] = pth; } // Wait for the threads to finish. for (int index = 0; index < kNumThreads; ++index) { PlatformThread::Join(thread_handles[index]); delete thread_delegates[index]; } } #endif } // namespace base