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 #ifndef __FRAMEWORK_THREADHELP_FAIRRWLOCK_HXX_
25 #define __FRAMEWORK_THREADHELP_FAIRRWLOCK_HXX_
26 
27 //_________________________________________________________________________________________________________________
28 //	my own includes
29 //_________________________________________________________________________________________________________________
30 
31 #include <threadhelp/inoncopyable.h>
32 #include <threadhelp/irwlock.h>
33 #include <macros/debug.hxx>
34 
35 //_________________________________________________________________________________________________________________
36 //	interface includes
37 //_________________________________________________________________________________________________________________
38 #include <com/sun/star/uno/XInterface.hpp>
39 
40 //_________________________________________________________________________________________________________________
41 //	other includes
42 //_________________________________________________________________________________________________________________
43 #include <osl/mutex.hxx>
44 #include <osl/conditn.hxx>
45 
46 //_________________________________________________________________________________________________________________
47 //	namespace
48 //_________________________________________________________________________________________________________________
49 
50 namespace framework{
51 
52 //_________________________________________________________________________________________________________________
53 //	const
54 //_________________________________________________________________________________________________________________
55 
56 //_________________________________________________________________________________________________________________
57 //	declarations
58 //_________________________________________________________________________________________________________________
59 
60 /*-************************************************************************************************************//**
61 	@short          implement a read/write lock with fairness between read/write accessors
62 	@descr			These implementation never should used as base class! Use it as a member every time.
63 					Use ReadGuard and/or WriteGuard in your methods (which work with these lock)
64 					to make your code threadsafe.
65 					Fair means: All reading or writing threads are synchronized AND serialzed by using one
66 					mutex. For reader this mutex is used to access internal variables of this lock only;
67 					for writer this mutex is used to have an exclusiv access on your class member!
68 					=> It's a multi-reader/single-writer lock, which no preferred accessor.
69 
70 	@implements		IRWlock
71     @base           INonCopyable
72 					IRWLock
73 
74 	@devstatus		ready to use
75 *//*-*************************************************************************************************************/
76 class FairRWLock : public  IRWLock
77                  , private INonCopyable
78 {
79 	//-------------------------------------------------------------------------------------------------------------
80 	//	public methods
81 	//-------------------------------------------------------------------------------------------------------------
82 	public:
83 
84         /*-****************************************************************************************************//**
85             @short      standard ctor
86             @descr      Initialize instance with right start values for correct working.
87                         no reader could exist               =>  m_nReadCount   = 0
88                         don't block first coming writer    =>  m_aWriteCondition.set()
89 
90             @seealso    -
91 
92             @param      -
93             @return     -
94 
95             @onerror    -
96         *//*-*****************************************************************************************************/
97         inline FairRWLock()
98             : m_nReadCount( 0 )
99         {
100             m_aWriteCondition.set();
101         }
102 
103         inline virtual ~FairRWLock()
104         {
105         }
106 
107         /*-****************************************************************************************************//**
108             @interface  IRWLock
109             @short      set lock for reading
110             @descr      A guard should call this method to acquire read access on your member.
111                         Writing isn't allowed then - but nobody could check it for you!
112 
113             @seealso    method releaseReadAccess()
114 
115             @param      -
116             @return     -
117 
118             @onerror    -
119         *//*-*****************************************************************************************************/
120         inline virtual void acquireReadAccess()
121         {
122             // Put call in "SERIALIZE"-queue!
123             // After successful acquiring this mutex we are alone ...
124             ::osl::MutexGuard aSerializeGuard( m_aSerializer );
125 
126             // ... but we should synchronize us with other reader!
127             // May be - they will unregister himself by using releaseReadAccess()!
128             ::osl::MutexGuard aAccessGuard( m_aAccessLock );
129 
130             // Now we must register us as reader by increasing counter.
131             // If this the first writer we must close door for possible writer.
132             // Other reader don't look for this barrier - they work parallel to us!
133             if( m_nReadCount == 0 )
134             {
135                 m_aWriteCondition.reset();
136             }
137             ++m_nReadCount;
138         }
139 
140         /*-****************************************************************************************************//**
141             @interface  IRWLock
142             @short      reset lock for reading
143             @descr      A guard should call this method to release read access on your member.
144 
145             @seealso    method acquireReadAccess()
146 
147             @param      -
148             @return     -
149 
150             @onerror    -
151         *//*-*****************************************************************************************************/
152         inline virtual void releaseReadAccess()
153         {
154             // The access lock is enough at this point
155             // because it's not allowed to wait for all reader or writer here!
156             // That will cause a deadlock!
157             ::osl::MutexGuard aAccessGuard( m_aAccessLock );
158 
159             // Unregister as reader first!
160             // Open writer barrier then if it was the last reader.
161             --m_nReadCount;
162             if( m_nReadCount == 0 )
163             {
164                 m_aWriteCondition.set();
165             }
166         }
167 
168         /*-****************************************************************************************************//**
169             @interface  IRWLock
170             @short      set lock for writing
171             @descr      A guard should call this method to acquire write access on your member.
172                         Reading is allowed too - of course.
173                         After successfully calling of this method you are the only writer.
174 
175             @seealso    method releaseWriteAccess()
176 
177             @param      -
178             @return     -
179 
180             @onerror    -
181         *//*-*****************************************************************************************************/
182         inline virtual void acquireWriteAccess()
183         {
184             // You have to stand in our serialize-queue till all reader
185             // are registered (not for releasing them!) or writer finished their work!
186             // Don't use a guard to do so - because you must hold the mutex till
187             // you call releaseWriteAccess()!
188             // After succesfull acquire you have to wait for current working reader.
189             // Used condition will open by last gone reader object.
190             m_aSerializer.acquire();
191             m_aWriteCondition.wait();
192 
193             #ifdef ENABLE_MUTEXDEBUG
194             // A writer is an exclusiv accessor!
195             LOG_ASSERT2( m_nReadCount!=0, "FairRWLock::acquireWriteAccess()", "No threadsafe code detected ... : Read count != 0!" )
196             #endif
197         }
198 
199         /*-****************************************************************************************************//**
200             @interface  IRWLock
201             @short      reset lock for writing
202             @descr      A guard should call this method to release write access on your member.
203 
204             @seealso    method acquireWriteAccess()
205 
206             @param      -
207             @return     -
208 
209             @onerror    -
210         *//*-*****************************************************************************************************/
211         inline virtual void releaseWriteAccess()
212         {
213             // The only one you have to do here is to release
214             // hold seriliaze-mutex. All other user of these instance are blocked
215             // by these mutex!
216             // You don't need any other mutex here - you are the only one in the moment!
217 
218             #ifdef ENABLE_MUTEXDEBUG
219             // A writer is an exclusiv accessor!
220             LOG_ASSERT2( m_nReadCount!=0, "FairRWLock::releaseWriteAccess()", "No threadsafe code detected ... : Read count != 0!" )
221             #endif
222 
223             m_aSerializer.release();
224         }
225 
226         /*-****************************************************************************************************//**
227             @interface  IRWLock
228             @short      downgrade a write access to a read access
229             @descr      A guard should call this method to change a write to a read access.
230                         New readers can work too - new writer are blocked!
231 
232             @attention  Don't call this method if you are not a writer!
233                         Results are not defined then ...
234                         An upgrade can't be really implemented ... because acquiring new access
235                         will be the same - there no differences!
236 
237             @seealso    -
238 
239             @param      -
240             @return     -
241 
242             @onerror    -
243         *//*-*****************************************************************************************************/
244         inline virtual void downgradeWriteAccess()
245         {
246             // You must be a writer to call this method!
247             // We can't check it - but otherwise it's your problem ...
248             // Thats why you don't need any mutex here.
249 
250             #ifdef ENABLE_MUTEXDEBUG
251             // A writer is an exclusiv accessor!
252             LOG_ASSERT2( m_nReadCount!=0, "FairRWLock::downgradeWriteAccess()", "No threadsafe code detected ... : Read count != 0!" )
253             #endif
254 
255             // Register himself as "new" reader.
256             // This value must be 0 before - because we support single writer access only!
257             ++m_nReadCount;
258             // Close barrier for other writer!
259             // Why?
260             // You hold the serializer mutex - next one can be a reader OR a writer.
261             // They must blocked then - because you will be a reader after this call
262             // and writer use this condition to wait for current reader!
263             m_aWriteCondition.reset();
264             // Open door for next waiting thread in serialize queue!
265             m_aSerializer.release();
266         }
267 
268 	//-------------------------------------------------------------------------------------------------------------
269 	//	private member
270 	//-------------------------------------------------------------------------------------------------------------
271 	private:
272 
273 		::osl::Mutex		m_aAccessLock		;	/// regulate access on internal member of this instance
274 		::osl::Mutex		m_aSerializer		;	/// serialze incoming read/write access threads
275 		::osl::Condition	m_aWriteCondition	;	/// a writer must wait till current working reader are gone
276 		sal_Int32			m_nReadCount		;	/// every reader is registered - the last one open the door for waiting writer
277 
278 };		//	class FairRWLock
279 
280 }		//	namespace framework
281 
282 #endif	//	#ifndef __FRAMEWORK_THREADHELP_FAIRRWLOCK_HXX_
283