Salome HOME
60a5dc254ee790643fc37421f624733cfc5abc05
[modules/gui.git] / src / Event / SALOME_Event.cxx
1 //  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 //  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 //  This library is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU Lesser General Public
8 //  License as published by the Free Software Foundation; either
9 //  version 2.1 of the License.
10 //
11 //  This library is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 //  Lesser General Public License for more details.
15 //
16 //  You should have received a copy of the GNU Lesser General Public
17 //  License along with this library; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 //  KERNEL SALOME_Event : Define event posting mechanism
23 //  File   : SALOME_Event.cxx
24 //  Author : Sergey ANIKIN
25 //
26 #include "SALOME_Event.h"
27
28 #include <QSemaphore>
29 #include <QApplication>
30
31 // asv 21.02.05 : introducing multi-platform approach of thread comparison
32 // - on Unix using pthread_t type for storing ThreadId
33 // - on Win32 using integer type for storing ThreadId
34 // NOT using integer ThreadId on both Unix and Win32 because (from documentation):
35 // "...Do not allow your program to rely on the internal structure or size of the pthread_t..."
36
37 #ifdef WIN32
38 #include <windows.h>
39 static DWORD myThread;
40 #else
41 #include <pthread.h>
42 static pthread_t myThread;
43 #endif
44
45 /*!
46   \class InitEvent
47   \brief Helper event class responsible for initializing SALOME_Event
48   mechanism by the main thread ID
49  */
50 class InitEvent : public SALOME_Event
51 {
52 public:
53   InitEvent();
54   virtual      ~InitEvent();
55   virtual void Execute();
56 };
57
58 /*!
59   \brief Constructor, initializes the event mechanism by the current thread ID.
60   It is asssumed to be the main thread ID, so be careful!
61 */
62 InitEvent::InitEvent()
63 {
64   GetSessionThread();
65 }
66
67 /*!
68   \brief Destructor, does nothing.
69 */
70 InitEvent::~InitEvent()
71 {
72 }
73
74 /*!
75   \brief Nothing to be executed for this kind of event.
76 */
77 void InitEvent::Execute()
78 {
79 }
80
81 // NOTE: Here the SALOME event mechanism is initalized by the 
82 // current thread ID that is always assumed to be the main thread ID.
83 // This should be revised as soon as the application library is no longer
84 // linked against the Event library (i.e. this static object is not created or created 
85 // outside the main thread).
86 static InitEvent myInitEvent;
87
88 /*!
89   \class SALOME_CustomEvent
90   \brief Generic event class for user-defined events
91   
92   This class contains a generic void* data member that may be used
93   for transferring event-specific data to the receiver.
94
95   \warning The internal data is not destroyed by the class destructor.
96 */
97
98 /*!
99   \brief Constructor.
100   \param type event type
101 */
102 SALOME_CustomEvent::SALOME_CustomEvent( int type )
103 : QEvent( (QEvent::Type)type ), d( 0 )
104 {
105 }
106
107 /*!
108   \brief Constructor.
109   \param type event type
110   \param data custom data
111 */
112 SALOME_CustomEvent::SALOME_CustomEvent( QEvent::Type type, void* data )
113 : QEvent( type ), d( data )
114 {
115 }
116
117 /*!
118   \brief Get custom data.
119   \return pointer to the internal data
120 */
121 void* SALOME_CustomEvent::data() const
122 {
123   return d;
124 }
125
126 /*!
127   \brief Set custom data.
128   \param data pointer to the internal data
129 */
130 void SALOME_CustomEvent::setData( void* data )
131 {
132   d = data;
133 }
134
135 /*!
136   \class SALOME_Event
137   \brief The class which encapsulates data and functionality required for 
138          posting component-specific events to perform arbitrary operations 
139          in the main GUI thread. 
140
141   SALOME_Event objects can be posted by any thread belonging to the GUI process.
142   
143   It is necessary to derive a custom event class from SALOME_Event and 
144   re-implement virtual Execute() method. This method should actually perform 
145   the desirable operation. To pass all the required data to Execute() and 
146   store a return value, arbitrary data fields can be added to the custom 
147   event class. There is no need to protect such fields with a mutex, for only
148   one thread working with a SALOME_Event object is active at any moment.
149   
150   Usage:
151   - Create SALOME_Event. Components can derive their own event class from 
152   SALOME_Event in order to pass custom data to the event handler.
153   - Call process() method to post the event. After process() execution
154   it is possible to examine fields of your custom event object.
155   - Perform delete operator on the event to wake up the desktop (you can also 
156   set <autoRelease>  parameter to \c true to automatically wake up desktop after 
157   process().
158   
159   The method processed() is used by the desktop to signal that event processing 
160   has been completed.
161
162   To make all this work, it is necessary to call static method GetSessionThread()
163   during the application initialization, i.e. from main() function.
164   It is important to call this method from the primary application thread.
165
166   Caveats: 
167   - there are no.
168 */
169
170 //! Total number of semaphore resources
171 const int NumberOfResources = 2;
172
173 /*!
174   \brief Initialize event mechanism.
175
176   This function sets up the main application thread. It should be called
177   during the application initialization, i.e. main() function.
178 */
179 void SALOME_Event::GetSessionThread(){
180 #ifdef WIN32
181   myThread = ::GetCurrentThreadId();
182 #else
183   myThread = pthread_self();
184 #endif
185 }
186
187 /*!
188   \brief Check if the processing is in the main application thread.
189   \return \c true if this method is called from the main application thread
190 */
191 bool SALOME_Event::IsSessionThread(){
192   bool aResult = false;
193 #ifdef WIN32
194   aResult = myThread == ::GetCurrentThreadId();
195 #else
196   aResult = myThread == pthread_self();
197 #endif
198   return aResult;
199 }
200
201 /*!
202   \brief Constructor.
203 */
204 SALOME_Event::SALOME_Event(){
205   // Prepare the semaphore 
206   mySemaphore = new QSemaphore( NumberOfResources );
207   mySemaphore->acquire( NumberOfResources );
208 }
209
210 /*!
211   \brief Destructor.
212 */
213 SALOME_Event::~SALOME_Event(){
214   if ( mySemaphore->available() < NumberOfResources )
215     mySemaphore->release( NumberOfResources - mySemaphore->available() );
216   delete mySemaphore;
217 }
218
219 /*!
220   \brief This method should be called by the main GUI thread
221   in order to execute the code specific for this event and finally
222   to inform the calling thread that the event 
223   has been processed waking it up with help of the semaphore .
224  */
225 void SALOME_Event::ExecutePostedEvent()
226 {
227   // Diagnose incorrect usage of SALOME_Event API
228   if ( !IsSessionThread() ){
229     qWarning( "SALOME_Event::ExecutePostedEvent() is called from a secondary thread that might mean an error in application logic!" );
230   }
231   // Actual execution specific for particular kind of event
232   Execute();
233   // Signal the calling thread that the event has been processed
234   processed();
235 }
236
237 /*!
238   \brief Post the event and wait for its completion.
239   process() should be called from a secondary thread only. 
240   \sa processed()
241 */
242 void SALOME_Event::process()
243 {
244   // Diagnose incorrect usage of SALOME_Event API
245   if ( IsSessionThread() ){
246     qWarning( "SALOME_Event::process() is called from the main GUI thread that might mean an error in application logic!" );
247   }
248
249   QApplication::postEvent( qApp, new SALOME_CustomEvent( SALOME_EVENT, (void*)this ) );
250   mySemaphore->acquire( 1 );
251 }
252
253 /*!
254   \brief Use this method to signal that this event has been processed.
255 */
256 void SALOME_Event::processed()
257 {
258   mySemaphore->release( 1 );
259 }
260
261 /*!
262   \fn virtual void SALOME_Event::Execute();
263   \brief This method should be redefined in the successor classes
264          to do real work.
265 */
266   
267 /*!
268   \class TMemFunEvent
269   \brief Template class for event which calls the function
270   without arguments and returning result.
271 */
272
273 /*!
274   \class TVoidMemFunEvent
275   \brief Template class for event which calls the function
276   without arguments and without return value.
277 */
278
279 /*!
280   \class TMemFun1ArgEvent
281   \brief Template class for event which calls the function
282   with one argument and returning result.
283 */
284
285 /*!
286   \class TVoidMemFun1ArgEvent
287   \brief Template class for event which calls the function
288   with one argument and without return value.
289 */
290
291 /*!
292   \class TMemFun2ArgEvent
293   \brief Template class for event which calls the function
294   with two arguments and returning result.
295 */
296
297 /*!
298   \class TVoidMemFun2ArgEvent
299   \brief Template class for event which calls the function
300   with two arguments and without return value.
301 */
302
303 /*!
304   \fn ProcessEvent
305   \brief Template function for processing events with return value.
306 */
307
308 /*!
309   \fn ProcessVoidEvent
310   \brief Template function for processing events without return value.
311 */