]>
git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOWorkLoop.cpp
c32a565f6861d4fa71bf875d51c4033870f0bebd
2 * Copyright (c) 1998-2007 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <pexpert/pexpert.h>
30 #include <IOKit/IOWorkLoop.h>
31 #include <IOKit/IOEventSource.h>
32 #include <IOKit/IOInterruptEventSource.h>
33 #include <IOKit/IOCommandGate.h>
34 #include <IOKit/IOTimeStamp.h>
35 #include <IOKit/IOKitDebug.h>
36 #include <libkern/OSDebug.h>
38 #define super OSObject
40 OSDefineMetaClassAndStructors(IOWorkLoop
, OSObject
);
42 // Block of unused functions intended for future use
44 OSMetaClassDefineReservedUnused(IOWorkLoop
, 0);
45 OSMetaClassDefineReservedUnused(IOWorkLoop
, 1);
46 OSMetaClassDefineReservedUnused(IOWorkLoop
, 2);
48 OSMetaClassDefineReservedUsed(IOWorkLoop
, 0);
49 OSMetaClassDefineReservedUsed(IOWorkLoop
, 1);
50 OSMetaClassDefineReservedUsed(IOWorkLoop
, 2);
52 OSMetaClassDefineReservedUnused(IOWorkLoop
, 3);
53 OSMetaClassDefineReservedUnused(IOWorkLoop
, 4);
54 OSMetaClassDefineReservedUnused(IOWorkLoop
, 5);
55 OSMetaClassDefineReservedUnused(IOWorkLoop
, 6);
56 OSMetaClassDefineReservedUnused(IOWorkLoop
, 7);
58 enum IOWorkLoopState
{ kLoopRestart
= 0x1, kLoopTerminate
= 0x2 };
60 static inline void SETP(void *addr
, unsigned int flag
)
61 { unsigned int *num
= (unsigned int *) addr
; *num
|= flag
; }
62 static inline void CLRP(void *addr
, unsigned int flag
)
63 { unsigned int *num
= (unsigned int *) addr
; *num
&= ~flag
; }
64 static inline bool ISSETP(void *addr
, unsigned int flag
)
65 { unsigned int *num
= (unsigned int *) addr
; return (*num
& flag
) != 0; }
67 static inline void SETP(void *addr
, unsigned int flag
)
68 { unsigned char *num
= (unsigned char *) addr
; *num
|= flag
; }
69 static inline void CLRP(void *addr
, unsigned int flag
)
70 { unsigned char *num
= (unsigned char *) addr
; *num
&= ~flag
; }
71 static inline bool ISSETP(void *addr
, unsigned int flag
)
72 { unsigned char *num
= (unsigned char *) addr
; return (*num
& flag
) != 0; }
75 #define fFlags loopRestart
78 bool IOWorkLoop::init()
80 // The super init and gateLock allocation MUST be done first
84 if ( gateLock
== NULL
) {
85 if ( !( gateLock
= IORecursiveLockAlloc()) )
89 if ( workToDoLock
== NULL
) {
90 if ( !(workToDoLock
= IOSimpleLockAlloc()) )
92 IOSimpleLockInit(workToDoLock
);
96 if ( controlG
== NULL
) {
97 controlG
= IOCommandGate::commandGate(
100 IOCommandGate::Action
,
102 &IOWorkLoop::_maintRequest
));
106 // Point the controlGate at the workLoop. Usually addEventSource
107 // does this automatically. The problem is in this case addEventSource
108 // uses the control gate and it has to be bootstrapped.
109 controlG
->setWorkLoop(this);
110 if (addEventSource(controlG
) != kIOReturnSuccess
)
114 if ( workThread
== NULL
) {
115 thread_continue_t cptr
= OSMemberFunctionCast(
118 &IOWorkLoop::threadMain
);
119 if (KERN_SUCCESS
!= kernel_thread_start(cptr
, this, &workThread
))
127 IOWorkLoop::workLoop()
129 return IOWorkLoop::workLoopWithOptions(0);
133 IOWorkLoop::workLoopWithOptions(IOOptionBits options
)
135 IOWorkLoop
*me
= new IOWorkLoop
;
138 me
->reserved
= IONew(ExpansionData
, 1);
143 me
->reserved
->options
= options
;
146 if (me
&& !me
->init()) {
154 // Free is called twice:
155 // First when the atomic retainCount transitions from 1 -> 0
156 // Secondly when the work loop itself is commiting hari kari
157 // Hence the each leg of the free must be single threaded.
158 void IOWorkLoop::free()
163 // If we are here then we must be trying to shut down this work loop
164 // in this case disable all of the event source, mark the loop
165 // as terminating and wakeup the work thread itself and return
166 // Note: we hold the gate across the entire operation mainly for the
167 // benefit of our event sources so we can disable them cleanly.
170 disableAllEventSources();
172 is
= IOSimpleLockLockDisableInterrupt(workToDoLock
);
173 SETP(&fFlags
, kLoopTerminate
);
174 thread_wakeup_one((void *) &workToDo
);
175 IOSimpleLockUnlockEnableInterrupt(workToDoLock
, is
);
179 else /* !workThread */ {
180 IOEventSource
*event
, *next
;
182 for (event
= eventChain
; event
; event
= next
) {
183 next
= event
->getNext();
184 event
->setWorkLoop(0);
190 // Either we have a partial initialization to clean up
191 // or the workThread itself is performing hari-kari.
192 // Either way clean up all of our resources and return.
200 IOSimpleLockFree(workToDoLock
);
205 IORecursiveLockFree(gateLock
);
209 IODelete(reserved
, ExpansionData
, 1);
217 IOReturn
IOWorkLoop::addEventSource(IOEventSource
*newEvent
)
219 return controlG
->runCommand((void *) mAddEvent
, (void *) newEvent
);
222 IOReturn
IOWorkLoop::removeEventSource(IOEventSource
*toRemove
)
224 return controlG
->runCommand((void *) mRemoveEvent
, (void *) toRemove
);
227 void IOWorkLoop::enableAllEventSources() const
229 IOEventSource
*event
;
231 for (event
= eventChain
; event
; event
= event
->getNext())
235 void IOWorkLoop::disableAllEventSources() const
237 IOEventSource
*event
;
239 for (event
= eventChain
; event
; event
= event
->getNext())
240 if (event
!= controlG
) // Don't disable the control gate
244 void IOWorkLoop::enableAllInterrupts() const
246 IOEventSource
*event
;
248 for (event
= eventChain
; event
; event
= event
->getNext())
249 if (OSDynamicCast(IOInterruptEventSource
, event
))
253 void IOWorkLoop::disableAllInterrupts() const
255 IOEventSource
*event
;
257 for (event
= eventChain
; event
; event
= event
->getNext())
258 if (OSDynamicCast(IOInterruptEventSource
, event
))
263 #define IOTimeClientS() \
265 IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT), \
266 (unsigned int) this, (unsigned int) event); \
269 #define IOTimeClientE() \
271 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT), \
272 (unsigned int) this, (unsigned int) event); \
275 #define IOTimeWorkS() \
277 IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK), (unsigned int) this); \
280 #define IOTimeWorkE() \
282 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_WORK),(unsigned int) this); \
287 #define IOTimeClientS()
288 #define IOTimeClientE()
289 #define IOTimeWorkS()
290 #define IOTimeWorkE()
294 /* virtual */ bool IOWorkLoop::runEventSources()
297 bool traceWL
= (gIOKitTrace
& kIOTraceWorkLoops
) ? true : false;
298 bool traceES
= (gIOKitTrace
& kIOTraceEventSources
) ? true : false;
301 if (ISSETP(&fFlags
, kLoopTerminate
))
305 IOTimeStampStartConstant(IODBG_WORKLOOP(IOWL_WORK
), (uintptr_t) this);
309 CLRP(&fFlags
, kLoopRestart
);
311 IOInterruptState is
= IOSimpleLockLockDisableInterrupt(workToDoLock
);
313 IOSimpleLockUnlockEnableInterrupt(workToDoLock
, is
);
314 for (IOEventSource
*evnt
= eventChain
; evnt
; evnt
= evnt
->getNext()) {
317 IOTimeStampStartConstant(IODBG_WORKLOOP(IOWL_CLIENT
), (uintptr_t) this, (uintptr_t) evnt
);
319 more
|= evnt
->checkForWork();
322 IOTimeStampEndConstant(IODBG_WORKLOOP(IOWL_CLIENT
), (uintptr_t) this, (uintptr_t) evnt
);
324 if (ISSETP(&fFlags
, kLoopTerminate
))
326 else if (fFlags
& kLoopRestart
) {
336 IOTimeStampEndConstant(IODBG_WORKLOOP(IOWL_WORK
), (uintptr_t) this);
343 /* virtual */ void IOWorkLoop::threadMain()
347 if ( !runEventSources() )
350 IOInterruptState is
= IOSimpleLockLockDisableInterrupt(workToDoLock
);
351 if ( !ISSETP(&fFlags
, kLoopTerminate
) && !workToDo
) {
352 assert_wait((void *) &workToDo
, false);
353 IOSimpleLockUnlockEnableInterrupt(workToDoLock
, is
);
354 thread_continue_t cptr
= NULL
;
355 if (!reserved
|| !(kPreciousStack
& reserved
->options
))
356 cptr
= OSMemberFunctionCast(
357 thread_continue_t
, this, &IOWorkLoop::threadMain
);
358 thread_block_parameter(cptr
, this);
363 // At this point we either have work to do or we need
364 // to commit suicide. But no matter
365 // Clear the simple lock and retore the interrupt state
366 IOSimpleLockUnlockEnableInterrupt(workToDoLock
, is
);
370 thread_t thread
= workThread
;
371 workThread
= 0; // Say we don't have a loop and free ourselves
374 thread_deallocate(thread
);
375 (void) thread_terminate(thread
);
378 IOThread
IOWorkLoop::getThread() const
383 bool IOWorkLoop::onThread() const
385 return (IOThreadSelf() == workThread
);
388 bool IOWorkLoop::inGate() const
390 return IORecursiveLockHaveLock(gateLock
);
393 // Internal APIs used by event sources to control the thread
394 void IOWorkLoop::signalWorkAvailable()
397 IOInterruptState is
= IOSimpleLockLockDisableInterrupt(workToDoLock
);
399 thread_wakeup_one((void *) &workToDo
);
400 IOSimpleLockUnlockEnableInterrupt(workToDoLock
, is
);
404 void IOWorkLoop::openGate()
406 IORecursiveLockUnlock(gateLock
);
409 void IOWorkLoop::closeGate()
411 IORecursiveLockLock(gateLock
);
414 bool IOWorkLoop::tryCloseGate()
416 return IORecursiveLockTryLock(gateLock
) != 0;
419 int IOWorkLoop::sleepGate(void *event
, UInt32 interuptibleType
)
421 return IORecursiveLockSleep(gateLock
, event
, interuptibleType
);
424 int IOWorkLoop::sleepGate(void *event
, AbsoluteTime deadline
, UInt32 interuptibleType
)
426 return IORecursiveLockSleepDeadline(gateLock
, event
, deadline
, interuptibleType
);
429 void IOWorkLoop::wakeupGate(void *event
, bool oneThread
)
431 IORecursiveLockWakeup(gateLock
, event
, oneThread
);
434 IOReturn
IOWorkLoop::runAction(Action inAction
, OSObject
*target
,
435 void *arg0
, void *arg1
,
436 void *arg2
, void *arg3
)
440 // closeGate is recursive so don't worry if we already hold the lock.
442 res
= (*inAction
)(target
, arg0
, arg1
, arg2
, arg3
);
448 IOReturn
IOWorkLoop::_maintRequest(void *inC
, void *inD
, void *, void *)
450 maintCommandEnum command
= (maintCommandEnum
) (uintptr_t) inC
;
451 IOEventSource
*inEvent
= (IOEventSource
*) inD
;
452 IOReturn res
= kIOReturnSuccess
;
457 if (!inEvent
->getWorkLoop()) {
458 SETP(&fFlags
, kLoopRestart
);
461 inEvent
->setWorkLoop(this);
465 eventChain
= inEvent
;
467 IOEventSource
*event
, *next
;
469 for (event
= eventChain
; (next
= event
->getNext()); event
= next
)
471 event
->setNext(inEvent
);
477 if (inEvent
->getWorkLoop()) {
478 if (eventChain
== inEvent
)
479 eventChain
= inEvent
->getNext();
481 IOEventSource
*event
, *next
;
484 while ((next
= event
->getNext()) && next
!= inEvent
)
488 res
= kIOReturnBadArgument
;
491 event
->setNext(inEvent
->getNext());
494 inEvent
->setWorkLoop(0);
497 SETP(&fFlags
, kLoopRestart
);
502 return kIOReturnUnsupported
;