platform_uithread.cc (6288B)
1 /* 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #if defined(WEBRTC_WIN) 12 13 # include "platform_uithread.h" 14 15 namespace webrtc { 16 17 // timer id used in delayed callbacks 18 static const UINT_PTR kTimerId = 1; 19 static const wchar_t kThisProperty[] = L"ThreadWindowsUIPtr"; 20 static const wchar_t kThreadWindow[] = L"WebrtcWindowsUIThread"; 21 22 PlatformUIThread::~PlatformUIThread() { 23 CritScope scoped_lock(&cs_); 24 switch (state_) { 25 case State::STARTED: { 26 MOZ_DIAGNOSTIC_ASSERT( 27 false, "PlatformUIThread must be stopped before destruction"); 28 break; 29 } 30 case State::STOPPED: 31 break; 32 case State::UNSTARTED: 33 break; 34 } 35 } 36 37 bool PlatformUIThread::InternalInit() { 38 // Create an event window for use in generating callbacks to capture 39 // objects. 40 CritScope scoped_lock(&cs_); 41 switch (state_) { 42 // We have already started there is nothing todo. Should this be assert? 43 case State::STARTED: 44 break; 45 // Stop() has already been called so there is likewise nothing to do. 46 case State::STOPPED: 47 break; 48 // Stop() has not been called yet, setup the UI thread, and set our 49 // state to STARTED. 50 case State::UNSTARTED: { 51 WNDCLASSW wc; 52 HMODULE hModule = GetModuleHandle(NULL); 53 if (!GetClassInfoW(hModule, kThreadWindow, &wc)) { 54 ZeroMemory(&wc, sizeof(WNDCLASSW)); 55 wc.hInstance = hModule; 56 wc.lpfnWndProc = EventWindowProc; 57 wc.lpszClassName = kThreadWindow; 58 RegisterClassW(&wc); 59 } 60 hwnd_ = CreateWindowW(kThreadWindow, L"", 0, 0, 0, 0, 0, NULL, NULL, 61 hModule, NULL); 62 // Added in review of bug 1760843, follow up to remove 1767861 63 MOZ_RELEASE_ASSERT(hwnd_); 64 // Expected to always work but if it doesn't we should still fulfill the 65 // contract of always running the process loop at least a single 66 // iteration. 67 // This could be rexamined in the future. 68 if (hwnd_) { 69 SetPropW(hwnd_, kThisProperty, this); 70 // state_ needs to be STARTED before we request the initial timer 71 state_ = State::STARTED; 72 if (timeout_) { 73 // if someone set the timer before we started 74 RequestCallbackTimer(timeout_); 75 } 76 } 77 break; 78 } 79 }; 80 return state_ == State::STARTED; 81 } 82 83 bool PlatformUIThread::RequestCallbackTimer(unsigned int milliseconds) { 84 CritScope scoped_lock(&cs_); 85 86 switch (state_) { 87 // InternalInit() has yet to run so we do not have a UI thread to use as a 88 // target of the timer. We should just remember what timer interval was 89 // requested and let InternalInit() call this function again when it is 90 // ready. 91 case State::UNSTARTED: { 92 timeout_ = milliseconds; 93 return false; 94 } 95 // We have already stopped, do not schedule a new timer. 96 case State::STOPPED: 97 return false; 98 case State::STARTED: { 99 if (timerid_) { 100 KillTimer(hwnd_, timerid_); 101 } 102 timeout_ = milliseconds; 103 timerid_ = SetTimer(hwnd_, kTimerId, milliseconds, NULL); 104 return !!timerid_; 105 } 106 } 107 // UNREACHABLE 108 } 109 110 void PlatformUIThread::Stop() { 111 { 112 RTC_DCHECK_RUN_ON(&thread_checker_); 113 CritScope scoped_lock(&cs_); 114 // Shut down the dispatch loop and let the background thread exit. 115 if (timerid_) { 116 MOZ_ASSERT(hwnd_); 117 KillTimer(hwnd_, timerid_); 118 timerid_ = 0; 119 } 120 switch (state_) { 121 // If we haven't started yet there is nothing to do, we will go into 122 // the STOPPED state at the end of the function and InternalInit() 123 // will not move us to STARTED. 124 case State::UNSTARTED: 125 break; 126 // If we have started, that means that InternalInit() has run and the 127 // message wait loop has or will run. We need to signal it to stop. wich 128 // will allow PlatformThread::Stop to join that thread. 129 case State::STARTED: { 130 MOZ_ASSERT(hwnd_); 131 PostMessage(hwnd_, WM_CLOSE, 0, 0); 132 break; 133 } 134 // We have already stopped. There is nothing to do. 135 case State::STOPPED: 136 break; 137 } 138 // Always set our state to STOPPED 139 state_ = State::STOPPED; 140 } 141 monitor_thread_.Finalize(); 142 } 143 144 void PlatformUIThread::Run() { 145 // InternalInit() will return false when the thread is already in shutdown. 146 // otherwise we must run until we get a Windows WM_QUIT msg. 147 const bool runUntilQuitMsg = InternalInit(); 148 // The interface contract of Start/Stop is that for a successful call to 149 // Start, there should be at least one call to the run function. 150 NativeEventCallback(); 151 while (runUntilQuitMsg) { 152 // Alertable sleep to receive WM_QUIT (following a WM_CLOSE triggering a 153 // WM_DESTROY) 154 if (MsgWaitForMultipleObjectsEx(0, nullptr, INFINITE, QS_ALLINPUT, 155 MWMO_ALERTABLE | MWMO_INPUTAVAILABLE) == 156 WAIT_OBJECT_0) { 157 MSG msg; 158 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 159 if (msg.message == WM_QUIT) { 160 // THE ONLY WAY to exit the thread loop 161 break; 162 } 163 TranslateMessage(&msg); 164 DispatchMessage(&msg); 165 } 166 } 167 } 168 } 169 170 void PlatformUIThread::NativeEventCallback() { native_event_callback_(); } 171 172 /* static */ 173 LRESULT CALLBACK PlatformUIThread::EventWindowProc(HWND hwnd, UINT uMsg, 174 WPARAM wParam, 175 LPARAM lParam) { 176 if (uMsg == WM_DESTROY) { 177 RemovePropW(hwnd, kThisProperty); 178 PostQuitMessage(0); 179 return 0; 180 } 181 182 PlatformUIThread* twui = 183 static_cast<PlatformUIThread*>(GetPropW(hwnd, kThisProperty)); 184 if (!twui) { 185 return DefWindowProc(hwnd, uMsg, wParam, lParam); 186 } 187 188 if (uMsg == WM_TIMER && wParam == kTimerId) { 189 twui->NativeEventCallback(); 190 return 0; 191 } 192 193 return DefWindowProc(hwnd, uMsg, wParam, lParam); 194 } 195 196 } // namespace webrtc 197 198 #endif