Update contrib.
1 // Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32\euser\us_rwlock.cpp
20 #include <e32atomics.h>
22 const TInt KReadersIndex = 0;
23 const TInt KWriterIndex = 1;
24 const TInt KReadersPendingIndex = 2;
25 const TInt KWritersPendingIndex = 3;
26 const TUint64 KReaderValue = UI64LIT(0x0000000000000001);
27 const TUint64 KWriterValue = UI64LIT(0x0000000000010000);
28 const TUint64 KReaderPendingValue = UI64LIT(0x0000000100000000);
29 const TUint64 KWriterPendingValue = UI64LIT(0x0001000000000000);
30 const TUint64 KReadersMask = UI64LIT(0x000000000000ffff);
31 const TUint64 KWriterMask = KWriterValue;
32 const TUint64 KReadersOrWritersMask = KReadersMask | KWriterMask;
33 const TUint64 KReadersPendingClearMask = UI64LIT(0xffff0000ffffffff);
36 Initialise a read-write lock object.
37 @param aPriority Type of priority to use - see RReadWriteLockPriority::TReadWriteLockPriority
38 @return KErrNone Instance successfully created
39 Otherwise an error returned by RSemaphore::CreateLocal
40 @panic EReadWriteLockInvalidPriority if aPriority is not valid.
42 EXPORT_C TInt RReadWriteLock::CreateLocal(TReadWriteLockPriority aPriority)
44 __ASSERT_ALWAYS(aPriority >= EWriterPriority && aPriority <= EReaderPriority, Panic(EReadWriteLockInvalidPriority));
46 iPriority = aPriority;
49 iSpare[0] = 0; // Keep a rough track of writer starvation
52 TInt ret = iReaderSem.CreateLocal(0, EOwnerProcess);
54 ret = iWriterSem.CreateLocal(0, EOwnerProcess);
62 Close a read-write lock object, releasing the associated semaphores.
63 @panic EReadWriteLockStillPending if there are any outstanding clients or pending clients
65 EXPORT_C void RReadWriteLock::Close()
67 __ASSERT_ALWAYS(iValues == 0, Panic(EReadWriteLockStillPending));
74 Ask for a read lock. Will be granted if:
75 1) No-one else currently holds the lock or
76 2) Only readers hold the lock and:
77 a) There are no pending writers or
78 b) The priority is for readers.
79 Otherwise this function blocks until the lock becomes available to it.
80 Please note that ReadLock() is not re-entrant - calling it a second time without releasing the first lock
81 runs the risk of being blocked and risking a deadlock situation.
82 @panic EReadWriteLockTooManyClients if the resulting number of readers or pending readers exceeds EReadWriteLockClientCategoryLimit
84 EXPORT_C void RReadWriteLock::ReadLock()
87 TUint64 initialValues;
88 TUint16* indexedValues = (TUint16*)&initialValues;
91 initialValues = iValues;
93 if (indexedValues[KWriterIndex] > 0 ||
94 (iPriority != EReaderPriority && indexedValues[KWritersPendingIndex] > 0))
96 __ASSERT_ALWAYS(indexedValues[KReadersPendingIndex] < KMaxTUint16, Panic(EReadWriteLockTooManyClients));
101 __ASSERT_ALWAYS(indexedValues[KReadersIndex] < KMaxTUint16, Panic(EReadWriteLockTooManyClients));
105 while (!__e32_atomic_cas_rel64(&iValues, &initialValues, initialValues + (blocked ? KReaderPendingValue : KReaderValue)));
112 Ask for a write lock. Will be granted if no-one else currently holds the lock.
113 Otherwise this function blocks until the lock becomes available to it.
114 Only one writer can hold the lock at one time. No readers can hold the lock while a writer has it.
115 Please note that WriteLock() is not re-entrant - calling it a second time without releasing the first lock
116 will block and cause a deadlock situation.
117 @panic EReadWriteLockTooManyClients if the resulting number of pending writers exceeds EReadWriteLockClientCategoryLimit
119 EXPORT_C void RReadWriteLock::WriteLock()
122 TUint64 initialValues;
123 TUint16* indexedValues = (TUint16*)&initialValues;
126 initialValues = iValues;
128 if (initialValues & KReadersOrWritersMask)
130 __ASSERT_ALWAYS(indexedValues[KWritersPendingIndex] < KMaxTUint16, Panic(EReadWriteLockTooManyClients));
138 while (!__e32_atomic_cas_rel64(&iValues, &initialValues, initialValues + (blocked ? KWriterPendingValue : KWriterValue)));
145 Ask for a read lock without blocking.
146 @return ETrue - lock granted
147 EFalse - failed to obtain the lock
148 @panic EReadWriteLockTooManyClients if the resulting number of readers exceeds EReadWriteLockClientCategoryLimit
151 EXPORT_C TBool RReadWriteLock::TryReadLock()
153 TUint64 initialValues;
154 TUint16* indexedValues = (TUint16*)&initialValues;
157 initialValues = iValues;
159 if (indexedValues[KWriterIndex] > 0 ||
160 (iPriority != EReaderPriority && indexedValues[KWritersPendingIndex] > 0))
163 __ASSERT_ALWAYS(indexedValues[KReadersIndex] < KMaxTUint16, Panic(EReadWriteLockTooManyClients));
165 while (!__e32_atomic_cas_rel64(&iValues, &initialValues, initialValues + KReaderValue));
171 Ask for a write lock without blocking.
172 @return ETrue - lock granted
173 EFalse - failed to obtain the lock
176 EXPORT_C TBool RReadWriteLock::TryWriteLock()
178 TUint64 initialValues;
181 initialValues = iValues;
183 if (initialValues & KReadersOrWritersMask)
186 while (!__e32_atomic_cas_rel64(&iValues, &initialValues, initialValues + KWriterValue));
192 Tries to atomically release a read lock and gain a write lock.
193 This function will succeed if:
194 - This is the only reader and
195 - There are no pending writers or
196 - The priority is reader
197 @return ETrue - write lock granted
198 EFalse - failed to obtain a write lock, read lock retained
199 @panic EReadWriteLockBadLockState if the read lock is not currently held
201 EXPORT_C TBool RReadWriteLock::TryUpgradeReadLock()
203 __ASSERT_ALWAYS((iValues & KReadersMask) != 0, Panic(EReadWriteLockBadLockState)); // Check we actually hold a read lock
204 __ASSERT_DEBUG((iValues & KWriterMask) == 0, Panic(EReadWriteLockBadLockState)); // Check we don't hold a write lock - shouldn't be possible
206 TUint64 initialValues;
207 TUint16* indexedValues = (TUint16*)&initialValues;
210 initialValues = iValues;
212 if (indexedValues[KReadersIndex] > 1 ||
213 (iPriority != EReaderPriority && indexedValues[KWritersPendingIndex] > 0))
216 while (!__e32_atomic_cas_acq64(&iValues, &initialValues, initialValues - KReaderValue + KWriterValue));
222 Atomically releases a held write lock and gains a read lock. Also unblocks any
224 - Priority is EPriorityReader or
225 - There are no pending writers
226 This function can not fail, so it does not return anything.
227 @panic EReadWriteLockBadLockState if the lock is not currently held
229 EXPORT_C void RReadWriteLock::DowngradeWriteLock()
231 __ASSERT_ALWAYS((iValues & KWriterMask) == KWriterValue, Panic(EReadWriteLockBadLockState)); // Check we actually hold a write lock
232 __ASSERT_DEBUG((iValues & KReadersMask) == 0, Panic(EReadWriteLockBadLockState)); // Check we don't hold a read lock - shouldn't be possible
235 TUint64 initialValues;
236 TUint16* indexedValues = (TUint16*)&initialValues;
241 initialValues = iValues;
242 newValues = initialValues - KWriterValue + KReaderValue; // Clear current write lock flag and add a read lock
244 if (indexedValues[KReadersPendingIndex] > 0 &&
245 (indexedValues[KWritersPendingIndex] == 0 || iPriority == EReaderPriority)) // Release any other pending readers
247 unlockReaders = indexedValues[KReadersPendingIndex];
248 newValues &= KReadersPendingClearMask; // Clear pending readers
250 if (unlockReaders == KMaxTUint16) // Put a pending reader back to avoid overflow in the readers field
253 newValues += KReaderPendingValue;
256 newValues += unlockReaders;
259 while (!__e32_atomic_cas_acq64(&iValues, &initialValues, newValues));
261 if (unlockReaders > 0)
262 iReaderSem.Signal(unlockReaders);
266 Releases a held read or write lock. If no-one else holds this lock (ie other
267 readers) then this will unblock one or more pending clients based on the priority:
268 EAlternatePriority - If a read lock is being released then:
269 - Give the lock to the first pending writer, if there is one
270 - Else give the lock to all pending readers, if there are any
271 - If a write lock is being released then:
272 - If there are pending readers:
273 - If there are pending writers then unblock one pending reader
274 - Else if there are no pending writers then unblock all pending readers
275 - Else unblock one pending writer, if there is one
276 EReaderPriority - Unblock all pending readers. If none then unblock one pending writer, if there is one
277 EWriterPriority - Unblock one pending writer, if there is one. If none then unblock any and all pending readers
278 @panic EReadWriteLockBadLockState if the lock is not currently held
280 EXPORT_C void RReadWriteLock::Unlock()
282 __ASSERT_ALWAYS((iValues & KReadersOrWritersMask) != 0, Panic(EReadWriteLockBadLockState)); // Check we actually hold a lock
283 __ASSERT_DEBUG((iValues & KReadersOrWritersMask) <= KWriterValue, Panic(EReadWriteLockBadLockState)); // Check we don't hold a read lock and a write lock at the same time - shouldn't be possible
285 TInt unlockClients = 0;
289 case EWriterPriority:
290 unlockClients = UnlockWriter(); break;
291 case EAlternatePriority:
292 unlockClients = UnlockAlternate(); break;
293 default: // EReaderPriority:
294 unlockClients = UnlockReader(); break;
297 if (unlockClients == -1)
300 iSpare[0] = 0; // Keep a rough track of writer starvation
304 else if (unlockClients > 0)
307 const TUint64 KWritersPendingMask = UI64LIT(0xffff000000000000);
308 if (iValues & KWritersPendingMask)
309 iSpare[0]++; // Keep a rough track of writer starvation
310 if (iSpare[0] > 1000)
311 Panic(EReadWriteLockWriterStarvation);
313 iReaderSem.Signal(unlockClients);
317 TInt RReadWriteLock::UnlockWriter()
319 TUint64 initialValues;
320 TUint16* indexedValues = (TUint16*)&initialValues;
326 initialValues = iValues;
327 newValues = initialValues - (indexedValues[KReadersIndex] > 0 ? KReaderValue : KWriterValue); // Clear current lock flag
329 if ((newValues & KReadersOrWritersMask) == 0) // No longer locked - release someone else
331 if (indexedValues[KWritersPendingIndex] > 0) // Release a writer
334 newValues -= KWriterPendingValue;
335 newValues += KWriterValue;
337 else if (indexedValues[KReadersPendingIndex] > 0) // Release all pending readers
339 unlockClients = indexedValues[KReadersPendingIndex];
340 newValues &= KReadersPendingClearMask; // Clear pending readers
341 newValues += unlockClients;
345 while (!__e32_atomic_cas_acq64(&iValues, &initialValues, newValues));
347 return unlockClients;
350 TInt RReadWriteLock::UnlockAlternate()
352 TUint64 initialValues;
353 TUint16* indexedValues = (TUint16*)&initialValues;
359 initialValues = iValues;
360 newValues = initialValues - (indexedValues[KReadersIndex] > 0 ? KReaderValue : KWriterValue); // Clear current lock flag
362 if ((newValues & KReadersOrWritersMask) == 0) // No longer locked - release someone else
364 if (indexedValues[KWritersPendingIndex] > 0 &&
365 (indexedValues[KReadersIndex] > 0 || indexedValues[KReadersPendingIndex] == 0)) // Release a writer if there is one and either this is a read unlock or there are no readers pending
368 newValues -= KWriterPendingValue;
369 newValues += KWriterValue;
371 else if (indexedValues[KReadersPendingIndex] > 0) // Release one or more readers
373 if (indexedValues[KWritersPendingIndex] > 0) // Just one because there are pending writers
376 newValues -= KReaderPendingValue;
377 newValues += KReaderValue;
381 unlockClients = indexedValues[KReadersPendingIndex];
382 newValues &= KReadersPendingClearMask; // Clear pending readers
383 newValues += unlockClients;
389 while (!__e32_atomic_cas_acq64(&iValues, &initialValues, newValues));
391 return unlockClients;
394 TInt RReadWriteLock::UnlockReader()
396 TUint64 initialValues;
397 TUint16* indexedValues = (TUint16*)&initialValues;
403 initialValues = iValues;
404 newValues = initialValues - (indexedValues[KReadersIndex] > 0 ? KReaderValue : KWriterValue); // Clear current lock flag
406 if ((newValues & KReadersOrWritersMask) == 0) // No longer locked - release someone else
408 if (indexedValues[KReadersPendingIndex] > 0) // Release all pending readers
410 unlockClients = indexedValues[KReadersPendingIndex];
411 newValues &= KReadersPendingClearMask; // Clear pending readers
412 newValues += unlockClients;
414 else if (indexedValues[KWritersPendingIndex] > 0) // Release a writer
417 newValues -= KWriterPendingValue;
418 newValues += KWriterValue;
422 while (!__e32_atomic_cas_acq64(&iValues, &initialValues, newValues));
424 return unlockClients;