bin/pc: Mark non-returning function as void
[haiku.git] / src / kits / support / Locker.cpp
blob6b42d90a9013afc664d1edb26af3e042b14fcd2b
1 /*
2 * Copyright 2001-2009 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Erik Jaesler, erik@cgsoftware.com
7 */
10 /*! Semaphore-type class for thread safety */
13 #include <OS.h>
14 #include <Locker.h>
15 #include <SupportDefs.h>
17 #include <stdio.h>
19 #include "support_kit_config.h"
22 // Data Member Documentation:
24 // The "fBenaphoreCount" member is set to 1 if the BLocker style is
25 // semaphore. If the style is benaphore, it is initialized to 0 and
26 // is incremented atomically when it is acquired, decremented when it
27 // is released. By setting the benaphore count to 1 when the style is
28 // semaphore, the benaphore effectively becomes a semaphore. I was able
29 // to determine this is what Be's implementation does by testing the
30 // result of the CountLockRequests() member.
32 // The "fSemaphoreID" member holds the sem_id returned from create_sem()
33 // when the BLocker is constructed. It is used to acquire and release
34 // the lock regardless of the lock style (semaphore or benaphore).
36 // The "fLockOwner" member holds the thread_id of the thread which
37 // currently holds the lock. If no thread holds the lock, it is set to
38 // B_ERROR.
40 // The "fRecursiveCount" member holds a count of the number of times the
41 // thread holding the lock has acquired the lock without a matching unlock.
42 // It is basically the number of times the thread must call Unlock() before
43 // the lock can be acquired by a different thread.
47 BLocker::BLocker()
49 InitLocker(NULL, true);
53 BLocker::BLocker(const char *name)
55 InitLocker(name, true);
59 BLocker::BLocker(bool benaphoreStyle)
61 InitLocker(NULL, benaphoreStyle);
65 BLocker::BLocker(const char *name, bool benaphoreStyle)
67 InitLocker(name, benaphoreStyle);
71 /*! This constructor is not documented. The final argument is ignored for
72 now. In Be's headers, its called "for_IPC". DO NOT USE THIS
73 CONSTRUCTOR!
75 BLocker::BLocker(const char *name, bool benaphoreStyle,
76 bool)
78 InitLocker(name, benaphoreStyle);
82 BLocker::~BLocker()
84 delete_sem(fSemaphoreID);
88 status_t
89 BLocker::InitCheck() const
91 return fSemaphoreID >= 0 ? B_OK : fSemaphoreID;
95 bool
96 BLocker::Lock()
98 status_t result;
99 return AcquireLock(B_INFINITE_TIMEOUT, &result);
103 status_t
104 BLocker::LockWithTimeout(bigtime_t timeout)
106 status_t result;
108 AcquireLock(timeout, &result);
109 return result;
113 void
114 BLocker::Unlock()
116 // The Be Book explicitly allows any thread, not just the lock owner, to
117 // unlock. This is bad practice, but we must allow it for compatibility
118 // reasons. We can at least warn the developer that something is probably
119 // wrong.
120 if (!IsLocked()) {
121 fprintf(stderr, "Unlocking BLocker with sem %" B_PRId32
122 " from wrong thread %" B_PRId32 ", current holder %" B_PRId32
123 " (see issue #6400).\n", fSemaphoreID, find_thread(NULL),
124 fLockOwner);
127 // Decrement the number of outstanding locks this thread holds
128 // on this BLocker.
129 fRecursiveCount--;
131 // If the recursive count is now at 0, that means the BLocker has
132 // been released by the thread.
133 if (fRecursiveCount == 0) {
134 // The BLocker is no longer owned by any thread.
135 fLockOwner = B_ERROR;
137 // Decrement the benaphore count and store the undecremented
138 // value in oldBenaphoreCount.
139 int32 oldBenaphoreCount = atomic_add(&fBenaphoreCount, -1);
141 // If the oldBenaphoreCount is greater than 1, then there is
142 // at least one thread waiting for the lock in the case of a
143 // benaphore.
144 if (oldBenaphoreCount > 1) {
145 // Since there are threads waiting for the lock, it must
146 // be released. Note, the old benaphore count will always be
147 // greater than 1 for a semaphore so the release is always done.
148 release_sem(fSemaphoreID);
154 thread_id
155 BLocker::LockingThread() const
157 return fLockOwner;
161 bool
162 BLocker::IsLocked() const
164 // This member returns true if the calling thread holds the lock.
165 // The easiest way to determine this is to compare the result of
166 // find_thread() to the fLockOwner.
167 return find_thread(NULL) == fLockOwner;
171 int32
172 BLocker::CountLocks() const
174 return fRecursiveCount;
178 int32
179 BLocker::CountLockRequests() const
181 return fBenaphoreCount;
185 sem_id
186 BLocker::Sem() const
188 return fSemaphoreID;
192 void
193 BLocker::InitLocker(const char *name, bool benaphore)
195 if (name == NULL)
196 name = "some BLocker";
198 if (benaphore && !BLOCKER_ALWAYS_SEMAPHORE_STYLE) {
199 // Because this is a benaphore, initialize the benaphore count and
200 // create the semaphore. Because this is a benaphore, the semaphore
201 // count starts at 0 (ie acquired).
202 fBenaphoreCount = 0;
203 fSemaphoreID = create_sem(0, name);
204 } else {
205 // Because this is a semaphore, initialize the benaphore count to -1
206 // and create the semaphore. Because this is semaphore style, the
207 // semaphore count starts at 1 so that one thread can acquire it and
208 // the next thread to acquire it will block.
209 fBenaphoreCount = 1;
210 fSemaphoreID = create_sem(1, name);
213 // The lock is currently not acquired so there is no owner.
214 fLockOwner = B_ERROR;
216 // The lock is currently not acquired so the recursive count is zero.
217 fRecursiveCount = 0;
221 bool
222 BLocker::AcquireLock(bigtime_t timeout, status_t *error)
224 // By default, return no error.
225 status_t status = B_OK;
227 // Only try to acquire the lock if the thread doesn't already own it.
228 if (!IsLocked()) {
229 // Increment the benaphore count and test to see if it was already
230 // greater than 0. If it is greater than 0, then some thread already has
231 // the benaphore or the style is a semaphore. Either way, we need to
232 // acquire the semaphore in this case.
233 int32 oldBenaphoreCount = atomic_add(&fBenaphoreCount, 1);
234 if (oldBenaphoreCount > 0) {
235 do {
236 status = acquire_sem_etc(fSemaphoreID, 1, B_RELATIVE_TIMEOUT,
237 timeout);
238 } while (status == B_INTERRUPTED);
240 // Note, if the lock here does time out, the benaphore count
241 // is not decremented. By doing this, the benaphore count will
242 // never go back to zero. This means that the locking essentially
243 // changes to semaphore style if this was a benaphore.
245 // Doing the decrement of the benaphore count when the acquisition
246 // fails is a risky thing to do. If you decrement the counter at
247 // the same time the thread which holds the benaphore does an
248 // Unlock(), there is serious risk of a race condition.
250 // If the Unlock() sees a positive count and releases the semaphore
251 // and then the timed out thread decrements the count to 0, there
252 // is no one to take the semaphore. The next two threads will be
253 // able to acquire the benaphore at the same time! The first will
254 // increment the counter and acquire the lock. The second will
255 // acquire the semaphore and therefore the lock. Not good.
257 // This has been discussed on the becodetalk mailing list and
258 // Trey from Be had this to say:
260 // I looked at the LockWithTimeout() code, and it does not have
261 // _this_ (ie the race condition) problem. It circumvents it by
262 // NOT doing the atomic_add(&count, -1) if the semaphore
263 // acquisition fails. This means that if a
264 // BLocker::LockWithTimeout() times out, all other Lock*() attempts
265 // turn into guaranteed semaphore grabs, _with_ the overhead of a
266 // (now) useless atomic_add().
268 // Given Trey's comments, it looks like Be took the same approach
269 // I did. The output of CountLockRequests() of Be's implementation
270 // confirms Trey's comments also.
272 // Finally some thoughts for the future with this code:
273 // - If 2^31 timeouts occur on a 32-bit machine (ie today),
274 // the benaphore count will wrap to a negative number. This
275 // would have unknown consequences on the ability of the BLocker
276 // to continue to function.
281 // If the lock has successfully been acquired.
282 if (status == B_OK) {
283 // Set the lock owner to this thread and increment the recursive count
284 // by one. The recursive count is incremented because one more Unlock()
285 // is now required to release the lock (ie, 0 => 1, 1 => 2 etc).
286 if (fLockOwner < 0) {
287 fLockOwner = find_thread(NULL);
288 fRecursiveCount = 1;
289 } else
290 fRecursiveCount++;
293 if (error != NULL)
294 *error = status;
296 // Return true if the lock has been acquired.
297 return (status == B_OK);