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