1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_framework.hxx"
30 
31 //_________________________________________________________________________________________________________________
32 //	my own includes
33 //_________________________________________________________________________________________________________________
34 #include <threadhelp/transactionmanager.hxx>
35 #include <threadhelp/resetableguard.hxx>
36 #include <macros/debug.hxx>
37 
38 #include <macros/generic.hxx>
39 #include <fwidllapi.h>
40 
41 //_________________________________________________________________________________________________________________
42 //	interface includes
43 //_________________________________________________________________________________________________________________
44 #include <com/sun/star/lang/DisposedException.hpp>
45 //_________________________________________________________________________________________________________________
46 //	other includes
47 //_________________________________________________________________________________________________________________
48 
49 //_________________________________________________________________________________________________________________
50 //	const
51 //_________________________________________________________________________________________________________________
52 
53 //_________________________________________________________________________________________________________________
54 //	namespace
55 //_________________________________________________________________________________________________________________
56 
57 namespace framework{
58 
59 //_________________________________________________________________________________________________________________
60 //	non exported const
61 //_________________________________________________________________________________________________________________
62 
63 //_________________________________________________________________________________________________________________
64 //	non exported declarations
65 //_________________________________________________________________________________________________________________
66 
67 //_________________________________________________________________________________________________________________
68 //	definitions
69 //_________________________________________________________________________________________________________________
70 
71 /*-************************************************************************************************************//**
72     @short      standard ctor
73     @descr      Initialize instance with right start values for correct working.
74 
75     @seealso    -
76 
77     @param      -
78     @return     -
79 
80     @onerror    -
81 *//*-*************************************************************************************************************/
82 TransactionManager::TransactionManager()
83     : m_eWorkingMode      ( E_INIT )
84     , m_nTransactionCount ( 0      )
85 {
86     m_aBarrier.open();
87 }
88 
89 /*-************************************************************************************************************//**
90     @short      standard dtor
91     @descr      -
92 
93     @seealso    -
94 
95     @param      -
96     @return     -
97 
98     @onerror    -
99 *//*-*************************************************************************************************************/
100 TransactionManager::~TransactionManager()
101 {
102 }
103 
104 /*-****************************************************************************************************//**
105     @interface  ITransactionManager
106     @short      set new working mode
107     @descr      These implementation knows for states of working: E_INIT, E_WORK, E_CLOSING, E_CLOSE
108                 You can step during this ones only from the left to the right side and start at left side again!
109                 (This is neccessary e.g. for refcounted objects!)
110                 This call will block till all current existing transactions was finished.
111                 Follow results occure:
112                     E_INIT        :  All requests on this implementation are refused.
113                                         It's your decision to react in a right way.
114 
115                     E_WORK        :  The object can work now. The full functionality is available.
116 
117                     E_BEFORECLOSE :  The object start the closing mechanism ... but sometimes
118                                         e.g. the dispose() method need to call some private methods.
119                                         These some special methods should use E_SOFTEXCEPTIONS or ignore
120                                         E_INCLOSE as returned reason for E_NOEXCEPTIONS to detect this special case!
121 
122                     E_CLOSE       :  Object is already dead! All further requests will be refused.
123                                         It's your decision to react in a right way.
124 
125     @seealso    -
126 
127     @param      "eMode", is the new mode - but we don't accept setting mode in wrong order!
128     @return     -
129 
130     @onerror    We do nothing.
131 *//*-*****************************************************************************************************/
132 void  TransactionManager::setWorkingMode( EWorkingMode eMode )
133 {
134     // Safe member access.
135     ::osl::ClearableMutexGuard  aAccessGuard( m_aAccessLock );
136     sal_Bool                    bWaitFor    = sal_False      ;
137     // Change working mode first!
138     if  (
139             ( m_eWorkingMode == E_INIT        && eMode == E_WORK        ) ||
140             ( m_eWorkingMode == E_WORK        && eMode == E_BEFORECLOSE ) ||
141             ( m_eWorkingMode == E_BEFORECLOSE && eMode == E_CLOSE       ) ||
142             ( m_eWorkingMode == E_CLOSE       && eMode == E_INIT        )
143         )
144     {
145         m_eWorkingMode = eMode;
146         if( m_eWorkingMode == E_BEFORECLOSE || m_eWorkingMode == E_CLOSE )
147         {
148             bWaitFor = sal_True;
149         }
150     }
151 
152     // Wait for current existing transactions then!
153     // (Only neccessary for changing to E_BEFORECLOSE or E_CLOSE! ...
154     // otherwise; if you wait at setting E_WORK another thrad could finish a acquire-call during our unlock() and wait() call
155     // ... and we will wait forever here!!!)
156     // Don't forget to release access mutex before.
157     aAccessGuard.clear();
158     if( bWaitFor == sal_True )
159     {
160         m_aBarrier.wait();
161     }
162 }
163 
164 /*-****************************************************************************************************//**
165     @interface  ITransactionManager
166     @short      get current working mode
167     @descr      If you stand in your close() or init() method ... but don't know
168                 if you called more then ones(!) ... you can use this function to get
169                 right information.
170                 e.g:    You have a method init() which is used to change working mode from
171                         E_INIT to E_WORK and should be used to initialize some member too ...
172                         What should you do:
173 
174                             void init( sal_Int32 nValue )
175                             {
176                                 // Reject this call if our transaction manager say: "Object already initialized!"
177                                 // Otherwise initialize your member.
178                                 if( m_aTransactionManager.getWorkingMode() == E_INIT )
179                                 {
180                                     // Object is uninitialized ...
181                                     // Make member access threadsafe!
182                                     ResetableGuard aGuard( m_aMutex );
183 
184                                     // Check working mode again .. because anozï¿œther instance could be faster.
185                                     // (It's possible to set this guard at first of this method too!)
186                                     if( m_aTransactionManager.getWorkingMode() == E_INIT )
187                                     {
188                                         m_aMember = nValue;
189 
190                                         // Object is initialized now ... set working mode to E_WORK!
191                                         m_aTransactionManager.setWorkingMode( E_WORK );
192                                     }
193                                 }
194                             }
195 
196     @seealso    method setWorkingMode()
197 
198     @param      -
199     @return     Current set mode.
200 
201     @onerror    No error should occure.
202 *//*-*****************************************************************************************************/
203 EWorkingMode TransactionManager::getWorkingMode() const
204 {
205     // Synchronize access to internal member!
206     ::osl::MutexGuard aAccessLock( m_aAccessLock );
207     return m_eWorkingMode;
208 }
209 
210 /*-****************************************************************************************************//**
211     @interface  ITransactionManager
212     @short      start new transaction
213     @descr      A guard should use this method to start a new transaction. He should looks for rejected
214                 calls to by using parameter eMode and eReason.
215                 If call was not rejected your transaction will be non breakable during releasing your transaction
216                 guard! BUT ... your code isn't threadsafe then! It's a transaction manager only ....
217 
218     @seealso    method unregisterTransaction()
219 
220     @param      "eMode"     ,used to enable/disable throwing exceptions automaticly for rejected calls
221     @param      "eReason"   ,reason for rejected calls if eMode=E_NOEXCEPTIONS
222     @return     -
223 
224     @onerror    -
225 *//*-*****************************************************************************************************/
226 void  TransactionManager::registerTransaction( EExceptionMode eMode, ERejectReason& eReason ) throw( css::uno::RuntimeException, css::lang::DisposedException )
227 {
228     // Look for rejected calls first.
229     // If call was refused we throw some exceptions or do nothing!
230     // It depends from given parameter eMode.
231     if( isCallRejected( eReason ) == sal_True )
232     {
233         impl_throwExceptions( eMode, eReason );
234     }
235 
236     // BUT if no exception was thrown ... (may be eMode = E_SOFTEXCEPTIONS!)
237     // we must register this transaction too!
238     // Don't use "else" or a new scope here!!!
239 
240     // Safe access to internal member.
241     ::osl::MutexGuard aAccessGuard( m_aAccessLock );
242 
243     #ifdef ENABLE_MUTEXDEBUG
244     LOG_ASSERT2( m_nTransactionCount<0, "TransactionManager::acquire()", "Wrong ref count detected!" )
245     #endif
246 
247     // Register this new transaction.
248     // If it is the first one .. close gate to disable changing of working mode.
249     ++m_nTransactionCount;
250     if( m_nTransactionCount == 1 )
251     {
252         m_aBarrier.close();
253     }
254 }
255 
256 /*-****************************************************************************************************//**
257     @interface  ITransactionManager
258     @short      finish transaction
259     @descr      A guard should call this method to release current transaction.
260 
261     @seealso    method registerTransaction()
262 
263     @param      -
264     @return     -
265 
266     @onerror    -
267 *//*-*****************************************************************************************************/
268 void  TransactionManager::unregisterTransaction() throw( css::uno::RuntimeException, css::lang::DisposedException )
269 {
270     // This call could not rejected!
271     // Safe access to internal member.
272     ::osl::MutexGuard aAccessGuard( m_aAccessLock );
273 
274     #ifdef ENABLE_MUTEXDEBUG
275     LOG_ASSERT2( m_nTransactionCount<=0, "TransactionManager::release()", "Wrong ref count detected!" )
276     #endif
277 
278     // Deregister this transaction.
279     // If it was the last one ... open gate to enable changing of working mode!
280     // (see setWorkingMode())
281 
282     --m_nTransactionCount;
283     if( m_nTransactionCount == 0 )
284     {
285         m_aBarrier.open();
286     }
287 }
288 
289 /*-****************************************************************************************************//**
290     @interface  ITransactionManager
291     @short      look for rejected calls
292     @descr      Sometimes user need a possibility to get information about rejected calls
293                 without starting a transaction!
294 
295     @seealso    -
296 
297     @param      "eReason" returns reason of a rejected call
298     @return     true if call was rejected, false otherwise
299 
300     @onerror    We return false.
301 *//*-*****************************************************************************************************/
302 sal_Bool  TransactionManager::isCallRejected( ERejectReason& eReason ) const
303 {
304     // This call must safe access to internal member only.
305     // Set "possible reason" for return and check reject-state then!
306     // User should look for return value first - reason then ...
307     ::osl::MutexGuard aAccessGuard( m_aAccessLock );
308     switch( m_eWorkingMode )
309     {
310         case E_INIT        : eReason = E_UNINITIALIZED ;
311                                 break;
312         case E_WORK        : eReason = E_NOREASON      ;
313                                 break;
314         case E_BEFORECLOSE : eReason = E_INCLOSE       ;
315                                 break;
316         case E_CLOSE       : eReason = E_CLOSED        ;
317                                 break;
318     }
319     return( eReason!=E_NOREASON );
320 }
321 
322 /*-****************************************************************************************************//**
323     @short      throw any exceptions for rejected calls
324     @descr      If user whish to use our automaticly exception mode we use this impl-method.
325                 We check all combinations of eReason and eExceptionMode and throw right exception with some
326                 descriptions for recipient of it.
327 
328     @seealso    method registerTransaction()
329     @seealso    enum ERejectReason
330     @seealso    enum EExceptionMode
331 
332     @param      "eReason" , reason for rejected call
333     @param      "eMode"   , exception mode - set by user
334     @return     -
335 
336     @onerror    -
337 *//*-*****************************************************************************************************/
338 void TransactionManager::impl_throwExceptions( EExceptionMode eMode, ERejectReason eReason ) const throw( css::uno::RuntimeException, css::lang::DisposedException )
339 {
340     if( eMode != E_NOEXCEPTIONS )
341     {
342         switch( eReason )
343         {
344             case E_UNINITIALIZED   :    if( eMode == E_HARDEXCEPTIONS )
345                                         {
346                                             // Help programmer to find out, why this exception is thrown!
347                                             LOG_ERROR( "TransactionManager...", "Owner instance not right initialized yet. Call was rejected! Normaly it's an algorithm error ... wrong usin of class!" )
348                                             //ATTENTION: temp. disabled - till all bad code positions are detected and changed! */
349                                             // throw css::uno::RuntimeException( DECLARE_ASCII("TransactionManager...\nOwner instance not right initialized yet. Call was rejected! Normaly it's an algorithm error ... wrong usin of class!\n" ), css::uno::Reference< css::uno::XInterface >() );
350                                         }
351                                         break;
352             case E_INCLOSE         :    if( eMode == E_HARDEXCEPTIONS )
353                                         {
354                                             // Help programmer to find out, why this exception is thrown!
355                                             LOG_ERROR( "TransactionManager...", "Owner instance stand in close method. Call was rejected!" )
356                                             throw css::lang::DisposedException( DECLARE_ASCII("TransactionManager...\nOwner instance stand in close method. Call was rejected!\n" ), css::uno::Reference< css::uno::XInterface >() );
357                                         }
358                                         break;
359             case E_CLOSED           :   {
360                                             // Help programmer to find out, why this exception is thrown!
361                                             LOG_ERROR( "TransactionManager...", "Owner instance already closed. Call was rejected!" )
362                                             throw css::lang::DisposedException( DECLARE_ASCII("TransactionManager...\nOwner instance already closed. Call was rejected!\n" ), css::uno::Reference< css::uno::XInterface >() );
363                                         }
364             case E_NOREASON         :   {
365                                             // Help programmer to find out
366                                             LOG_ERROR( "TransactionManager...", "Impossible case E_NOREASON!" )
367                                         }
368                                         break;
369             default:                    break; // nothing to do
370         }
371     }
372 }
373 
374 }	//	namespace framework
375