Serenity Operating System
1/*
2 * Copyright (c) 2022, Tim Schumacher <timschumi@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibTest/TestCase.h>
8#include <pthread.h>
9#include <unistd.h>
10
11#define TEST_CASE_IN_PTHREAD(x) \
12 static void* __TESTCASE_FUNC(x##__inner)(void*); \
13 TEST_CASE(x) \
14 { \
15 pthread_t thread; \
16 pthread_create(&thread, nullptr, __TESTCASE_FUNC(x##__inner), nullptr); \
17 pthread_join(thread, nullptr); \
18 } \
19 static void* __TESTCASE_FUNC(x##__inner)(void*)
20
21TEST_CASE_IN_PTHREAD(cancel_state_valid)
22{
23 int old_state = 0;
24
25 // Ensure that we return the default state correctly.
26 EXPECT_EQ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state), 0);
27 EXPECT_EQ(old_state, PTHREAD_CANCEL_ENABLE);
28
29 // Make sure that PTHREAD_CANCEL_DISABLE sticks.
30 EXPECT_EQ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state), 0);
31 EXPECT_EQ(old_state, PTHREAD_CANCEL_DISABLE);
32
33 return nullptr;
34}
35
36TEST_CASE_IN_PTHREAD(cancel_state_invalid)
37{
38 constexpr int lower_invalid_state = min(PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE) - 1;
39 constexpr int upper_invalid_state = max(PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE) + 1;
40
41 int old_state = 0;
42
43 // Check that both invalid states are rejected and don't change the old state.
44 EXPECT_EQ(pthread_setcancelstate(lower_invalid_state, &old_state), EINVAL);
45 EXPECT_EQ(old_state, 0);
46 EXPECT_EQ(pthread_setcancelstate(upper_invalid_state, &old_state), EINVAL);
47 EXPECT_EQ(old_state, 0);
48
49 // Ensure that we are still in the default state afterwards.
50 EXPECT_EQ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state), 0);
51 EXPECT_EQ(old_state, PTHREAD_CANCEL_ENABLE);
52
53 return nullptr;
54}
55
56TEST_CASE_IN_PTHREAD(cancel_type_valid)
57{
58 int old_type = 0;
59
60 // Ensure that we return the default type correctly.
61 EXPECT_EQ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type), 0);
62 EXPECT_EQ(old_type, PTHREAD_CANCEL_DEFERRED);
63
64 // Make sure that PTHREAD_CANCEL_ASYNCHRONOUS sticks (not that it should ever be used).
65 EXPECT_EQ(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old_type), 0);
66 EXPECT_EQ(old_type, PTHREAD_CANCEL_ASYNCHRONOUS);
67
68 return nullptr;
69}
70
71TEST_CASE_IN_PTHREAD(cancel_type_invalid)
72{
73 constexpr int lower_invalid_type = min(PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS) - 1;
74 constexpr int upper_invalid_type = max(PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS) + 1;
75
76 int old_type = 0;
77
78 // Check that both invalid types are rejected and don't change the old type.
79 EXPECT_EQ(pthread_setcanceltype(lower_invalid_type, &old_type), EINVAL);
80 EXPECT_EQ(old_type, 0);
81 EXPECT_EQ(pthread_setcanceltype(upper_invalid_type, &old_type), EINVAL);
82 EXPECT_EQ(old_type, 0);
83
84 // Ensure that we are still in the default state afterwards.
85 EXPECT_EQ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type), 0);
86 EXPECT_EQ(old_type, PTHREAD_CANCEL_DEFERRED);
87
88 return nullptr;
89}
90
91static void cancel_clenaup_handler(void* data)
92{
93 (*static_cast<bool*>(data)) = true;
94}
95
96static void* cancel_inner(void* data)
97{
98 pthread_cleanup_push(cancel_clenaup_handler, data);
99
100 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
101 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, nullptr);
102
103 // Sleep for a second until the other side sets up their end of the check,
104 // then do a call to write, which should be a cancellation point.
105 sleep(1);
106 write(STDOUT_FILENO, nullptr, 0);
107
108 pthread_exit(nullptr);
109}
110
111TEST_CASE(cancel)
112{
113 pthread_t thread;
114
115 bool called_cleanup_handler = false;
116 pthread_create(&thread, nullptr, cancel_inner, &called_cleanup_handler);
117
118 int rc = pthread_cancel(thread);
119
120 void* exit_code;
121 pthread_join(thread, &exit_code);
122
123 EXPECT_EQ(rc, 0);
124 EXPECT_EQ(called_cleanup_handler, true);
125 EXPECT_EQ(exit_code, PTHREAD_CANCELED);
126}