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