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