blob: 928326a52985a39dd2cbd7a8027772b7730a5d66 [file] [log] [blame]
// 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 <process.h> // _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<int16>(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<int*>(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<int>(reinterpret_cast<INT_PTR>(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<int*>(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<char *>(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<void*>(index);
unsigned thread_id;
threads[index] = reinterpret_cast<HANDLE>(
_beginthreadex(NULL, 0, MultipleThreadMain, argument, 0, &thread_id));
EXPECT_NE(threads[index], static_cast<HANDLE>(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<void*>(static_cast<INT_PTR>(index));
threads[index] = reinterpret_cast<HANDLE>(
_beginthreadex(NULL, 0, &MultipleLockThread, argument, 0, NULL));
EXPECT_NE(threads[index], static_cast<HANDLE>(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]);
}
}