chromium之message_pump_win之二
接下来分析 MessagePumpForUI
上一篇分析MessagePumpWin,可以参考chromium之message_pump_win之一
根据对MessagePumpWin的分析,MessagePumpForUI肯定要继承MessagePumpWin,且实现三个接口
// MessagePump methods: virtual void ScheduleWork(); virtual void ScheduleDelayedWork(const Time& delayed_work_time); virtual void DoRunLoop();
先看看介绍,有点长
//----------------------------------------------------------------------------- // MessagePumpForUI extends MessagePumpWin with methods that are particular to a // MessageLoop instantiated with TYPE_UI. // // MessagePumpForUI implements a "traditional" Windows message pump. It contains // a nearly infinite loop that peeks out messages, and then dispatches them. // Intermixed with those peeks are callouts to DoWork for pending tasks, and // DoDelayedWork for pending timers. When there are no events to be serviced, // this pump goes into a wait state. In most cases, this message pump handles // all processing. // // However, when a task, or windows event, invokes on the stack a native dialog // box or such, that window typically provides a bare bones (native?) message // pump. That bare-bones message pump generally supports little more than a // peek of the Windows message queue, followed by a dispatch of the peeked // message. MessageLoop extends that bare-bones message pump to also service // Tasks, at the cost of some complexity. // // The basic structure of the extension (refered to as a sub-pump) is that a // special message, kMsgHaveWork, is repeatedly injected into the Windows // Message queue. Each time the kMsgHaveWork message is peeked, checks are // made for an extended set of events, including the availability of Tasks to // run. // // After running a task, the special message kMsgHaveWork is again posted to // the Windows Message queue, ensuring a future time slice for processing a // future event. To prevent flooding the Windows Message queue, care is taken // to be sure that at most one kMsgHaveWork message is EVER pending in the // Window's Message queue. // // There are a few additional complexities in this system where, when there are // no Tasks to run, this otherwise infinite stream of messages which drives the // sub-pump is halted. The pump is automatically re-started when Tasks are // queued. // // A second complexity is that the presence of this stream of posted tasks may // prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER. // Such paint and timer events always give priority to a posted message, such as // kMsgHaveWork messages. As a result, care is taken to do some peeking in // between the posting of each kMsgHaveWork message (i.e., after kMsgHaveWork // is peeked, and before a replacement kMsgHaveWork is posted). // // NOTE: Although it may seem odd that messages are used to start and stop this // flow (as opposed to signaling objects, etc.), it should be understood that // the native message pump will *only* respond to messages. As a result, it is // an excellent choice. It is also helpful that the starter messages that are // placed in the queue when new task arrive also awakens DoRunLoop. //
看不下去了,看代码把,
总共两个步骤:
1) have_work_ = 1;
2) 发送一个kMsgHaveWork消息,通知MessagePump 工作
void MessagePumpForUI::ScheduleWork() { if (InterlockedExchange(&have_work_, 1)) return; // Someone else continued the pumping. // Make sure the MessagePump does some work for us. PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0); }
接下来是ScheduleDelayedWork
SetTimer, https://msdn.microsoft.com/en-us/library/windows/desktop/ms644906(v=vs.85).aspx
SetTimer的精度是10ms,通过SetTimer来设置延时任务,SetTimer的第四个参数是NULL,定时到的时候,
系统会发一个WM_TIMER消息到消息队列
void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { // // We would *like* to provide high resolution timers. Windows timers using // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup // mechanism because the application can enter modal windows loops where it // is not running our MessageLoop; the only way to have our timers fire in // these cases is to post messages there. // // To provide sub-10ms timers, we process timers directly from our run loop. // For the common case, timers will be processed there as the run loop does // its normal work. However, we *also* set the system timer so that WM_TIMER // events fire. This mops up the case of timers not being able to work in // modal message loops. It is possible for the SetTimer to pop and have no // pending timers, because they could have already been processed by the // run loop itself. // // We use a single SetTimer corresponding to the timer that will expire // soonest. As new timers are created and destroyed, we update SetTimer. // Getting a spurrious SetTimer event firing is benign, as we'll just be // processing an empty timer queue. // delayed_work_time_ = delayed_work_time; int delay_msec = GetCurrentDelay(); DCHECK(delay_msec >= 0); if (delay_msec < USER_TIMER_MINIMUM) delay_msec = USER_TIMER_MINIMUM; // Create a WM_TIMER event that will wake us up to check for any pending // timers (in case we are running within a nested, external sub-pump). SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), delay_msec, NULL); }
最后一个需要实现的函数DoMainLoop
void MessagePumpForUI::DoRunLoop() { // IF this was just a simple PeekMessage() loop (servicing all possible work // queues), then Windows would try to achieve the following order according // to MSDN documentation about PeekMessage with no filter): // * Sent messages // * Posted messages // * Sent messages (again) // * WM_PAINT messages // * WM_TIMER messages // // Summary: none of the above classes is starved, and sent messages has twice // the chance of being processed (i.e., reduced service time). for (;;) { // If we do any work, we may create more messages etc., and more work may // possibly be waiting in another task group. When we (for example) // ProcessNextWindowsMessage(), there is a good chance there are still more // messages waiting. On the other hand, when any of these methods return // having done no work, then it is pretty unlikely that calling them again // quickly will find any work to do. Finally, if they all say they had no // work, then it is a good time to consider sleeping (waiting) for more // work. bool more_work_is_plausible = ProcessNextWindowsMessage(); if (state_->should_quit) break; more_work_is_plausible |= state_->delegate->DoWork(); if (state_->should_quit) break; more_work_is_plausible |= state_->delegate->DoDelayedWork(&delayed_work_time_); // If we did not process any delayed work, then we can assume that our // existing WM_TIMER if any will fire when delayed work should run. We // don't want to disturb that timer if it is already in flight. However, // if we did do all remaining delayed work, then lets kill the WM_TIMER. if (more_work_is_plausible && delayed_work_time_.is_null()) KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); if (state_->should_quit) break; if (more_work_is_plausible) continue; more_work_is_plausible = state_->delegate->DoIdleWork(); if (state_->should_quit) break; if (more_work_is_plausible) continue; WaitForWork(); // Wait (sleep) until we have work to do again. } }