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