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