OLD | NEW |
| (Empty) |
1 // Copyright 2003-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 #include "omaha/base/sta.h" | |
17 | |
18 #include <atlbase.h> | |
19 #include <atlwin.h> | |
20 #include "base/scoped_ptr.h" | |
21 #include "omaha/base/debug.h" | |
22 #include "omaha/base/logging.h" | |
23 #include "omaha/base/sta_call.h" | |
24 #include "omaha/base/utils.h" | |
25 | |
26 namespace omaha { | |
27 | |
28 namespace { | |
29 | |
30 class CallDispatcher; | |
31 | |
32 // ApartmentState maintains the state of the apartment. It keeps a reference | |
33 // to a call dispatcher. The dispatcher is basically a window object that | |
34 // handles user messages corresponding to each call. | |
35 // | |
36 // ApartmentState is a singleton. It's lifetime is controlled by the | |
37 // 'InitializeApartment' and 'UnintializeApartment'. | |
38 // | |
39 // The current implementation is limited to the main STA. Creating multiple | |
40 // apartments in a process is not possible. | |
41 // | |
42 // TODO(omaha): implement a simple reference counting of the threads to make | |
43 // sure the all the calling threads have returned when the apt is destroyed. | |
44 | |
45 class ApartmentState { | |
46 public: | |
47 ApartmentState(DWORD thread_id, CallDispatcher* call_disp) | |
48 : thread_id_(thread_id), call_dispatcher_(call_disp) {} | |
49 | |
50 ~ApartmentState() { | |
51 ASSERT1(ref_cnt_ == 0); | |
52 } | |
53 | |
54 // Initialize the state of the singleton. | |
55 static HRESULT Initialize(DWORD reserved); | |
56 | |
57 // Uninitialize the state of the singleton. | |
58 static HRESULT Uninitialize(); | |
59 | |
60 // Accessors. | |
61 static ApartmentState* apartment_state() { | |
62 return apartment_state_; | |
63 } | |
64 | |
65 CallDispatcher& call_dispatcher() const { | |
66 ASSERT(call_dispatcher_.get(), | |
67 (_T("'InitializeApartment' has not been called."))); | |
68 return *call_dispatcher_; | |
69 } | |
70 | |
71 DWORD thread_id() const { | |
72 ASSERT(thread_id_, | |
73 (_T("'InitializeApartment' has not been called."))); | |
74 return thread_id_; | |
75 } | |
76 | |
77 private: | |
78 static int ref_cnt_; // the reference count of init/uninit. | |
79 | |
80 const DWORD thread_id_; // thread that called the InitializeApartment | |
81 scoped_ptr<CallDispatcher> call_dispatcher_; | |
82 | |
83 static ApartmentState* apartment_state_; // the instance of the state | |
84 DISALLOW_EVIL_CONSTRUCTORS(ApartmentState); | |
85 }; | |
86 | |
87 | |
88 // CallDispatcher uses functors to cross call from the caller's thread to | |
89 // this apartment thread (main thread). | |
90 class CallDispatcher | |
91 : public CWindowImpl<CallDispatcher, | |
92 CWindow, | |
93 CWinTraits<WS_OVERLAPPED, WS_EX_TOOLWINDOW> > { | |
94 public: | |
95 explicit CallDispatcher(DWORD options); | |
96 ~CallDispatcher(); | |
97 | |
98 // Two-phase initialization | |
99 HRESULT Init(); | |
100 | |
101 // The initiator of the cross call. | |
102 HRESULT DoCrossApartmentCall(BaseFunctor* caller, void* presult); | |
103 | |
104 private: | |
105 static const UINT WM_METHOD_CALL = WM_USER + 0x100; | |
106 static const UINT WM_METHOD_CALL_COMPLETE = WM_USER + 0x101; | |
107 | |
108 BEGIN_MSG_MAP(CallDispatcher) | |
109 MESSAGE_HANDLER(WM_METHOD_CALL, OnMethodCall) | |
110 END_MSG_MAP() | |
111 | |
112 private: | |
113 LRESULT OnMethodCall(UINT uMsg, WPARAM wParam, | |
114 LPARAM lParam, BOOL& bHandled); | |
115 | |
116 DWORD options_; | |
117 | |
118 // Currently only one option is supported for testing purposes. | |
119 // It testing mode, this option disables the cross-call mechanism and | |
120 // directly calls the functor in the same thread as the invoker. | |
121 static const DWORD kTestingMode = DWORD(-1); | |
122 }; | |
123 | |
124 // initialize the static data memebers | |
125 ApartmentState* ApartmentState::apartment_state_ = 0; | |
126 int ApartmentState::ref_cnt_ = 0; | |
127 | |
128 HRESULT ApartmentState::Initialize(DWORD reserved) { | |
129 CORE_LOG(L3, (_T("[ApartmentState::Initialize]"))); | |
130 ASSERT(ref_cnt_ >= 0, (_T("Apartment Reference Counting"))); | |
131 if (ref_cnt_ < 0) return E_UNEXPECTED; | |
132 | |
133 DWORD thread_id = ::GetCurrentThreadId(); | |
134 ASSERT1(thread_id); | |
135 | |
136 if (ref_cnt_ > 0) { | |
137 ASSERT(apartment_state(), (_T("Apartment State is 0."))); | |
138 bool same_thread = thread_id == apartment_state()->thread_id(); | |
139 // if initialized multiple times verify the thread identity just in case | |
140 ASSERT(same_thread, (_T("Wrong Thread."))); | |
141 if (!same_thread) return E_UNEXPECTED; | |
142 ++ref_cnt_; | |
143 return S_OK; | |
144 } | |
145 | |
146 ASSERT1(ref_cnt_ == 0); | |
147 | |
148 // do the initialization of the apartment | |
149 scoped_ptr<CallDispatcher> call_disp(new CallDispatcher(reserved)); | |
150 RET_IF_FAILED(call_disp->Init()); | |
151 scoped_ptr<ApartmentState> ap_state( | |
152 new ApartmentState(thread_id, call_disp.get())); | |
153 | |
154 call_disp.release(); | |
155 ApartmentState::apartment_state_ = ap_state.release(); | |
156 | |
157 ++ref_cnt_; | |
158 return S_OK; | |
159 } | |
160 | |
161 HRESULT ApartmentState::Uninitialize() { | |
162 ASSERT(ref_cnt_ > 0, (_T("Apartment Reference Counting"))); | |
163 if (ref_cnt_ <= 0) return E_UNEXPECTED; | |
164 | |
165 DWORD thread_id = ::GetCurrentThreadId(); | |
166 ASSERT1(thread_id); | |
167 | |
168 ASSERT(apartment_state(), (_T("Apartment State is 0."))); | |
169 bool same_thread = thread_id == apartment_state()->thread_id(); | |
170 // verify the thread identity just in case | |
171 ASSERT(same_thread, (_T("Wrong Thread."))); | |
172 if (!same_thread) return E_UNEXPECTED; | |
173 | |
174 if (--ref_cnt_ == 0) { | |
175 delete ApartmentState::apartment_state(); | |
176 ApartmentState::apartment_state_ = 0; | |
177 } | |
178 | |
179 return S_OK; | |
180 } | |
181 | |
182 CallDispatcher::CallDispatcher(DWORD options) : options_(options) { | |
183 // TODO(omaha): Log | |
184 } | |
185 | |
186 CallDispatcher::~CallDispatcher() { | |
187 // TODO(omaha): Log | |
188 if (m_hWnd) { | |
189 DestroyWindow(); | |
190 } | |
191 } | |
192 | |
193 HRESULT CallDispatcher::Init() { | |
194 // Create a message-only window for the dispatcher. It is not visible, | |
195 // has no z-order, cannot be enumerated, and does not receive broadcast | |
196 // messages. The window simply dispatches messages. | |
197 const TCHAR kWndName[] = _T("{FFE21900-612E-44a9-8424-3FC71B382E61}"); | |
198 HWND hwnd = Create(HWND_MESSAGE, NULL, kWndName); | |
199 return hwnd ? S_OK : HRESULT_FROM_WIN32(::GetLastError()); | |
200 } | |
201 | |
202 // | |
203 LRESULT CallDispatcher::OnMethodCall(UINT, WPARAM wParam, | |
204 LPARAM result, BOOL&) { | |
205 CORE_LOG(L6, (_T("[CallDispatcher::OnMethodCall]"))); | |
206 | |
207 ASSERT1(wParam); | |
208 BaseFunctor& call = *reinterpret_cast<BaseFunctor*>(wParam); | |
209 | |
210 // presult is non-zero if the method or function has a return type. | |
211 // presult is zero for void methods and functions. | |
212 | |
213 void* presult = reinterpret_cast<void*>(result); | |
214 | |
215 ASSERT( | |
216 ApartmentState::apartment_state()->thread_id() == ::GetCurrentThreadId(), | |
217 (_T("Wrong Thread"))); | |
218 | |
219 // the function object virtual call; | |
220 call(presult); | |
221 | |
222 bool is_async = call.is_async(); | |
223 // For async calls, do not post a message, because the caller will not be | |
224 // waiting for the call to complete. | |
225 if (!is_async && | |
226 !::PostThreadMessage(call.thread_id(), WM_METHOD_CALL_COMPLETE, 0, 0)) { | |
227 DWORD error = ::GetLastError(); | |
228 CORE_LOG(LEVEL_ERROR, | |
229 (_T("[CallDispatcher::OnMethodCall - PostThreadMessage][%d]"), error)); | |
230 ASSERT(false, (_T("Failed to PostThreadMessage."))); | |
231 | |
232 // TODO(omaha): raise here. | |
233 } | |
234 | |
235 // DO NOT ACCESS THE CALL OBJECT FROM DOWN ON. IN THE CASE OF A SYNCHRONOUS | |
236 // CALL THE CALL OBJECT MAY HAVE ALREADY DESTROYED. | |
237 | |
238 if (is_async) { | |
239 // Auto cleanup of the call object in the case of a async call. | |
240 delete &call; | |
241 } | |
242 | |
243 CORE_LOG(L6, (_T("CallDispatcher::OnMethodCall returns."))); | |
244 return true; | |
245 } | |
246 | |
247 // | |
248 HRESULT CallDispatcher::DoCrossApartmentCall(BaseFunctor* call, | |
249 void* presult) { | |
250 CORE_LOG(L6, (_T("[CallDispatcher::DoCrossApartmentCall]"))); | |
251 | |
252 ASSERT(IsWindow(), (_T("The dispatcher must have a window."))); | |
253 bool is_async = call->is_async(); | |
254 if (options_ == kTestingMode) { | |
255 (*call)(presult); | |
256 if (is_async) { | |
257 // We need to delete the functor as if we were the callee. | |
258 delete call; | |
259 } | |
260 return S_OK; | |
261 } | |
262 | |
263 if (!is_async) { | |
264 // Usually it is a mistake to call a synchronous method from the main STA | |
265 // to the main STA. | |
266 | |
267 DWORD thread_id = ApartmentState::apartment_state()->thread_id(); | |
268 ASSERT(thread_id != ::GetCurrentThreadId(), (_T("Wrong Thread"))); | |
269 | |
270 ASSERT(GetWindowThreadID() != ::GetCurrentThreadId(), | |
271 (_T("DoCrossApartmentCall calling its own thread."))); | |
272 } | |
273 | |
274 if (!PostMessage(WM_METHOD_CALL, | |
275 reinterpret_cast<WPARAM>(call), | |
276 reinterpret_cast<LPARAM>(presult))) { | |
277 DWORD err = ::GetLastError(); | |
278 CORE_LOG(LEVEL_ERROR, | |
279 (_T("[CallDispatcher::DoCrossApartmentCall - PostMessage][%d]"), err)); | |
280 ASSERT(false, (_T("Failed to PostMessage."))); | |
281 | |
282 return HRESULT_FROM_WIN32(err); | |
283 } | |
284 | |
285 // Once the call has been made, do not access the state of the functor as | |
286 // the other end might have already executed the call and delete the functor. | |
287 // This is true for asyncronous calls but it would not hurt for synchronous | |
288 // calls as well. | |
289 call = NULL; | |
290 | |
291 if (is_async) { | |
292 // Do not wait for the call to complete. The call will complete at | |
293 // some time in the future and the call object is going to be cleaned up. | |
294 return S_OK; | |
295 } | |
296 | |
297 // Pump all messages, waiting for WM_METHOD_CALL_COMPLETE or WM_QUIT. | |
298 MSG msg; | |
299 SetZero(msg); | |
300 int ret = 0; | |
301 while ((ret = ::GetMessage(&msg, 0, 0, 0)) != 0) { | |
302 if (ret == -1) { | |
303 DWORD error = ::GetLastError(); | |
304 CORE_LOG(LEVEL_ERROR, | |
305 (_T("[CallDispatcher::DoCrossApartmentCall - GetMessage][%d]"), | |
306 error)); | |
307 // TODO(omaha): raise here. | |
308 } | |
309 | |
310 if (msg.message == WM_METHOD_CALL_COMPLETE) { | |
311 break; | |
312 } | |
313 | |
314 ::DispatchMessage(&msg); | |
315 } | |
316 | |
317 // Repost the WM_QUIT message to properly exit all message loops. | |
318 if (msg.message == WM_QUIT) { | |
319 ASSERT1(ret == 0); | |
320 ::PostQuitMessage(msg.wParam); | |
321 } | |
322 | |
323 CORE_LOG(L6, (_T("CallDispatcher::DoCrossApartmentCall returns"))); | |
324 return S_OK; | |
325 } | |
326 | |
327 } // namespace | |
328 | |
329 void BaseFunctor::DoInvoke(void* presult) { | |
330 ASSERT(ApartmentState::apartment_state(), | |
331 (_T("Did you forgot to call 'InitializeApartment'?"))); | |
332 | |
333 CallDispatcher& call_dispatcher = | |
334 ApartmentState::apartment_state()->call_dispatcher(); | |
335 | |
336 HRESULT hr = call_dispatcher.DoCrossApartmentCall(this, presult); | |
337 | |
338 if (FAILED(hr)) { | |
339 ASSERT(false, (_T("Failed to call across apartments."))); | |
340 // TODO(omaha): log, report, raise. | |
341 } | |
342 } | |
343 | |
344 HRESULT InitializeApartment(DWORD reserved) { | |
345 return ApartmentState::Initialize(reserved); | |
346 } | |
347 | |
348 HRESULT UninitializeApartment() { | |
349 return ApartmentState::Uninitialize(); | |
350 } | |
351 | |
352 } // namespace omaha | |
353 | |
OLD | NEW |