1 /*
2  * Copyright (C) 2002 - David W. Durham
3  *
4  * This file is part of ReZound, an audio editing application.
5  *
6  * ReZound is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published
8  * by the Free Software Foundation; either version 2 of the License,
9  * or (at your option) any later version.
10  *
11  * ReZound is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19  */
20 #ifndef __CRWMutex_H__
21 #define __CRWMutex_H__
22 
23 #include "../../config/common.h"
24 
25 /*
26  * This is a multi-platform rwlock wrapper.
27  *
28  * The public interface looks like:
29 
30 	class CRWMutex
31 	{
32 	public:
33 		CRWMutex();
34 		virtual ~CRWMutex() throw();
35 
36 		// read-locks can be obtained recursively with other read-locks, but not with other write-locks
37 		void readLock();
38 		bool tryReadLock();
39 		size_t getReadLockCount() const;
40 
41 		// write-locks can be obtained recursively with other write-locks, but not with other read-locks
42 		void writeLock();
43 		bool tryWriteLock();
44 		bool isLockedForWrite() const;
45 
46 		void unlock();
47 	};
48 
49  *
50  *
51  * A utility class exists for dealing with CRWMutexes
52  * The public interface looks like:
53 
54 	class CRWMutexLocker
55 	{
56 	public:
57 		enum LockTypes
58 		{
59 			ltReader,
60 			ltWriter
61 		};
62 
63 		// construct a locker object with no mutex assigned
64 		CRWMutexLocker();
65 
66 		// construct a locker object with the given mutex.. the mutex is locked/try-locked on construction
67 		CRWMutexLocker(CRWMutex &_m,LockTypes lockType,bool block=true);
68 
69 		// the destructor unlocks the mutex iff it was locked
70 		virtual ~CRWMutexLocker() throw();
71 
72 		// returned whether the mutex lock was obtained by this object (sometimes false if a try-lock occurred)
73 		bool didLock() const;
74 
75 		// unlocks the currently assigned mutex if it was locked, assigns a new mutex to this locked and locks/try-locks the new mutex
76 		// the locked status is returned
77 		bool reassign(CRWMutex &_m,LockTypes lockType,bool block=true);
78 
79 		// unlocks the currently assigned mutex if it was locked and leaves the locker object with no assigned mutex
80 		void unassign();
81 	};
82 
83  */
84 
85 // a skeleton stub for extenting functionality
86 class ARWMutex
87 {
88 public:
89 
90 	ARWMutex() {}
91 	virtual ~ARWMutex() throw() {}
92 
93 	virtual void readLock() {}
94 	virtual bool tryReadLock() { return false; }
95 
96 	virtual void writeLock() {}
97 	virtual bool tryWriteLock() { return false; }
98 
99 	virtual void unlock() {}
100 };
101 
102 #ifdef _WIN32
103 	// *** WIN32 implementation ***
104 	// based on code in the PTypes library: http://www.melikyan.com/ptypes/
105 
106 	#include "CMutex.h"
107 
108 	class CRWMutex : public ARWMutex
109 	{
110 	public:
111 
112 		CRWMutex();
113 		virtual ~CRWMutex() throw();
114 
115 		void readLock();
116 		bool tryReadLock();
117 		size_t getReadLockCount() const { return readcnt+1; }
118 
119 		void writeLock();
120 		bool tryWriteLock();
121 		bool isLockedForWrite() const { return writecnt>0; }
122 
123 		void unlock();
124 
125 	private:
126 		void *reading; // ??? would make this HANDLE, but really don't think I need to include a 30k line header file for it
127 		void *finished; // ??? would make this HANDLE, but really don't think I need to include a 30k line header file for it
128 		int readcnt;
129 		int writecnt;
130 
131 		CMutex wrMutex;
132 	};
133 
134 
135 
136 #else
137 	// *** posix implementation ***
138 
139 	#include <pthread.h>
140 	#include "CAtomicCounter.h"
141 
142 	class CRWMutex : public ARWMutex
143 	{
144 	public:
145 
146 		CRWMutex();
147 		virtual ~CRWMutex() throw();
148 
149 		void readLock();
150 		bool tryReadLock();
151 		size_t getReadLockCount() const { return readLockCount.value(); }
152 
153 		void writeLock();
154 		bool tryWriteLock();
155 		bool isLockedForWrite() const { return writeLockCount.value()>0; }
156 
157 		void unlock();
158 
159 	private:
160 		pthread_rwlock_t rwlock;
161 
162 		// even though only one write lock can be aquired, it can be locked more than once
163 		// if the thread currently holding the write-lock locks again (which is allowed)
164 		mutable CAtomicCounter writeLockCount;
165 		void *writeLockOwner;
166 
167 		mutable CAtomicCounter readLockCount;
168 
169 	};
170 
171 #endif
172 
173 
174 /*
175  * This class simply locks the given mutex on construct and unlocks on destruction
176  * it is useful to use where a lock should be obtained, then released on return or
177  * exception... when an object of this class goes out of scope, the lock will be
178  * released
179  *
180  * The construct can optionally take a second parameter that says not to wait when
181  * locking, but to do a trylock.  If this method is used, then it is necessary to
182  * call didLock() to see if a lock was obtained.
183  */
184 // TODO: dispatcher performance testing was ~2% faster if these methods were made inline
185 class CRWMutexLocker
186 {
187 public:
188 	enum LockTypes
189 	{
190 		ltReader,
191 		ltWriter
192 	};
193 
194 	// construct a locker object with no mutex assigned
195 	CRWMutexLocker();
196 
197 	// construct a locker object with the given mutex.. the mutex is locked/try-locked on construction
198 	CRWMutexLocker(CRWMutex &_m,LockTypes lockType,bool block=true);
199 
200 	// the destructor unlocks the mutex iff it was locked
201 	virtual ~CRWMutexLocker() throw();
202 
203 	// returned whether the mutex lock was obtained by this object (sometimes false if a try-lock occurred)
204 	bool didLock() const { return locked; }
205 
206 	// unlocks the currently assigned mutex if it was locked, assigns a new mutex to this locked and locks/try-locks the new mutex
207 	// the locked status is returned
208 	bool reassign(CRWMutex &_m,LockTypes lockType,bool block=true);
209 
210 	// unlocks the currently assigned mutex if it was locked and leaves the locker object with no assigned mutex
211 	void unassign();
212 
213 private:
214 	CRWMutex *m;
215 	bool locked;
216 };
217 
218 
219 
220 
221 #endif
222 
223