]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOWorkLoop.cpp
1581f38ca02a226847681d898ce06cbac57de65c
[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 register thread_t mythread = current_thread();
63
64 // Make sure that this thread always has a kernel stack
65 stack_privilege(mythread);
66 thread_set_cont_arg((int) self);
67 threadMainContinuation();
68 }
69
70 bool IOWorkLoop::init()
71 {
72 // The super init and gateLock allocation MUST be done first
73 if ( !super::init() )
74 return false;
75
76 if ( !(gateLock = IORecursiveLockAlloc()) )
77 return false;
78
79 if ( !(workToDoLock = IOSimpleLockAlloc()) )
80 return false;
81
82 controlG = IOCommandGate::
83 commandGate(this, (IOCommandGate::Action) &IOWorkLoop::_maintRequest);
84 if ( !controlG )
85 return false;
86
87 IOSimpleLockInit(workToDoLock);
88 workToDo = false;
89
90 // Point the controlGate at the workLoop. Usually addEventSource
91 // does this automatically. The problem is in this case addEventSource
92 // uses the control gate and it has to be bootstrapped.
93 controlG->setWorkLoop(this);
94 if (addEventSource(controlG) != kIOReturnSuccess)
95 return false;
96
97 workThread = IOCreateThread(launchThreadMain, (void *) this);
98 if (!workThread)
99 return false;
100
101 return true;
102 }
103
104 IOWorkLoop *
105 IOWorkLoop::workLoop()
106 {
107 IOWorkLoop *me = new IOWorkLoop;
108
109 if (me && !me->init()) {
110 me->free();
111 return 0;
112 }
113
114 return me;
115 }
116
117 // Free is called twice:
118 // First when the atomic retainCount transitions from 1 -> 0
119 // Secondly when the work loop itself is commiting hari kari
120 // Hence the each leg of the free must be single threaded.
121 void IOWorkLoop::free()
122 {
123 if (workThread) {
124 IOInterruptState is;
125
126 // If we are here then we must be trying to shut down this work loop
127 // in this case disable all of the event source, mark the loop for
128 // as terminating and wakeup the work thread itself and return
129 // Note: we hold the gate across the entire operation mainly for the
130 // benefit of our event sources so we can disable them cleanly.
131 closeGate();
132
133 disableAllEventSources();
134
135 is = IOSimpleLockLockDisableInterrupt(workToDoLock);
136 SETP(&fFlags, kLoopTerminate);
137 thread_wakeup_one((void *) &workToDo);
138 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
139
140 openGate();
141 }
142 else /* !workThread */ {
143 IOEventSource *event, *next;
144
145 for (event = eventChain; event; event = next) {
146 next = event->getNext();
147 event->setWorkLoop(0);
148 event->setNext(0);
149 event->release();
150 }
151 eventChain = 0;
152
153 // Either we have a partial initialisation to clean up
154 // or we the workThread itself is performing hari-kari.
155 // either way clean up all of our resources and return.
156
157 if (controlG) {
158 controlG->release();
159 controlG = 0;
160 }
161
162 if (workToDoLock) {
163 IOSimpleLockFree(workToDoLock);
164 workToDoLock = 0;
165 }
166
167 if (gateLock) {
168 IORecursiveLockFree(gateLock);
169 gateLock = 0;
170 }
171
172 super::free();
173 }
174 }
175
176 IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent)
177 {
178 return controlG->runCommand((void *) mAddEvent, (void *) newEvent);
179 }
180
181 IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove)
182 {
183 return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove);
184 }
185
186 void IOWorkLoop::enableAllEventSources() const
187 {
188 IOEventSource *event;
189
190 for (event = eventChain; event; event = event->getNext())
191 event->enable();
192 }
193
194 void IOWorkLoop::disableAllEventSources() const
195 {
196 IOEventSource *event;
197
198 for (event = eventChain; event; event = event->getNext())
199 if (event != controlG) // Don't disable the control gate
200 event->disable();
201 }
202
203 void IOWorkLoop::enableAllInterrupts() const
204 {
205 IOEventSource *event;
206
207 for (event = eventChain; event; event = event->getNext())
208 if (OSDynamicCast(IOInterruptEventSource, event))
209 event->enable();
210 }
211
212 void IOWorkLoop::disableAllInterrupts() const
213 {
214 IOEventSource *event;
215
216 for (event = eventChain; event; event = event->getNext())
217 if (OSDynamicCast(IOInterruptEventSource, event))
218 event->disable();
219 }
220
221 #if KDEBUG
222 #define IOTimeClientS() \
223 do { \
224 IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT), \
225 (unsigned int) this, (unsigned int) event); \
226 } while(0)
227
228 #define IOTimeClientE() \
229 do { \
230 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT), \
231 (unsigned int) this, (unsigned int) event); \
232 } while(0)
233
234 #define IOTimeWorkS() \
235 do { \
236 IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK), (unsigned int) this); \
237 } while(0)
238
239 #define IOTimeWorkE() \
240 do { \
241 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_WORK),(unsigned int) this); \
242 } while(0)
243
244 #else /* !KDEBUG */
245
246 #define IOTimeClientS()
247 #define IOTimeClientE()
248 #define IOTimeWorkS()
249 #define IOTimeWorkE()
250
251 #endif /* KDEBUG */
252
253 void IOWorkLoop::threadMainContinuation()
254 {
255 IOWorkLoop* self;
256 self = (IOWorkLoop *) thread_get_cont_arg();
257
258 self->threadMain();
259 }
260
261 void IOWorkLoop::threadMain()
262 {
263 CLRP(&fFlags, kLoopRestart);
264
265 for (;;) {
266 bool more;
267 IOInterruptState is;
268
269 IOTimeWorkS();
270
271 closeGate();
272 if (ISSETP(&fFlags, kLoopTerminate))
273 goto exitThread;
274
275 do {
276 workToDo = more = false;
277 for (IOEventSource *event = eventChain; event; event = event->getNext()) {
278
279 IOTimeClientS();
280 more |= event->checkForWork();
281 IOTimeClientE();
282
283 if (ISSETP(&fFlags, kLoopTerminate))
284 goto exitThread;
285 else if (fFlags & kLoopRestart) {
286 CLRP(&fFlags, kLoopRestart);
287 continue;
288 }
289 }
290 } while (more);
291
292 IOTimeWorkE();
293
294 openGate();
295
296 is = IOSimpleLockLockDisableInterrupt(workToDoLock);
297 if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) {
298 assert_wait((void *) &workToDo, false);
299 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
300
301 #if defined (__i386__)
302 thread_block(0);
303 continue;
304 #else
305 thread_set_cont_arg((int) this);
306 thread_block(&threadMainContinuation);
307 #endif
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(0);
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 SETP(&fFlags, kLoopRestart);
402 inEvent->retain();
403 inEvent->setWorkLoop(this);
404 inEvent->setNext(0);
405
406 if (!eventChain)
407 eventChain = inEvent;
408 else {
409 IOEventSource *event, *next;
410
411 for (event = eventChain; (next = event->getNext()); event = next)
412 ;
413 event->setNext(inEvent);
414 }
415 break;
416
417 case mRemoveEvent:
418 if (eventChain == inEvent)
419 eventChain = inEvent->getNext();
420 else {
421 IOEventSource *event, *next;
422
423 event = eventChain;
424 while ((next = event->getNext()) && next != inEvent)
425 event = next;
426
427 if (!next) {
428 res = kIOReturnBadArgument;
429 break;
430 }
431 event->setNext(inEvent->getNext());
432 }
433
434 inEvent->setWorkLoop(0);
435 inEvent->setNext(0);
436 inEvent->release();
437 SETP(&fFlags, kLoopRestart);
438 break;
439
440 default:
441 return kIOReturnUnsupported;
442 }
443
444 return res;
445 }