| 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 |