| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/fdio/spawn.h> |
| #include <pthread.h> |
| #include <stdlib.h> |
| #include <zircon/syscalls.h> |
| |
| #include <fbl/algorithm.h> |
| #include <zxtest/zxtest.h> |
| |
| // This file is for regression tests for race conditions where the test was |
| // only observed to reproduce the race condition when some scheduling |
| // tweaks were applied to the software under test -- e.g. adding sleeps or |
| // sched_yield()/thread_yield() calls, or changing the scheduler to |
| // randomize its scheduling decisions. |
| |
| static const char* g_executable_filename; |
| |
| static void* ThreadFunc(void* thread_arg) { zx_process_exit(200); } |
| |
| static void Subprocess() { |
| pthread_t thread; |
| pthread_create(&thread, NULL, ThreadFunc, NULL); |
| zx_process_exit(100); |
| } |
| |
| // This is a regression test for an issue where the exit status for a |
| // process -- as reported by zx_object_get_info()'s return_code field -- |
| // could change. That could happen if multiple threads called |
| // zx_process_exit() concurrently. |
| TEST(RaceTests, TestProcessExitStatusRace) { |
| // Launch a subprocess. |
| const char* argv[] = {g_executable_filename, "--subprocess", nullptr}; |
| zx_handle_t proc; |
| ASSERT_EQ(fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, g_executable_filename, argv, &proc), |
| ZX_OK); |
| |
| for (;;) { |
| // Query the process state. |
| zx_info_process_t info1; |
| size_t records_read; |
| ASSERT_EQ(zx_object_get_info(proc, ZX_INFO_PROCESS, &info1, sizeof(info1), &records_read, NULL), |
| ZX_OK); |
| ASSERT_EQ(records_read, 1u); |
| |
| // If the process was reported as exited, query its state again. |
| if (info1.flags & ZX_INFO_PROCESS_FLAG_EXITED) { |
| EXPECT_TRUE(info1.return_code == 100 || info1.return_code == 200); |
| |
| zx_info_process_t info2; |
| ASSERT_EQ( |
| zx_object_get_info(proc, ZX_INFO_PROCESS, &info2, sizeof(info2), &records_read, NULL), |
| ZX_OK); |
| ASSERT_EQ(records_read, 1u); |
| // Do the results match what we got before? |
| EXPECT_TRUE(info1.flags & ZX_INFO_PROCESS_FLAG_EXITED); |
| EXPECT_EQ(info1.return_code, info2.return_code); |
| break; |
| } |
| sched_yield(); |
| } |
| |
| // Clean up. |
| ASSERT_EQ(zx_handle_close(proc), ZX_OK); |
| } |
| |
| int main(int argc, char** argv) { |
| g_executable_filename = argv[0]; |
| |
| if (argc == 2 && !strcmp(argv[1], "--subprocess")) { |
| Subprocess(); |
| return 0; |
| } |
| |
| return zxtest::RunAllTests(argc, argv); |
| } |