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
43OSDefineMetaClassAndStructors(IOConditionLock, OSObject)
44
45bool 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
61IOConditionLock *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}
72void IOConditionLock::free()
73{
74    if (cond_interlock)
75        IOLockFree(cond_interlock);
76    if (sleep_interlock)
77        IOLockFree(sleep_interlock);
78    super::free();
79}
80
81bool IOConditionLock::getInterruptible() const
82{
83    return interruptible;
84}
85
86int IOConditionLock:: getCondition() const
87{
88    return condition;
89}
90
91int 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
101void 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
116void 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
129bool 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
144int 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
164int 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