Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGWin32ProxyBase.cxx
Go to the documentation of this file.
1 // @(#)root/win32gdk:$Id$
2 // Author: Valeriy Onuchin 08/08/2003
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2001, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /** \class TGWin32ProxyBase
13 \ingroup win32
14 
15 Proxy classes provide thread-safe interface to global objects.
16 
17 For example: TGWin32VirtualXProxy (to gVirtualX).
18 
19 Proxy object creates callback object and posts a windows message to
20 "processing thread". When windows message is received callback
21 ("real method") is executed.
22 
23 For example:
24  gVirtualX->ClearWindow()
25 
26  - callback object created containing pointer to function
27  corresponding TGWin32::ClearWindow() method
28  - message to "processing thread" (main thread) is posted
29  - TGWin32::ClearWindow() method is executed inside main thread
30  - thread containing gVirtualX proxy object waits for reply
31  from main thread that TGWin32::ClearWindow() is completed.
32 
33 Howto create proxy class:
34 
35  1. Naming.
36  name of proxy = TGWin32 + the name of "virtual base class" + Proxy
37 
38  e.g. TGWin32VirtualXProxy = TGWin32 + VirtualX + Proxy
39 
40  2. Definition of global object
41  As example check definition and implementation of
42  gVirtualX global object
43 
44  3. Class definition.
45  proxy class must be inherited from "virtual base class" and
46  TGWin32ProxyBase class. For example:
47 
48  class TGWin32VirtualX : public TVirtualX , public TGWin32ProxyBase
49 
50  4. Constructors, destructor, extra methods.
51  - constructors and destructor of proxy class do nothing
52  - proxy class must contain two extra static methods
53  RealObject(), ProxyObject(). Each of them return pointer to object
54  of virtual base class.
55 
56  For example:
57  static TVirtualX *RealObject();
58  static TVirtualX *ProxyObject();
59 
60  5. Implementation
61  TGWin32ProxyDefs.h file contains a set of macros which very
62  simplify implementation.
63  - RETURN_PROXY_OBJECT macro implements ProxyObject() method, e.g.
64  RETURN_PROXY_OBJECT(VirtualX)
65  - the names of other macros say about itself.
66 
67  For example:
68  VOID_METHOD_ARG0(VirtualX,SetFillAttributes,1)
69  void TGWin32VirtualXProxy::SetFillAttributes()
70 
71  RETURN_METHOD_ARG0_CONST(VirtualX,Visual_t,GetVisual)
72  Visual_t TGWin32VirtualXProxy::GetVisual() const
73 
74  RETURN_METHOD_ARG2(VirtualX,Int_t,OpenPixmap,UInt_t,w,UInt_t,h)
75  Int_t TGWin32VirtualXProxy::OpenPixmap,UInt_t w,UInt_t h)
76 
77 */
78 
79 
80 #include "Windows4Root.h"
81 #include <windows.h>
82 
83 #include "TGWin32ProxyBase.h"
84 #include "TRefCnt.h"
85 #include "TList.h"
86 #include "TGWin32.h"
87 #include "TROOT.h"
88 
89 ////////////////////////////////////////////////////////////////////////////////
90 class TGWin32CallBackObject : public TObject {
91 public:
92  TGWin32CallBack fCallBack; // callback function (called by GUI thread)
93  void *fParam; // arguments passed to/from callback function
94 
95  TGWin32CallBackObject(TGWin32CallBack cb,void *p):fCallBack(cb),fParam(p) {}
96  ~TGWin32CallBackObject() { if (fParam) delete fParam; }
97 };
98 
99 ////////////////////////////////////////////////////////////////////////////////
100 class TGWin32ProxyBasePrivate {
101 public:
102  HANDLE fEvent; // event used for syncronization
103  TGWin32ProxyBasePrivate();
104  ~TGWin32ProxyBasePrivate();
105 };
106 
107 ////////////////////////////////////////////////////////////////////////////////
108 /// ctor
109 
110 TGWin32ProxyBasePrivate::TGWin32ProxyBasePrivate()
111 {
112  fEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
113 }
114 
115 ////////////////////////////////////////////////////////////////////////////////
116 /// dtor
117 
118 TGWin32ProxyBasePrivate::~TGWin32ProxyBasePrivate()
119 {
120  if (fEvent) ::CloseHandle(fEvent);
121  fEvent = 0;
122 }
123 
124 
125 ULong_t TGWin32ProxyBase::fgPostMessageId = 0;
126 ULong_t TGWin32ProxyBase::fgPingMessageId = 0;
127 ULong_t TGWin32ProxyBase::fgMainThreadId = 0;
128 ULong_t TGWin32ProxyBase::fgUserThreadId = 0;
129 Long_t TGWin32ProxyBase::fgLock = 0;
130 UInt_t TGWin32ProxyBase::fMaxResponseTime = 0;
131 
132 ////////////////////////////////////////////////////////////////////////////////
133 ////////////////////////////////////////////////////////////////////////////////
134 /// ctor
135 
136 TGWin32ProxyBase::TGWin32ProxyBase()
137 {
138  fIsVirtualX = kFALSE;
139  fCallBack = 0;
140  fParam = 0;
141  fListOfCallBacks = new TList();
142  fBatchLimit = 100;
143  fId = ::GetCurrentThreadId();
144  fPimpl = new TGWin32ProxyBasePrivate();
145 
146  if (!fgPostMessageId) fgPostMessageId = ::RegisterWindowMessage("TGWin32ProxyBase::Post");
147  if (!fgPingMessageId) fgPingMessageId = ::RegisterWindowMessage("TGWin32ProxyBase::Ping");
148 }
149 
150 ////////////////////////////////////////////////////////////////////////////////
151 /// dtor
152 
153 TGWin32ProxyBase::~TGWin32ProxyBase()
154 {
155  fListOfCallBacks->Delete();
156  delete fListOfCallBacks;
157  fListOfCallBacks = 0;
158 
159  delete fPimpl;
160 }
161 
162 ////////////////////////////////////////////////////////////////////////////////
163 /// enter critical section
164 
165 void TGWin32ProxyBase::Lock()
166 {
167  TGWin32::Lock();
168 }
169 
170 ////////////////////////////////////////////////////////////////////////////////
171 /// leave critical section
172 
173 void TGWin32ProxyBase::Unlock()
174 {
175  TGWin32::Unlock();
176 }
177 
178 ////////////////////////////////////////////////////////////////////////////////
179 /// lock any proxy (client thread)
180 
181 void TGWin32ProxyBase::GlobalLock()
182 {
183  if (IsGloballyLocked()) return;
184  ::InterlockedIncrement(&fgLock);
185 }
186 
187 ////////////////////////////////////////////////////////////////////////////////
188 /// unlock any proxy (client thread)
189 
190 void TGWin32ProxyBase::GlobalUnlock()
191 {
192  if (!IsGloballyLocked()) return;
193  ::InterlockedDecrement(&fgLock);
194 }
195 
196 ////////////////////////////////////////////////////////////////////////////////
197 /// send ping messsage to server thread
198 
199 Bool_t TGWin32ProxyBase::Ping()
200 {
201  return ::PostThreadMessage(fgMainThreadId, fgPingMessageId, (WPARAM)0, 0L);
202 }
203 
204 ////////////////////////////////////////////////////////////////////////////////
205 /// returns elapsed time in milliseconds with microseconds precision
206 
207 Double_t TGWin32ProxyBase::GetMilliSeconds()
208 {
209  static LARGE_INTEGER freq;
210  static Bool_t first = kTRUE;
211  LARGE_INTEGER count;
212  static Double_t overhead = 0;
213 
214  if (first) {
215  LARGE_INTEGER count0;
216  ::QueryPerformanceFrequency(&freq);
217  ::QueryPerformanceCounter(&count0);
218  if (1) {
219  Double_t dummy;
220  dummy = ((Double_t)count0.QuadPart - overhead)*1000./((Double_t)freq.QuadPart);
221  }
222  ::QueryPerformanceCounter(&count);
223  overhead = (Double_t)count.QuadPart - (Double_t)count0.QuadPart;
224  first = kFALSE;
225  }
226 
227  ::QueryPerformanceCounter(&count);
228  return ((Double_t)count.QuadPart - overhead)*1000./((Double_t)freq.QuadPart);
229 }
230 
231 ////////////////////////////////////////////////////////////////////////////////
232 /// Executes all batched callbacks and the latest callback
233 /// This method is executed by server thread
234 
235 void TGWin32ProxyBase::ExecuteCallBack(Bool_t sync)
236 {
237  // process batched callbacks
238  if (fListOfCallBacks && fListOfCallBacks->GetSize()) {
239  TIter next(fListOfCallBacks);
240  TGWin32CallBackObject *obj;
241 
242  while ((obj = (TGWin32CallBackObject*)next())) {
243  obj->fCallBack(obj->fParam); // execute callback
244  }
245  }
246  if (sync) {
247  if (fCallBack) fCallBack(fParam);
248  ::SetEvent(fPimpl->fEvent);
249  }
250 }
251 
252 ////////////////////////////////////////////////////////////////////////////////
253 /// if sync is kTRUE:
254 /// - post message to main thread.
255 /// - execute callbacks from fListOfCallBacks
256 /// - wait for response
257 /// else
258 /// - add callback to fListOfCallBacks
259 ///
260 /// returns kTRUE if callback execution is delayed (batched)
261 
262 Bool_t TGWin32ProxyBase::ForwardCallBack(Bool_t sync)
263 {
264  Int_t wait = 0;
265 
266  if (!fgMainThreadId) return kFALSE;
267 
268  while (IsGloballyLocked()) {
269  Ping();
270 #ifdef OLD_THREAD_IMPLEMENTATION
271  if (GetCurrentThreadId() == fgMainThreadId)
272  break;
273 #endif
274  ::SleepEx(10, 1); // take a rest
275  if (!fgMainThreadId) return kFALSE; // server thread terminated
276  }
277 
278  Bool_t batch = !sync && (fListOfCallBacks->GetSize() < fBatchLimit);
279 
280  // if it is a call to gVirtualX and comes from a secondary thread,
281  // delay it and process it via the main thread (to avoid deadlocks).
282  if (!fgUserThreadId && fIsVirtualX &&
283  (GetCurrentThreadId() != fgMainThreadId) &&
284  (fListOfCallBacks->GetSize() < fBatchLimit))
285  batch = kTRUE;
286 
287  if (batch) {
288  fListOfCallBacks->Add(new TGWin32CallBackObject(fCallBack, fParam));
289  return kTRUE;
290  }
291 
292  while (!::PostThreadMessage(fgMainThreadId, fgPostMessageId, (WPARAM)this, 0L)) {
293  // wait because there is a chance that message queue does not exist yet
294  ::SleepEx(50, 1);
295  if (wait++ > 5) return kFALSE; // failed to post
296  }
297 
298 #ifdef OLD_THREAD_IMPLEMENTATION
299  Int_t cnt = 0; //VO attempt counters
300 #endif
301  // limiting wait time
302  DWORD res = WAIT_TIMEOUT;
303  while (res == WAIT_TIMEOUT) {
304  res = ::WaitForSingleObject(fPimpl->fEvent, 100);
305 #ifdef OLD_THREAD_IMPLEMENTATION
306  if ((GetCurrentThreadId() == fgMainThreadId) ||
307  (!gROOT->IsLineProcessing() && IsGloballyLocked())) {
308  break;
309  }
310  if (cnt++ > 20) break; // VO after some efforts go out from loop
311 #endif
312  }
313  ::ResetEvent(fPimpl->fEvent);
314 
315  if (res == WAIT_TIMEOUT) { // server thread is blocked
316  GlobalLock();
317  return kTRUE;
318  }
319 
320  fListOfCallBacks->Delete();
321  return kFALSE;
322 }
323 
324 ////////////////////////////////////////////////////////////////////////////////
325 /// Check the status of the lock.
326 
327 Bool_t TGWin32ProxyBase::IsGloballyLocked()
328 {
329  return fgLock;
330 }
331 
332 ////////////////////////////////////////////////////////////////////////////////
333 /// send exit message to server thread
334 
335 void TGWin32ProxyBase::SendExitMessage()
336 {
337  ::PostThreadMessage(fgMainThreadId, WM_QUIT, 0, 0L);
338 }
339