1 /*
2  * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /* Copyright (c) 1997 Apple Computer, Inc.  All rights reserved.
29  * Copyright (c) 1994-1996 NeXT Software, Inc.  All rights reserved.
30  *
31  * AppleIOPSSafeCondLock.m. Lock object with exported condition variable,
32  *	kernel version.
33  *
34  * HISTORY
35  * 1997-11-
36  * 01-Aug-91    Doug Mitchell at NeXT
37  *      Created.
38  */
39 
40 #include <IOKit/IOConditionLock.h>
41 
42 #define super OSObject
OSDefineMetaClassAndStructors(IOConditionLock,OSObject)43 OSDefineMetaClassAndStructors(IOConditionLock, OSObject)
44 
45 bool
46 IOConditionLock::initWithCondition(int inCondition, bool inIntr)
47 {
48 	if (!super::init()) {
49 		return false;
50 	}
51 
52 	cond_interlock = IOLockAlloc();
53 	sleep_interlock = IOLockAlloc();
54 
55 	condition = inCondition;
56 	want_lock    = false;
57 	waiting      = false;
58 	interruptible = (inIntr) ? THREAD_INTERRUPTIBLE : THREAD_UNINT;
59 
60 	return cond_interlock && sleep_interlock;
61 }
62 
63 IOConditionLock *
withCondition(int condition,bool intr)64 IOConditionLock::withCondition(int condition, bool intr)
65 {
66 	IOConditionLock *me = new IOConditionLock;
67 
68 	if (me && !me->initWithCondition(condition, intr)) {
69 		me->release();
70 		return NULL;
71 	}
72 
73 	return me;
74 }
75 void
free()76 IOConditionLock::free()
77 {
78 	if (cond_interlock) {
79 		IOLockFree(cond_interlock);
80 	}
81 	if (sleep_interlock) {
82 		IOLockFree(sleep_interlock);
83 	}
84 	super::free();
85 }
86 
87 bool
getInterruptible() const88 IOConditionLock::getInterruptible() const
89 {
90 	return interruptible;
91 }
92 
93 int
getCondition() const94 IOConditionLock:: getCondition() const
95 {
96 	return condition;
97 }
98 
99 int
setCondition(int inCondition)100 IOConditionLock:: setCondition(int inCondition)
101 {
102 	int old = condition;
103 
104 	condition = inCondition;
105 	thread_wakeup_one((void *) &condition);
106 
107 	return old;
108 }
109 
110 void
unlock()111 IOConditionLock::unlock()
112 {
113 	IOTakeLock(sleep_interlock);
114 
115 	thread_wakeup_one((void *) &condition);
116 
117 	want_lock = false;
118 	if (waiting) {
119 		waiting = false;
120 		IOLockWakeup(sleep_interlock, this, /* one-thread */ false); // Wakeup everybody
121 	}
122 
123 	IOUnlock(sleep_interlock);
124 }
125 
126 void
unlockWith(int inCondition)127 IOConditionLock::unlockWith(int inCondition)
128 {
129 	IOTakeLock(sleep_interlock);
130 	IOTakeLock(cond_interlock);
131 
132 	condition = inCondition;
133 
134 	IOUnlock(cond_interlock);
135 	IOUnlock(sleep_interlock);
136 
137 	unlock();
138 }
139 
140 bool
tryLock()141 IOConditionLock::tryLock()
142 {
143 	bool result;
144 
145 	IOTakeLock(sleep_interlock);
146 
147 	result = !want_lock;
148 	if (result) {
149 		want_lock = true;
150 	}
151 
152 	IOUnlock(sleep_interlock);
153 
154 	return result;
155 }
156 
157 int
lock()158 IOConditionLock::lock()
159 {
160 	int thread_res = THREAD_AWAKENED;
161 
162 	IOTakeLock(sleep_interlock);
163 
164 	/* Try to acquire the want_lock bit. */
165 	while (want_lock && thread_res == THREAD_AWAKENED) {
166 		waiting = true;
167 		thread_res = IOLockSleep(sleep_interlock, (void *) this, interruptible);
168 	}
169 	if (thread_res == THREAD_AWAKENED) {
170 		want_lock = true;
171 	}
172 
173 	IOUnlock(sleep_interlock);
174 
175 	return thread_res;
176 }
177 
178 int
lockWhen(int inCondition)179 IOConditionLock::lockWhen(int inCondition)
180 {
181 	int thread_res;
182 
183 	do{
184 		/* First get the actual lock */
185 		thread_res = lock();
186 		if (thread_res != THREAD_AWAKENED) {
187 			break; // Failed to acquire lock
188 		}
189 		if (inCondition == condition) {
190 			break; // Hold lock and condition is expected value
191 		}
192 		/*
193 		 * Need to hold a IOTakeLock when we call thread_sleep().
194 		 * Both _cond_interlock and want_lock must be held to
195 		 * change _condition.
196 		 */
197 		IOTakeLock(cond_interlock);
198 		unlock(); // Release lock and sleep
199 
200 		/*
201 		 * this is the critical section on a multi in which
202 		 * another thread could hold _sleep_interlock, but they
203 		 * can't change _condition. Holding _cond_interlock here
204 		 * (until after assert_wait() is called from
205 		 * thread_sleep()) ensures that we'll be notified
206 		 * of changes in _condition.
207 		 */
208 		assert_wait((void *) &condition, interruptible); /* assert event */
209 		IOUnlock(cond_interlock);                /* release the lock */
210 		thread_res = thread_block(THREAD_CONTINUE_NULL); /* block ourselves */
211 	} while (thread_res == THREAD_AWAKENED);
212 
213 	return thread_res;
214 }
215