Source to iokit/Kernel/IOWorkLoop.cpp


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
Copyright (c) 1998 Apple Computer, Inc.  All rights reserved.

HISTORY
    1998-7-13	Godfrey van der Linden(gvdl)
        Created.
*/
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOEventSource.h>
#include <IOKit/IOInterruptEventSource.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOTimeStamp.h>

#define super OSObject

OSDefineMetaClassAndStructors(IOWorkLoop, OSObject)

void IOWorkLoop::launchThreadMain(void *self)
{
    IOWorkLoop *me = (IOWorkLoop *) self;
    register thread_t mythread = current_thread();

    // Make sure that this thread always has a kernel stack
    stack_privilege(mythread);
    thread_set_cont_arg((int) me);
    threadMainContinuation();
}

bool IOWorkLoop::init()
{
    // The super init and gateLock allocation MUST be done first
    if ( !super::init() || !(gateLock = IORecursiveLockAlloc()) )
        return false;

    if ( !(workToDoLock = IOSimpleLockAlloc()) )
        return false;
    IOSimpleLockInit(workToDoLock);
    workToDo = false;

    controlG = IOCommandGate::commandGate(this,
                                          (IOCommandGate::Action) &IOWorkLoop::_maintRequest);
    if ( !controlG )
        return false;

    // Point the controlGate at the workLoop.  Usually addEventSource
    // does this automatically.  The problem is in this case addEventSource
    // uses the control gate and it has to be bootstrapped.
    controlG->setWorkLoop(this);
    if (addEventSource(controlG) != kIOReturnSuccess)
        return false;

    workThread = IOCreateThread(launchThreadMain, (void *) this);
    if (!workThread)
        return false;

    return true;
}

IOWorkLoop *
IOWorkLoop::workLoop()
{
    IOWorkLoop *me = new IOWorkLoop;

    if (me && !me->init()) {
        me->free();
        return 0;
    }

    return me;
}

void IOWorkLoop::free()
{
    // Hmm, we didn't even acquire the gate lock succesfully...
    if (!gateLock) {
	super::free();	// ... so just issue a super free
	return;		// and return immediately
    }

    if (workThread && !onThread())
        closeGate();

    if (controlG) {
        IOCommandGate *tmpControlG = controlG;
        controlG = 0;
        tmpControlG->release();
    }

    disableAllEventSources();

    // Partial initialisation no thread yet so get rid of event sources.
    if (!workThread && eventChain) {
        IOEventSource *event, *next;

        for (event = eventChain; event; event = next) {
            next = event->getNext();
            event->release();
        }
        eventChain = 0;
    }

    // This is safe as we have disabled all of our interrupt event sources.
    if (workToDoLock) {
        IOSimpleLockFree(workToDoLock);
        workToDoLock = 0;
    }

    //
    // Three cases:
    // 1> If I don't have a work thread yet just delete the command gate and
    // free the super class
    // 2> If I have a work loop and I'm on the thread, then one of the event
    // sources must be issueing a free request.  In which case we issue a
    // loop restart request and return for the main loop to detect.
    // 3> Client free request.  The workToDoLock being set to 0 is a signal
    // for the main thread to wake up and kill itself.
    // 
    if (!workThread) {
	IORecursiveLockFree(gateLock);
        super::free();
    }
    else if (onThread())
        loopRestart = true;
    else {
        thread_wakeup_one((void *) &workToDo);	// Wakeup any sleepers
        openGate();
    }
}

IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent)
{
    return controlG->runCommand((void *) mAddEvent, (void *) newEvent);
}
    
IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove)
{
    return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove);
}

void IOWorkLoop::enableAllEventSources() const
{
    IOEventSource *event;

    for (event = eventChain; event; event = event->getNext())
        event->enable();
}

void IOWorkLoop::disableAllEventSources() const
{
    IOEventSource *event;

    for (event = eventChain; event; event = event->getNext())
        event->disable();
}

void IOWorkLoop::enableAllInterrupts() const
{
    IOEventSource *event;

    for (event = eventChain; event; event = event->getNext())
        if (OSDynamicCast(IOInterruptEventSource, event))
            event->enable();
}

void IOWorkLoop::disableAllInterrupts() const
{
    IOEventSource *event;

    for (event = eventChain; event; event = event->getNext())
        if (OSDynamicCast(IOInterruptEventSource, event))
            event->disable();
}

