]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOWorkLoop.cpp
xnu-517.12.7.tar.gz
[apple/xnu.git] / iokit / Kernel / IOWorkLoop.cpp
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 /*
23 Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
24
25 HISTORY
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
37 OSDefineMetaClassAndStructors(IOWorkLoop, OSObject);
38
39 // Block of unused functions intended for future use
40 OSMetaClassDefineReservedUsed(IOWorkLoop, 0);
41
42 OSMetaClassDefineReservedUnused(IOWorkLoop, 1);
43 OSMetaClassDefineReservedUnused(IOWorkLoop, 2);
44 OSMetaClassDefineReservedUnused(IOWorkLoop, 3);
45 OSMetaClassDefineReservedUnused(IOWorkLoop, 4);
46 OSMetaClassDefineReservedUnused(IOWorkLoop, 5);
47 OSMetaClassDefineReservedUnused(IOWorkLoop, 6);
48 OSMetaClassDefineReservedUnused(IOWorkLoop, 7);
49
50 enum IOWorkLoopState { kLoopRestart = 0x1, kLoopTerminate = 0x2 };
51 static inline void SETP(void *addr, unsigned int flag)
52 { unsigned int *num = (unsigned int *) addr; *num |= flag; }
53 static inline void CLRP(void *addr, unsigned int flag)
54 { unsigned int *num = (unsigned int *) addr; *num &= ~flag; }
55 static 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
60 void IOWorkLoop::launchThreadMain(void *self)
61 {
62 thread_set_cont_arg((int) self);
63 threadMainContinuation();
64 }
65
66 bool 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
100 IOWorkLoop *
101 IOWorkLoop::workLoop()
102 {
103 IOWorkLoop *me = new IOWorkLoop;
104
105 if (me && !me->init()) {
106 me->release();
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.
117 void 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
172 IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent)
173 {
174 return controlG->runCommand((void *) mAddEvent, (void *) newEvent);
175 }
176
177 IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove)
178 {
179 return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove);
180 }
181
182 void IOWorkLoop::enableAllEventSources() const
183 {
184 IOEventSource *event;
185
186 for (event = eventChain; event; event = event->getNext())
187 event->enable();
188 }
189
190 void 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
199 void 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
208 void 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() \
219 do { \
220 IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT), \
221 (unsigned int) this, (unsigned int) event); \
222 } while(0)
223
224 #define IOTimeClientE() \
225 do { \
226 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT), \
227 (unsigned int) this, (unsigned int) event); \
228 } while(0)
229
230 #define IOTimeWorkS() \
231 do { \
232 IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK), (unsigned int) this); \
233 } while(0)
234
235 #define IOTimeWorkE() \
236 do { \
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
249 void IOWorkLoop::threadMainContinuation()
250 {
251 IOWorkLoop* self;
252 self = (IOWorkLoop *) thread_get_cont_arg();
253
254 self->threadMain();
255 }
256
257 void 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
297 thread_set_cont_arg((int) this);
298 thread_block(&threadMainContinuation);
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
312 exitThread:
313 workThread = 0; // Say we don't have a loop and free ourselves
314 free();
315 IOExitThread();
316 }
317
318 IOThread IOWorkLoop::getThread() const
319 {
320 return workThread;
321 }
322
323 bool IOWorkLoop::onThread() const
324 {
325 return (IOThreadSelf() == workThread);
326 }
327
328 bool IOWorkLoop::inGate() const
329 {
330 return IORecursiveLockHaveLock(gateLock);
331 }
332
333 // Internal APIs used by event sources to control the thread
334 void 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
344 void IOWorkLoop::openGate()
345 {
346 IORecursiveLockUnlock(gateLock);
347 }
348
349 void IOWorkLoop::closeGate()
350 {
351 IORecursiveLockLock(gateLock);
352 }
353
354 bool IOWorkLoop::tryCloseGate()
355 {
356 return IORecursiveLockTryLock(gateLock) != 0;
357 }
358
359 int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType)
360 {
361 return IORecursiveLockSleep(gateLock, event, interuptibleType);
362 }
363
364 void IOWorkLoop::wakeupGate(void *event, bool oneThread)
365 {
366 IORecursiveLockWakeup(gateLock, event, oneThread);
367 }
368
369 IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target,
370 void *arg0, void *arg1,
371 void *arg2, void *arg3)
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
383 IOReturn 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:
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 }
408 }
409 break;
410
411 case mRemoveEvent:
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());
427 }
428
429 inEvent->setWorkLoop(0);
430 inEvent->setNext(0);
431 inEvent->release();
432 SETP(&fFlags, kLoopRestart);
433 }
434 break;
435
436 default:
437 return kIOReturnUnsupported;
438 }
439
440 return res;
441 }