1 // Copyright 2017 The Fuchsia 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 <pthread.h>
6 #include <stdlib.h>
7
8 #include <fbl/algorithm.h>
9 #include <lib/fdio/spawn.h>
10 #include <unittest/unittest.h>
11 #include <zircon/syscalls.h>
12
13 // This file is for regression tests for race conditions where the test was
14 // only observed to reproduce the race condition when some scheduling
15 // tweaks were applied to the software under test -- e.g. adding sleeps or
16 // sched_yield()/thread_yield() calls, or changing the scheduler to
17 // randomize its scheduling decisions.
18
19 static const char* g_executable_filename;
20
ThreadFunc(void * thread_arg)21 static void* ThreadFunc(void* thread_arg) {
22 zx_process_exit(200);
23 }
24
Subprocess()25 static void Subprocess() {
26 pthread_t thread;
27 pthread_create(&thread, NULL, ThreadFunc, NULL);
28 zx_process_exit(100);
29 }
30
31 // This is a regression test for an issue where the exit status for a
32 // process -- as reported by zx_object_get_info()'s return_code field --
33 // could change. That could happen if multiple threads called
34 // zx_process_exit() concurrently.
TestProcessExitStatusRace()35 static bool TestProcessExitStatusRace() {
36 BEGIN_TEST;
37
38 // Launch a subprocess.
39 const char* argv[] = { g_executable_filename, "--subprocess", nullptr };
40 zx_handle_t proc;
41 ASSERT_EQ(fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, g_executable_filename, argv, &proc), ZX_OK);
42
43 for (;;) {
44 // Query the process state.
45 zx_info_process_t info1;
46 size_t records_read;
47 ASSERT_EQ(zx_object_get_info(
48 proc, ZX_INFO_PROCESS, &info1, sizeof(info1),
49 &records_read, NULL), ZX_OK);
50 ASSERT_EQ(records_read, 1u);
51
52 // If the process was reported as exited, query its state again.
53 if (info1.exited) {
54 EXPECT_TRUE(info1.return_code == 100 ||
55 info1.return_code == 200);
56
57 zx_info_process_t info2;
58 ASSERT_EQ(zx_object_get_info(
59 proc, ZX_INFO_PROCESS, &info2, sizeof(info2),
60 &records_read, NULL), ZX_OK);
61 ASSERT_EQ(records_read, 1u);
62 // Do the results match what we got before?
63 EXPECT_TRUE(info2.exited);
64 EXPECT_EQ(info1.return_code, info2.return_code);
65 break;
66 }
67 sched_yield();
68 }
69
70 // Clean up.
71 ASSERT_EQ(zx_handle_close(proc), ZX_OK);
72
73 END_TEST;
74 }
75
76 BEGIN_TEST_CASE(race_tests)
RUN_TEST(TestProcessExitStatusRace)77 RUN_TEST(TestProcessExitStatusRace)
78 END_TEST_CASE(race_tests)
79
80 int main(int argc, char** argv) {
81 g_executable_filename = argv[0];
82
83 if (argc == 2 && !strcmp(argv[1], "--subprocess")) {
84 Subprocess();
85 return 0;
86 }
87
88 bool success = unittest_run_all_tests(argc, argv);
89 return success ? 0 : -1;
90 }
91