1 // Copyright (c) 2012 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.
5 #include "base/basictypes.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/memory/shared_memory.h"
8 #include "base/process/kill.h"
9 #include "base/rand_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/sys_info.h"
12 #include "base/test/multiprocess_test.h"
13 #include "base/threading/platform_thread.h"
14 #include "base/time/time.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "testing/multiprocess_func_list.h"
18 #if defined(OS_MACOSX)
19 #include "base/mac/scoped_nsautorelease_pool.h"
27 #include <sys/types.h>
32 #include "base/win/scoped_handle.h"
35 static const int kNumThreads
= 5;
36 #if !defined(OS_IOS) // iOS does not allow multiple processes.
37 static const int kNumTasks
= 5;
44 // Each thread will open the shared memory. Each thread will take a different 4
45 // byte int pointer, and keep changing it, with some small pauses in between.
46 // Verify that each thread's value in the shared memory is always correct.
47 class MultipleThreadMain
: public PlatformThread::Delegate
{
49 explicit MultipleThreadMain(int16 id
) : id_(id
) {}
50 ~MultipleThreadMain() override
{}
52 static void CleanUp() {
54 memory
.Delete(s_test_name_
);
57 // PlatformThread::Delegate interface.
58 void ThreadMain() override
{
59 #if defined(OS_MACOSX)
60 mac::ScopedNSAutoreleasePool pool
;
62 const uint32 kDataSize
= 1024;
64 bool rv
= memory
.CreateNamedDeprecated(s_test_name_
, true, kDataSize
);
66 rv
= memory
.Map(kDataSize
);
68 int *ptr
= static_cast<int*>(memory
.memory()) + id_
;
71 for (int idx
= 0; idx
< 100; idx
++) {
73 PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
76 // Reset back to 0 for the next test that uses the same name.
85 static const char* const s_test_name_
;
87 DISALLOW_COPY_AND_ASSIGN(MultipleThreadMain
);
90 const char* const MultipleThreadMain::s_test_name_
=
91 "SharedMemoryOpenThreadTest";
94 // This test requires the ability to pass file descriptors between processes.
95 // We haven't done that yet in Chrome for POSIX.
97 // Each thread will open the shared memory. Each thread will take the memory,
98 // and keep changing it while trying to lock it, with some small pauses in
99 // between. Verify that each thread's value in the shared memory is always
101 class MultipleLockThread
: public PlatformThread::Delegate
{
103 explicit MultipleLockThread(int id
) : id_(id
) {}
104 ~MultipleLockThread() override
{}
106 // PlatformThread::Delegate interface.
107 void ThreadMain() override
{
108 const uint32 kDataSize
= sizeof(int);
109 SharedMemoryHandle handle
= NULL
;
111 SharedMemory memory1
;
112 EXPECT_TRUE(memory1
.CreateNamedDeprecated(
113 "SharedMemoryMultipleLockThreadTest", true, kDataSize
));
114 EXPECT_TRUE(memory1
.ShareToProcess(GetCurrentProcess(), &handle
));
115 // TODO(paulg): Implement this once we have a posix version of
116 // SharedMemory::ShareToProcess.
120 SharedMemory
memory2(handle
, false);
121 EXPECT_TRUE(memory2
.Map(kDataSize
));
122 volatile int* const ptr
= static_cast<int*>(memory2
.memory());
124 for (int idx
= 0; idx
< 20; idx
++) {
125 memory2
.LockDeprecated();
126 int i
= (id_
<< 16) + idx
;
128 PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
130 memory2
.UnlockDeprecated();
139 DISALLOW_COPY_AND_ASSIGN(MultipleLockThread
);
145 // Android doesn't support SharedMemory::Open/Delete/
146 // CreateNamedDeprecated(openExisting=true)
147 #if !defined(OS_ANDROID)
148 TEST(SharedMemoryTest
, OpenClose
) {
149 const uint32 kDataSize
= 1024;
150 std::string test_name
= "SharedMemoryOpenCloseTest";
152 // Open two handles to a memory segment, confirm that they are mapped
153 // separately yet point to the same space.
154 SharedMemory memory1
;
155 bool rv
= memory1
.Delete(test_name
);
157 rv
= memory1
.Delete(test_name
);
159 rv
= memory1
.Open(test_name
, false);
161 rv
= memory1
.CreateNamedDeprecated(test_name
, false, kDataSize
);
163 rv
= memory1
.Map(kDataSize
);
165 SharedMemory memory2
;
166 rv
= memory2
.Open(test_name
, false);
168 rv
= memory2
.Map(kDataSize
);
170 EXPECT_NE(memory1
.memory(), memory2
.memory()); // Compare the pointers.
172 // Make sure we don't segfault. (it actually happened!)
173 ASSERT_NE(memory1
.memory(), static_cast<void*>(NULL
));
174 ASSERT_NE(memory2
.memory(), static_cast<void*>(NULL
));
176 // Write data to the first memory segment, verify contents of second.
177 memset(memory1
.memory(), '1', kDataSize
);
178 EXPECT_EQ(memcmp(memory1
.memory(), memory2
.memory(), kDataSize
), 0);
180 // Close the first memory segment, and verify the second has the right data.
182 char *start_ptr
= static_cast<char *>(memory2
.memory());
183 char *end_ptr
= start_ptr
+ kDataSize
;
184 for (char* ptr
= start_ptr
; ptr
< end_ptr
; ptr
++)
185 EXPECT_EQ(*ptr
, '1');
187 // Close the second memory segment.
190 rv
= memory1
.Delete(test_name
);
192 rv
= memory2
.Delete(test_name
);
196 TEST(SharedMemoryTest
, OpenExclusive
) {
197 const uint32 kDataSize
= 1024;
198 const uint32 kDataSize2
= 2048;
199 std::ostringstream test_name_stream
;
200 test_name_stream
<< "SharedMemoryOpenExclusiveTest."
201 << Time::Now().ToDoubleT();
202 std::string test_name
= test_name_stream
.str();
204 // Open two handles to a memory segment and check that
205 // open_existing_deprecated works as expected.
206 SharedMemory memory1
;
207 bool rv
= memory1
.CreateNamedDeprecated(test_name
, false, kDataSize
);
210 // Memory1 knows it's size because it created it.
211 EXPECT_EQ(memory1
.requested_size(), kDataSize
);
213 rv
= memory1
.Map(kDataSize
);
216 // The mapped memory1 must be at least the size we asked for.
217 EXPECT_GE(memory1
.mapped_size(), kDataSize
);
219 // The mapped memory1 shouldn't exceed rounding for allocation granularity.
220 EXPECT_LT(memory1
.mapped_size(),
221 kDataSize
+ base::SysInfo::VMAllocationGranularity());
223 memset(memory1
.memory(), 'G', kDataSize
);
225 SharedMemory memory2
;
226 // Should not be able to create if openExisting is false.
227 rv
= memory2
.CreateNamedDeprecated(test_name
, false, kDataSize2
);
230 // Should be able to create with openExisting true.
231 rv
= memory2
.CreateNamedDeprecated(test_name
, true, kDataSize2
);
234 // Memory2 shouldn't know the size because we didn't create it.
235 EXPECT_EQ(memory2
.requested_size(), 0U);
237 // We should be able to map the original size.
238 rv
= memory2
.Map(kDataSize
);
241 // The mapped memory2 must be at least the size of the original.
242 EXPECT_GE(memory2
.mapped_size(), kDataSize
);
244 // The mapped memory2 shouldn't exceed rounding for allocation granularity.
245 EXPECT_LT(memory2
.mapped_size(),
246 kDataSize2
+ base::SysInfo::VMAllocationGranularity());
248 // Verify that opening memory2 didn't truncate or delete memory 1.
249 char *start_ptr
= static_cast<char *>(memory2
.memory());
250 char *end_ptr
= start_ptr
+ kDataSize
;
251 for (char* ptr
= start_ptr
; ptr
< end_ptr
; ptr
++) {
252 EXPECT_EQ(*ptr
, 'G');
258 rv
= memory1
.Delete(test_name
);
263 // Check that memory is still mapped after its closed.
264 TEST(SharedMemoryTest
, CloseNoUnmap
) {
265 const size_t kDataSize
= 4096;
268 ASSERT_TRUE(memory
.CreateAndMapAnonymous(kDataSize
));
269 char* ptr
= static_cast<char*>(memory
.memory());
270 ASSERT_NE(ptr
, static_cast<void*>(NULL
));
271 memset(ptr
, 'G', kDataSize
);
275 EXPECT_EQ(ptr
, memory
.memory());
276 EXPECT_EQ(SharedMemory::NULLHandle(), memory
.handle());
278 for (size_t i
= 0; i
< kDataSize
; i
++) {
279 EXPECT_EQ('G', ptr
[i
]);
283 EXPECT_EQ(nullptr, memory
.memory());
286 // Create a set of N threads to each open a shared memory segment and write to
287 // it. Verify that they are always reading/writing consistent data.
288 TEST(SharedMemoryTest
, MultipleThreads
) {
289 MultipleThreadMain::CleanUp();
290 // On POSIX we have a problem when 2 threads try to create the shmem
291 // (a file) at exactly the same time, since create both creates the
292 // file and zerofills it. We solve the problem for this unit test
293 // (make it not flaky) by starting with 1 thread, then
294 // intentionally don't clean up its shmem before running with
297 int threadcounts
[] = { 1, kNumThreads
};
298 for (size_t i
= 0; i
< arraysize(threadcounts
); i
++) {
299 int numthreads
= threadcounts
[i
];
300 scoped_ptr
<PlatformThreadHandle
[]> thread_handles
;
301 scoped_ptr
<MultipleThreadMain
*[]> thread_delegates
;
303 thread_handles
.reset(new PlatformThreadHandle
[numthreads
]);
304 thread_delegates
.reset(new MultipleThreadMain
*[numthreads
]);
306 // Spawn the threads.
307 for (int16 index
= 0; index
< numthreads
; index
++) {
308 PlatformThreadHandle pth
;
309 thread_delegates
[index
] = new MultipleThreadMain(index
);
310 EXPECT_TRUE(PlatformThread::Create(0, thread_delegates
[index
], &pth
));
311 thread_handles
[index
] = pth
;
314 // Wait for the threads to finish.
315 for (int index
= 0; index
< numthreads
; index
++) {
316 PlatformThread::Join(thread_handles
[index
]);
317 delete thread_delegates
[index
];
320 MultipleThreadMain::CleanUp();
323 // TODO(port): this test requires the MultipleLockThread class
324 // (defined above), which requires the ability to pass file
325 // descriptors between processes. We haven't done that yet in Chrome
328 // Create a set of threads to each open a shared memory segment and write to it
329 // with the lock held. Verify that they are always reading/writing consistent
331 TEST(SharedMemoryTest
, Lock
) {
332 PlatformThreadHandle thread_handles
[kNumThreads
];
333 MultipleLockThread
* thread_delegates
[kNumThreads
];
335 // Spawn the threads.
336 for (int index
= 0; index
< kNumThreads
; ++index
) {
337 PlatformThreadHandle pth
;
338 thread_delegates
[index
] = new MultipleLockThread(index
);
339 EXPECT_TRUE(PlatformThread::Create(0, thread_delegates
[index
], &pth
));
340 thread_handles
[index
] = pth
;
343 // Wait for the threads to finish.
344 for (int index
= 0; index
< kNumThreads
; ++index
) {
345 PlatformThread::Join(thread_handles
[index
]);
346 delete thread_delegates
[index
];
351 // Allocate private (unique) shared memory with an empty string for a
352 // name. Make sure several of them don't point to the same thing as
353 // we might expect if the names are equal.
354 TEST(SharedMemoryTest
, AnonymousPrivate
) {
358 const uint32 kDataSize
= 8192;
360 scoped_ptr
<SharedMemory
[]> memories(new SharedMemory
[count
]);
361 scoped_ptr
<int*[]> pointers(new int*[count
]);
362 ASSERT_TRUE(memories
.get());
363 ASSERT_TRUE(pointers
.get());
365 for (i
= 0; i
< count
; i
++) {
366 rv
= memories
[i
].CreateAndMapAnonymous(kDataSize
);
368 int *ptr
= static_cast<int*>(memories
[i
].memory());
373 for (i
= 0; i
< count
; i
++) {
374 // zero out the first int in each except for i; for that one, make it 100.
375 for (j
= 0; j
< count
; j
++) {
377 pointers
[j
][0] = 100;
381 // make sure there is no bleeding of the 100 into the other pointers
382 for (j
= 0; j
< count
; j
++) {
384 EXPECT_EQ(100, pointers
[j
][0]);
386 EXPECT_EQ(0, pointers
[j
][0]);
390 for (int i
= 0; i
< count
; i
++) {
395 TEST(SharedMemoryTest
, ShareReadOnly
) {
396 StringPiece contents
= "Hello World";
398 SharedMemory writable_shmem
;
399 SharedMemoryCreateOptions options
;
400 options
.size
= contents
.size();
401 options
.share_read_only
= true;
402 ASSERT_TRUE(writable_shmem
.Create(options
));
403 ASSERT_TRUE(writable_shmem
.Map(options
.size
));
404 memcpy(writable_shmem
.memory(), contents
.data(), contents
.size());
405 EXPECT_TRUE(writable_shmem
.Unmap());
407 SharedMemoryHandle readonly_handle
;
408 ASSERT_TRUE(writable_shmem
.ShareReadOnlyToProcess(GetCurrentProcessHandle(),
410 SharedMemory
readonly_shmem(readonly_handle
, /*readonly=*/true);
412 ASSERT_TRUE(readonly_shmem
.Map(contents
.size()));
414 StringPiece(static_cast<const char*>(readonly_shmem
.memory()),
416 EXPECT_TRUE(readonly_shmem
.Unmap());
418 // Make sure the writable instance is still writable.
419 ASSERT_TRUE(writable_shmem
.Map(contents
.size()));
420 StringPiece new_contents
= "Goodbye";
421 memcpy(writable_shmem
.memory(), new_contents
.data(), new_contents
.size());
422 EXPECT_EQ(new_contents
,
423 StringPiece(static_cast<const char*>(writable_shmem
.memory()),
424 new_contents
.size()));
426 // We'd like to check that if we send the read-only segment to another
427 // process, then that other process can't reopen it read/write. (Since that
428 // would be a security hole.) Setting up multiple processes is hard in a
429 // unittest, so this test checks that the *current* process can't reopen the
430 // segment read/write. I think the test here is stronger than we actually
431 // care about, but there's a remote possibility that sending a file over a
432 // pipe would transform it into read/write.
433 SharedMemoryHandle handle
= readonly_shmem
.handle();
435 #if defined(OS_ANDROID)
436 // The "read-only" handle is still writable on Android:
437 // http://crbug.com/320865
439 #elif defined(OS_POSIX)
440 EXPECT_EQ(O_RDONLY
, fcntl(handle
.fd
, F_GETFL
) & O_ACCMODE
)
441 << "The descriptor itself should be read-only.";
444 void* writable
= mmap(
445 NULL
, contents
.size(), PROT_READ
| PROT_WRITE
, MAP_SHARED
, handle
.fd
, 0);
446 int mmap_errno
= errno
;
447 EXPECT_EQ(MAP_FAILED
, writable
)
448 << "It shouldn't be possible to re-mmap the descriptor writable.";
449 EXPECT_EQ(EACCES
, mmap_errno
) << strerror(mmap_errno
);
450 if (writable
!= MAP_FAILED
)
451 EXPECT_EQ(0, munmap(writable
, readonly_shmem
.mapped_size()));
453 #elif defined(OS_WIN)
454 EXPECT_EQ(NULL
, MapViewOfFile(handle
, FILE_MAP_WRITE
, 0, 0, 0))
455 << "Shouldn't be able to map memory writable.";
458 BOOL rv
= ::DuplicateHandle(GetCurrentProcess(),
466 << "Shouldn't be able to duplicate the handle into a writable one.";
468 base::win::ScopedHandle
writable_handle(temp_handle
);
469 rv
= ::DuplicateHandle(GetCurrentProcess(),
477 << "Should be able to duplicate the handle into a readable one.";
479 base::win::ScopedHandle
writable_handle(temp_handle
);
481 #error Unexpected platform; write a test that tries to make 'handle' writable.
482 #endif // defined(OS_POSIX) || defined(OS_WIN)
485 TEST(SharedMemoryTest
, ShareToSelf
) {
486 StringPiece contents
= "Hello World";
489 ASSERT_TRUE(shmem
.CreateAndMapAnonymous(contents
.size()));
490 memcpy(shmem
.memory(), contents
.data(), contents
.size());
491 EXPECT_TRUE(shmem
.Unmap());
493 SharedMemoryHandle shared_handle
;
494 ASSERT_TRUE(shmem
.ShareToProcess(GetCurrentProcessHandle(), &shared_handle
));
495 SharedMemory
shared(shared_handle
, /*readonly=*/false);
497 ASSERT_TRUE(shared
.Map(contents
.size()));
500 StringPiece(static_cast<const char*>(shared
.memory()), contents
.size()));
502 ASSERT_TRUE(shmem
.ShareToProcess(GetCurrentProcessHandle(), &shared_handle
));
503 SharedMemory
readonly(shared_handle
, /*readonly=*/true);
505 ASSERT_TRUE(readonly
.Map(contents
.size()));
507 StringPiece(static_cast<const char*>(readonly
.memory()),
511 TEST(SharedMemoryTest
, MapAt
) {
512 ASSERT_TRUE(SysInfo::VMAllocationGranularity() >= sizeof(uint32
));
513 const size_t kCount
= SysInfo::VMAllocationGranularity();
514 const size_t kDataSize
= kCount
* sizeof(uint32
);
517 ASSERT_TRUE(memory
.CreateAndMapAnonymous(kDataSize
));
518 uint32
* ptr
= static_cast<uint32
*>(memory
.memory());
519 ASSERT_NE(ptr
, static_cast<void*>(NULL
));
521 for (size_t i
= 0; i
< kCount
; ++i
) {
527 off_t offset
= SysInfo::VMAllocationGranularity();
528 ASSERT_TRUE(memory
.MapAt(offset
, kDataSize
- offset
));
529 offset
/= sizeof(uint32
);
530 ptr
= static_cast<uint32
*>(memory
.memory());
531 ASSERT_NE(ptr
, static_cast<void*>(NULL
));
532 for (size_t i
= offset
; i
< kCount
; ++i
) {
533 EXPECT_EQ(ptr
[i
- offset
], i
);
537 TEST(SharedMemoryTest
, MapTwice
) {
538 const uint32 kDataSize
= 1024;
540 bool rv
= memory
.CreateAndMapAnonymous(kDataSize
);
543 void* old_address
= memory
.memory();
545 rv
= memory
.Map(kDataSize
);
547 EXPECT_EQ(old_address
, memory
.memory());
550 #if defined(OS_POSIX)
551 // This test is not applicable for iOS (crbug.com/399384).
553 // Create a shared memory object, mmap it, and mprotect it to PROT_EXEC.
554 TEST(SharedMemoryTest
, AnonymousExecutable
) {
555 const uint32 kTestSize
= 1 << 16;
557 SharedMemory shared_memory
;
558 SharedMemoryCreateOptions options
;
559 options
.size
= kTestSize
;
560 options
.executable
= true;
562 EXPECT_TRUE(shared_memory
.Create(options
));
563 EXPECT_TRUE(shared_memory
.Map(shared_memory
.requested_size()));
565 EXPECT_EQ(0, mprotect(shared_memory
.memory(), shared_memory
.requested_size(),
566 PROT_READ
| PROT_EXEC
));
568 #endif // !defined(OS_IOS)
570 // Android supports a different permission model than POSIX for its "ashmem"
571 // shared memory implementation. So the tests about file permissions are not
572 // included on Android.
573 #if !defined(OS_ANDROID)
575 // Set a umask and restore the old mask on destruction.
576 class ScopedUmaskSetter
{
578 explicit ScopedUmaskSetter(mode_t target_mask
) {
579 old_umask_
= umask(target_mask
);
581 ~ScopedUmaskSetter() { umask(old_umask_
); }
584 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedUmaskSetter
);
587 // Create a shared memory object, check its permissions.
588 TEST(SharedMemoryTest
, FilePermissionsAnonymous
) {
589 const uint32 kTestSize
= 1 << 8;
591 SharedMemory shared_memory
;
592 SharedMemoryCreateOptions options
;
593 options
.size
= kTestSize
;
594 // Set a file mode creation mask that gives all permissions.
595 ScopedUmaskSetter
permissive_mask(S_IWGRP
| S_IWOTH
);
597 EXPECT_TRUE(shared_memory
.Create(options
));
599 int shm_fd
= shared_memory
.handle().fd
;
600 struct stat shm_stat
;
601 EXPECT_EQ(0, fstat(shm_fd
, &shm_stat
));
602 // Neither the group, nor others should be able to read the shared memory
604 EXPECT_FALSE(shm_stat
.st_mode
& S_IRWXO
);
605 EXPECT_FALSE(shm_stat
.st_mode
& S_IRWXG
);
608 // Create a shared memory object, check its permissions.
609 TEST(SharedMemoryTest
, FilePermissionsNamed
) {
610 const uint32 kTestSize
= 1 << 8;
612 SharedMemory shared_memory
;
613 SharedMemoryCreateOptions options
;
614 options
.size
= kTestSize
;
615 std::string shared_mem_name
= "shared_perm_test-" + IntToString(getpid()) +
616 "-" + Uint64ToString(RandUint64());
617 options
.name_deprecated
= &shared_mem_name
;
618 // Set a file mode creation mask that gives all permissions.
619 ScopedUmaskSetter
permissive_mask(S_IWGRP
| S_IWOTH
);
621 EXPECT_TRUE(shared_memory
.Create(options
));
622 // Clean-up the backing file name immediately, we don't need it.
623 EXPECT_TRUE(shared_memory
.Delete(shared_mem_name
));
625 int shm_fd
= shared_memory
.handle().fd
;
626 struct stat shm_stat
;
627 EXPECT_EQ(0, fstat(shm_fd
, &shm_stat
));
628 // Neither the group, nor others should have been able to open the shared
629 // memory file while its name existed.
630 EXPECT_FALSE(shm_stat
.st_mode
& S_IRWXO
);
631 EXPECT_FALSE(shm_stat
.st_mode
& S_IRWXG
);
633 #endif // !defined(OS_ANDROID)
635 #endif // defined(OS_POSIX)
637 // Map() will return addresses which are aligned to the platform page size, this
638 // varies from platform to platform though. Since we'd like to advertise a
639 // minimum alignment that callers can count on, test for it here.
640 TEST(SharedMemoryTest
, MapMinimumAlignment
) {
641 static const int kDataSize
= 8192;
643 SharedMemory shared_memory
;
644 ASSERT_TRUE(shared_memory
.CreateAndMapAnonymous(kDataSize
));
645 EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(
646 shared_memory
.memory()) & (SharedMemory::MAP_MINIMUM_ALIGNMENT
- 1));
647 shared_memory
.Close();
650 #if !defined(OS_IOS) // iOS does not allow multiple processes.
652 // On POSIX it is especially important we test shmem across processes,
653 // not just across threads. But the test is enabled on all platforms.
654 class SharedMemoryProcessTest
: public MultiProcessTest
{
657 static void CleanUp() {
659 memory
.Delete(s_test_name_
);
662 static int TaskTestMain() {
664 #if defined(OS_MACOSX)
665 mac::ScopedNSAutoreleasePool pool
;
667 const uint32 kDataSize
= 1024;
669 bool rv
= memory
.CreateNamedDeprecated(s_test_name_
, true, kDataSize
);
673 rv
= memory
.Map(kDataSize
);
677 int *ptr
= static_cast<int*>(memory
.memory());
679 for (int idx
= 0; idx
< 20; idx
++) {
680 memory
.LockDeprecated();
681 int i
= (1 << 16) + idx
;
683 PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
686 memory
.UnlockDeprecated();
694 static const char* const s_test_name_
;
697 const char* const SharedMemoryProcessTest::s_test_name_
= "MPMem";
699 TEST_F(SharedMemoryProcessTest
, Tasks
) {
700 SharedMemoryProcessTest::CleanUp();
702 Process processes
[kNumTasks
];
703 for (int index
= 0; index
< kNumTasks
; ++index
) {
704 processes
[index
] = SpawnChild("SharedMemoryTestMain");
705 ASSERT_TRUE(processes
[index
].IsValid());
709 for (int index
= 0; index
< kNumTasks
; ++index
) {
710 EXPECT_TRUE(processes
[index
].WaitForExit(&exit_code
));
711 EXPECT_EQ(0, exit_code
);
714 SharedMemoryProcessTest::CleanUp();
717 MULTIPROCESS_TEST_MAIN(SharedMemoryTestMain
) {
718 return SharedMemoryProcessTest::TaskTestMain();