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/lockhelper.hxx>
35 #include <general.h>
36 #include <macros/debug.hxx>
37 
38 #include <macros/generic.hxx>
39 
40 //_________________________________________________________________________________________________________________
41 //	interface includes
42 //_________________________________________________________________________________________________________________
43 
44 //_________________________________________________________________________________________________________________
45 //	other includes
46 //_________________________________________________________________________________________________________________
47 #include <vos/process.hxx>
48 
49 //_________________________________________________________________________________________________________________
50 //	namespace
51 //_________________________________________________________________________________________________________________
52 
53 namespace framework{
54 
55 //_________________________________________________________________________________________________________________
56 //	const
57 //_________________________________________________________________________________________________________________
58 
59 //_________________________________________________________________________________________________________________
60 //	declarations
61 //_________________________________________________________________________________________________________________
62 
63 /*-************************************************************************************************************//**
64     @short      use ctor to initialize instance
65     @descr      We must initialize our member "m_eLockType". This value specify handling of locking.
66                 User use this helper as parameter for a guard creation.
67                 These guard use "m_eLockType" to set lock in the right way by using right mutex or rw-lock.
68 
69     @seealso    enum ELockType
70     @seealso    class ReadGuard
71     @seealso    class WriteGuard
72 
73     @param      "rSolarMutex", for some components we must be "vcl-free"! So we can't work with our solar mutex
74                                 directly. User must set his reference at this instance - so we can work with it!
75     @return     -
76 
77     @onerror    -
78 *//*-*************************************************************************************************************/
79 LockHelper::LockHelper( ::vos::IMutex* pSolarMutex )
80     :   m_pFairRWLock       ( NULL )
81     ,   m_pOwnMutex         ( NULL )
82     ,   m_pSolarMutex       ( NULL )
83     ,   m_pShareableOslMutex( NULL )
84     ,   m_bDummySolarMutex  ( sal_False )
85 {
86     m_eLockType = implts_getLockType();
87     switch( m_eLockType )
88     {
89         case E_NOTHING      :   break; // There is nothing to do ...
90         case E_OWNMUTEX     :   {
91                                     m_pOwnMutex = new ::osl::Mutex;
92                                 }
93                                 break;
94         case E_SOLARMUTEX   :   {
95                                     if( pSolarMutex == NULL )
96                                     {
97                                         m_pSolarMutex      = new ::vos::OMutex;
98                                         m_bDummySolarMutex = sal_True;
99                                     }
100                                     else
101                                     {
102                                         m_pSolarMutex = pSolarMutex;
103                                     }
104                                 }
105                                 break;
106         case E_FAIRRWLOCK   :   {
107                                     m_pFairRWLock = new FairRWLock;
108                                 }
109                                 break;
110         #ifdef ENABLE_ASSERTIONS
111         default             :   LOG_ASSERT2( m_eLockType!=E_NOTHING, "LockHelper::ctor()", "Invalid lock type found .. so code will not be threadsafe!" )
112         #endif
113     }
114 }
115 
116 /*-************************************************************************************************************//**
117     @short      default dtor to release safed pointer
118     @descr      We have created dynamical mutex- or lock-member ... or we hold a pointer to external objects.
119                 We must release it!
120 
121     @seealso    ctor()
122 
123     @param      -
124     @return     -
125 
126     @onerror    -
127 *//*-*************************************************************************************************************/
128 LockHelper::~LockHelper()
129 {
130     if( m_pShareableOslMutex != NULL )
131     {
132         // Sometimes we hold two pointer to same object!
133         // (e.g. if m_eLockType==E_OWNMUTEX!)
134         // So we should forget it ... but don't delete it twice!
135         if( m_pShareableOslMutex != m_pOwnMutex )
136         {
137             delete m_pShareableOslMutex;
138         }
139         m_pShareableOslMutex = NULL;
140     }
141     if( m_pOwnMutex != NULL )
142     {
143         delete m_pOwnMutex;
144         m_pOwnMutex = NULL;
145     }
146     if( m_pSolarMutex != NULL )
147     {
148         if (m_bDummySolarMutex)
149         {
150             delete static_cast<vos::OMutex*>(m_pSolarMutex);
151             m_bDummySolarMutex = sal_False;
152         }
153         m_pSolarMutex = NULL;
154     }
155     if( m_pFairRWLock != NULL )
156     {
157         delete m_pFairRWLock;
158         m_pFairRWLock = NULL;
159     }
160 }
161 
162 /*-************************************************************************************************************//**
163     @interface  IMutex
164     @short      set an exclusiv lock
165     @descr      We must match this lock call with current set lock type and used lock member.
166                 If a mutex should be used - it will be easy ... but if a rw-lock should be used
167                 we must simulate it as a write access!
168 
169     @attention  If a shareable osl mutex exist, he must be used as twice!
170                 It's neccessary for some cppu-helper classes ...
171 
172     @seealso    method acquireWriteAccess()
173 
174     @param      -
175     @return     -
176 
177     @onerror    -
178 *//*-*************************************************************************************************************/
179 void LockHelper::acquire()
180 {
181     switch( m_eLockType )
182     {
183         case E_NOTHING      :   break; // There is nothing to do ...
184         case E_OWNMUTEX     :   {
185                                     m_pOwnMutex->acquire();
186                                 }
187                                 break;
188         case E_SOLARMUTEX   :   {
189                                     m_pSolarMutex->acquire();
190                                 }
191                                 break;
192         case E_FAIRRWLOCK   :   {
193                                     m_pFairRWLock->acquireWriteAccess();
194                                 }
195                                 break;
196     }
197 }
198 
199 /*-************************************************************************************************************//**
200     @interface  IMutex
201     @short      release exclusiv lock
202     @descr      We must match this unlock call with current set lock type and used lock member.
203                 If a mutex should be used - it will be easy ... but if a rw-lock should be used
204                 we must simulate it as a write access!
205 
206     @attention  If a shareable osl mutex exist, he must be used as twice!
207                 It's neccessary for some cppu-helper classes ...
208 
209     @seealso    method releaseWriteAccess()
210 
211     @param      -
212     @return     -
213 
214     @onerror    -
215 *//*-*************************************************************************************************************/
216 void LockHelper::release()
217 {
218     switch( m_eLockType )
219     {
220         case E_NOTHING      :   break; // There is nothing to do ...
221         case E_OWNMUTEX     :   {
222                                     m_pOwnMutex->release();
223                                 }
224                                 break;
225         case E_SOLARMUTEX   :   {
226                                     m_pSolarMutex->release();
227                                 }
228                                 break;
229         case E_FAIRRWLOCK   :   {
230                                     m_pFairRWLock->releaseWriteAccess();
231                                 }
232                                 break;
233     }
234 }
235 
236 /*-************************************************************************************************************//**
237     @interface  IRWLock
238     @short      set lock for reading
239     @descr      A guard should call this method to acquire read access on your member.
240                 Writing isn't allowed then - but nobody could check it for you!
241                 We use m_eLockType to differ between all possible "lock-member"!!!
242 
243     @attention  If a shareable osl mutex exist, he must be used as twice!
244                 It's neccessary for some cppu-helper classes ...
245 
246     @seealso    method releaseReadAccess()
247 
248     @param      -
249     @return     -
250 
251     @onerror    -
252 *//*-*************************************************************************************************************/
253 void LockHelper::acquireReadAccess()
254 {
255     switch( m_eLockType )
256     {
257         case E_NOTHING      :   break; // There is nothing to do ...
258         case E_OWNMUTEX     :   {
259                                     m_pOwnMutex->acquire();
260                                 }
261                                 break;
262         case E_SOLARMUTEX   :   {
263                                     m_pSolarMutex->acquire();
264                                 }
265                                 break;
266         case E_FAIRRWLOCK   :   {
267                                     m_pFairRWLock->acquireReadAccess();
268                                 }
269                                 break;
270     }
271 }
272 
273 /*-************************************************************************************************************//**
274     @interface  IRWLock
275     @short      reset lock for reading
276     @descr      A guard should call this method to release read access on your member.
277                 We use m_eLockType to differ between all possible "lock-member"!!!
278 
279     @attention  If a shareable osl mutex exist, he must be used as twice!
280                 It's neccessary for some cppu-helper classes ...
281 
282     @seealso    method acquireReadAccess()
283 
284     @param      -
285     @return     -
286 
287     @onerror    -
288 *//*-*************************************************************************************************************/
289 void LockHelper::releaseReadAccess()
290 {
291     switch( m_eLockType )
292     {
293         case E_NOTHING      :   break; // There is nothing to do ...
294         case E_OWNMUTEX     :   {
295                                     m_pOwnMutex->release();
296                                 }
297                                 break;
298         case E_SOLARMUTEX   :   {
299                                     m_pSolarMutex->release();
300                                 }
301                                 break;
302         case E_FAIRRWLOCK   :   {
303                                     m_pFairRWLock->releaseReadAccess();
304                                 }
305                                 break;
306     }
307 }
308 
309 /*-************************************************************************************************************//**
310     @interface  IRWLock
311     @short      set lock for writing
312     @descr      A guard should call this method to acquire write access on your member.
313                 Reading is allowed too - of course.
314                 After successfully calling of this method you are the only writer.
315                 We use m_eLockType to differ between all possible "lock-member"!!!
316 
317     @attention  If a shareable osl mutex exist, he must be used as twice!
318                 It's neccessary for some cppu-helper classes ...
319 
320     @seealso    method releaseWriteAccess()
321 
322     @param      -
323     @return     -
324 
325     @onerror    -
326 *//*-*************************************************************************************************************/
327 void LockHelper::acquireWriteAccess()
328 {
329     switch( m_eLockType )
330     {
331         case E_NOTHING      :   break; // There is nothing to do ...
332         case E_OWNMUTEX     :   {
333                                     m_pOwnMutex->acquire();
334                                 }
335                                 break;
336         case E_SOLARMUTEX   :   {
337                                     m_pSolarMutex->acquire();
338                                 }
339                                 break;
340         case E_FAIRRWLOCK   :   {
341                                     m_pFairRWLock->acquireWriteAccess();
342                                 }
343                                 break;
344     }
345 }
346 
347 /*-************************************************************************************************************//**
348     @interface  IRWLock
349     @short      reset lock for writing
350     @descr      A guard should call this method to release write access on your member.
351                 We use m_eLockType to differ between all possible "lock-member"!!!
352 
353     @attention  If a shareable osl mutex exist, he must be used as twice!
354                 It's neccessary for some cppu-helper classes ...
355 
356     @seealso    method acquireWriteAccess()
357 
358     @param      -
359     @return     -
360 
361     @onerror    -
362 *//*-*************************************************************************************************************/
363 void LockHelper::releaseWriteAccess()
364 {
365     switch( m_eLockType )
366     {
367         case E_NOTHING      :   break; // There is nothing to do ...
368         case E_OWNMUTEX     :   {
369                                     m_pOwnMutex->release();
370                                 }
371                                 break;
372         case E_SOLARMUTEX   :   {
373                                     m_pSolarMutex->release();
374                                 }
375                                 break;
376         case E_FAIRRWLOCK   :   {
377                                     m_pFairRWLock->releaseWriteAccess();
378                                 }
379                                 break;
380     }
381 }
382 
383 /*-************************************************************************************************************//**
384     @interface  IRWLock
385     @short      downgrade a write access to a read access
386     @descr      A guard should call this method to change a write to a read access.
387                 New readers can work too - new writer are blocked!
388                 We use m_eLockType to differ between all possible "lock-member"!!!
389 
390     @attention  Ignore shareable mutex(!) - because this call never should release a lock completly!
391                 We change a write access to a read access only.
392 
393     @attention  a) Don't call this method if you are not a writer!
394                     Results are not defined then ...
395                     An upgrade can't be implemented realy ... because acquiring new access
396                     will be the same - there no differences!
397                 b) Without function if m_eLockTyp is different from E_FAIRRWLOCK(!) ...
398                     because, a mutex don't support it realy.
399 
400     @seealso    -
401 
402     @param      -
403     @return     -
404 
405     @onerror    -
406 *//*-*************************************************************************************************************/
407 void LockHelper::downgradeWriteAccess()
408 {
409     switch( m_eLockType )
410     {
411         case E_NOTHING      :   break; // There is nothing to do ...
412         case E_OWNMUTEX     :   break; // Not supported for mutex!
413         case E_SOLARMUTEX   :   break; // Not supported for mutex!
414         case E_FAIRRWLOCK   :   m_pFairRWLock->downgradeWriteAccess();
415                                 break;
416     }
417 }
418 
419 /*-************************************************************************************************************//**
420     @short      return a reference to a static lock helper
421     @descr      Sometimes we need the global mutex or rw-lock! (e.g. in our own static methods)
422                 But it's not a good idea to use these global one very often ...
423                 Thats why we use this little helper method.
424                 We create our own "class global static" lock.
425                 It will be created at first call only!
426                 All other requests use these created one then directly.
427 
428     @seealso    -
429 
430     @param      -
431     @return     A reference to a static mutex/lock member.
432 
433     @onerror    No error should occure.
434 *//*-*************************************************************************************************************/
435 LockHelper& LockHelper::getGlobalLock( ::vos::IMutex* pSolarMutex )
436 {
437     // Initialize static "member" only for one time!
438     // Algorithm:
439     // a) Start with an invalid lock (NULL pointer)
440     // b) If these method first called (lock not already exist!) ...
441     // c) ... we must create a new one. Protect follow code with the global mutex -
442     //    (It must be - we create a static variable!)
443     // d) Check pointer again - because ... another instance of our class could be faster then these one!
444     // e) Create the new lock and set it for return on static variable.
445     // f) Return new created or already existing lock object.
446     static LockHelper* pLock = NULL;
447     if( pLock == NULL )
448     {
449         ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
450         if( pLock == NULL )
451         {
452             static LockHelper aLock( pSolarMutex );
453             pLock = &aLock;
454         }
455     }
456     return *pLock;
457 }
458 
459 /*-************************************************************************************************************//**
460     @short      return a reference to shared mutex member
461     @descr      Sometimes we need a osl-mutex for sharing with our uno helper ...
462                 What can we do?
463                 a) If we have an initialized "own mutex" ... we can use it!
464                 b) Otherwhise we must use a different mutex member :-(
465                 I HOPE IT WORKS!
466 
467     @seealso    -
468 
469     @param      -
470     @return     A reference to a shared mutex.
471 
472     @onerror    No error should occure.
473 *//*-*************************************************************************************************************/
474 ::osl::Mutex& LockHelper::getShareableOslMutex()
475 {
476     if( m_pShareableOslMutex == NULL )
477     {
478         ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
479         if( m_pShareableOslMutex == NULL )
480         {
481             switch( m_eLockType )
482             {
483                 case E_OWNMUTEX     :   {
484                                             m_pShareableOslMutex = m_pOwnMutex;
485                                         }
486                                         break;
487                 default             :   {
488                                             m_pShareableOslMutex = new ::osl::Mutex;
489                                         }
490                                         break;
491             }
492         }
493     }
494     return *m_pShareableOslMutex;
495 }
496 
497 /*-************************************************************************************************************//**
498     @short      search for right lock type, which should be used by an instance of this struct
499     @descr      We must initialize our member "m_eLockType". This value specify handling of locking.
500                 How we can do that? We search for an environment variable. We do it only for one time ....
501                 because the environment is fix. So we safe this value and use it for all further requests.
502                 If no variable could be found - we use a fallback!
503 
504     @attention  We have numbered all our enum values for ELockType. So we can use it as value of searched
505                 environment variable too!
506 
507     @seealso    enum ELockType
508     @seealso    environment LOCKTYPE
509 
510     @param      -
511     @return     A reference to a created and right initialized lock type!
512 
513     @onerror    We use a fallback!
514 *//*-*************************************************************************************************************/
515 ELockType& LockHelper::implts_getLockType()
516 {
517     // Initialize static "member" only for one time!
518     // Algorithm:
519     // a) Start with an invalid variable (NULL pointer)
520     // b) If these method first called (value not already exist!) ...
521     // c) ... we must create a new one. Protect follow code with the global mutex -
522     //    (It must be - we create a static variable!)
523     // d) Check pointer again - because ... another instance of our class could be faster then these one!
524     // e) Create the new static variable, get value from the environment and set it
525     // f) Return new created or already existing static variable.
526     static ELockType* pType = NULL;
527     if( pType == NULL )
528     {
529         ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
530         if( pType == NULL )
531         {
532             static ELockType eType = FALLBACK_LOCKTYPE;
533 
534             ::vos::OStartupInfo aEnvironment;
535             ::rtl::OUString     sValue      ;
536             if( aEnvironment.getEnvironment( ENVVAR_LOCKTYPE, sValue ) == ::vos::OStartupInfo::E_None )
537             {
538                 eType = (ELockType)(sValue.toInt32());
539             }
540 
541             LOG_LOCKTYPE( FALLBACK_LOCKTYPE, eType )
542 
543             pType = &eType;
544         }
545     }
546     return *pType;
547 }
548 
549 } //  namespace framework
550