]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOWorkLoop.cpp
f2c11d9486da297fb715d8dddab212b631a771ef
[apple/xnu.git] / iokit / Kernel / IOWorkLoop.cpp
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
30
31 HISTORY
32 1998-7-13 Godfrey van der Linden(gvdl)
33 Created.
34 */
35 #include <IOKit/IOWorkLoop.h>
36 #include <IOKit/IOEventSource.h>
37 #include <IOKit/IOInterruptEventSource.h>
38 #include <IOKit/IOCommandGate.h>
39 #include <IOKit/IOTimeStamp.h>
40
41 #define super OSObject
42
43 OSDefineMetaClassAndStructors(IOWorkLoop, OSObject);
44
45 // Block of unused functions intended for future use
46 OSMetaClassDefineReservedUsed(IOWorkLoop, 0);
47
48 OSMetaClassDefineReservedUnused(IOWorkLoop, 1);
49 OSMetaClassDefineReservedUnused(IOWorkLoop, 2);
50 OSMetaClassDefineReservedUnused(IOWorkLoop, 3);
51 OSMetaClassDefineReservedUnused(IOWorkLoop, 4);
52 OSMetaClassDefineReservedUnused(IOWorkLoop, 5);
53 OSMetaClassDefineReservedUnused(IOWorkLoop, 6);
54 OSMetaClassDefineReservedUnused(IOWorkLoop, 7);
55
56 enum IOWorkLoopState { kLoopRestart = 0x1, kLoopTerminate = 0x2 };
57 static inline void SETP(void *addr, unsigned int flag)
58 { unsigned int *num = (unsigned int *) addr; *num |= flag; }
59 static inline void CLRP(void *addr, unsigned int flag)
60 { unsigned int *num = (unsigned int *) addr; *num &= ~flag; }
61 static inline bool ISSETP(void *addr, unsigned int flag)
62 { unsigned int *num = (unsigned int *) addr; return (*num & flag) != 0; }
63
64 #define fFlags loopRestart
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((thread_continue_t)threadMainContinuation, 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(IOWorkLoop *self)
250 {
251 self->threadMain();
252 }
253
254 void IOWorkLoop::threadMain()
255 {
256 CLRP(&fFlags, kLoopRestart);
257
258 for (;;) {
259 bool more;
260 IOInterruptState is;
261
262 IOTimeWorkS();
263
264 closeGate();
265 if (ISSETP(&fFlags, kLoopTerminate))
266 goto exitThread;
267
268 do {
269 workToDo = more = false;
270 for (IOEventSource *event = eventChain; event; event = event->getNext()) {
271
272 IOTimeClientS();
273 more |= event->checkForWork();
274 IOTimeClientE();
275
276 if (ISSETP(&fFlags, kLoopTerminate))
277 goto exitThread;
278 else if (fFlags & kLoopRestart) {
279 CLRP(&fFlags, kLoopRestart);
280 continue;
281 }
282 }
283 } while (more);
284
285 IOTimeWorkE();
286
287 openGate();
288
289 is = IOSimpleLockLockDisableInterrupt(workToDoLock);
290 if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) {
291 assert_wait((void *) &workToDo, false);
292 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
293
294 thread_block_parameter((thread_continue_t)threadMainContinuation, this);
295 /* NOTREACHED */
296 }
297
298 // At this point we either have work to do or we need
299 // to commit suicide. But no matter
300 // Clear the simple lock and retore the interrupt state
301 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
302 if (workToDo)
303 continue;
304 else
305 break;
306 }
307
308 exitThread:
309 workThread = 0; // Say we don't have a loop and free ourselves
310 free();
311 IOExitThread();
312 }
313
314 IOThread IOWorkLoop::getThread() const
315 {
316 return workThread;
317 }
318
319 bool IOWorkLoop::onThread() const
320 {
321 return (IOThreadSelf() == workThread);
322 }
323
324 bool IOWorkLoop::inGate() const
325 {
326 return IORecursiveLockHaveLock(gateLock);
327 }
328
329 // Internal APIs used by event sources to control the thread
330 void IOWorkLoop::signalWorkAvailable()
331 {
332 if (workToDoLock) {
333 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
334 workToDo = true;
335 thread_wakeup_one((void *) &workToDo);
336 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
337 }
338 }
339
340 void IOWorkLoop::openGate()
341 {
342 IORecursiveLockUnlock(gateLock);
343 }
344
345 void IOWorkLoop::closeGate()
346 {
347 IORecursiveLockLock(gateLock);
348 }
349
350 bool IOWorkLoop::tryCloseGate()
351 {
352 return IORecursiveLockTryLock(gateLock) != 0;
353 }
354
355 int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType)
356 {
357 return IORecursiveLockSleep(gateLock, event, interuptibleType);
358 }
359
360 void IOWorkLoop::wakeupGate(void *event, bool oneThread)
361 {
362 IORecursiveLockWakeup(gateLock, event, oneThread);
363 }
364
365 IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target,
366 void *arg0, void *arg1,
367 void *arg2, void *arg3)
368 {
369 IOReturn res;
370
371 // closeGate is recursive so don't worry if we already hold the lock.
372 closeGate();
373 res = (*inAction)(target, arg0, arg1, arg2, arg3);
374 openGate();
375
376 return res;
377 }
378
379 IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *)
380 {
381 maintCommandEnum command = (maintCommandEnum) (vm_address_t) inC;
382 IOEventSource *inEvent = (IOEventSource *) inD;
383 IOReturn res = kIOReturnSuccess;
384
385 switch (command)
386 {
387 case mAddEvent:
388 if (!inEvent->getWorkLoop()) {
389 SETP(&fFlags, kLoopRestart);
390
391 inEvent->retain();
392 inEvent->setWorkLoop(this);
393 inEvent->setNext(0);
394
395 if (!eventChain)
396 eventChain = inEvent;
397 else {
398 IOEventSource *event, *next;
399
400 for (event = eventChain; (next = event->getNext()); event = next)
401 ;
402 event->setNext(inEvent);
403 }
404 }
405 break;
406
407 case mRemoveEvent:
408 if (inEvent->getWorkLoop()) {
409 if (eventChain == inEvent)
410 eventChain = inEvent->getNext();
411 else {
412 IOEventSource *event, *next;
413
414 event = eventChain;
415 while ((next = event->getNext()) && next != inEvent)
416 event = next;
417
418 if (!next) {
419 res = kIOReturnBadArgument;
420 break;
421 }
422 event->setNext(inEvent->getNext());
423 }
424
425 inEvent->setWorkLoop(0);
426 inEvent->setNext(0);
427 inEvent->release();
428 SETP(&fFlags, kLoopRestart);
429 }
430 break;
431
432 default:
433 return kIOReturnUnsupported;
434 }
435
436 return res;
437 }