#if KDEBUG
#define IOTimeClientS()							\
do {									\
    IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT),			\
                     (unsigned int) this, (unsigned int) event);	\
} while(0)

#define IOTimeClientE()							\
do {									\
    IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT),				\
                   (unsigned int) this, (unsigned int) event);		\
} while(0)

#define IOTimeWorkS()							\
do {									\
    IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK),	(unsigned int) this);	\
} while(0)

#define IOTimeWorkE()							\
do {									\
    IOTimeStampEnd(IODBG_WORKLOOP(IOWL_WORK),(unsigned int) this);	\
} while(0)

#else /* !KDEBUG */

#define IOTimeClientS()
#define IOTimeClientE()
#define IOTimeWorkS()
#define IOTimeWorkE()

#endif /* KDEBUG */

void IOWorkLoop::threadMainContinuation()
{
  IOWorkLoop* self;
  self = (IOWorkLoop *) thread_get_cont_arg();

  self->threadMain();
}

void IOWorkLoop::threadMain()
{
    loopRestart = false;

    for (;;) {
        bool more;

    IOTimeWorkS();

        closeGate();
        if (!workToDoLock)
            goto exitThread;

        do {
            IOEventSource *event;

restartLoop:

            workToDo = more = false;
            for (event = eventChain; event; event = event->getNext()) {

            IOTimeClientS();
                more |= event->checkForWork();
            IOTimeClientE();

                if (!workToDoLock)
                    goto exitThread;
                else if (loopRestart) {
                    loopRestart = false;

                    goto restartLoop;
                }
            }
        } while (more);

    IOTimeWorkE();

        openGate();

        if (workToDoLock) {
            IOInterruptState is;

            is = IOSimpleLockLockDisableInterrupt(workToDoLock);
            if (!workToDo) {
                assert_wait((void *) &workToDo, false);
                IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
                thread_block(&threadMainContinuation);
                /* NOTREACHED */
            }
            else
                IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
        }

        if (!workToDoLock) {
            IOEventSource *event, *next;
exitThread:

            for (event = eventChain; event; event = next) {
                next = event->getNext();
                event->release();
            }
            eventChain = 0;

	    IORecursiveLockFree(gateLock);
            super::free();
            IOExitThread(0);
        }
    }
}

IOThread IOWorkLoop::getThread() const
{
    return workThread;
}

bool IOWorkLoop::onThread() const
{
    return (IOThreadSelf() == workThread);
}

bool IOWorkLoop::inGate() const
{
    return IORecursiveLockHaveLock(gateLock);
}

// Internal APIs used by event sources to control the thread
void IOWorkLoop::signalWorkAvailable()
{
    if (workToDoLock) {
        IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
        workToDo = true;
        thread_wakeup_one((void *) &workToDo);
        IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
    }
}

void IOWorkLoop::openGate()
{
    IORecursiveLockUnlock(gateLock);
}

void IOWorkLoop::closeGate()
{
    IORecursiveLockLock(gateLock);
}

IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *)
{
    maintCommandEnum command = (maintCommandEnum) (vm_address_t) inC;
    IOEventSource *inEvent = (IOEventSource *) inD;
    IOReturn res = kIOReturnSuccess;

    switch (command)
    {
    case mAddEvent:
        loopRestart = true;
        inEvent->retain();
        inEvent->setWorkLoop(this);
        inEvent->setNext(0);

        if (!eventChain)
            eventChain = inEvent;
        else {
            IOEventSource *event, *next;

            for (event = eventChain; (next = event->getNext()); event = next)
                ;
            event->setNext(inEvent);
        }
        break;

    case mRemoveEvent:
        if (eventChain == inEvent)
            eventChain = inEvent->getNext();
        else {
            IOEventSource *event, *next;

            event = eventChain;
            while ((next = event->getNext()) && next != inEvent)
                event = next;

            if (!next) {
                res = kIOReturnBadArgument;
                break;
            }
            event->setNext(inEvent->getNext());
        }

        inEvent->setWorkLoop(0);
        inEvent->setNext(0);
        inEvent->release();
        loopRestart = true;
        break;

    default:
        return kIOReturnUnsupported;
    }

    return res;
}