| // 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 |