]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOWorkLoop.cpp
xnu-792.6.61.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 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
37839358
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.
1c79356b 11 *
37839358
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
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
37839358
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.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
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
A
40OSMetaClassDefineReservedUsed(IOWorkLoop, 0);
41
1c79356b
A
42OSMetaClassDefineReservedUnused(IOWorkLoop, 1);
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::
73 commandGate(this, (IOCommandGate::Action) &IOWorkLoop::_maintRequest);
74 if ( !controlG )
75 return false;
76
77 IOSimpleLockInit(workToDoLock);
78 workToDo = false;
79
80 // Point the controlGate at the workLoop. Usually addEventSource
81 // does this automatically. The problem is in this case addEventSource
82 // uses the control gate and it has to be bootstrapped.
83 controlG->setWorkLoop(this);
84 if (addEventSource(controlG) != kIOReturnSuccess)
85 return false;
86
91447636 87 workThread = IOCreateThread((thread_continue_t)threadMainContinuation, this);
1c79356b
A
88 if (!workThread)
89 return false;
90
91 return true;
92}
93
94IOWorkLoop *
95IOWorkLoop::workLoop()
96{
97 IOWorkLoop *me = new IOWorkLoop;
98
99 if (me && !me->init()) {
55e303ae 100 me->release();
1c79356b
A
101 return 0;
102 }
103
104 return me;
105}
106
107// Free is called twice:
108// First when the atomic retainCount transitions from 1 -> 0
109// Secondly when the work loop itself is commiting hari kari
110// Hence the each leg of the free must be single threaded.
111void IOWorkLoop::free()
112{
113 if (workThread) {
114 IOInterruptState is;
115
116 // If we are here then we must be trying to shut down this work loop
117 // in this case disable all of the event source, mark the loop for
118 // as terminating and wakeup the work thread itself and return
119 // Note: we hold the gate across the entire operation mainly for the
120 // benefit of our event sources so we can disable them cleanly.
121 closeGate();
122
123 disableAllEventSources();
124
125 is = IOSimpleLockLockDisableInterrupt(workToDoLock);
126 SETP(&fFlags, kLoopTerminate);
127 thread_wakeup_one((void *) &workToDo);
128 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
129
130 openGate();
131 }
132 else /* !workThread */ {
133 IOEventSource *event, *next;
134
135 for (event = eventChain; event; event = next) {
136 next = event->getNext();
137 event->setWorkLoop(0);
138 event->setNext(0);
139 event->release();
140 }
141 eventChain = 0;
142
143 // Either we have a partial initialisation to clean up
144 // or we the workThread itself is performing hari-kari.
145 // either way clean up all of our resources and return.
146
147 if (controlG) {
148 controlG->release();
149 controlG = 0;
150 }
151
152 if (workToDoLock) {
153 IOSimpleLockFree(workToDoLock);
154 workToDoLock = 0;
155 }
156
157 if (gateLock) {
158 IORecursiveLockFree(gateLock);
159 gateLock = 0;
160 }
161
162 super::free();
163 }
164}
165
166IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent)
167{
168 return controlG->runCommand((void *) mAddEvent, (void *) newEvent);
169}
170
171IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove)
172{
173 return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove);
174}
175
176void IOWorkLoop::enableAllEventSources() const
177{
178 IOEventSource *event;
179
180 for (event = eventChain; event; event = event->getNext())
181 event->enable();
182}
183
184void IOWorkLoop::disableAllEventSources() const
185{
186 IOEventSource *event;
187
188 for (event = eventChain; event; event = event->getNext())
189 if (event != controlG) // Don't disable the control gate
190 event->disable();
191}
192
193void IOWorkLoop::enableAllInterrupts() const
194{
195 IOEventSource *event;
196
197 for (event = eventChain; event; event = event->getNext())
198 if (OSDynamicCast(IOInterruptEventSource, event))
199 event->enable();
200}
201
202void IOWorkLoop::disableAllInterrupts() const
203{
204 IOEventSource *event;
205
206 for (event = eventChain; event; event = event->getNext())
207 if (OSDynamicCast(IOInterruptEventSource, event))
208 event->disable();
209}
210
211#if KDEBUG
212#define IOTimeClientS() \
213do { \
214 IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT), \
215 (unsigned int) this, (unsigned int) event); \
216} while(0)
217
218#define IOTimeClientE() \
219do { \
220 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT), \
221 (unsigned int) this, (unsigned int) event); \
222} while(0)
223
224#define IOTimeWorkS() \
225do { \
226 IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK), (unsigned int) this); \
227} while(0)
228
229#define IOTimeWorkE() \
230do { \
231 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_WORK),(unsigned int) this); \
232} while(0)
233
234#else /* !KDEBUG */
235
236#define IOTimeClientS()
237#define IOTimeClientE()
238#define IOTimeWorkS()
239#define IOTimeWorkE()
240
241#endif /* KDEBUG */
242
91447636 243void IOWorkLoop::threadMainContinuation(IOWorkLoop *self)
1c79356b 244{
91447636 245 self->threadMain();
1c79356b
A
246}
247
248void IOWorkLoop::threadMain()
249{
250 CLRP(&fFlags, kLoopRestart);
251
252 for (;;) {
253 bool more;
254 IOInterruptState is;
255
256 IOTimeWorkS();
257
258 closeGate();
259 if (ISSETP(&fFlags, kLoopTerminate))
260 goto exitThread;
261
262 do {
263 workToDo = more = false;
264 for (IOEventSource *event = eventChain; event; event = event->getNext()) {
265
266 IOTimeClientS();
267 more |= event->checkForWork();
268 IOTimeClientE();
269
270 if (ISSETP(&fFlags, kLoopTerminate))
271 goto exitThread;
272 else if (fFlags & kLoopRestart) {
273 CLRP(&fFlags, kLoopRestart);
274 continue;
275 }
276 }
277 } while (more);
278
279 IOTimeWorkE();
280
281 openGate();
282
283 is = IOSimpleLockLockDisableInterrupt(workToDoLock);
284 if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) {
285 assert_wait((void *) &workToDo, false);
286 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
287
91447636 288 thread_block_parameter((thread_continue_t)threadMainContinuation, this);
1c79356b
A
289 /* NOTREACHED */
290 }
291
292 // At this point we either have work to do or we need
293 // to commit suicide. But no matter
294 // Clear the simple lock and retore the interrupt state
295 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
296 if (workToDo)
297 continue;
298 else
299 break;
300 }
301
302exitThread:
303 workThread = 0; // Say we don't have a loop and free ourselves
304 free();
9bccf70c 305 IOExitThread();
1c79356b
A
306}
307
308IOThread IOWorkLoop::getThread() const
309{
310 return workThread;
311}
312
313bool IOWorkLoop::onThread() const
314{
315 return (IOThreadSelf() == workThread);
316}
317
318bool IOWorkLoop::inGate() const
319{
320 return IORecursiveLockHaveLock(gateLock);
321}
322
323// Internal APIs used by event sources to control the thread
324void IOWorkLoop::signalWorkAvailable()
325{
326 if (workToDoLock) {
327 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
328 workToDo = true;
329 thread_wakeup_one((void *) &workToDo);
330 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
331 }
332}
333
334void IOWorkLoop::openGate()
335{
336 IORecursiveLockUnlock(gateLock);
337}
338
339void IOWorkLoop::closeGate()
340{
341 IORecursiveLockLock(gateLock);
342}
343
344bool IOWorkLoop::tryCloseGate()
345{
346 return IORecursiveLockTryLock(gateLock) != 0;
347}
348
349int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType)
350{
351 return IORecursiveLockSleep(gateLock, event, interuptibleType);
352}
353
354void IOWorkLoop::wakeupGate(void *event, bool oneThread)
355{
356 IORecursiveLockWakeup(gateLock, event, oneThread);
357}
358
0b4e3aa0 359IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target,
55e303ae
A
360 void *arg0, void *arg1,
361 void *arg2, void *arg3)
0b4e3aa0
A
362{
363 IOReturn res;
364
365 // closeGate is recursive so don't worry if we already hold the lock.
366 closeGate();
367 res = (*inAction)(target, arg0, arg1, arg2, arg3);
368 openGate();
369
370 return res;
371}
372
1c79356b
A
373IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *)
374{
375 maintCommandEnum command = (maintCommandEnum) (vm_address_t) inC;
376 IOEventSource *inEvent = (IOEventSource *) inD;
377 IOReturn res = kIOReturnSuccess;
378
379 switch (command)
380 {
381 case mAddEvent:
9bccf70c
A
382 if (!inEvent->getWorkLoop()) {
383 SETP(&fFlags, kLoopRestart);
384
385 inEvent->retain();
386 inEvent->setWorkLoop(this);
387 inEvent->setNext(0);
388
389 if (!eventChain)
390 eventChain = inEvent;
391 else {
392 IOEventSource *event, *next;
393
394 for (event = eventChain; (next = event->getNext()); event = next)
395 ;
396 event->setNext(inEvent);
397 }
1c79356b
A
398 }
399 break;
400
401 case mRemoveEvent:
9bccf70c
A
402 if (inEvent->getWorkLoop()) {
403 if (eventChain == inEvent)
404 eventChain = inEvent->getNext();
405 else {
406 IOEventSource *event, *next;
407
408 event = eventChain;
409 while ((next = event->getNext()) && next != inEvent)
410 event = next;
411
412 if (!next) {
413 res = kIOReturnBadArgument;
414 break;
415 }
416 event->setNext(inEvent->getNext());
1c79356b 417 }
9bccf70c
A
418
419 inEvent->setWorkLoop(0);
420 inEvent->setNext(0);
421 inEvent->release();
422 SETP(&fFlags, kLoopRestart);
1c79356b 423 }
1c79356b
A
424 break;
425
426 default:
427 return kIOReturnUnsupported;
428 }
429
430 return res;
431}