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