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