bool gCPUsRunning;
static thread_t gIOTerminateThread;
+static thread_t gIOTerminateWorkerThread;
static UInt32 gIOTerminateWork;
static OSArray * gIOTerminatePhase2List;
static OSArray * gIOStopList;
gIOStopProviderList = OSArray::withCapacity( 16 );
gIOFinalizeList = OSArray::withCapacity( 16 );
assert( gIOTerminatePhase2List && gIOStopList && gIOStopProviderList && gIOFinalizeList );
+
+ // worker thread that is responsible for terminating / cleaning up threads
+ kernel_thread_start(&terminateThread, NULL, &gIOTerminateWorkerThread);
+ assert(gIOTerminateWorkerThread);
+ thread_set_thread_name(gIOTerminateWorkerThread, "IOServiceTerminateThread");
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
return sCPULatencyHolderName[kCpuDelayBusStall];
}
+const char *getCpuInterruptDelayHolderName(void);
+const char *getCpuInterruptDelayHolderName(void) {
+ return sCPULatencyHolderName[kCpuDelayInterrupt];
+}
+
}
#endif
}
}
+// Must call this while holding gJobsLock
+void IOService::waitToBecomeTerminateThread(void)
+{
+ IOLockAssert(gJobsLock, kIOLockAssertOwned);
+ bool wait;
+ do {
+ wait = (gIOTerminateThread != THREAD_NULL);
+ if (wait) {
+ IOLockSleep(gJobsLock, &gIOTerminateThread, THREAD_UNINT);
+ }
+ } while (wait);
+ gIOTerminateThread = current_thread();
+}
+
// call with lockForArbitration
void IOService::scheduleTerminatePhase2( IOOptionBits options )
{
AbsoluteTime deadline;
uint64_t regID1;
int waitResult = THREAD_AWAKENED;
- bool wait, haveDeadline = false;
+ bool wait = false, haveDeadline = false;
if (!(__state[0] & kIOServiceInactiveState)) return;
if( (options & kIOServiceSynchronous)
&& (current_thread() != gIOTerminateThread)) {
- do {
- wait = (gIOTerminateThread != 0);
- if( wait) {
- // wait to become the terminate thread
- IOLockSleep( gJobsLock, &gIOTerminateThread, THREAD_UNINT);
- }
- } while( wait );
-
- gIOTerminateThread = current_thread();
+ waitToBecomeTerminateThread();
gIOTerminatePhase2List->setObject( this );
gIOTerminateWork++;
terminateWorker( options );
wait = (0 != (__state[1] & kIOServiceBusyStateMask));
if( wait) {
- // wait for the victim to go non-busy
+ /* wait for the victim to go non-busy */
if( !haveDeadline) {
clock_interval_to_deadline( 15, kSecondScale, &deadline );
haveDeadline = true;
}
+ /* let others do work while we wait */
+ gIOTerminateThread = 0;
+ IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false);
waitResult = IOLockSleepDeadline( gJobsLock, &gIOTerminateWork,
deadline, THREAD_UNINT );
- if( waitResult == THREAD_TIMED_OUT) {
- IOLog("%s[0x%qx]::terminate(kIOServiceSynchronous) timeout\n", getName(), getRegistryEntryID());
- }
+ if (__improbable(waitResult == THREAD_TIMED_OUT)) {
+ panic("%s[0x%qx]::terminate(kIOServiceSynchronous) timeout\n", getName(), getRegistryEntryID());
+ }
+ waitToBecomeTerminateThread();
}
} while(gIOTerminateWork || (wait && (waitResult != THREAD_TIMED_OUT)));
gIOTerminatePhase2List->setObject( this );
if( 0 == gIOTerminateWork++) {
- if( !gIOTerminateThread)
- kernel_thread_start(&terminateThread, (void *)(uintptr_t) options, &gIOTerminateThread);
- else
- IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false );
- }
+ assert(gIOTerminateWorkerThread);
+ IOLockWakeup(gJobsLock, (event_t)&gIOTerminateWork, /* one-thread */ false );
+ }
}
IOLockUnlock( gJobsLock );
release();
}
+__attribute__((__noreturn__))
void IOService::terminateThread( void * arg, wait_result_t waitResult )
{
- IOLockLock( gJobsLock );
-
- while (gIOTerminateWork)
- terminateWorker( (uintptr_t) arg );
+ // IOLockSleep re-acquires the lock on wakeup, so we only need to do this once
+ IOLockLock(gJobsLock);
+ while (true) {
+ if (gIOTerminateThread != gIOTerminateWorkerThread) {
+ waitToBecomeTerminateThread();
+ }
- thread_deallocate(gIOTerminateThread);
- gIOTerminateThread = 0;
- IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false);
+ while (gIOTerminateWork)
+ terminateWorker( (uintptr_t)arg );
- IOLockUnlock( gJobsLock );
+ gIOTerminateThread = 0;
+ IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false);
+ IOLockSleep(gJobsLock, &gIOTerminateWork, THREAD_UNINT);
+ }
}
void IOService::scheduleStop( IOService * provider )
gIOStopProviderList->tailQ( provider );
if( 0 == gIOTerminateWork++) {
- if( !gIOTerminateThread)
- kernel_thread_start(&terminateThread, (void *) 0, &gIOTerminateThread);
- else
- IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false );
+ assert(gIOTerminateWorkerThread);
+ IOLockWakeup(gJobsLock, (event_t)&gIOTerminateWork, /* one-thread */ false );
}
IOLockUnlock( gJobsLock );
{
IOLockLock( gJobsLock );
gIOFinalizeList->tailQ(this);
- if( 0 == gIOTerminateWork++)
- {
- if( !gIOTerminateThread)
- kernel_thread_start(&terminateThread, (void *) 0, &gIOTerminateThread);
- else
- IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false );
+ if( 0 == gIOTerminateWork++) {
+ assert(gIOTerminateWorkerThread);
+ IOLockWakeup(gJobsLock, (event_t)&gIOTerminateWork, /* one-thread */ false );
}
IOLockUnlock( gJobsLock );
}
notifiers = victim->copyNotifiers(gIOWillTerminateNotification, 0, 0xffffffff);
victim->invokeNotifiers(¬ifiers);
- if( 0 == victim->getClient()) {
-
- // no clients - will go to finalize
- victim->scheduleFinalize(false);
-
- } else {
- _workLoopAction( (IOWorkLoop::Action) &actionWillTerminate,
+ _workLoopAction( (IOWorkLoop::Action) &actionWillTerminate,
victim, (void *)(uintptr_t) options, (void *)(uintptr_t) doPhase2List );
- }
+
didPhase2List->headQ( victim );
}
victim->release();
}
while( (victim = (IOService *) didPhase2List->getObject(0)) ) {
-
- if( victim->lockForArbitration( true )) {
+ bool scheduleFinalize = false;
+ if( victim->lockForArbitration( true )) {
victim->__state[1] |= kIOServiceTermPhase3State;
+ scheduleFinalize = (0 == victim->getClient());
victim->unlockForArbitration();
}
_workLoopAction( (IOWorkLoop::Action) &actionDidTerminate,
_workLoopAction( (IOWorkLoop::Action) &actionDidStop,
victim, (void *)(uintptr_t) options, NULL );
}
+ // no clients - will go to finalize
+ if (scheduleFinalize) victim->scheduleFinalize(false);
didPhase2List->removeObject(0);
}
IOLockLock( gJobsLock );
doPhase3 = false;
// finalize leaves
while( (victim = (IOService *) gIOFinalizeList->getObject(0))) {
-
+ bool sendFinal = false;
IOLockUnlock( gJobsLock );
- _workLoopAction( (IOWorkLoop::Action) &actionFinalize,
+ if (victim->lockForArbitration(true)) {
+ sendFinal = (0 == (victim->__state[1] & kIOServiceFinalized));
+ if (sendFinal) victim->__state[1] |= kIOServiceFinalized;
+ victim->unlockForArbitration();
+ }
+ if (sendFinal) {
+ _workLoopAction( (IOWorkLoop::Action) &actionFinalize,
victim, (void *)(uintptr_t) options );
+ }
IOLockLock( gJobsLock );
// hold off free
freeList->setObject( victim );
} else {
// a terminated client is not ready for stop if it has clients, skip it
- if( (kIOServiceInactiveState & client->__state[0]) && client->getClient()) {
- TLOG("%s[0x%qx]::defer stop(%s[0x%qx])\n",
- client->getName(), regID2,
- client->getClient()->getName(), client->getClient()->getRegistryEntryID());
- IOServiceTrace(
- IOSERVICE_TERMINATE_STOP_DEFER,
- (uintptr_t) regID1,
- (uintptr_t) (regID1 >> 32),
- (uintptr_t) regID2,
- (uintptr_t) (regID2 >> 32));
-
- idx++;
- continue;
- }
-
+ bool deferStop = (0 != (kIOServiceInactiveState & client->__state[0]));
IOLockUnlock( gJobsLock );
+ if (deferStop && client->lockForArbitration(true)) {
+ deferStop = (0 == (client->__state[1] & kIOServiceFinalized));
+ //deferStop = (!deferStop && (0 != client->getClient()));
+ //deferStop = (0 != client->getClient());
+ client->unlockForArbitration();
+ if (deferStop) {
+ TLOG("%s[0x%qx]::defer stop()\n", client->getName(), regID2);
+ IOServiceTrace(IOSERVICE_TERMINATE_STOP_DEFER,
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32),
+ (uintptr_t) regID2,
+ (uintptr_t) (regID2 >> 32));
+
+ idx++;
+ IOLockLock( gJobsLock );
+ continue;
+ }
+ }
_workLoopAction( (IOWorkLoop::Action) &actionStop,
provider, (void *) client );
IOLockLock( gJobsLock );
OSString * str;
OSSet * set;
OSIterator * iter;
+ OSObject * obj;
OSArray * keys;
bool ok = true;
}
else if ((prop = table->getObject(gIOResourceMatchedKey)))
{
- keys = (OSArray *) copyProperty(gIOResourceMatchedKey);
+ obj = copyProperty(gIOResourceMatchedKey);
+ keys = OSDynamicCast(OSArray, obj);
ok = false;
if (keys)
{
// assuming OSSymbol
ok = ((-1U) != keys->getNextIndexOfObject(prop, 0));
- keys->release();
}
+ OSSafeReleaseNULL(obj);
}
return( ok );