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