| Index: base/sta.cc
|
| diff --git a/base/sta.cc b/base/sta.cc
|
| deleted file mode 100644
|
| index 86604d4547a0195b56bc33f1780799618159d08c..0000000000000000000000000000000000000000
|
| --- a/base/sta.cc
|
| +++ /dev/null
|
| @@ -1,353 +0,0 @@
|
| -// Copyright 2003-2009 Google Inc.
|
| -//
|
| -// Licensed under the Apache License, Version 2.0 (the "License");
|
| -// you may not use this file except in compliance with the License.
|
| -// You may obtain a copy of the License at
|
| -//
|
| -// http://www.apache.org/licenses/LICENSE-2.0
|
| -//
|
| -// Unless required by applicable law or agreed to in writing, software
|
| -// distributed under the License is distributed on an "AS IS" BASIS,
|
| -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| -// See the License for the specific language governing permissions and
|
| -// limitations under the License.
|
| -// ========================================================================
|
| -
|
| -#include "omaha/base/sta.h"
|
| -
|
| -#include <atlbase.h>
|
| -#include <atlwin.h>
|
| -#include "base/scoped_ptr.h"
|
| -#include "omaha/base/debug.h"
|
| -#include "omaha/base/logging.h"
|
| -#include "omaha/base/sta_call.h"
|
| -#include "omaha/base/utils.h"
|
| -
|
| -namespace omaha {
|
| -
|
| -namespace {
|
| -
|
| -class CallDispatcher;
|
| -
|
| -// ApartmentState maintains the state of the apartment. It keeps a reference
|
| -// to a call dispatcher. The dispatcher is basically a window object that
|
| -// handles user messages corresponding to each call.
|
| -//
|
| -// ApartmentState is a singleton. It's lifetime is controlled by the
|
| -// 'InitializeApartment' and 'UnintializeApartment'.
|
| -//
|
| -// The current implementation is limited to the main STA. Creating multiple
|
| -// apartments in a process is not possible.
|
| -//
|
| -// TODO(omaha): implement a simple reference counting of the threads to make
|
| -// sure the all the calling threads have returned when the apt is destroyed.
|
| -
|
| -class ApartmentState {
|
| - public:
|
| - ApartmentState(DWORD thread_id, CallDispatcher* call_disp)
|
| - : thread_id_(thread_id), call_dispatcher_(call_disp) {}
|
| -
|
| - ~ApartmentState() {
|
| - ASSERT1(ref_cnt_ == 0);
|
| - }
|
| -
|
| - // Initialize the state of the singleton.
|
| - static HRESULT Initialize(DWORD reserved);
|
| -
|
| - // Uninitialize the state of the singleton.
|
| - static HRESULT Uninitialize();
|
| -
|
| - // Accessors.
|
| - static ApartmentState* apartment_state() {
|
| - return apartment_state_;
|
| - }
|
| -
|
| - CallDispatcher& call_dispatcher() const {
|
| - ASSERT(call_dispatcher_.get(),
|
| - (_T("'InitializeApartment' has not been called.")));
|
| - return *call_dispatcher_;
|
| - }
|
| -
|
| - DWORD thread_id() const {
|
| - ASSERT(thread_id_,
|
| - (_T("'InitializeApartment' has not been called.")));
|
| - return thread_id_;
|
| - }
|
| -
|
| - private:
|
| - static int ref_cnt_; // the reference count of init/uninit.
|
| -
|
| - const DWORD thread_id_; // thread that called the InitializeApartment
|
| - scoped_ptr<CallDispatcher> call_dispatcher_;
|
| -
|
| - static ApartmentState* apartment_state_; // the instance of the state
|
| - DISALLOW_EVIL_CONSTRUCTORS(ApartmentState);
|
| -};
|
| -
|
| -
|
| -// CallDispatcher uses functors to cross call from the caller's thread to
|
| -// this apartment thread (main thread).
|
| -class CallDispatcher
|
| - : public CWindowImpl<CallDispatcher,
|
| - CWindow,
|
| - CWinTraits<WS_OVERLAPPED, WS_EX_TOOLWINDOW> > {
|
| - public:
|
| - explicit CallDispatcher(DWORD options);
|
| - ~CallDispatcher();
|
| -
|
| - // Two-phase initialization
|
| - HRESULT Init();
|
| -
|
| - // The initiator of the cross call.
|
| - HRESULT DoCrossApartmentCall(BaseFunctor* caller, void* presult);
|
| -
|
| - private:
|
| - static const UINT WM_METHOD_CALL = WM_USER + 0x100;
|
| - static const UINT WM_METHOD_CALL_COMPLETE = WM_USER + 0x101;
|
| -
|
| - BEGIN_MSG_MAP(CallDispatcher)
|
| - MESSAGE_HANDLER(WM_METHOD_CALL, OnMethodCall)
|
| - END_MSG_MAP()
|
| -
|
| - private:
|
| - LRESULT OnMethodCall(UINT uMsg, WPARAM wParam,
|
| - LPARAM lParam, BOOL& bHandled);
|
| -
|
| - DWORD options_;
|
| -
|
| - // Currently only one option is supported for testing purposes.
|
| - // It testing mode, this option disables the cross-call mechanism and
|
| - // directly calls the functor in the same thread as the invoker.
|
| - static const DWORD kTestingMode = DWORD(-1);
|
| -};
|
| -
|
| -// initialize the static data memebers
|
| -ApartmentState* ApartmentState::apartment_state_ = 0;
|
| -int ApartmentState::ref_cnt_ = 0;
|
| -
|
| -HRESULT ApartmentState::Initialize(DWORD reserved) {
|
| - CORE_LOG(L3, (_T("[ApartmentState::Initialize]")));
|
| - ASSERT(ref_cnt_ >= 0, (_T("Apartment Reference Counting")));
|
| - if (ref_cnt_ < 0) return E_UNEXPECTED;
|
| -
|
| - DWORD thread_id = ::GetCurrentThreadId();
|
| - ASSERT1(thread_id);
|
| -
|
| - if (ref_cnt_ > 0) {
|
| - ASSERT(apartment_state(), (_T("Apartment State is 0.")));
|
| - bool same_thread = thread_id == apartment_state()->thread_id();
|
| - // if initialized multiple times verify the thread identity just in case
|
| - ASSERT(same_thread, (_T("Wrong Thread.")));
|
| - if (!same_thread) return E_UNEXPECTED;
|
| - ++ref_cnt_;
|
| - return S_OK;
|
| - }
|
| -
|
| - ASSERT1(ref_cnt_ == 0);
|
| -
|
| - // do the initialization of the apartment
|
| - scoped_ptr<CallDispatcher> call_disp(new CallDispatcher(reserved));
|
| - RET_IF_FAILED(call_disp->Init());
|
| - scoped_ptr<ApartmentState> ap_state(
|
| - new ApartmentState(thread_id, call_disp.get()));
|
| -
|
| - call_disp.release();
|
| - ApartmentState::apartment_state_ = ap_state.release();
|
| -
|
| - ++ref_cnt_;
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT ApartmentState::Uninitialize() {
|
| - ASSERT(ref_cnt_ > 0, (_T("Apartment Reference Counting")));
|
| - if (ref_cnt_ <= 0) return E_UNEXPECTED;
|
| -
|
| - DWORD thread_id = ::GetCurrentThreadId();
|
| - ASSERT1(thread_id);
|
| -
|
| - ASSERT(apartment_state(), (_T("Apartment State is 0.")));
|
| - bool same_thread = thread_id == apartment_state()->thread_id();
|
| - // verify the thread identity just in case
|
| - ASSERT(same_thread, (_T("Wrong Thread.")));
|
| - if (!same_thread) return E_UNEXPECTED;
|
| -
|
| - if (--ref_cnt_ == 0) {
|
| - delete ApartmentState::apartment_state();
|
| - ApartmentState::apartment_state_ = 0;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -CallDispatcher::CallDispatcher(DWORD options) : options_(options) {
|
| - // TODO(omaha): Log
|
| -}
|
| -
|
| -CallDispatcher::~CallDispatcher() {
|
| - // TODO(omaha): Log
|
| - if (m_hWnd) {
|
| - DestroyWindow();
|
| - }
|
| -}
|
| -
|
| -HRESULT CallDispatcher::Init() {
|
| - // Create a message-only window for the dispatcher. It is not visible,
|
| - // has no z-order, cannot be enumerated, and does not receive broadcast
|
| - // messages. The window simply dispatches messages.
|
| - const TCHAR kWndName[] = _T("{FFE21900-612E-44a9-8424-3FC71B382E61}");
|
| - HWND hwnd = Create(HWND_MESSAGE, NULL, kWndName);
|
| - return hwnd ? S_OK : HRESULT_FROM_WIN32(::GetLastError());
|
| -}
|
| -
|
| -//
|
| -LRESULT CallDispatcher::OnMethodCall(UINT, WPARAM wParam,
|
| - LPARAM result, BOOL&) {
|
| - CORE_LOG(L6, (_T("[CallDispatcher::OnMethodCall]")));
|
| -
|
| - ASSERT1(wParam);
|
| - BaseFunctor& call = *reinterpret_cast<BaseFunctor*>(wParam);
|
| -
|
| - // presult is non-zero if the method or function has a return type.
|
| - // presult is zero for void methods and functions.
|
| -
|
| - void* presult = reinterpret_cast<void*>(result);
|
| -
|
| - ASSERT(
|
| - ApartmentState::apartment_state()->thread_id() == ::GetCurrentThreadId(),
|
| - (_T("Wrong Thread")));
|
| -
|
| - // the function object virtual call;
|
| - call(presult);
|
| -
|
| - bool is_async = call.is_async();
|
| - // For async calls, do not post a message, because the caller will not be
|
| - // waiting for the call to complete.
|
| - if (!is_async &&
|
| - !::PostThreadMessage(call.thread_id(), WM_METHOD_CALL_COMPLETE, 0, 0)) {
|
| - DWORD error = ::GetLastError();
|
| - CORE_LOG(LEVEL_ERROR,
|
| - (_T("[CallDispatcher::OnMethodCall - PostThreadMessage][%d]"), error));
|
| - ASSERT(false, (_T("Failed to PostThreadMessage.")));
|
| -
|
| - // TODO(omaha): raise here.
|
| - }
|
| -
|
| - // DO NOT ACCESS THE CALL OBJECT FROM DOWN ON. IN THE CASE OF A SYNCHRONOUS
|
| - // CALL THE CALL OBJECT MAY HAVE ALREADY DESTROYED.
|
| -
|
| - if (is_async) {
|
| - // Auto cleanup of the call object in the case of a async call.
|
| - delete &call;
|
| - }
|
| -
|
| - CORE_LOG(L6, (_T("CallDispatcher::OnMethodCall returns.")));
|
| - return true;
|
| -}
|
| -
|
| -//
|
| -HRESULT CallDispatcher::DoCrossApartmentCall(BaseFunctor* call,
|
| - void* presult) {
|
| - CORE_LOG(L6, (_T("[CallDispatcher::DoCrossApartmentCall]")));
|
| -
|
| - ASSERT(IsWindow(), (_T("The dispatcher must have a window.")));
|
| - bool is_async = call->is_async();
|
| - if (options_ == kTestingMode) {
|
| - (*call)(presult);
|
| - if (is_async) {
|
| - // We need to delete the functor as if we were the callee.
|
| - delete call;
|
| - }
|
| - return S_OK;
|
| - }
|
| -
|
| - if (!is_async) {
|
| - // Usually it is a mistake to call a synchronous method from the main STA
|
| - // to the main STA.
|
| -
|
| - DWORD thread_id = ApartmentState::apartment_state()->thread_id();
|
| - ASSERT(thread_id != ::GetCurrentThreadId(), (_T("Wrong Thread")));
|
| -
|
| - ASSERT(GetWindowThreadID() != ::GetCurrentThreadId(),
|
| - (_T("DoCrossApartmentCall calling its own thread.")));
|
| - }
|
| -
|
| - if (!PostMessage(WM_METHOD_CALL,
|
| - reinterpret_cast<WPARAM>(call),
|
| - reinterpret_cast<LPARAM>(presult))) {
|
| - DWORD err = ::GetLastError();
|
| - CORE_LOG(LEVEL_ERROR,
|
| - (_T("[CallDispatcher::DoCrossApartmentCall - PostMessage][%d]"), err));
|
| - ASSERT(false, (_T("Failed to PostMessage.")));
|
| -
|
| - return HRESULT_FROM_WIN32(err);
|
| - }
|
| -
|
| - // Once the call has been made, do not access the state of the functor as
|
| - // the other end might have already executed the call and delete the functor.
|
| - // This is true for asyncronous calls but it would not hurt for synchronous
|
| - // calls as well.
|
| - call = NULL;
|
| -
|
| - if (is_async) {
|
| - // Do not wait for the call to complete. The call will complete at
|
| - // some time in the future and the call object is going to be cleaned up.
|
| - return S_OK;
|
| - }
|
| -
|
| - // Pump all messages, waiting for WM_METHOD_CALL_COMPLETE or WM_QUIT.
|
| - MSG msg;
|
| - SetZero(msg);
|
| - int ret = 0;
|
| - while ((ret = ::GetMessage(&msg, 0, 0, 0)) != 0) {
|
| - if (ret == -1) {
|
| - DWORD error = ::GetLastError();
|
| - CORE_LOG(LEVEL_ERROR,
|
| - (_T("[CallDispatcher::DoCrossApartmentCall - GetMessage][%d]"),
|
| - error));
|
| - // TODO(omaha): raise here.
|
| - }
|
| -
|
| - if (msg.message == WM_METHOD_CALL_COMPLETE) {
|
| - break;
|
| - }
|
| -
|
| - ::DispatchMessage(&msg);
|
| - }
|
| -
|
| - // Repost the WM_QUIT message to properly exit all message loops.
|
| - if (msg.message == WM_QUIT) {
|
| - ASSERT1(ret == 0);
|
| - ::PostQuitMessage(msg.wParam);
|
| - }
|
| -
|
| - CORE_LOG(L6, (_T("CallDispatcher::DoCrossApartmentCall returns")));
|
| - return S_OK;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -void BaseFunctor::DoInvoke(void* presult) {
|
| - ASSERT(ApartmentState::apartment_state(),
|
| - (_T("Did you forgot to call 'InitializeApartment'?")));
|
| -
|
| - CallDispatcher& call_dispatcher =
|
| - ApartmentState::apartment_state()->call_dispatcher();
|
| -
|
| - HRESULT hr = call_dispatcher.DoCrossApartmentCall(this, presult);
|
| -
|
| - if (FAILED(hr)) {
|
| - ASSERT(false, (_T("Failed to call across apartments.")));
|
| - // TODO(omaha): log, report, raise.
|
| - }
|
| -}
|
| -
|
| -HRESULT InitializeApartment(DWORD reserved) {
|
| - return ApartmentState::Initialize(reserved);
|
| -}
|
| -
|
| -HRESULT UninitializeApartment() {
|
| - return ApartmentState::Uninitialize();
|
| -}
|
| -
|
| -} // namespace omaha
|
| -
|
|
|