Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(488)

Side by Side Diff: base/sta.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/sta.h ('k') | base/sta_call.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « base/sta.h ('k') | base/sta_call.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698