// Copyright (c) 2010 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. // // ExecutorsManager implementation. #include "ceee/ie/broker/executors_manager.h" #include "base/logging.h" #include "ceee/ie/broker/broker_module_util.h" #include "ceee/common/com_utils.h" namespace { // The timeout we set before accepting a failure when we wait for events. const DWORD kTimeOut = 20000; // Utility class to ensure the setting of an event before we exit a method. class AutoSetEvent { public: explicit AutoSetEvent(HANDLE event_handle) : event_handle_(event_handle) {} ~AutoSetEvent() { ::SetEvent(event_handle_); } private: HANDLE event_handle_; }; // A cached handle to our current process which we use when we call // DuplicateHandle. GetCurrentProcess returns a pseudo HANDLE that doesn't // need to be closed. const HANDLE kProcessHandle = ::GetCurrentProcess(); } // namespace const size_t ExecutorsManager::kTerminationHandleIndexOffset = 0; const size_t ExecutorsManager::kUpdateHandleIndexOffset = 1; const size_t ExecutorsManager::kLastHandleIndexOffset = kUpdateHandleIndexOffset; const size_t ExecutorsManager::kExtraHandles = kLastHandleIndexOffset + 1; ExecutorsManager::ExecutorsManager(bool no_thread) : update_threads_list_gate_(::CreateEvent(NULL, FALSE, FALSE, NULL)), // Termination is manual reset. When we're terminated... We're terminated! termination_gate_(::CreateEvent(NULL, TRUE, FALSE, NULL)) { DCHECK(update_threads_list_gate_ != NULL); DCHECK(termination_gate_ != NULL); if (!no_thread) { ThreadStartData thread_start_data; thread_start_data.me = this; // Again, manual reset, because when we are started... WE ARE STARTED!!! :-) thread_start_data.thread_started_gate.Attach(::CreateEvent(NULL, TRUE, FALSE, NULL)); DCHECK(thread_start_data.thread_started_gate != NULL); // Since we provide the this pointer to the thread, we must make sure to // keep ALL INITIALIZATION CODE ABOVE THIS LINE!!! thread_.Attach(::CreateThread(NULL, 0, ThreadProc, &thread_start_data, 0, 0)); DCHECK(thread_ != NULL); // Make sure the thread is ready before continuing DWORD result = WaitForSingleObject(thread_start_data.thread_started_gate, kTimeOut); DCHECK(result == WAIT_OBJECT_0); } } HRESULT ExecutorsManager::RegisterTabExecutor(ThreadId thread_id, IUnknown* executor) { // We will need to know outside of the lock if the map was empty or not. // This way we can add a ref to the module for the existence of the map. bool map_was_empty = false; { AutoLock lock(lock_); map_was_empty = executors_.empty(); if (!map_was_empty && executors_.find(thread_id) != executors_.end()) { return S_OK; } CHandle thread_handle(::OpenThread(SYNCHRONIZE, FALSE, thread_id)); if (thread_handle == NULL) { DCHECK(false) << "Can't Open thread: " << thread_id; return E_UNEXPECTED; } ExecutorInfo& new_executor_info = executors_[thread_id]; new_executor_info.executor = executor; new_executor_info.thread_handle = thread_handle; } // End of lock. if (map_was_empty) { // We go from empty to not empty, // so lock the module to make sure we stay alive. ceee_module_util::LockModule(); } return S_OK; } HRESULT ExecutorsManager::RegisterWindowExecutor(ThreadId thread_id, IUnknown* executor) { // We need to fetch the event handle associated to this thread ID from // our map in a thread safe way... CHandle executor_registration_gate; { AutoLock lock(lock_); if (executors_.find(thread_id) != executors_.end()) { DCHECK(false) << "Unexpected registered thread_id: " << thread_id; return E_UNEXPECTED; } Tid2Event::iterator iter = pending_registrations_.find(thread_id); if (iter == pending_registrations_.end() || executor == NULL) { DCHECK(false) << "Invalid thread_id: " << thread_id << ", or NULL executor."; return E_INVALIDARG; } // Make sure we use a duplicate handle so that we don't get caught setting // a dead handle when we exit, in case the other thread wakes up because // of a (unlikely) double registration. BOOL success = ::DuplicateHandle( kProcessHandle, iter->second.m_h, kProcessHandle, &executor_registration_gate.m_h, 0, FALSE, DUPLICATE_SAME_ACCESS); DCHECK(success) << com::LogWe(); } // End of lock. // We must make sure to wake up the thread(s) that might be waiting on us. // But only when we are done. AutoSetEvent auto_set_event(executor_registration_gate); // Try to get a handle to this thread right away so that we can do the rest // atomically. We need it to wake us up when it dies. CHandle thread_handle(::OpenThread(SYNCHRONIZE, FALSE, thread_id)); if (thread_handle == NULL) { DCHECK(false) << "Can't Open thread: " << thread_id; return S_FALSE; } // We will need to know outside of the lock if the map was empty or not. // This way we can add a ref to the module for the existence of the map. bool map_was_empty = false; { AutoLock lock(lock_); map_was_empty = executors_.empty(); // We should not get here if we already have an executor for that thread. DCHECK(executors_.find(thread_id) == executors_.end()); ExecutorInfo& new_executor_info = executors_[thread_id]; new_executor_info.executor = executor; new_executor_info.thread_handle = thread_handle; } // End of lock. if (map_was_empty) { // We go from empty to not empty, // so lock the module to make sure we stay alive. ceee_module_util::LockModule(); } // Update the list of handles that our thread is waiting on. BOOL success = ::SetEvent(update_threads_list_gate_); DCHECK(success); return S_OK; } HRESULT ExecutorsManager::GetExecutor(ThreadId thread_id, HWND window, REFIID riid, void** executor) { DCHECK(executor != NULL); // We may need to wait for either a currently pending // or own newly created registration of a new executor. CHandle executor_registration_gate; // We need to remember if we must create a new one or not. // But we must create the executor creator outside of the lock. bool create_executor = false; { AutoLock lock(lock_); ExecutorsMap::iterator exec_iter = executors_.find(thread_id); if (exec_iter != executors_.end()) { // Found it... We're done... That was quick!!! :-) DCHECK(exec_iter->second.executor != NULL); return exec_iter->second.executor->QueryInterface(riid, executor); } // Check if we need to wait for a pending registration. Tid2Event::iterator event_iter = pending_registrations_.find(thread_id); if (event_iter == pending_registrations_.end()) { // No pending registration, so we will need to create a new executor. create_executor = true; // Use the thread id as a cookie to only allow known threads to register. // Also use it to map to a new event we will use to signal the end of this // registration. We use a manual reset event so that more than one thread // can wait for it, and once we're done... we're done... period! :-) executor_registration_gate.Attach(::CreateEvent(NULL, TRUE, FALSE, NULL)); DCHECK(executor_registration_gate != NULL); CHandle& new_registration_handle = pending_registrations_[thread_id]; // Make sure we use a duplicate handle so that we don't get caught waiting // on a dead handle later, in case other threads wake up before we do and // close the handle before we wake up. BOOL success = ::DuplicateHandle( kProcessHandle, executor_registration_gate, kProcessHandle, &new_registration_handle.m_h, 0, FALSE, DUPLICATE_SAME_ACCESS); DCHECK(success) << com::LogWe(); } else { // Same comment as above... BOOL success = ::DuplicateHandle( kProcessHandle, event_iter->second.m_h, kProcessHandle, &executor_registration_gate.m_h, 0, FALSE, DUPLICATE_SAME_ACCESS); DCHECK(success) << com::LogWe(); } } // End of lock. CComPtr executor_creator; if (create_executor) { // We need to create an executor creator so that the code setting up // a Windows Hook in the other process, runs from a DLL that can be // injected in that other process... WE are running in an executable. HRESULT hr = GetExecutorCreator(&executor_creator); DCHECK(SUCCEEDED(hr) && executor_creator != NULL) << "CoCreating Executor Creator. " << com::LogHr(hr); hr = executor_creator->CreateWindowExecutor(thread_id, reinterpret_cast(window)); if (FAILED(hr)) { // This could happen if the thread we want to hook to died prematurely. AutoLock lock(lock_); pending_registrations_.erase(thread_id); return hr; } } // Wait for the registration to complete. DWORD result = WaitForSingleObject(executor_registration_gate, kTimeOut); LOG_IF(INFO, result != WAIT_OBJECT_0) << "Registration problem? " << "Wait Result: " << com::LogWe(result); // Let the executor creator know that we got the registration // and it can tear down what was needed to trigger it. if (executor_creator != NULL) { HRESULT hr = executor_creator->Teardown(thread_id); DCHECK(SUCCEEDED(hr)) << "Tearing down executor creator" << com::LogHr(hr); } // Do our own cleanup and return a reference thread safely... AutoLock lock(lock_); pending_registrations_.erase(thread_id); ExecutorsMap::iterator iter = executors_.find(thread_id); if (iter == executors_.end()) { DCHECK(false) << "New executor registration failed."; return E_UNEXPECTED; } DCHECK(iter->second.executor != NULL); return iter->second.executor->QueryInterface(riid, executor); } HRESULT ExecutorsManager::RemoveExecutor(ThreadId thread_id) { // Make sure to Release the executor outside the lock. CComPtr dead_executor; bool map_is_empty = false; { AutoLock lock(lock_); ExecutorsMap::iterator iter = executors_.find(thread_id); if (iter == executors_.end()) { return S_FALSE; } dead_executor.Attach(iter->second.executor.Detach()); executors_.erase(iter); map_is_empty = executors_.empty(); } // End of lock. if (map_is_empty) { // We go from not empty to empty, // so unlock the module it can leave in peace. ceee_module_util::UnlockModule(); } return S_OK; } HRESULT ExecutorsManager::Terminate() { if (thread_ != NULL) { // Ask our thread to quit and wait for it to be done. DWORD result = ::SignalObjectAndWait(termination_gate_, thread_, kTimeOut, FALSE); DCHECK(result == WAIT_OBJECT_0); thread_.Close(); } if (!executors_.empty()) { // TODO(mad@chromium.org): Can this happen??? NOTREACHED(); ceee_module_util::UnlockModule(); } executors_.clear(); update_threads_list_gate_.Close(); termination_gate_.Close(); return S_OK; } void ExecutorsManager::SetTabIdForHandle(long tab_id, HWND handle) { AutoLock lock(lock_); DCHECK(tab_id_map_.end() == tab_id_map_.find(tab_id)); DCHECK(handle_map_.end() == handle_map_.find(handle)); if (handle == reinterpret_cast(INVALID_HANDLE_VALUE) || tab_id == kInvalidChromeSessionId) { NOTREACHED(); return; } tab_id_map_[tab_id] = handle; handle_map_[handle] = tab_id; } void ExecutorsManager::DeleteTabHandle(HWND handle) { AutoLock lock(lock_); HandleMap::iterator handle_it = handle_map_.find(handle); if(handle_map_.end() != handle_it) { DCHECK(false); return; } TabIdMap::iterator tab_id_it = tab_id_map_.find(handle_it->second); if(tab_id_map_.end() != tab_id_it) { DCHECK(false); return; } #ifdef DEBUG tab_id_map_[handle_it->second] = reinterpret_cast(INVALID_HANDLE_VALUE); handle_map_[handle] = kInvalidChromeSessionId; #else tab_id_map_.erase(handle_it->second); handle_map_.erase(handle); #endif // DEBUG } HWND ExecutorsManager::GetTabHandleFromId(int tab_id) { AutoLock lock(lock_); TabIdMap::const_iterator it = tab_id_map_.find(tab_id); DCHECK(it != tab_id_map_.end()); if (it == tab_id_map_.end()) return reinterpret_cast(INVALID_HANDLE_VALUE); // Deleted? I hope not. DCHECK(it->second != reinterpret_cast(INVALID_HANDLE_VALUE)); return it->second; } int ExecutorsManager::GetTabIdFromHandle(HWND tab_handle) { AutoLock lock(lock_); HandleMap::const_iterator it = handle_map_.find(tab_handle); DCHECK(it != handle_map_.end()); if (it == handle_map_.end()) return kInvalidChromeSessionId; DCHECK(it->second != kInvalidChromeSessionId); // Deleted? I hope not. return it->second; } HRESULT ExecutorsManager::GetExecutorCreator( ICeeeExecutorCreator** executor_creator) { return ::CoCreateInstance(CLSID_CeeeExecutorCreator, NULL, CLSCTX_INPROC_SERVER, IID_ICeeeExecutorCreator, reinterpret_cast(executor_creator)); } size_t ExecutorsManager::GetThreadHandles( CHandle thread_handles[], ThreadId thread_ids[], size_t num_threads) { AutoLock lock(lock_); ExecutorsMap::iterator iter = executors_.begin(); size_t index = 0; for (; index < num_threads && iter != executors_.end(); ++index, ++iter) { DCHECK(thread_handles[index].m_h == NULL); // We need to duplicate the handle to make sure the caller will not wait // on a closed handle. BOOL success = ::DuplicateHandle( kProcessHandle, iter->second.thread_handle, kProcessHandle, &thread_handles[index].m_h, 0, FALSE, DUPLICATE_SAME_ACCESS); DCHECK(success) << com::LogWe(); thread_ids[index] = iter->first; } return index; } DWORD ExecutorsManager::WaitForSingleObject(HANDLE wait_handle, DWORD timeout) { return ::WaitForSingleObject(wait_handle, timeout); } DWORD ExecutorsManager::WaitForMultipleObjects(DWORD num_handles, const HANDLE* wait_handles, BOOL wait_all, DWORD timeout) { return ::WaitForMultipleObjects(num_handles, wait_handles, wait_all, timeout); } DWORD ExecutorsManager::ThreadProc(LPVOID parameter) { // We must make sure to join the multi thread apartment so that the executors // get released properly in the same apartment they were acquired from. ::CoInitializeEx(NULL, COINIT_MULTITHREADED); ThreadStartData* thread_start_data = reinterpret_cast(parameter); DCHECK(thread_start_data != NULL); ExecutorsManager* me = thread_start_data->me; DCHECK(me != NULL); // Let our parent know that we are old enough now! ::SetEvent(thread_start_data->thread_started_gate); // Setting the event will destroy the thread start data living on the stack // so make sure we don't use it anymore. thread_start_data = NULL; while (true) { CHandle smart_handles[MAXIMUM_WAIT_OBJECTS]; HANDLE handles[MAXIMUM_WAIT_OBJECTS]; ThreadId thread_ids[MAXIMUM_WAIT_OBJECTS]; // Get as many handles as we can, leaving room for kExtraHandles. size_t num_threads = me->GetThreadHandles( smart_handles, thread_ids, MAXIMUM_WAIT_OBJECTS - kExtraHandles); // Wait function needs an array of raw handles, not smart ones. for (size_t index = 0; index < num_threads; ++index) handles[index] = smart_handles[index]; // We also need to wait for our termination signal. handles[num_threads + kTerminationHandleIndexOffset] = me->termination_gate_; // As well as a signal warning us to go fetch more thread handles. handles[num_threads + kUpdateHandleIndexOffset] = me->update_threads_list_gate_; size_t num_handles = num_threads + kExtraHandles; DWORD result = me->WaitForMultipleObjects(num_handles, handles, FALSE, INFINITE); if (result == WAIT_OBJECT_0 + num_threads + kUpdateHandleIndexOffset) { // We got a new thread added, // simply let the loop turn to add it to our watch list. } else if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + num_threads) { // One of our threads have died, cleanup time. me->RemoveExecutor(thread_ids[result - WAIT_OBJECT_0]); } else if (result == WAIT_OBJECT_0 + num_threads + kTerminationHandleIndexOffset) { // we are being terminated, break the cycle. break; } else { DCHECK(result == WAIT_FAILED); LOG(ERROR) << "ExecutorsManager::ThreadProc " << com::LogWe(); break; } } // Merci... Bonsoir... ::CoUninitialize(); return 1; }