DSS Source Code Analyse (08) - EventContext::RequestEvent

来源:互联网 发布:mac pro价格 编辑:程序博客网 时间:2024/05/18 11:01
void EventContext::RequestEvent(int theMask)
{
#if DEBUG
    fModwatched = true;
#endif


    //
    // The first time this function gets called, we're supposed to
    // call watchevent. Each subsequent time, call modwatch. That's
    // the way the MacOS X event queue works.
    
    if (fWatchEventCalled)   // the first time fWatchEventCalled == false
    {
        fEventReq.er_eventbits = theMask;
#if MACOSXEVENTQUEUE
        if (modwatch(&fEventReq, theMask) != 0)
#else
        if (select_modwatch(&fEventReq, theMask) != 0)
#endif  
            AssertV(false, OSThread::GetErrno());
    }
    else

    {

        // the first time, branch along here.

        //allocate a Unique ID for this socket, and add it to the ref table
        
#ifdef __Win32__
        //
        // Kind of a hack. On Win32, the way that we pass around the unique ID is
        // by making it the message ID of our Win32 message (see win32ev.cpp).
        // Messages must be >= WM_USER. Hence this code to restrict the numberspace
        // of our UniqueIDs. 
        if (!compare_and_store(8192, WM_USER, &sUniqueID))  // Fix 2466667: message IDs above a
            fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1);         // level are ignored, so wrap at 8192
        else
            fUniqueID = (PointerSizedInt)WM_USER;
#else
        if (!compare_and_store(10000000, 1, &sUniqueID))
            fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1);
        else
            fUniqueID = 1;
#endif


        fRef.Set(fUniqueIDStr, this);
        fEventThread->fRefTable.Register(&fRef);
            
        //fill out the eventreq data structure
        ::memset( &fEventReq, '\0', sizeof(fEventReq));
        fEventReq.er_type = EV_FD;
        fEventReq.er_handle = fFileDesc;
        fEventReq.er_eventbits = theMask;
        fEventReq.er_data = (void*)fUniqueID;

        // set fWatchEventCalled true so that above branch will be called next time
        fWatchEventCalled = true;
#if MACOSXEVENTQUEUE
        if (watchevent(&fEventReq, theMask) != 0)

#else

        // call select_watchevent, which directly call select_modwatch in Linux

        // int select_watchevent(struct eventreq *req, int which)
        // {
        //          return select_modwatch(req, which);
        // }

        if (select_watchevent(&fEventReq, theMask) != 0)
#endif  
            //this should never fail, but if it does, cleanup.
            AssertV(false, OSThread::GetErrno());
            
    }

}


// Notes: select_modwatch(struct eventreq *req, int which)

// Add or clear fd from readset or writeset for select mode

int select_modwatch(struct eventreq *req, int which)
{
    {
        //Manipulating sMaxFDPos is not pre-emptive safe, so we have to wrap it in a mutex
        //I believe this is the only variable that is not preemptive safe....
        OSMutexLocker locker(&sMaxFDPosMutex);


        //Add or remove this fd from the specified sets
        if (which & EV_RE)
        {
    #if EV_DEBUGGING
            qtss_printf("modwatch: Enabling %d in readset\n", req->er_handle);
    #endif
            FD_SET(req->er_handle, &sReadSet);
        }
        else
        {
    #if EV_DEBUGGING
            qtss_printf("modwatch: Disbling %d in readset\n", req->er_handle);
    #endif
            FD_CLR(req->er_handle, &sReadSet);
        }
        if (which & EV_WR)
        {
    #if EV_DEBUGGING
            qtss_printf("modwatch: Enabling %d in writeset\n", req->er_handle);
    #endif
            FD_SET(req->er_handle, &sWriteSet);
        }
        else
        {
    #if EV_DEBUGGING
            qtss_printf("modwatch: Disabling %d in writeset\n", req->er_handle);
    #endif
            FD_CLR(req->er_handle, &sWriteSet);
        }


        if (req->er_handle > sMaxFDPos)
            sMaxFDPos = req->er_handle;


#if EV_DEBUGGING
        qtss_printf("modwatch: MaxFDPos=%d\n", sMaxFDPos);
#endif
        //
        // Also, modifying the cookie is not preemptive safe. This must be
        // done atomically wrt setting the fd in the set. Otherwise, it is
        // possible to have a NULL cookie on a fd.
        Assert(req->er_handle < (int)(sizeof(fd_set) * 8));
        Assert(req->er_data != NULL);

        // fUniqueID is stored in req->er_data, here is set to sCookieArray

        // in select_waitevent => constructeventreq call sequence, i.e., in constructeventreq 

        //  this one of sCookieArray is set back to req->er_data, see below
        sCookieArray[req->er_handle] = req->er_data;
    }
    
    // write to the pipe so that select wakes up and registers the new mask

    // this will wake eventthread which wait in select for fds, include sPipes[0]
    int theErr = ::write(sPipes[1], "p", 1);
    Assert(theErr == 1);


    return 0;
}


// sCookieArray back to req->er_data procedure

EventThread::Entry()  {

       struct eventreq theCurrentEvent;
       ::memset( &theCurrentEvent, '\0', sizeof(theCurrentEvent) );


       select_waitevent(&theCurrentEvent, NULL) { // select_waitevent(struct eventreq *req, void* /*onlyForMacOSX*/) 

             constructeventreq(req, sCurrentFDPos, EV_RE); { // constructeventreq(struct eventreq* req, int fd, int event)

                       req->er_handle = fd;
                       req->er_eventbits = event;
                       req->er_data = sCookieArray[fd];

             }

       }

        //ok, there's data waiting on this socket. Send a wakeup.
        if (theCurrentEvent.er_data != NULL)
        {
            //The cookie in this event is an ObjectID. Resolve that objectID into
            //a pointer.
            StrPtrLen idStr((char*)&theCurrentEvent.er_data, sizeof(theCurrentEvent.er_data));
            OSRef* ref = fRefTable.Resolve(&idStr);
            if (ref != NULL)
            {
                EventContext* theContext = (EventContext*)ref->GetObject();
#if DEBUG
                theContext->fModwatched = false;
#endif
                theContext->ProcessEvent(theCurrentEvent.er_eventbits);
                fRefTable.Release(ref);         
            }
        }

}