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