xref: /haiku/src/kits/shared/RWLockManager.cpp (revision 474d27da32e2b8dddad376fbaf0d8ddb5bb88d0e)
1*474d27daSIngo Weinhold /*
2*474d27daSIngo Weinhold  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3*474d27daSIngo Weinhold  * Distributed under the terms of the MIT License.
4*474d27daSIngo Weinhold  */
5*474d27daSIngo Weinhold 
6*474d27daSIngo Weinhold #include <RWLockManager.h>
7*474d27daSIngo Weinhold 
8*474d27daSIngo Weinhold #include <AutoLocker.h>
9*474d27daSIngo Weinhold 
10*474d27daSIngo Weinhold #include <syscalls.h>
11*474d27daSIngo Weinhold #include <user_thread.h>
12*474d27daSIngo Weinhold 
13*474d27daSIngo Weinhold 
RWLockable()14*474d27daSIngo Weinhold RWLockable::RWLockable()
15*474d27daSIngo Weinhold 	:
16*474d27daSIngo Weinhold 	fOwner(-1),
17*474d27daSIngo Weinhold 	fOwnerCount(0),
18*474d27daSIngo Weinhold 	fReaderCount(0)
19*474d27daSIngo Weinhold {
20*474d27daSIngo Weinhold }
21*474d27daSIngo Weinhold 
22*474d27daSIngo Weinhold 
RWLockManager()23*474d27daSIngo Weinhold RWLockManager::RWLockManager()
24*474d27daSIngo Weinhold 	:
25*474d27daSIngo Weinhold 	fLock("r/w lock manager")
26*474d27daSIngo Weinhold {
27*474d27daSIngo Weinhold }
28*474d27daSIngo Weinhold 
29*474d27daSIngo Weinhold 
~RWLockManager()30*474d27daSIngo Weinhold RWLockManager::~RWLockManager()
31*474d27daSIngo Weinhold {
32*474d27daSIngo Weinhold }
33*474d27daSIngo Weinhold 
34*474d27daSIngo Weinhold 
35*474d27daSIngo Weinhold bool
ReadLock(RWLockable * lockable)36*474d27daSIngo Weinhold RWLockManager::ReadLock(RWLockable* lockable)
37*474d27daSIngo Weinhold {
38*474d27daSIngo Weinhold 	AutoLocker<RWLockManager> locker(this);
39*474d27daSIngo Weinhold 
40*474d27daSIngo Weinhold 	if (lockable->fWaiters.IsEmpty()) {
41*474d27daSIngo Weinhold 		lockable->fReaderCount++;
42*474d27daSIngo Weinhold 		return true;
43*474d27daSIngo Weinhold 	}
44*474d27daSIngo Weinhold 
45*474d27daSIngo Weinhold 	return _Wait(lockable, false, B_INFINITE_TIMEOUT) == B_OK;
46*474d27daSIngo Weinhold }
47*474d27daSIngo Weinhold 
48*474d27daSIngo Weinhold 
49*474d27daSIngo Weinhold bool
TryReadLock(RWLockable * lockable)50*474d27daSIngo Weinhold RWLockManager::TryReadLock(RWLockable* lockable)
51*474d27daSIngo Weinhold {
52*474d27daSIngo Weinhold 	AutoLocker<RWLockManager> locker(this);
53*474d27daSIngo Weinhold 
54*474d27daSIngo Weinhold 	if (lockable->fWaiters.IsEmpty()) {
55*474d27daSIngo Weinhold 		lockable->fReaderCount++;
56*474d27daSIngo Weinhold 		return true;
57*474d27daSIngo Weinhold 	}
58*474d27daSIngo Weinhold 
59*474d27daSIngo Weinhold 	return false;
60*474d27daSIngo Weinhold }
61*474d27daSIngo Weinhold 
62*474d27daSIngo Weinhold 
63*474d27daSIngo Weinhold status_t
ReadLockWithTimeout(RWLockable * lockable,bigtime_t timeout)64*474d27daSIngo Weinhold RWLockManager::ReadLockWithTimeout(RWLockable* lockable, bigtime_t timeout)
65*474d27daSIngo Weinhold {
66*474d27daSIngo Weinhold 	AutoLocker<RWLockManager> locker(this);
67*474d27daSIngo Weinhold 
68*474d27daSIngo Weinhold 	if (lockable->fWaiters.IsEmpty()) {
69*474d27daSIngo Weinhold 		lockable->fReaderCount++;
70*474d27daSIngo Weinhold 		return B_OK;
71*474d27daSIngo Weinhold 	}
72*474d27daSIngo Weinhold 
73*474d27daSIngo Weinhold 	return _Wait(lockable, false, timeout);
74*474d27daSIngo Weinhold }
75*474d27daSIngo Weinhold 
76*474d27daSIngo Weinhold 
77*474d27daSIngo Weinhold void
ReadUnlock(RWLockable * lockable)78*474d27daSIngo Weinhold RWLockManager::ReadUnlock(RWLockable* lockable)
79*474d27daSIngo Weinhold {
80*474d27daSIngo Weinhold 	AutoLocker<RWLockManager> locker(this);
81*474d27daSIngo Weinhold 
82*474d27daSIngo Weinhold 	if (lockable->fReaderCount <= 0) {
83*474d27daSIngo Weinhold 		debugger("RWLockManager::ReadUnlock(): Not read-locked!");
84*474d27daSIngo Weinhold 		return;
85*474d27daSIngo Weinhold 	}
86*474d27daSIngo Weinhold 
87*474d27daSIngo Weinhold 	if (--lockable->fReaderCount == 0)
88*474d27daSIngo Weinhold 		_Unblock(lockable);
89*474d27daSIngo Weinhold }
90*474d27daSIngo Weinhold 
91*474d27daSIngo Weinhold 
92*474d27daSIngo Weinhold bool
WriteLock(RWLockable * lockable)93*474d27daSIngo Weinhold RWLockManager::WriteLock(RWLockable* lockable)
94*474d27daSIngo Weinhold {
95*474d27daSIngo Weinhold 	AutoLocker<RWLockManager> locker(this);
96*474d27daSIngo Weinhold 
97*474d27daSIngo Weinhold 	thread_id thread = find_thread(NULL);
98*474d27daSIngo Weinhold 
99*474d27daSIngo Weinhold 	if (lockable->fOwner == thread) {
100*474d27daSIngo Weinhold 		lockable->fOwnerCount++;
101*474d27daSIngo Weinhold 		return true;
102*474d27daSIngo Weinhold 	}
103*474d27daSIngo Weinhold 
104*474d27daSIngo Weinhold 	if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) {
105*474d27daSIngo Weinhold 		lockable->fOwnerCount = 1;
106*474d27daSIngo Weinhold 		lockable->fOwner = find_thread(NULL);
107*474d27daSIngo Weinhold 		return true;
108*474d27daSIngo Weinhold 	}
109*474d27daSIngo Weinhold 
110*474d27daSIngo Weinhold 	return _Wait(lockable, true, B_INFINITE_TIMEOUT) == B_OK;
111*474d27daSIngo Weinhold }
112*474d27daSIngo Weinhold 
113*474d27daSIngo Weinhold 
114*474d27daSIngo Weinhold bool
TryWriteLock(RWLockable * lockable)115*474d27daSIngo Weinhold RWLockManager::TryWriteLock(RWLockable* lockable)
116*474d27daSIngo Weinhold {
117*474d27daSIngo Weinhold 	AutoLocker<RWLockManager> locker(this);
118*474d27daSIngo Weinhold 
119*474d27daSIngo Weinhold 	thread_id thread = find_thread(NULL);
120*474d27daSIngo Weinhold 
121*474d27daSIngo Weinhold 	if (lockable->fOwner == thread) {
122*474d27daSIngo Weinhold 		lockable->fOwnerCount++;
123*474d27daSIngo Weinhold 		return true;
124*474d27daSIngo Weinhold 	}
125*474d27daSIngo Weinhold 
126*474d27daSIngo Weinhold 	if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) {
127*474d27daSIngo Weinhold 		lockable->fOwnerCount++;
128*474d27daSIngo Weinhold 		lockable->fOwner = thread;
129*474d27daSIngo Weinhold 		return true;
130*474d27daSIngo Weinhold 	}
131*474d27daSIngo Weinhold 
132*474d27daSIngo Weinhold 	return false;
133*474d27daSIngo Weinhold }
134*474d27daSIngo Weinhold 
135*474d27daSIngo Weinhold 
136*474d27daSIngo Weinhold status_t
WriteLockWithTimeout(RWLockable * lockable,bigtime_t timeout)137*474d27daSIngo Weinhold RWLockManager::WriteLockWithTimeout(RWLockable* lockable, bigtime_t timeout)
138*474d27daSIngo Weinhold {
139*474d27daSIngo Weinhold 	AutoLocker<RWLockManager> locker(this);
140*474d27daSIngo Weinhold 
141*474d27daSIngo Weinhold 	thread_id thread = find_thread(NULL);
142*474d27daSIngo Weinhold 
143*474d27daSIngo Weinhold 	if (lockable->fOwner == thread) {
144*474d27daSIngo Weinhold 		lockable->fOwnerCount++;
145*474d27daSIngo Weinhold 		return B_OK;
146*474d27daSIngo Weinhold 	}
147*474d27daSIngo Weinhold 
148*474d27daSIngo Weinhold 	if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) {
149*474d27daSIngo Weinhold 		lockable->fOwnerCount++;
150*474d27daSIngo Weinhold 		lockable->fOwner = thread;
151*474d27daSIngo Weinhold 		return B_OK;
152*474d27daSIngo Weinhold 	}
153*474d27daSIngo Weinhold 
154*474d27daSIngo Weinhold 	return _Wait(lockable, true, timeout);
155*474d27daSIngo Weinhold }
156*474d27daSIngo Weinhold 
157*474d27daSIngo Weinhold 
158*474d27daSIngo Weinhold void
WriteUnlock(RWLockable * lockable)159*474d27daSIngo Weinhold RWLockManager::WriteUnlock(RWLockable* lockable)
160*474d27daSIngo Weinhold {
161*474d27daSIngo Weinhold 	AutoLocker<RWLockManager> locker(this);
162*474d27daSIngo Weinhold 
163*474d27daSIngo Weinhold 	if (find_thread(NULL) != lockable->fOwner) {
164*474d27daSIngo Weinhold 		debugger("RWLockManager::WriteUnlock(): Not write-locked by calling "
165*474d27daSIngo Weinhold 			"thread!");
166*474d27daSIngo Weinhold 		return;
167*474d27daSIngo Weinhold 	}
168*474d27daSIngo Weinhold 
169*474d27daSIngo Weinhold 	if (--lockable->fOwnerCount == 0) {
170*474d27daSIngo Weinhold 		lockable->fOwner = -1;
171*474d27daSIngo Weinhold 		_Unblock(lockable);
172*474d27daSIngo Weinhold 	}
173*474d27daSIngo Weinhold }
174*474d27daSIngo Weinhold 
175*474d27daSIngo Weinhold 
176*474d27daSIngo Weinhold status_t
_Wait(RWLockable * lockable,bool writer,bigtime_t timeout)177*474d27daSIngo Weinhold RWLockManager::_Wait(RWLockable* lockable, bool writer, bigtime_t timeout)
178*474d27daSIngo Weinhold {
179*474d27daSIngo Weinhold 	if (timeout == 0)
180*474d27daSIngo Weinhold 		return B_TIMED_OUT;
181*474d27daSIngo Weinhold 
182*474d27daSIngo Weinhold 	// enqueue a waiter
183*474d27daSIngo Weinhold 	RWLockable::Waiter waiter(writer);
184*474d27daSIngo Weinhold 	lockable->fWaiters.Add(&waiter);
185*474d27daSIngo Weinhold 	waiter.queued = true;
186*474d27daSIngo Weinhold 	get_user_thread()->wait_status = 1;
187*474d27daSIngo Weinhold 
188*474d27daSIngo Weinhold 	// wait
189*474d27daSIngo Weinhold 	Unlock();
190*474d27daSIngo Weinhold 
191*474d27daSIngo Weinhold 	status_t error;
192*474d27daSIngo Weinhold 	do {
193*474d27daSIngo Weinhold 		error = _kern_block_thread(
194*474d27daSIngo Weinhold 			timeout >= 0 ? B_RELATIVE_TIMEOUT : 0, timeout);
195*474d27daSIngo Weinhold 			// TODO: When interrupted we should adjust the timeout, respectively
196*474d27daSIngo Weinhold 			// convert to an absolute timeout in the first place!
197*474d27daSIngo Weinhold 	} while (error == B_INTERRUPTED);
198*474d27daSIngo Weinhold 
199*474d27daSIngo Weinhold 	Lock();
200*474d27daSIngo Weinhold 
201*474d27daSIngo Weinhold 	if (!waiter.queued)
202*474d27daSIngo Weinhold 		return waiter.status;
203*474d27daSIngo Weinhold 
204*474d27daSIngo Weinhold 	// we're still queued, which means an error (timeout, interrupt)
205*474d27daSIngo Weinhold 	// occurred
206*474d27daSIngo Weinhold 	lockable->fWaiters.Remove(&waiter);
207*474d27daSIngo Weinhold 
208*474d27daSIngo Weinhold 	_Unblock(lockable);
209*474d27daSIngo Weinhold 
210*474d27daSIngo Weinhold 	return error;
211*474d27daSIngo Weinhold }
212*474d27daSIngo Weinhold 
213*474d27daSIngo Weinhold 
214*474d27daSIngo Weinhold void
_Unblock(RWLockable * lockable)215*474d27daSIngo Weinhold RWLockManager::_Unblock(RWLockable* lockable)
216*474d27daSIngo Weinhold {
217*474d27daSIngo Weinhold 	// Check whether there any waiting threads at all and whether anyone
218*474d27daSIngo Weinhold 	// has the write lock
219*474d27daSIngo Weinhold 	RWLockable::Waiter* waiter = lockable->fWaiters.Head();
220*474d27daSIngo Weinhold 	if (waiter == NULL || lockable->fOwner >= 0)
221*474d27daSIngo Weinhold 		return;
222*474d27daSIngo Weinhold 
223*474d27daSIngo Weinhold 	// writer at head of queue?
224*474d27daSIngo Weinhold 	if (waiter->writer) {
225*474d27daSIngo Weinhold 		if (lockable->fReaderCount == 0) {
226*474d27daSIngo Weinhold 			waiter->status = B_OK;
227*474d27daSIngo Weinhold 			waiter->queued = false;
228*474d27daSIngo Weinhold 			lockable->fWaiters.Remove(waiter);
229*474d27daSIngo Weinhold 			lockable->fOwner = waiter->thread;
230*474d27daSIngo Weinhold 			lockable->fOwnerCount = 1;
231*474d27daSIngo Weinhold 
232*474d27daSIngo Weinhold 			_kern_unblock_thread(waiter->thread, B_OK);
233*474d27daSIngo Weinhold 		}
234*474d27daSIngo Weinhold 		return;
235*474d27daSIngo Weinhold 	}
236*474d27daSIngo Weinhold 
237*474d27daSIngo Weinhold 	// wake up one or more readers -- we unblock more than one reader at
238*474d27daSIngo Weinhold 	// a time to save trips to the kernel
239*474d27daSIngo Weinhold 	while (!lockable->fWaiters.IsEmpty()
240*474d27daSIngo Weinhold 			&& !lockable->fWaiters.Head()->writer) {
241*474d27daSIngo Weinhold 		static const int kMaxReaderUnblockCount = 128;
242*474d27daSIngo Weinhold 		thread_id readers[kMaxReaderUnblockCount];
243*474d27daSIngo Weinhold 		int readerCount = 0;
244*474d27daSIngo Weinhold 
245*474d27daSIngo Weinhold 		while (readerCount < kMaxReaderUnblockCount
246*474d27daSIngo Weinhold 				&& (waiter = lockable->fWaiters.Head()) != NULL
247*474d27daSIngo Weinhold 				&& !waiter->writer) {
248*474d27daSIngo Weinhold 			waiter->status = B_OK;
249*474d27daSIngo Weinhold 			waiter->queued = false;
250*474d27daSIngo Weinhold 			lockable->fWaiters.Remove(waiter);
251*474d27daSIngo Weinhold 
252*474d27daSIngo Weinhold 			readers[readerCount++] = waiter->thread;
253*474d27daSIngo Weinhold 			lockable->fReaderCount++;
254*474d27daSIngo Weinhold 		}
255*474d27daSIngo Weinhold 
256*474d27daSIngo Weinhold 		if (readerCount > 0)
257*474d27daSIngo Weinhold 			_kern_unblock_threads(readers, readerCount, B_OK);
258*474d27daSIngo Weinhold 	}
259*474d27daSIngo Weinhold }
260