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