]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOWorkLoop.cpp
xnu-792.25.20.tar.gz
[apple/xnu.git] / iokit / Kernel / IOWorkLoop.cpp
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
6601e61a 4 * @APPLE_LICENSE_HEADER_START@
1c79356b 5 *
6601e61a
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
8f6c56a5 11 *
6601e61a
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
6601e61a
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
8f6c56a5 19 *
6601e61a 20 * @APPLE_LICENSE_HEADER_END@
1c79356b
A
21 */
22/*
23Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
24
25HISTORY
26 1998-7-13 Godfrey van der Linden(gvdl)
27 Created.
28*/
29#include <IOKit/IOWorkLoop.h>
30#include <IOKit/IOEventSource.h>
31#include <IOKit/IOInterruptEventSource.h>
32#include <IOKit/IOCommandGate.h>
33#include <IOKit/IOTimeStamp.h>
34
35#define super OSObject
36
37OSDefineMetaClassAndStructors(IOWorkLoop, OSObject);
38
39// Block of unused functions intended for future use
0b4e3aa0 40OSMetaClassDefineReservedUsed(IOWorkLoop, 0);
0c530ab8 41OSMetaClassDefineReservedUsed(IOWorkLoop, 1);
0b4e3aa0 42
1c79356b
A
43OSMetaClassDefineReservedUnused(IOWorkLoop, 2);
44OSMetaClassDefineReservedUnused(IOWorkLoop, 3);
45OSMetaClassDefineReservedUnused(IOWorkLoop, 4);
46OSMetaClassDefineReservedUnused(IOWorkLoop, 5);
47OSMetaClassDefineReservedUnused(IOWorkLoop, 6);
48OSMetaClassDefineReservedUnused(IOWorkLoop, 7);
49
50enum IOWorkLoopState { kLoopRestart = 0x1, kLoopTerminate = 0x2 };
51static inline void SETP(void *addr, unsigned int flag)
52 { unsigned int *num = (unsigned int *) addr; *num |= flag; }
53static inline void CLRP(void *addr, unsigned int flag)
54 { unsigned int *num = (unsigned int *) addr; *num &= ~flag; }
55static inline bool ISSETP(void *addr, unsigned int flag)
56 { unsigned int *num = (unsigned int *) addr; return (*num & flag) != 0; }
57
58#define fFlags loopRestart
59
1c79356b
A
60bool IOWorkLoop::init()
61{
62 // The super init and gateLock allocation MUST be done first
63 if ( !super::init() )
64 return false;
65
66 if ( !(gateLock = IORecursiveLockAlloc()) )
67 return false;
68
69 if ( !(workToDoLock = IOSimpleLockAlloc()) )
70 return false;
71
72 controlG = IOCommandGate::
0c530ab8
A
73 commandGate(this, OSMemberFunctionCast(IOCommandGate::Action,
74 this, &IOWorkLoop::_maintRequest));
1c79356b
A
75 if ( !controlG )
76 return false;
77
78 IOSimpleLockInit(workToDoLock);
79 workToDo = false;
80
81 // Point the controlGate at the workLoop. Usually addEventSource
82 // does this automatically. The problem is in this case addEventSource
83 // uses the control gate and it has to be bootstrapped.
84 controlG->setWorkLoop(this);
85 if (addEventSource(controlG) != kIOReturnSuccess)
86 return false;
87
0c530ab8
A
88 IOThreadFunc cptr =
89 OSMemberFunctionCast(IOThreadFunc, this, &IOWorkLoop::threadMain);
90 workThread = IOCreateThread(cptr, this);
1c79356b
A
91 if (!workThread)
92 return false;
93
94 return true;
95}
96
97IOWorkLoop *
98IOWorkLoop::workLoop()
99{
100 IOWorkLoop *me = new IOWorkLoop;
101
102 if (me && !me->init()) {
55e303ae 103 me->release();
1c79356b
A
104 return 0;
105 }
106
107 return me;
108}
109
110// Free is called twice:
111// First when the atomic retainCount transitions from 1 -> 0
112// Secondly when the work loop itself is commiting hari kari
113// Hence the each leg of the free must be single threaded.
114void IOWorkLoop::free()
115{
116 if (workThread) {
117 IOInterruptState is;
118
119 // If we are here then we must be trying to shut down this work loop
120 // in this case disable all of the event source, mark the loop for
121 // as terminating and wakeup the work thread itself and return
122 // Note: we hold the gate across the entire operation mainly for the
123 // benefit of our event sources so we can disable them cleanly.
124 closeGate();
125
126 disableAllEventSources();
127
128 is = IOSimpleLockLockDisableInterrupt(workToDoLock);
129 SETP(&fFlags, kLoopTerminate);
130 thread_wakeup_one((void *) &workToDo);
131 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
132
133 openGate();
134 }
135 else /* !workThread */ {
136 IOEventSource *event, *next;
137
138 for (event = eventChain; event; event = next) {
139 next = event->getNext();
140 event->setWorkLoop(0);
141 event->setNext(0);
142 event->release();
143 }
144 eventChain = 0;
145
146 // Either we have a partial initialisation to clean up
147 // or we the workThread itself is performing hari-kari.
148 // either way clean up all of our resources and return.
149
150 if (controlG) {
151 controlG->release();
152 controlG = 0;
153 }
154
155 if (workToDoLock) {
156 IOSimpleLockFree(workToDoLock);
157 workToDoLock = 0;
158 }
159
160 if (gateLock) {
161 IORecursiveLockFree(gateLock);
162 gateLock = 0;
163 }
164
165 super::free();
166 }
167}
168
169IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent)
170{
171 return controlG->runCommand((void *) mAddEvent, (void *) newEvent);
172}
173
174IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove)
175{
176 return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove);
177}
178
179void IOWorkLoop::enableAllEventSources() const
180{
181 IOEventSource *event;
182
183 for (event = eventChain; event; event = event->getNext())
184 event->enable();
185}
186
187void IOWorkLoop::disableAllEventSources() const
188{
189 IOEventSource *event;
190
191 for (event = eventChain; event; event = event->getNext())
192 if (event != controlG) // Don't disable the control gate
193 event->disable();
194}
195
196void IOWorkLoop::enableAllInterrupts() const
197{
198 IOEventSource *event;
199
200 for (event = eventChain; event; event = event->getNext())
201 if (OSDynamicCast(IOInterruptEventSource, event))
202 event->enable();
203}
204
205void IOWorkLoop::disableAllInterrupts() const
206{
207 IOEventSource *event;
208
209 for (event = eventChain; event; event = event->getNext())
210 if (OSDynamicCast(IOInterruptEventSource, event))
211 event->disable();
212}
213
214#if KDEBUG
215#define IOTimeClientS() \
216do { \
217 IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT), \
218 (unsigned int) this, (unsigned int) event); \
219} while(0)
220
221#define IOTimeClientE() \
222do { \
223 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT), \
224 (unsigned int) this, (unsigned int) event); \
225} while(0)
226
227#define IOTimeWorkS() \
228do { \
229 IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK), (unsigned int) this); \
230} while(0)
231
232#define IOTimeWorkE() \
233do { \
234 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_WORK),(unsigned int) this); \
235} while(0)
236
237#else /* !KDEBUG */
238
239#define IOTimeClientS()
240#define IOTimeClientE()
241#define IOTimeWorkS()
242#define IOTimeWorkE()
243
244#endif /* KDEBUG */
245
0c530ab8 246/* virtual */ bool IOWorkLoop::runEventSources()
1c79356b 247{
0c530ab8
A
248 bool res = false;
249 closeGate();
250 if (ISSETP(&fFlags, kLoopTerminate))
251 goto abort;
21362eb3 252
6601e61a 253 IOTimeWorkS();
0c530ab8
A
254 bool more;
255 do {
256 CLRP(&fFlags, kLoopRestart);
257 workToDo = more = false;
258 for (IOEventSource *evnt = eventChain; evnt; evnt = evnt->getNext()) {
259
260 IOTimeClientS();
261 more |= evnt->checkForWork();
262 IOTimeClientE();
263
264 if (ISSETP(&fFlags, kLoopTerminate))
265 goto abort;
266 else if (fFlags & kLoopRestart) {
267 more = true;
268 break;
269 }
270 }
271 } while (more);
4452a7af 272
0c530ab8 273 res = true;
6601e61a
A
274 IOTimeWorkE();
275
0c530ab8
A
276abort:
277 openGate();
278 return res;
279}
280
281/* virtual */ void IOWorkLoop::threadMain()
282{
283 do {
284 if ( !runEventSources() )
285 goto exitThread;
6601e61a 286
0c530ab8 287 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
1c79356b
A
288 if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) {
289 assert_wait((void *) &workToDo, false);
290 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
291
0c530ab8
A
292 thread_continue_t cptr = OSMemberFunctionCast(
293 thread_continue_t, this, &IOWorkLoop::threadMain);
294 thread_block_parameter(cptr, this);
1c79356b
A
295 /* NOTREACHED */
296 }
297
298 // At this point we either have work to do or we need
299 // to commit suicide. But no matter
300 // Clear the simple lock and retore the interrupt state
301 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
0c530ab8 302 } while(workToDo);
1c79356b
A
303
304exitThread:
305 workThread = 0; // Say we don't have a loop and free ourselves
306 free();
9bccf70c 307 IOExitThread();
1c79356b
A
308}
309
310IOThread IOWorkLoop::getThread() const
311{
312 return workThread;
313}
314
315bool IOWorkLoop::onThread() const
316{
317 return (IOThreadSelf() == workThread);
318}
319
320bool IOWorkLoop::inGate() const
321{
322 return IORecursiveLockHaveLock(gateLock);
323}
324
325// Internal APIs used by event sources to control the thread
326void IOWorkLoop::signalWorkAvailable()
327{
328 if (workToDoLock) {
329 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
330 workToDo = true;
331 thread_wakeup_one((void *) &workToDo);
332 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
333 }
334}
335
336void IOWorkLoop::openGate()
337{
338 IORecursiveLockUnlock(gateLock);
339}
340
341void IOWorkLoop::closeGate()
342{
343 IORecursiveLockLock(gateLock);
344}
345
346bool IOWorkLoop::tryCloseGate()
347{
348 return IORecursiveLockTryLock(gateLock) != 0;
349}
350
351int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType)
352{
353 return IORecursiveLockSleep(gateLock, event, interuptibleType);
354}
355
356void IOWorkLoop::wakeupGate(void *event, bool oneThread)
357{
358 IORecursiveLockWakeup(gateLock, event, oneThread);
359}
360
0b4e3aa0 361IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target,
55e303ae
A
362 void *arg0, void *arg1,
363 void *arg2, void *arg3)
0b4e3aa0
A
364{
365 IOReturn res;
366
367 // closeGate is recursive so don't worry if we already hold the lock.
368 closeGate();
369 res = (*inAction)(target, arg0, arg1, arg2, arg3);
370 openGate();
371
372 return res;
373}
374
1c79356b
A
375IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *)
376{
377 maintCommandEnum command = (maintCommandEnum) (vm_address_t) inC;
378 IOEventSource *inEvent = (IOEventSource *) inD;
379 IOReturn res = kIOReturnSuccess;
380
381 switch (command)
382 {
383 case mAddEvent:
9bccf70c
A
384 if (!inEvent->getWorkLoop()) {
385 SETP(&fFlags, kLoopRestart);
386
387 inEvent->retain();
388 inEvent->setWorkLoop(this);
389 inEvent->setNext(0);
390
391 if (!eventChain)
392 eventChain = inEvent;
393 else {
394 IOEventSource *event, *next;
395
396 for (event = eventChain; (next = event->getNext()); event = next)
397 ;
398 event->setNext(inEvent);
399 }
1c79356b
A
400 }
401 break;
402
403 case mRemoveEvent:
9bccf70c
A
404 if (inEvent->getWorkLoop()) {
405 if (eventChain == inEvent)
406 eventChain = inEvent->getNext();
407 else {
408 IOEventSource *event, *next;
409
410 event = eventChain;
411 while ((next = event->getNext()) && next != inEvent)
412 event = next;
413
414 if (!next) {
415 res = kIOReturnBadArgument;
416 break;
417 }
418 event->setNext(inEvent->getNext());
1c79356b 419 }
9bccf70c
A
420
421 inEvent->setWorkLoop(0);
422 inEvent->setNext(0);
423 inEvent->release();
424 SETP(&fFlags, kLoopRestart);
1c79356b 425 }
1c79356b
A
426 break;
427
428 default:
429 return kIOReturnUnsupported;
430 }
431
432 return res;
433}