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