// @(#)root/win32gdk:$Id$ // Author: Valeriy Onuchin 08/08/2003 /************************************************************************* * Copyright (C) 1995-2001, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ ////////////////////////////////////////////////////////////////////////// // // Proxy classes provide thread-safe interface to global objects. // // For example: TGWin32VirtualXProxy (to gVirtualX), // TGWin32InterpreterProxy (to gInterpreter). // // Proxy object creates callback object and posts a windows message to // "processing thread". When windows message is received callback // ("real method") is executed. // // For example: // gVirtualX->ClearWindow() // // - callback object created containing pointer to function // corresponding TGWin32::ClearWindow() method // - message to "processing thread" (main thread) is posted // - TGWin32::ClearWindow() method is executed inside main thread // - thread containing gVirtualX proxy object waits for reply // from main thread that TGWin32::ClearWindow() is completed. // // Howto create proxy class: // // 1. Naming. // name of proxy = TGWin32 + the name of "virtual base class" + Proxy // // e.g. TGWin32VirtualXProxy = TGWin32 + VirtualX + Proxy // // 2. Definition of global object // As example check definition and implementation of // gVirtualX, gInterpreter global objects // // 3. Class definition. // proxy class must be inherited from "virtual base class" and // TGWin32ProxyBase class. For example: // // class TGWin32VirtualX : public TVirtualX , public TGWin32ProxyBase // // 4. Constructors, destructor, extra methods. // - constructors and destructor of proxy class do nothing // - proxy class must contain two extra static methods // RealObject(), ProxyObject(). Each of them return pointer to object // of virtual base class. // // For example: // static TInterpreter *RealObject(); // static TInterpreter *ProxyObject(); // // 5. Implementation // TGWin32ProxyDefs.h file contains a set of macros which very // simplify implementation. // - RETURN_PROXY_OBJECT macro implements ProxyObject() method, e.g. // RETURN_PROXY_OBJECT(Interpreter) // - the names of other macros say about itself. // // For example: // VOID_METHOD_ARG0(Interpreter,ClearFileBusy,1) // void TGWin32InterpreterProxy::ClearFileBusy() // // RETURN_METHOD_ARG0_CONST(VirtualX,Visual_t,GetVisual) // Visual_t TGWin32VirtualXProxy::GetVisual() const // // RETURN_METHOD_ARG2(VirtualX,Int_t,OpenPixmap,UInt_t,w,UInt_t,h) // Int_t TGWin32VirtualXProxy::OpenPixmap,UInt_t w,UInt_t h) // // - few methods has _LOCK part in the name // VOID_METHOD_ARG1_LOCK(Interpreter,CreateListOfMethods,TClass*,cl) // // /////////////////////////////////////////////////////////////////////////////// #include "Windows4Root.h" #include #include "TGWin32ProxyBase.h" #include "TRefCnt.h" #include "TList.h" #include "TGWin32.h" #include "TROOT.h" //////////////////////////////////////////////////////////////////////////////// class TGWin32CallBackObject : public TObject { public: TGWin32CallBack fCallBack; // callback function (called by GUI thread) void *fParam; // arguments passed to/from callback function TGWin32CallBackObject(TGWin32CallBack cb,void *p):fCallBack(cb),fParam(p) {} ~TGWin32CallBackObject() { if (fParam) delete fParam; } }; //////////////////////////////////////////////////////////////////////////////// class TGWin32ProxyBasePrivate { public: HANDLE fEvent; // event used for syncronization TGWin32ProxyBasePrivate(); ~TGWin32ProxyBasePrivate(); }; //////////////////////////////////////////////////////////////////////////////// /// ctor TGWin32ProxyBasePrivate::TGWin32ProxyBasePrivate() { fEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); } //////////////////////////////////////////////////////////////////////////////// /// dtor TGWin32ProxyBasePrivate::~TGWin32ProxyBasePrivate() { if (fEvent) ::CloseHandle(fEvent); fEvent = 0; } ULong_t TGWin32ProxyBase::fgPostMessageId = 0; ULong_t TGWin32ProxyBase::fgPingMessageId = 0; ULong_t TGWin32ProxyBase::fgMainThreadId = 0; ULong_t TGWin32ProxyBase::fgUserThreadId = 0; Long_t TGWin32ProxyBase::fgLock = 0; UInt_t TGWin32ProxyBase::fMaxResponseTime = 0; //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// ctor TGWin32ProxyBase::TGWin32ProxyBase() { fIsVirtualX = kFALSE; fCallBack = 0; fParam = 0; fListOfCallBacks = new TList(); fBatchLimit = 100; fId = ::GetCurrentThreadId(); fPimpl = new TGWin32ProxyBasePrivate(); if (!fgPostMessageId) fgPostMessageId = ::RegisterWindowMessage("TGWin32ProxyBase::Post"); if (!fgPingMessageId) fgPingMessageId = ::RegisterWindowMessage("TGWin32ProxyBase::Ping"); } //////////////////////////////////////////////////////////////////////////////// /// dtor TGWin32ProxyBase::~TGWin32ProxyBase() { fListOfCallBacks->Delete(); delete fListOfCallBacks; fListOfCallBacks = 0; delete fPimpl; } //////////////////////////////////////////////////////////////////////////////// /// enter critical section void TGWin32ProxyBase::Lock() { TGWin32::Lock(); } //////////////////////////////////////////////////////////////////////////////// /// leave critical section void TGWin32ProxyBase::Unlock() { TGWin32::Unlock(); } //////////////////////////////////////////////////////////////////////////////// /// lock any proxy (client thread) void TGWin32ProxyBase::GlobalLock() { if (IsGloballyLocked()) return; ::InterlockedIncrement(&fgLock); } //////////////////////////////////////////////////////////////////////////////// /// unlock any proxy (client thread) void TGWin32ProxyBase::GlobalUnlock() { if (!IsGloballyLocked()) return; ::InterlockedDecrement(&fgLock); } //////////////////////////////////////////////////////////////////////////////// /// send ping messsage to server thread Bool_t TGWin32ProxyBase::Ping() { return ::PostThreadMessage(fgMainThreadId, fgPingMessageId, (WPARAM)0, 0L); } //////////////////////////////////////////////////////////////////////////////// /// returns elapsed time in milliseconds with microseconds precision Double_t TGWin32ProxyBase::GetMilliSeconds() { static LARGE_INTEGER freq; static Bool_t first = kTRUE; LARGE_INTEGER count; static Double_t overhead = 0; if (first) { LARGE_INTEGER count0; ::QueryPerformanceFrequency(&freq); ::QueryPerformanceCounter(&count0); if (1) { Double_t dummy; dummy = ((Double_t)count0.QuadPart - overhead)*1000./((Double_t)freq.QuadPart); } ::QueryPerformanceCounter(&count); overhead = (Double_t)count.QuadPart - (Double_t)count0.QuadPart; first = kFALSE; } ::QueryPerformanceCounter(&count); return ((Double_t)count.QuadPart - overhead)*1000./((Double_t)freq.QuadPart); } //////////////////////////////////////////////////////////////////////////////// /// Executes all batched callbacks and the latest callback /// This method is executed by server thread void TGWin32ProxyBase::ExecuteCallBack(Bool_t sync) { // process batched callbacks if (fListOfCallBacks && fListOfCallBacks->GetSize()) { TIter next(fListOfCallBacks); TGWin32CallBackObject *obj; while ((obj = (TGWin32CallBackObject*)next())) { obj->fCallBack(obj->fParam); // execute callback } } if (sync) { if (fCallBack) fCallBack(fParam); ::SetEvent(fPimpl->fEvent); } } //////////////////////////////////////////////////////////////////////////////// /// if sync is kTRUE: /// - post message to main thread. /// - execute callbacks from fListOfCallBacks /// - wait for response /// else /// - add callback to fListOfCallBacks /// /// returns kTRUE if callback execution is delayed (batched) Bool_t TGWin32ProxyBase::ForwardCallBack(Bool_t sync) { Int_t wait = 0; if (!fgMainThreadId) return kFALSE; while (IsGloballyLocked()) { Ping(); #ifdef OLD_THREAD_IMPLEMENTATION if (GetCurrentThreadId() == fgMainThreadId) break; #endif ::SleepEx(10, 1); // take a rest if (!fgMainThreadId) return kFALSE; // server thread terminated } Bool_t batch = !sync && (fListOfCallBacks->GetSize() < fBatchLimit); // if it is a call to gVirtualX and comes from a secondary thread, // delay it and process it via the main thread (to avoid deadlocks). if (!fgUserThreadId && fIsVirtualX && (GetCurrentThreadId() != fgMainThreadId) && (fListOfCallBacks->GetSize() < fBatchLimit)) batch = kTRUE; if (batch) { fListOfCallBacks->Add(new TGWin32CallBackObject(fCallBack, fParam)); return kTRUE; } while (!::PostThreadMessage(fgMainThreadId, fgPostMessageId, (WPARAM)this, 0L)) { // wait because there is a chance that message queue does not exist yet ::SleepEx(50, 1); if (wait++ > 5) return kFALSE; // failed to post } #ifdef OLD_THREAD_IMPLEMENTATION Int_t cnt = 0; //VO attempt counters #endif // limiting wait time DWORD res = WAIT_TIMEOUT; while (res == WAIT_TIMEOUT) { res = ::WaitForSingleObject(fPimpl->fEvent, 100); #ifdef OLD_THREAD_IMPLEMENTATION if ((GetCurrentThreadId() == fgMainThreadId) || (!gROOT->IsLineProcessing() && IsGloballyLocked())) { break; } if (cnt++ > 20) break; // VO after some efforts go out from loop #endif } ::ResetEvent(fPimpl->fEvent); if (res == WAIT_TIMEOUT) { // server thread is blocked GlobalLock(); return kTRUE; } fListOfCallBacks->Delete(); return kFALSE; } //////////////////////////////////////////////////////////////////////////////// /// Check the status of the lock. Bool_t TGWin32ProxyBase::IsGloballyLocked() { return fgLock; } //////////////////////////////////////////////////////////////////////////////// /// send exit message to server thread void TGWin32ProxyBase::SendExitMessage() { ::PostThreadMessage(fgMainThreadId, WM_QUIT, 0, 0L); }