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