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
43 OSDefineMetaClassAndStructors(IOConditionLock, OSObject)
44 
45 bool IOConditionLock::initWithCondition(int inCondition, bool inIntr)
46 {
47     if (!super::init())
48         return false;
49 
50     cond_interlock = IOLockAlloc();
51     sleep_interlock = IOLockAlloc();
52 
53     condition = inCondition;
54     want_lock    = false;
55     waiting      = false;
56     interruptible = (inIntr) ? THREAD_INTERRUPTIBLE : THREAD_UNINT;
57 
58     return cond_interlock && sleep_interlock;
59 }
60 
61 IOConditionLock *IOConditionLock::withCondition(int condition, bool intr)
62 {
63     IOConditionLock *me = new IOConditionLock;
64 
65     if (me && !me->initWithCondition(condition, intr)) {
66         me->release();
67         return 0;
68     }
69 
70     return me;
71 }
72 void IOConditionLock::free()
73 {
74     if (cond_interlock)
75         IOLockFree(cond_interlock);
76     if (sleep_interlock)
77         IOLockFree(sleep_interlock);
78     super::free();
79 }
80 
81 bool IOConditionLock::getInterruptible() const
82 {
83     return interruptible;
84 }
85 
86 int IOConditionLock:: getCondition() const
87 {
88     return condition;
89 }
90 
91 int IOConditionLock:: setCondition(int inCondition)
92 {
93     int old = condition;
94 
95     condition = inCondition;
96     thread_wakeup_one((void *) &condition);
97 
98     return old;
99 }
100 
101 void IOConditionLock::unlock()
102 {
103     IOTakeLock(sleep_interlock);
104 
105     thread_wakeup_one((void *) &condition);
106 
107     want_lock = false;
108     if (waiting) {
109 	waiting = false;
110 	IOLockWakeup(sleep_interlock, this, /* one-thread */ false);	// Wakeup everybody
111     }
112 
113     IOUnlock(sleep_interlock);
114 }
115 
116 void IOConditionLock::unlockWith(int inCondition)
117 {
118     IOTakeLock(sleep_interlock);
119     IOTakeLock(cond_interlock);
120 
121     condition = inCondition;
122 
123     IOUnlock(cond_interlock);
124     IOUnlock(sleep_interlock);
125 
126     unlock();
127 }
128 
129 bool IOConditionLock::tryLock()
130 {
131     bool result;
132 
133     IOTakeLock(sleep_interlock);
134 
135     result = !want_lock;
136     if (result)
137 	want_lock = true;
138 
139     IOUnlock(sleep_interlock);
140 
141     return result;
142 }
143 
144 int IOConditionLock::lock()
145 {
146     int thread_res = THREAD_AWAKENED;
147 
148     IOTakeLock(sleep_interlock);
149 
150     /* Try to acquire the want_lock bit. */
151     while (want_lock && thread_res == THREAD_AWAKENED)
152     {
153 	waiting = true;
154 	thread_res = IOLockSleep(sleep_interlock, (void *) this, interruptible);
155     }
156     if (thread_res == THREAD_AWAKENED)
157 	want_lock = true;
158 
159     IOUnlock(sleep_interlock);
160 
161     return thread_res;
162 }
163 
164 int IOConditionLock::lockWhen(int inCondition)
165 {
166     int thread_res;
167 
168     do
169     {
170 	/* First get the actual lock */
171 	thread_res = lock();
172 	if (thread_res != THREAD_AWAKENED)
173 	    break;	// Failed to acquire lock
174 
175 	if (inCondition == condition)
176 	    break;	// Hold lock and condition is expected value
177 
178 	/*
179 	 * Need to hold a IOTakeLock when we call thread_sleep().
180 	 * Both _cond_interlock and want_lock must be held to
181 	 * change _condition.
182 	 */
183 	IOTakeLock(cond_interlock);
184         unlock();	// Release lock and sleep
185 
186 	/*
187 	 * this is the critical section on a multi in which
188 	 * another thread could hold _sleep_interlock, but they
189 	 * can't change _condition. Holding _cond_interlock here
190 	 * (until after assert_wait() is called from
191 	 * thread_sleep()) ensures that we'll be notified
192 	 * of changes in _condition.
193 	 */
194         assert_wait((void *) &condition, interruptible); /* assert event */
195 	IOUnlock(cond_interlock);			 /* release the lock */
196 	thread_res = thread_block(THREAD_CONTINUE_NULL); /* block ourselves */
197     } while (thread_res == THREAD_AWAKENED);
198 
199     return thread_res;
200 }
201