// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // _beginthreadex #include "base/shared_memory.h" #include "testing/gtest/include/gtest/gtest.h" namespace { class SharedMemoryTest : public testing::Test { }; unsigned __stdcall MultipleThreadMain(void* param) { // 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. const int kDataSize = 1024; std::wstring test_name = L"SharedMemoryOpenThreadTest"; int16 id = reinterpret_cast(param); 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; Sleep(1); // short wait EXPECT_EQ(*ptr, idx); } memory.Close(); return 0; } unsigned __stdcall MultipleLockThread(void* param) { // 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. const int kDataSize = sizeof(int); int id = static_cast(reinterpret_cast(param)); SharedMemoryHandle handle = NULL; { SharedMemory memory1; EXPECT_TRUE(memory1.Create(L"SharedMemoryMultipleLockThreadTest", false, true, kDataSize)); EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle)); } 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; // short wait Sleep(1); EXPECT_EQ(*ptr, i); memory2.Unlock(); } memory2.Close(); return 0; } } // 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 // 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 still 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(); } TEST(SharedMemoryTest, MultipleThreads) { // 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. const int kNumThreads = 5; HANDLE threads[kNumThreads]; // Spawn the threads. for (int16 index = 0; index < kNumThreads; index++) { void *argument = reinterpret_cast(index); unsigned thread_id; threads[index] = reinterpret_cast( _beginthreadex(NULL, 0, MultipleThreadMain, argument, 0, &thread_id)); EXPECT_NE(threads[index], static_cast(NULL)); } // Wait for the threads to finish. for (int index = 0; index < kNumThreads; index++) { DWORD rv = WaitForSingleObject(threads[index], 60*1000); EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished CloseHandle(threads[index]); } } TEST(SharedMemoryTest, Lock) { // 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. const int kNumThreads = 5; HANDLE threads[kNumThreads]; // Spawn the threads. for (int index = 0; index < kNumThreads; ++index) { void *argument = reinterpret_cast(static_cast(index)); threads[index] = reinterpret_cast( _beginthreadex(NULL, 0, &MultipleLockThread, argument, 0, NULL)); EXPECT_NE(threads[index], static_cast(NULL)); } // Wait for the threads to finish. for (int index = 0; index < kNumThreads; ++index) { DWORD rv = WaitForSingleObject(threads[index], 60*1000); EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished CloseHandle(threads[index]); } }