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