Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium 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 "base/win/wait_chain.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/command_line.h" | |
| 11 #include "base/strings/string_number_conversions.h" | |
| 12 #include "base/strings/string_piece.h" | |
| 13 #include "base/test/multiprocess_test.h" | |
| 14 #include "base/threading/simple_thread.h" | |
| 15 #include "base/win/win_util.h" | |
| 16 #include "testing/gtest/include/gtest/gtest.h" | |
| 17 #include "testing/multiprocess_func_list.h" | |
| 18 | |
| 19 namespace base { | |
| 20 namespace win { | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 // Appends |handle| as a command line switch. | |
| 25 void AppendSwitchHandle(CommandLine* command_line, | |
| 26 StringPiece switch_name, | |
| 27 HANDLE handle) { | |
| 28 command_line->AppendSwitchASCII(switch_name.as_string(), | |
| 29 UintToString(HandleToUint32(handle))); | |
| 30 } | |
| 31 | |
| 32 // Retrieves the |handle| associated to |switch_name| from the command line. | |
| 33 ScopedHandle GetSwitchValueHandle(CommandLine* command_line, | |
| 34 StringPiece switch_name) { | |
| 35 std::string switch_string = | |
| 36 command_line->GetSwitchValueASCII(switch_name.as_string()); | |
| 37 unsigned int switch_uint = 0; | |
| 38 if (switch_string.empty() || !StringToUint(switch_string, &switch_uint)) { | |
| 39 DLOG(ERROR) << "Missing or invalid " << switch_name << " argument."; | |
| 40 return ScopedHandle(); | |
| 41 } | |
| 42 return ScopedHandle(reinterpret_cast<HANDLE>(switch_uint)); | |
| 43 } | |
| 44 | |
| 45 // Helper function to create a mutex. | |
| 46 ScopedHandle CreateMutex(bool inheritable) { | |
| 47 SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), | |
| 48 nullptr, inheritable}; | |
| 49 return ScopedHandle(::CreateMutex(&security_attributes, FALSE, NULL)); | |
| 50 } | |
| 51 | |
| 52 // Helper function to create an event. | |
| 53 ScopedHandle CreateEvent(bool inheritable) { | |
| 54 SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), | |
| 55 nullptr, inheritable}; | |
| 56 return ScopedHandle( | |
| 57 ::CreateEvent(&security_attributes, FALSE, FALSE, nullptr)); | |
| 58 } | |
| 59 | |
| 60 class SimpleThreadWithHandle : public SimpleThread { | |
| 61 public: | |
| 62 explicit SimpleThreadWithHandle(const std::string& name_prefix) | |
| 63 : SimpleThread(name_prefix) {} | |
| 64 | |
| 65 // Returns a handle to the thread so that other threads can wait on it. | |
| 66 HANDLE get_thread_handle() { | |
| 67 if (!thread_handle_.Get()) { | |
| 68 thread_handle_.Set( | |
| 69 ::OpenThread(SYNCHRONIZE | THREAD_TERMINATE, FALSE, tid())); | |
| 70 } | |
| 71 return thread_handle_.Get(); | |
| 72 } | |
| 73 | |
| 74 private: | |
| 75 ScopedHandle thread_handle_; | |
| 76 | |
| 77 DISALLOW_COPY_AND_ASSIGN(SimpleThreadWithHandle); | |
| 78 }; | |
| 79 | |
| 80 // Helper thread class that runs the callback then stops. | |
| 81 class SingleTaskThread : public SimpleThreadWithHandle { | |
| 82 public: | |
| 83 explicit SingleTaskThread(const Closure& task) | |
| 84 : SimpleThreadWithHandle("WaitChainTest SingleTaskThread"), task_(task) {} | |
| 85 | |
| 86 void Run() override { task_.Run(); } | |
| 87 | |
| 88 private: | |
| 89 Closure task_; | |
| 90 | |
| 91 DISALLOW_COPY_AND_ASSIGN(SingleTaskThread); | |
| 92 }; | |
| 93 | |
| 94 // Helper thread to cause a deadlock by acquiring 2 mutexes in a given order. | |
| 95 class DeadlockThread : public SimpleThreadWithHandle { | |
| 96 public: | |
| 97 DeadlockThread(HANDLE mutex_1, HANDLE mutex_2) | |
| 98 : SimpleThreadWithHandle("WaitChainTest SingleTaskThread"), | |
| 99 wait_event_(CreateEvent(false)), | |
| 100 mutex_acquired_event_(CreateEvent(false)), | |
| 101 mutex_1_(mutex_1), | |
| 102 mutex_2_(mutex_2) {} | |
| 103 | |
| 104 void Run() override { | |
| 105 // Acquire the mutex then signal the main thread. | |
| 106 EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(mutex_1_, INFINITE)); | |
| 107 EXPECT_TRUE(::SetEvent(mutex_acquired_event_.Get())); | |
| 108 | |
| 109 // Wait until both threads are holding their mutex before trying to acquire | |
| 110 // the other one. | |
| 111 EXPECT_EQ(WAIT_OBJECT_0, | |
| 112 ::WaitForSingleObject(wait_event_.Get(), INFINITE)); | |
| 113 | |
| 114 // To unblock the deadlock, one of the threads will get terminated (via | |
| 115 // TerminateThread()) without releasing the mutex. This causes the other | |
| 116 // thread to wake up with WAIT_ABANDONED. | |
| 117 EXPECT_EQ(WAIT_ABANDONED, ::WaitForSingleObject(mutex_2_, INFINITE)); | |
| 118 } | |
| 119 | |
| 120 // Blocks until a mutex is acquired. | |
| 121 void WaitForMutexAcquired() { | |
| 122 EXPECT_EQ(WAIT_OBJECT_0, | |
| 123 ::WaitForSingleObject(mutex_acquired_event_.Get(), INFINITE)); | |
| 124 } | |
| 125 | |
| 126 // Signal the thread to acquire the second mutex. | |
| 127 void SignalToAcquireMutex() { EXPECT_TRUE(::SetEvent(wait_event_.Get())); } | |
| 128 | |
| 129 private: | |
| 130 ScopedHandle wait_event_; | |
| 131 ScopedHandle mutex_acquired_event_; | |
| 132 | |
| 133 // The 2 mutex to acquire. | |
| 134 HANDLE mutex_1_; | |
| 135 HANDLE mutex_2_; | |
| 136 | |
| 137 DISALLOW_COPY_AND_ASSIGN(DeadlockThread); | |
| 138 }; | |
| 139 | |
| 140 // Creates a thread that calls WaitForSingleObject() on the handle and then | |
| 141 // terminates when it unblocks. | |
| 142 scoped_ptr<SingleTaskThread> CreateWaitingThread(HANDLE handle) { | |
| 143 scoped_ptr<SingleTaskThread> thread(new SingleTaskThread( | |
| 144 Bind(IgnoreResult(&::WaitForSingleObject), handle, INFINITE))); | |
| 145 thread->Start(); | |
| 146 | |
| 147 return std::move(thread); | |
| 148 } | |
| 149 | |
| 150 // Creates a thread that calls WaitForSingleObject() on the handle and then | |
| 151 // terminates when it unblocks. | |
| 152 scoped_ptr<DeadlockThread> CreateDeadlockThread(HANDLE mutex_1, | |
| 153 HANDLE mutex_2) { | |
| 154 scoped_ptr<DeadlockThread> thread(new DeadlockThread(mutex_1, mutex_2)); | |
| 155 thread->Start(); | |
| 156 | |
| 157 // Wait until the first mutex is acquired before returning. | |
| 158 thread->WaitForMutexAcquired(); | |
| 159 | |
| 160 return std::move(thread); | |
| 161 } | |
| 162 | |
| 163 // Child process to test the cross-process capability of the WCT api. | |
| 164 // This process will simulate a hang while holding a mutex that the parent | |
| 165 // process is waiting on. | |
| 166 MULTIPROCESS_TEST_MAIN(WaitChainTestProc) { | |
| 167 CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
| 168 | |
| 169 ScopedHandle mutex = GetSwitchValueHandle(command_line, "mutex"); | |
| 170 CHECK(mutex.IsValid()); | |
| 171 | |
| 172 ScopedHandle sync_event(GetSwitchValueHandle(command_line, "sync_event")); | |
| 173 CHECK(sync_event.IsValid()); | |
| 174 | |
| 175 // Acquire mutex. | |
| 176 CHECK(::WaitForSingleObject(mutex.Get(), INFINITE) == WAIT_OBJECT_0); | |
| 177 | |
| 178 // Signal back to the parent process that the mutex is hold. | |
| 179 CHECK(::SetEvent(sync_event.Get())); | |
| 180 | |
| 181 // Wait on a signal from the parent process before terminating. | |
| 182 CHECK(::WaitForSingleObject(sync_event.Get(), INFINITE) == WAIT_OBJECT_0); | |
| 183 | |
| 184 return 0; | |
| 185 } | |
| 186 | |
| 187 // Start a child process and passes the |mutex| and the |sync_event| to the | |
| 188 // command line. | |
| 189 Process StartChildProcess(HANDLE mutex, HANDLE sync_event) { | |
| 190 CommandLine command_line = GetMultiProcessTestChildBaseCommandLine(); | |
| 191 | |
| 192 AppendSwitchHandle(&command_line, "mutex", mutex); | |
| 193 AppendSwitchHandle(&command_line, "sync_event", sync_event); | |
| 194 | |
| 195 LaunchOptions options; | |
| 196 HandlesToInheritVector handle_vector; | |
| 197 handle_vector.push_back(mutex); | |
| 198 handle_vector.push_back(sync_event); | |
| 199 options.handles_to_inherit = &handle_vector; | |
| 200 return SpawnMultiProcessTestChild("WaitChainTestProc", command_line, options); | |
| 201 } | |
| 202 | |
| 203 // Returns true if the |wait_chain| is an alternating sequence of thread objects | |
| 204 // and synchronization objects. | |
| 205 bool WaitChainStructureIsCorrect(const WaitChainNodeVector& wait_chain) { | |
| 206 // Checks thread objects. | |
| 207 for (size_t i = 0; i < wait_chain.size(); i += 2) { | |
| 208 if (wait_chain[i].ObjectType != WctThreadType) | |
| 209 return false; | |
| 210 } | |
| 211 | |
| 212 // Check synchronization objects. | |
| 213 for (size_t i = 1; i < wait_chain.size(); i += 2) { | |
| 214 if (wait_chain[i].ObjectType == WctThreadType) | |
| 215 return false; | |
| 216 } | |
| 217 return true; | |
| 218 } | |
| 219 | |
| 220 // Returns true if the |wait_chain| goes through more than 1 process. | |
| 221 bool WaitChainIsCrossProcess(const WaitChainNodeVector& wait_chain) { | |
| 222 if (wait_chain.size() == 0) | |
| 223 return false; | |
| 224 | |
| 225 // Just check that the process id changes somewhere in the chain. | |
| 226 // Note: ThreadObjects are every 2 nodes. | |
| 227 DWORD first_process = wait_chain[0].ThreadObject.ProcessId; | |
| 228 for (size_t i = 2; i < wait_chain.size(); i += 2) { | |
| 229 if (first_process != wait_chain[i].ThreadObject.ProcessId) | |
| 230 return true; | |
| 231 } | |
| 232 return false; | |
| 233 } | |
| 234 | |
| 235 } // namespace | |
| 236 | |
| 237 // Creates 2 threads that acquire their designated mutex and then try to | |
| 238 // acquire each others' mutex to cause a deadlock. | |
| 239 TEST(WaitChainTest, Deadlock) { | |
| 240 // 2 mutexes are needed to get a deadlock. | |
| 241 ScopedHandle mutex_1 = CreateMutex(false); | |
| 242 ASSERT_TRUE(mutex_1.IsValid()); | |
| 243 ScopedHandle mutex_2 = CreateMutex(false); | |
| 244 ASSERT_TRUE(mutex_2.IsValid()); | |
| 245 | |
| 246 scoped_ptr<DeadlockThread> deadlock_thread_1 = | |
| 247 CreateDeadlockThread(mutex_1.Get(), mutex_2.Get()); | |
| 248 scoped_ptr<DeadlockThread> deadlock_thread_2 = | |
| 249 CreateDeadlockThread(mutex_2.Get(), mutex_1.Get()); | |
| 250 | |
| 251 // Signal the threads to try to acquire the other mutex. | |
| 252 deadlock_thread_1->SignalToAcquireMutex(); | |
| 253 deadlock_thread_2->SignalToAcquireMutex(); | |
| 254 // Sleep to make sure the 2 threads got a chance to execute. | |
| 255 Sleep(10); | |
| 256 | |
| 257 // Create a few waiting threads to get a longer wait chain. | |
| 258 scoped_ptr<SingleTaskThread> waiting_thread_1 = | |
| 259 CreateWaitingThread(deadlock_thread_1->get_thread_handle()); | |
| 260 scoped_ptr<SingleTaskThread> waiting_thread_2 = | |
| 261 CreateWaitingThread(waiting_thread_1->get_thread_handle()); | |
| 262 | |
| 263 WaitChainNodeVector wait_chain; | |
| 264 bool is_deadlock; | |
| 265 ASSERT_TRUE( | |
| 266 GetThreadWaitChain(waiting_thread_2->tid(), &wait_chain, &is_deadlock)); | |
| 267 | |
| 268 EXPECT_EQ(9, wait_chain.size()); | |
|
Nico
2016/04/12 19:55:17
please send a win_clang try job to find sign misma
Patrick Monette
2016/04/12 21:47:48
Done.
| |
| 269 EXPECT_TRUE(is_deadlock); | |
| 270 EXPECT_TRUE(WaitChainStructureIsCorrect(wait_chain)); | |
| 271 EXPECT_FALSE(WaitChainIsCrossProcess(wait_chain)); | |
| 272 | |
| 273 ASSERT_TRUE(::TerminateThread(deadlock_thread_1->get_thread_handle(), 0)); | |
| 274 } | |
| 275 | |
| 276 // Creates a child process that acquires a mutex and then blocks. A chain of | |
| 277 // threads then blocks on that mutex. | |
| 278 TEST(WaitChainTest, CrossProcess) { | |
| 279 ScopedHandle mutex = CreateMutex(true); | |
| 280 ASSERT_TRUE(mutex.IsValid()); | |
| 281 ScopedHandle sync_event = CreateEvent(true); | |
| 282 ASSERT_TRUE(sync_event.IsValid()); | |
| 283 | |
| 284 Process child_process = StartChildProcess(mutex.Get(), sync_event.Get()); | |
| 285 ASSERT_TRUE(child_process.IsValid()); | |
| 286 | |
| 287 // Wait for the child process to signal when it's holding the mutex. | |
| 288 EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(sync_event.Get(), INFINITE)); | |
| 289 | |
| 290 // Create a few waiting threads to get a longer wait chain. | |
| 291 scoped_ptr<SingleTaskThread> waiting_thread_1 = | |
| 292 CreateWaitingThread(mutex.Get()); | |
| 293 scoped_ptr<SingleTaskThread> waiting_thread_2 = | |
| 294 CreateWaitingThread(waiting_thread_1->get_thread_handle()); | |
| 295 scoped_ptr<SingleTaskThread> waiting_thread_3 = | |
| 296 CreateWaitingThread(waiting_thread_2->get_thread_handle()); | |
| 297 | |
| 298 WaitChainNodeVector wait_chain; | |
| 299 bool is_deadlock; | |
| 300 ASSERT_TRUE( | |
| 301 GetThreadWaitChain(waiting_thread_3->tid(), &wait_chain, &is_deadlock)); | |
| 302 | |
| 303 EXPECT_EQ(7, wait_chain.size()); | |
| 304 EXPECT_FALSE(is_deadlock); | |
| 305 EXPECT_TRUE(WaitChainStructureIsCorrect(wait_chain)); | |
| 306 EXPECT_TRUE(WaitChainIsCrossProcess(wait_chain)); | |
| 307 | |
| 308 // Unblock child process and wait for it to terminate. | |
| 309 ASSERT_TRUE(::SetEvent(sync_event.Get())); | |
| 310 ASSERT_TRUE(child_process.WaitForExit(nullptr)); | |
| 311 } | |
| 312 | |
| 313 } // namespace win | |
| 314 } // namespace base | |
| OLD | NEW |