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