]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
37839358 A |
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. | |
1c79356b | 11 | * |
37839358 A |
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 | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
37839358 A |
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. | |
1c79356b A |
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 | |
0b4e3aa0 A |
40 | OSMetaClassDefineReservedUsed(IOWorkLoop, 0); |
41 | ||
1c79356b A |
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 | ||
1c79356b A |
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 | ||
91447636 | 87 | workThread = IOCreateThread((thread_continue_t)threadMainContinuation, this); |
1c79356b A |
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()) { | |
55e303ae | 100 | me->release(); |
1c79356b A |
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 | ||
91447636 | 243 | void IOWorkLoop::threadMainContinuation(IOWorkLoop *self) |
1c79356b | 244 | { |
91447636 | 245 | self->threadMain(); |
1c79356b A |
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 | ||
91447636 | 288 | thread_block_parameter((thread_continue_t)threadMainContinuation, this); |
1c79356b A |
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(); | |
9bccf70c | 305 | IOExitThread(); |
1c79356b A |
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 | ||
0b4e3aa0 | 359 | IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target, |
55e303ae A |
360 | void *arg0, void *arg1, |
361 | void *arg2, void *arg3) | |
0b4e3aa0 A |
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 | ||
1c79356b A |
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: | |
9bccf70c A |
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 | } | |
1c79356b A |
398 | } |
399 | break; | |
400 | ||
401 | case mRemoveEvent: | |
9bccf70c A |
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()); | |
1c79356b | 417 | } |
9bccf70c A |
418 | |
419 | inEvent->setWorkLoop(0); | |
420 | inEvent->setNext(0); | |
421 | inEvent->release(); | |
422 | SETP(&fFlags, kLoopRestart); | |
1c79356b | 423 | } |
1c79356b A |
424 | break; |
425 | ||
426 | default: | |
427 | return kIOReturnUnsupported; | |
428 | } | |
429 | ||
430 | return res; | |
431 | } |