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