| // Copyright 2016 The Chromium 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 "base/win/wait_chain.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/command_line.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/test/multiprocess_test.h" |
| #include "base/threading/simple_thread.h" |
| #include "base/win/win_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/multiprocess_func_list.h" |
| |
| namespace base { |
| namespace win { |
| |
| namespace { |
| |
| // Appends |handle| as a command line switch. |
| void AppendSwitchHandle(CommandLine* command_line, |
| StringPiece switch_name, |
| HANDLE handle) { |
| command_line->AppendSwitchASCII(switch_name.as_string(), |
| UintToString(HandleToUint32(handle))); |
| } |
| |
| // Retrieves the |handle| associated to |switch_name| from the command line. |
| ScopedHandle GetSwitchValueHandle(CommandLine* command_line, |
| StringPiece switch_name) { |
| std::string switch_string = |
| command_line->GetSwitchValueASCII(switch_name.as_string()); |
| unsigned int switch_uint = 0; |
| if (switch_string.empty() || !StringToUint(switch_string, &switch_uint)) { |
| DLOG(ERROR) << "Missing or invalid " << switch_name << " argument."; |
| return ScopedHandle(); |
| } |
| return ScopedHandle(reinterpret_cast<HANDLE>(switch_uint)); |
| } |
| |
| // Helper function to create a mutex. |
| ScopedHandle CreateMutex(bool inheritable) { |
| SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), |
| nullptr, inheritable}; |
| return ScopedHandle(::CreateMutex(&security_attributes, FALSE, NULL)); |
| } |
| |
| // Helper function to create an event. |
| ScopedHandle CreateEvent(bool inheritable) { |
| SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), |
| nullptr, inheritable}; |
| return ScopedHandle( |
| ::CreateEvent(&security_attributes, FALSE, FALSE, nullptr)); |
| } |
| |
| // Helper thread class that runs the callback then stops. |
| class SingleTaskThread : public SimpleThread { |
| public: |
| explicit SingleTaskThread(const Closure& task) |
| : SimpleThread("WaitChainTest SingleTaskThread"), task_(task) {} |
| |
| void Run() override { task_.Run(); } |
| |
| private: |
| Closure task_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SingleTaskThread); |
| }; |
| |
| // Helper thread to cause a deadlock by acquiring 2 mutexes in a given order. |
| class DeadlockThread : public SimpleThread { |
| public: |
| DeadlockThread(HANDLE mutex_1, HANDLE mutex_2) |
| : SimpleThread("WaitChainTest DeadlockThread"), |
| wait_event_(CreateEvent(false)), |
| mutex_acquired_event_(CreateEvent(false)), |
| mutex_1_(mutex_1), |
| mutex_2_(mutex_2) {} |
| |
| void Run() override { |
| // Acquire the mutex then signal the main thread. |
| EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(mutex_1_, INFINITE)); |
| EXPECT_TRUE(::SetEvent(mutex_acquired_event_.Get())); |
| |
| // Wait until both threads are holding their mutex before trying to acquire |
| // the other one. |
| EXPECT_EQ(WAIT_OBJECT_0, |
| ::WaitForSingleObject(wait_event_.Get(), INFINITE)); |
| |
| // To unblock the deadlock, one of the threads will get terminated (via |
| // TerminateThread()) without releasing the mutex. This causes the other |
| // thread to wake up with WAIT_ABANDONED. |
| EXPECT_EQ(WAIT_ABANDONED, ::WaitForSingleObject(mutex_2_, INFINITE)); |
| } |
| |
| // Blocks until a mutex is acquired. |
| void WaitForMutexAcquired() { |
| EXPECT_EQ(WAIT_OBJECT_0, |
| ::WaitForSingleObject(mutex_acquired_event_.Get(), INFINITE)); |
| } |
| |
| // Signal the thread to acquire the second mutex. |
| void SignalToAcquireMutex() { EXPECT_TRUE(::SetEvent(wait_event_.Get())); } |
| |
| // Terminates the thread. |
| bool Terminate() { |
| ScopedHandle thread_handle(::OpenThread(THREAD_TERMINATE, FALSE, tid())); |
| return ::TerminateThread(thread_handle.Get(), 0); |
| } |
| |
| private: |
| ScopedHandle wait_event_; |
| ScopedHandle mutex_acquired_event_; |
| |
| // The 2 mutex to acquire. |
| HANDLE mutex_1_; |
| HANDLE mutex_2_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DeadlockThread); |
| }; |
| |
| // Creates a thread that joins |thread_to_join| and then terminates when it |
| // finishes execution. |
| std::unique_ptr<SingleTaskThread> CreateJoiningThread( |
| SimpleThread* thread_to_join) { |
| std::unique_ptr<SingleTaskThread> thread(new SingleTaskThread( |
| Bind(&SimpleThread::Join, Unretained(thread_to_join)))); |
| thread->Start(); |
| |
| return thread; |
| } |
| |
| // Creates a thread that calls WaitForSingleObject() on the handle and then |
| // terminates when it unblocks. |
| std::unique_ptr<SingleTaskThread> CreateWaitingThread(HANDLE handle) { |
| std::unique_ptr<SingleTaskThread> thread(new SingleTaskThread( |
| Bind(IgnoreResult(&::WaitForSingleObject), handle, INFINITE))); |
| thread->Start(); |
| |
| return thread; |
| } |
| |
| // Creates a thread that blocks on |mutex_2| after acquiring |mutex_1|. |
| std::unique_ptr<DeadlockThread> CreateDeadlockThread(HANDLE mutex_1, |
| HANDLE mutex_2) { |
| std::unique_ptr<DeadlockThread> thread(new DeadlockThread(mutex_1, mutex_2)); |
| thread->Start(); |
| |
| // Wait until the first mutex is acquired before returning. |
| thread->WaitForMutexAcquired(); |
| |
| return thread; |
| } |
| |
| // Child process to test the cross-process capability of the WCT api. |
| // This process will simulate a hang while holding a mutex that the parent |
| // process is waiting on. |
| MULTIPROCESS_TEST_MAIN(WaitChainTestProc) { |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| |
| ScopedHandle mutex = GetSwitchValueHandle(command_line, "mutex"); |
| CHECK(mutex.IsValid()); |
| |
| ScopedHandle sync_event(GetSwitchValueHandle(command_line, "sync_event")); |
| CHECK(sync_event.IsValid()); |
| |
| // Acquire mutex. |
| CHECK(::WaitForSingleObject(mutex.Get(), INFINITE) == WAIT_OBJECT_0); |
| |
| // Signal back to the parent process that the mutex is hold. |
| CHECK(::SetEvent(sync_event.Get())); |
| |
| // Wait on a signal from the parent process before terminating. |
| CHECK(::WaitForSingleObject(sync_event.Get(), INFINITE) == WAIT_OBJECT_0); |
| |
| return 0; |
| } |
| |
| // Start a child process and passes the |mutex| and the |sync_event| to the |
| // command line. |
| Process StartChildProcess(HANDLE mutex, HANDLE sync_event) { |
| CommandLine command_line = GetMultiProcessTestChildBaseCommandLine(); |
| |
| AppendSwitchHandle(&command_line, "mutex", mutex); |
| AppendSwitchHandle(&command_line, "sync_event", sync_event); |
| |
| LaunchOptions options; |
| options.handles_to_inherit.push_back(mutex); |
| options.handles_to_inherit.push_back(sync_event); |
| return SpawnMultiProcessTestChild("WaitChainTestProc", command_line, options); |
| } |
| |
| // Returns true if the |wait_chain| is an alternating sequence of thread objects |
| // and synchronization objects. |
| bool WaitChainStructureIsCorrect(const WaitChainNodeVector& wait_chain) { |
| // Checks thread objects. |
| for (size_t i = 0; i < wait_chain.size(); i += 2) { |
| if (wait_chain[i].ObjectType != WctThreadType) |
| return false; |
| } |
| |
| // Check synchronization objects. |
| for (size_t i = 1; i < wait_chain.size(); i += 2) { |
| if (wait_chain[i].ObjectType == WctThreadType) |
| return false; |
| } |
| return true; |
| } |
| |
| // Returns true if the |wait_chain| goes through more than 1 process. |
| bool WaitChainIsCrossProcess(const WaitChainNodeVector& wait_chain) { |
| if (wait_chain.size() == 0) |
| return false; |
| |
| // Just check that the process id changes somewhere in the chain. |
| // Note: ThreadObjects are every 2 nodes. |
| DWORD first_process = wait_chain[0].ThreadObject.ProcessId; |
| for (size_t i = 2; i < wait_chain.size(); i += 2) { |
| if (first_process != wait_chain[i].ThreadObject.ProcessId) |
| return true; |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| // Creates 2 threads that acquire their designated mutex and then try to |
| // acquire each others' mutex to cause a deadlock. |
| TEST(WaitChainTest, Deadlock) { |
| // 2 mutexes are needed to get a deadlock. |
| ScopedHandle mutex_1 = CreateMutex(false); |
| ASSERT_TRUE(mutex_1.IsValid()); |
| ScopedHandle mutex_2 = CreateMutex(false); |
| ASSERT_TRUE(mutex_2.IsValid()); |
| |
| std::unique_ptr<DeadlockThread> deadlock_thread_1 = |
| CreateDeadlockThread(mutex_1.Get(), mutex_2.Get()); |
| std::unique_ptr<DeadlockThread> deadlock_thread_2 = |
| CreateDeadlockThread(mutex_2.Get(), mutex_1.Get()); |
| |
| // Signal the threads to try to acquire the other mutex. |
| deadlock_thread_1->SignalToAcquireMutex(); |
| deadlock_thread_2->SignalToAcquireMutex(); |
| // Sleep to make sure the 2 threads got a chance to execute. |
| Sleep(10); |
| |
| // Create a few waiting threads to get a longer wait chain. |
| std::unique_ptr<SingleTaskThread> waiting_thread_1 = |
| CreateJoiningThread(deadlock_thread_1.get()); |
| std::unique_ptr<SingleTaskThread> waiting_thread_2 = |
| CreateJoiningThread(waiting_thread_1.get()); |
| |
| WaitChainNodeVector wait_chain; |
| bool is_deadlock; |
| ASSERT_TRUE(GetThreadWaitChain(waiting_thread_2->tid(), &wait_chain, |
| &is_deadlock, nullptr, nullptr)); |
| |
| EXPECT_EQ(9U, wait_chain.size()); |
| EXPECT_TRUE(is_deadlock); |
| EXPECT_TRUE(WaitChainStructureIsCorrect(wait_chain)); |
| EXPECT_FALSE(WaitChainIsCrossProcess(wait_chain)); |
| |
| ASSERT_TRUE(deadlock_thread_1->Terminate()); |
| |
| // The SimpleThread API expect Join() to be called before destruction. |
| deadlock_thread_2->Join(); |
| waiting_thread_2->Join(); |
| } |
| |
| // Creates a child process that acquires a mutex and then blocks. A chain of |
| // threads then blocks on that mutex. |
| TEST(WaitChainTest, CrossProcess) { |
| ScopedHandle mutex = CreateMutex(true); |
| ASSERT_TRUE(mutex.IsValid()); |
| ScopedHandle sync_event = CreateEvent(true); |
| ASSERT_TRUE(sync_event.IsValid()); |
| |
| Process child_process = StartChildProcess(mutex.Get(), sync_event.Get()); |
| ASSERT_TRUE(child_process.IsValid()); |
| |
| // Wait for the child process to signal when it's holding the mutex. |
| EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(sync_event.Get(), INFINITE)); |
| |
| // Create a few waiting threads to get a longer wait chain. |
| std::unique_ptr<SingleTaskThread> waiting_thread_1 = |
| CreateWaitingThread(mutex.Get()); |
| std::unique_ptr<SingleTaskThread> waiting_thread_2 = |
| CreateJoiningThread(waiting_thread_1.get()); |
| std::unique_ptr<SingleTaskThread> waiting_thread_3 = |
| CreateJoiningThread(waiting_thread_2.get()); |
| |
| WaitChainNodeVector wait_chain; |
| bool is_deadlock; |
| ASSERT_TRUE(GetThreadWaitChain(waiting_thread_3->tid(), &wait_chain, |
| &is_deadlock, nullptr, nullptr)); |
| |
| EXPECT_EQ(7U, wait_chain.size()); |
| EXPECT_FALSE(is_deadlock); |
| EXPECT_TRUE(WaitChainStructureIsCorrect(wait_chain)); |
| EXPECT_TRUE(WaitChainIsCrossProcess(wait_chain)); |
| |
| // Unblock child process and wait for it to terminate. |
| ASSERT_TRUE(::SetEvent(sync_event.Get())); |
| ASSERT_TRUE(child_process.WaitForExit(nullptr)); |
| |
| // The SimpleThread API expect Join() to be called before destruction. |
| waiting_thread_3->Join(); |
| } |
| |
| } // namespace win |
| } // namespace base |