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