1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "renderer/win32/win32_renderer_client.h"
31
32 #include <memory>
33
34 #include "base/logging.h"
35 #include "base/mutex.h"
36 #include "base/scoped_handle.h"
37 #include "base/system_util.h"
38 #include "base/util.h"
39 #include "renderer/renderer_client.h"
40 #include "protocol/renderer_command.pb.h"
41
42 namespace mozc {
43 namespace renderer {
44 namespace win32 {
45
46 namespace {
47 using ::mozc::commands::RendererCommand;
48
49 class SenderThread;
50 SenderThread *g_sender_thread = nullptr;
51
52 // Used to lock |g_sender_thread|.
53 Mutex *g_mutex = nullptr;
54
55 // Represents the module handle of this module.
56 volatile HMODULE g_module = nullptr;
57
58 // True if the the DLL received DLL_PROCESS_DETACH notification.
59 volatile bool g_module_unloaded = false;
60
61 // Represents the number of UI threads that are recognized by this module.
62 volatile LONG g_ui_thread_count = 0;
63
64 // Thread Local Storage (TLS) index to specify the current UI thread is
65 // initialized or not. if ::GetTlsValue(g_tls_index) returns non-zero
66 // value, the current thread is initialized.
67 volatile DWORD g_tls_index = TLS_OUT_OF_INDEXES;
68
69 class SenderThread {
70 public:
SenderThread(HANDLE command_event,HANDLE quit_event)71 SenderThread(HANDLE command_event, HANDLE quit_event)
72 : command_event_(command_event),
73 quit_event_(quit_event) {
74 }
75
RequestQuit()76 void RequestQuit() {
77 ::SetEvent(quit_event_.get());
78 }
79
UpdateCommand(const RendererCommand & new_command)80 void UpdateCommand(const RendererCommand &new_command) {
81 scoped_lock lock(&mutex_);
82 renderer_command_.CopyFrom(new_command);
83 ::SetEvent(command_event_.get());
84 }
85
RenderLoop()86 void RenderLoop() {
87 // Wait until desktop name is ready. b/10403163
88 while (SystemUtil::GetDesktopNameAsString().empty()) {
89 const DWORD wait_result = ::WaitForSingleObject(quit_event_.get(), 500);
90 const DWORD wait_error = ::GetLastError();
91 if (wait_result == WAIT_OBJECT_0) {
92 return;
93 }
94 if (wait_result == WAIT_TIMEOUT) {
95 continue;
96 }
97 LOG(ERROR) << "Unknown result: " << wait_result
98 << ", error: " << wait_error;
99 return;
100 }
101
102 mozc::renderer::RendererClient renderer_client;
103 while (true) {
104 const HANDLE handles[] = {quit_event_.get(), command_event_.get()};
105 const DWORD wait_result = ::WaitForMultipleObjects(
106 arraysize(handles), handles, FALSE, INFINITE);
107 const DWORD wait_error = ::GetLastError();
108 if (g_module_unloaded) {
109 break;
110 }
111 const DWORD kQuitEventSignaled = WAIT_OBJECT_0;
112 const DWORD kRendererEventSignaled = WAIT_OBJECT_0 + 1;
113 if (wait_result == kQuitEventSignaled) {
114 // handles[0], that is, quit event is signaled.
115 break;
116 }
117 if (wait_result != kRendererEventSignaled) {
118 LOG(ERROR) << "WaitForMultipleObjects failed. error: " << wait_error;
119 break;
120 }
121 // handles[1], that is, renderer event is signaled.
122 RendererCommand command;
123 {
124 scoped_lock lock(&mutex_);
125 command.Swap(&renderer_command_);
126 ::ResetEvent(command_event_.get());
127 }
128 if (!renderer_client.ExecCommand(command)) {
129 DLOG(ERROR) << "RendererClient::ExecCommand failed.";
130 }
131 }
132 }
133
134 private:
135 ScopedHandle command_event_;
136 ScopedHandle quit_event_;
137 RendererCommand renderer_command_;
138 Mutex mutex_;
139
140 DISALLOW_COPY_AND_ASSIGN(SenderThread);
141 };
142
ThreadProc(void *)143 static DWORD WINAPI ThreadProc(void * /*unused*/) {
144 SenderThread *thread = nullptr;
145 {
146 scoped_lock lock(g_mutex);
147 thread = g_sender_thread;
148 }
149 if (thread != nullptr) {
150 thread->RenderLoop();
151 delete thread;
152 }
153 ::FreeLibraryAndExitThread(g_module, 0);
154 }
155
CreateSenderThread()156 SenderThread *CreateSenderThread() {
157 // Here we directly use CreateThread API rather than _beginthreadex because
158 // the command sender thread will be eventually terminated via
159 // FreeLibraryAndExitThread API. Regarding CRT memory resources, this will
160 // be OK because this code will be running as DLL and the CRT can manage
161 // thread specific resources through attach/detach notification in DllMain.
162 DWORD thread_id = 0;
163 ScopedHandle thread_handle(::CreateThread(
164 nullptr, 0, ThreadProc, nullptr, CREATE_SUSPENDED, &thread_id));
165 if (thread_handle.get() == nullptr) {
166 // Failed to create the thread. Restore the reference count of the DLL.
167 return nullptr;
168 }
169
170 // Increment the reference count of the IME DLL so that the DLL will not
171 // be unloaded while the sender thread is running. The reference count
172 // will be decremented by FreeLibraryAndExitThread API in the thread routine.
173 HMODULE loaded_module = nullptr;
174 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
175 reinterpret_cast<const wchar_t *>(g_module),
176 &loaded_module) == FALSE) {
177 ::TerminateThread(thread_handle.get(), 0);
178 return nullptr;
179 }
180 if (loaded_module != g_module) {
181 ::TerminateThread(thread_handle.get(), 0);
182 ::FreeLibrary(loaded_module);
183 return nullptr;
184 }
185
186 // Create shared objects. We use manual reset events for simplicity.
187 ScopedHandle command_event(::CreateEventW(nullptr, TRUE, FALSE, nullptr));
188 ScopedHandle quit_event(::CreateEventW(nullptr, TRUE, FALSE, nullptr));
189 if ((command_event.get() == nullptr) || (quit_event.get() == nullptr)) {
190 ::TerminateThread(thread_handle.get(), 0);
191 return nullptr;
192 }
193
194 std::unique_ptr<SenderThread> thread(new SenderThread(
195 command_event.take(), quit_event.take()));
196
197 // Resume the thread.
198 if (::ResumeThread(thread_handle.get()) == -1) {
199 ::TerminateThread(thread_handle.get(), 0);
200 ::FreeLibrary(loaded_module);
201 return nullptr;
202 }
203
204 return thread.release();
205 }
206
CanIgnoreRequest(const RendererCommand & command)207 bool CanIgnoreRequest(const RendererCommand &command) {
208 if (g_module_unloaded) {
209 return true;
210 }
211 if (g_tls_index == TLS_OUT_OF_INDEXES) {
212 return true;
213 }
214 if ((::TlsGetValue(g_tls_index) == nullptr) &&
215 !command.visible()) {
216 // The sender threaed is not initialized and |command| is to hide the
217 // renderer. We are likely to be able to skip this request.
218 return true;
219 }
220 return false;
221 }
222
223 // Returns true when the required initialization is finished successfully.
EnsureUIThreadInitialized()224 bool EnsureUIThreadInitialized() {
225 if (g_module_unloaded) {
226 return false;
227 }
228 if (g_tls_index == TLS_OUT_OF_INDEXES) {
229 return false;
230 }
231 if (::TlsGetValue(g_tls_index) != nullptr) {
232 // already initialized.
233 return true;
234 }
235 {
236 scoped_lock lock(g_mutex);
237 ++g_ui_thread_count;
238 if (g_ui_thread_count == 1) {
239 g_sender_thread = CreateSenderThread();
240 }
241 }
242 // Mark this thread is initialized.
243 ::TlsSetValue(g_tls_index, reinterpret_cast<void *>(1));
244 return true;
245 }
246
247 } // namespace
248
OnModuleLoaded(HMODULE module_handle)249 void Win32RendererClient::OnModuleLoaded(HMODULE module_handle) {
250 g_module = module_handle;
251 g_mutex = new Mutex();
252 g_tls_index = ::TlsAlloc();
253 }
254
OnModuleUnloaded()255 void Win32RendererClient::OnModuleUnloaded() {
256 if (g_tls_index != TLS_OUT_OF_INDEXES) {
257 ::TlsFree(g_tls_index);
258 }
259 delete g_mutex;
260 g_module_unloaded = true;
261 g_module = nullptr;
262 }
263
OnUIThreadUninitialized()264 void Win32RendererClient::OnUIThreadUninitialized() {
265 if (g_module_unloaded) {
266 return;
267 }
268 if (g_tls_index == TLS_OUT_OF_INDEXES) {
269 return;
270 }
271 if (::TlsGetValue(g_tls_index) == nullptr) {
272 // Do nothing because this thread did not increment |g_ui_thread_count|.
273 return;
274 }
275 {
276 scoped_lock lock(g_mutex);
277 if (g_ui_thread_count > 0) {
278 --g_ui_thread_count;
279 if (g_ui_thread_count == 0 && g_sender_thread != nullptr) {
280 g_sender_thread->RequestQuit();
281 g_sender_thread = nullptr;
282 }
283 }
284 }
285 // Mark this thread is uninitialized.
286 ::TlsSetValue(g_tls_index, nullptr);
287 }
288
OnUpdated(const RendererCommand & command)289 void Win32RendererClient::OnUpdated(const RendererCommand &command) {
290 if (CanIgnoreRequest(command)) {
291 return;
292 }
293 if (!EnsureUIThreadInitialized()) {
294 return;
295 }
296 SenderThread *thread = nullptr;
297 {
298 scoped_lock lock(g_mutex);
299 thread = g_sender_thread;
300 }
301 if (thread != nullptr) {
302 thread->UpdateCommand(command);
303 }
304 }
305
306 } // namespace win32
307 } // namespace renderer
308 } // namespace mozc
309