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