bool BProcess::InsertHandler(BHandler **handlerP, BHandler *handler) { BHandler *p = *handlerP; if (!p) { *handlerP = handler; handler->m_next = handler->m_prev = NULL; return true; } const nsecs_t msgTime = handler->NextMessageTime(NULL); if (p->NextMessageTime(NULL) > msgTime) { (*handlerP)->m_prev = handler; *handlerP = handler; handler->m_next = p; handler->m_prev = NULL; } else { while (p->m_next && (p->m_next->NextMessageTime(NULL) < msgTime)) p = p->m_next; handler->m_next = p->m_next; handler->m_prev = p; if (p->m_next) p->m_next->m_prev = handler; p->m_next = handler; } return false; }
void BProcess::DispatchMessage(SLooper* looper) { //bout << "BProcess::DispatchMessage: (" << SysCurrentThread() << ")@" << SysGetRunTime() << endl; BHandler* handler = NULL; int32_t priority = B_NORMAL_PRIORITY; bool firstTime = true; m_lock.LockQuick(); //DbgOnlyFatalErrorIf(m_currentEventConcurrency >= m_maxEventConcurrency, "We have gone past the limit on concurrent handlers!"); if (m_currentEventConcurrency >= m_maxEventConcurrency) { m_lock.Unlock(); return; } m_currentEventConcurrency++; // The binder driver clears its event time when processing an event, // so we always must inform it if there is another one to schedule. m_nextEventTime = B_INFINITE_TIMEOUT; nsecs_t curTime = exact_SysGetRunTime(); while (1) { if ((handler = m_pendingHandlers) != NULL && handler->NextMessageTime(&priority) <= curTime) { m_pendingHandlers = handler->m_next; handler->m_next = handler->m_prev = NULL; if (m_pendingHandlers) m_pendingHandlers->m_prev = NULL; handler->defer_scheduling(); } else { // Nothing to do right now, time to leave. break; } // If this is the first time through the loop, we need to schedule // the next pending handler so that another SLooper thread can be // activated to execute it. if (firstTime) { ScheduleNextEvent(); firstTime = false; } // We are now going to enter user code, don't hold the lock. m_lock.Unlock(); if (handler->AttemptAcquire(this)) { shortcutDispatch: // bout << "BProcess: Thread " << SysCurrentThread() << " dispatch to " << handler << " at pri " << priority << endl; looper->_SetThreadPriority(priority); handler->dispatch_message(); // bout << "BProcess: Thread " << SysCurrentThread() << " returned from dispatch!" << endl; // Update our concept of the time. curTime = approx_SysGetRunTime(); // Big shortcut (one could even call this an optimization // for a particular performance test). If this handler // has the next message that will be processed, just do // it right now. m_lock.LockQuick(); // We can do this if... there is a pending message and it is next... const nsecs_t when = handler->NextMessageTime(&priority); if (m_pendingHandlers == NULL || when <= m_pendingHandlers->NextMessageTime(NULL)) { // And ResumeScheduling() wasn't called while processing the message... if (ResumingScheduling()) { // The above flagged that this thread called ResumeScheduling(), but it // really hasn't. Psych!! ClearSchedulingResumed(); // And it is time for the message. if (when <= curTime) { m_lock.Unlock(); // Whoosh! // bout << "BProcess: Thread " << SysCurrentThread() << " redispatch to same handler!" << endl; goto shortcutDispatch; } } } m_lock.Unlock(); handler->ResumeScheduling(); ClearSchedulingResumed(); handler->Release(this); } handler->DecRefs(this); m_lock.LockQuick(); } // We have handled all available messages. While still holding the // lock, reduce concurrency and schedule the next event to make sure // the binder is up-to-date about when it is to occur. m_currentEventConcurrency--; ScheduleNextEvent(); m_lock.Unlock(); // bout << "BProcess::DispatchMessage Exit: (" << SysCurrentThread() << ")@" << SysGetRunTime() << endl; #if TARGET_HOST == TARGET_HOST_WIN32 // Handler thread pool is implemented entirely in user space on Windows. looper->_SetThreadPriority(B_REAL_TIME_PRIORITY); #endif }