{
#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);
// }
#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);
}
}
}