1 | //===-- sanitizer_thread_registry_test.cpp --------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of shared sanitizer runtime. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | #include "sanitizer_common/sanitizer_thread_arg_retval.h" |
13 | |
14 | #include "gtest/gtest.h" |
15 | #include "sanitizer_mutex.h" |
16 | |
17 | namespace __sanitizer { |
18 | |
19 | static int t; |
20 | static void* arg = &t; |
21 | static void* (*routine)(void*) = [](void*) { return arg; }; |
22 | static void* retval = (&t) + 1; |
23 | |
24 | TEST(ThreadArgRetvalTest, CreateFail) { |
25 | ThreadArgRetval td; |
26 | td.Create(detached: false, args: {.routine: routine, .arg_retval: arg}, fn: []() { return 0; }); |
27 | EXPECT_EQ(0u, td.size()); |
28 | } |
29 | |
30 | TEST(ThreadArgRetvalTest, CreateRunJoin) { |
31 | ThreadArgRetval td; |
32 | td.Create(detached: false, args: {.routine: routine, .arg_retval: arg}, fn: []() { return 1; }); |
33 | EXPECT_EQ(1u, td.size()); |
34 | |
35 | EXPECT_EQ(arg, td.GetArgs(thread: 1).arg_retval); |
36 | EXPECT_EQ(routine, td.GetArgs(thread: 1).routine); |
37 | EXPECT_EQ(1u, td.size()); |
38 | |
39 | td.Finish(thread: 1, retval); |
40 | EXPECT_EQ(1u, td.size()); |
41 | |
42 | td.Join(thread: 1, fn: []() { return false; }); |
43 | EXPECT_EQ(1u, td.size()); |
44 | |
45 | td.Join(thread: 1, fn: []() { return true; }); |
46 | EXPECT_EQ(0u, td.size()); |
47 | } |
48 | |
49 | TEST(ThreadArgRetvalTest, CreateJoinRun) { |
50 | ThreadArgRetval td; |
51 | td.Create(detached: false, args: {.routine: routine, .arg_retval: arg}, fn: []() { return 1; }); |
52 | EXPECT_EQ(1u, td.size()); |
53 | |
54 | td.Join(thread: 1, fn: []() { return false; }); |
55 | EXPECT_EQ(1u, td.size()); |
56 | |
57 | td.Join(thread: 1, fn: [&]() { |
58 | // Expected to happen on another thread. |
59 | EXPECT_EQ(1u, td.size()); |
60 | |
61 | EXPECT_EQ(arg, td.GetArgs(thread: 1).arg_retval); |
62 | EXPECT_EQ(routine, td.GetArgs(thread: 1).routine); |
63 | EXPECT_EQ(1u, td.size()); |
64 | |
65 | td.Finish(thread: 1, retval); |
66 | EXPECT_EQ(1u, td.size()); |
67 | return true; |
68 | }); |
69 | EXPECT_EQ(0u, td.size()); |
70 | } |
71 | |
72 | TEST(ThreadArgRetvalTest, CreateRunDetach) { |
73 | ThreadArgRetval td; |
74 | td.Create(detached: false, args: {.routine: routine, .arg_retval: arg}, fn: []() { return 1; }); |
75 | EXPECT_EQ(1u, td.size()); |
76 | |
77 | EXPECT_EQ(arg, td.GetArgs(thread: 1).arg_retval); |
78 | EXPECT_EQ(routine, td.GetArgs(thread: 1).routine); |
79 | EXPECT_EQ(1u, td.size()); |
80 | |
81 | td.Finish(thread: 1, retval); |
82 | EXPECT_EQ(1u, td.size()); |
83 | |
84 | td.Detach(thread: 1, fn: []() { return false; }); |
85 | EXPECT_EQ(1u, td.size()); |
86 | |
87 | td.Detach(thread: 1, fn: []() { return true; }); |
88 | EXPECT_EQ(0u, td.size()); |
89 | } |
90 | |
91 | TEST(ThreadArgRetvalTest, CreateDetachRun) { |
92 | ThreadArgRetval td; |
93 | td.Create(detached: false, args: {.routine: routine, .arg_retval: arg}, fn: []() { return 1; }); |
94 | EXPECT_EQ(1u, td.size()); |
95 | |
96 | td.Detach(thread: 1, fn: []() { return true; }); |
97 | EXPECT_EQ(1u, td.size()); |
98 | |
99 | EXPECT_EQ(arg, td.GetArgs(thread: 1).arg_retval); |
100 | EXPECT_EQ(routine, td.GetArgs(thread: 1).routine); |
101 | EXPECT_EQ(1u, td.size()); |
102 | |
103 | td.Finish(thread: 1, retval); |
104 | EXPECT_EQ(0u, td.size()); |
105 | } |
106 | |
107 | TEST(ThreadArgRetvalTest, CreateRunJoinReuse) { |
108 | ThreadArgRetval td; |
109 | td.Create(detached: false, args: {.routine: routine, .arg_retval: arg}, fn: []() { return 1; }); |
110 | EXPECT_EQ(1u, td.size()); |
111 | |
112 | td.Finish(thread: 1, retval); |
113 | EXPECT_EQ(1u, td.size()); |
114 | |
115 | td.Join(thread: 1, fn: [&]() { |
116 | // Reuse thread id. |
117 | td.Create(detached: false, args: {.routine: routine, .arg_retval: arg}, fn: []() { return 1; }); |
118 | EXPECT_EQ(1u, td.size()); |
119 | return true; |
120 | }); |
121 | // Even if JoinFn succeeded, we can't erase mismatching thread. |
122 | EXPECT_EQ(1u, td.size()); |
123 | |
124 | // Now we can join another one. |
125 | td.Join(thread: 1, fn: []() { return true; }); |
126 | EXPECT_EQ(0u, td.size()); |
127 | } |
128 | |
129 | TEST(ThreadArgRetvalTest, GetAllPtrsLocked) { |
130 | ThreadArgRetval td; |
131 | td.Create(detached: false, args: {.routine: routine, .arg_retval: arg}, fn: []() { return 1; }); |
132 | { |
133 | GenericScopedLock<ThreadArgRetval> lock(&td); |
134 | InternalMmapVector<uptr> ptrs; |
135 | td.GetAllPtrsLocked(ptrs: &ptrs); |
136 | EXPECT_EQ(1u, ptrs.size()); |
137 | EXPECT_EQ((uptr)arg, ptrs[0]); |
138 | } |
139 | |
140 | td.Finish(thread: 1, retval); |
141 | { |
142 | GenericScopedLock<ThreadArgRetval> lock(&td); |
143 | InternalMmapVector<uptr> ptrs; |
144 | td.GetAllPtrsLocked(ptrs: &ptrs); |
145 | EXPECT_EQ(1u, ptrs.size()); |
146 | EXPECT_EQ((uptr)retval, ptrs[0]); |
147 | } |
148 | |
149 | td.Join(thread: 1, fn: []() { return true; }); |
150 | { |
151 | GenericScopedLock<ThreadArgRetval> lock(&td); |
152 | InternalMmapVector<uptr> ptrs; |
153 | td.GetAllPtrsLocked(ptrs: &ptrs); |
154 | EXPECT_TRUE(ptrs.empty()); |
155 | } |
156 | } |
157 | |
158 | } // namespace __sanitizer |
159 | |