2 * Copyright 2001-2009 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
6 * Erik Jaesler, erik@cgsoftware.com
10 /*! Semaphore-type class for thread safety */
15 #include <SupportDefs.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
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.
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
75 BLocker::BLocker(const char *name
, bool benaphoreStyle
,
78 InitLocker(name
, benaphoreStyle
);
84 delete_sem(fSemaphoreID
);
89 BLocker::InitCheck() const
91 return fSemaphoreID
>= 0 ? B_OK
: fSemaphoreID
;
99 return AcquireLock(B_INFINITE_TIMEOUT
, &result
);
104 BLocker::LockWithTimeout(bigtime_t timeout
)
108 AcquireLock(timeout
, &result
);
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
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
),
127 // Decrement the number of outstanding locks this thread holds
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
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
);
155 BLocker::LockingThread() const
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
;
172 BLocker::CountLocks() const
174 return fRecursiveCount
;
179 BLocker::CountLockRequests() const
181 return fBenaphoreCount
;
193 BLocker::InitLocker(const char *name
, bool benaphore
)
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).
203 fSemaphoreID
= create_sem(0, name
);
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.
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.
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.
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) {
236 status
= acquire_sem_etc(fSemaphoreID
, 1, B_RELATIVE_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
);
296 // Return true if the lock has been acquired.
297 return (status
== B_OK
);