summaryrefslogtreecommitdiffstats
path: root/base/message_loop.cc
diff options
context:
space:
mode:
authordarin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-16 03:09:05 +0000
committerdarin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-16 03:09:05 +0000
commitfc7fb6e30c5c3574dfade2803f1793b9110d1370 (patch)
tree0ac30f51beace4b794501ad120f437f9149c65fb /base/message_loop.cc
parent3705baeff3bc0c1f63d2376e16c427df18c7564a (diff)
downloadchromium_src-fc7fb6e30c5c3574dfade2803f1793b9110d1370.zip
chromium_src-fc7fb6e30c5c3574dfade2803f1793b9110d1370.tar.gz
chromium_src-fc7fb6e30c5c3574dfade2803f1793b9110d1370.tar.bz2
Take 2 at the new MessageLoop implementation.
R=jar git-svn-id: svn://svn.chromium.org/chrome/trunk/src@973 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/message_loop.cc')
-rw-r--r--base/message_loop.cc691
1 files changed, 120 insertions, 571 deletions
diff --git a/base/message_loop.cc b/base/message_loop.cc
index 18f4448..4d61b6a 100644
--- a/base/message_loop.cc
+++ b/base/message_loop.cc
@@ -27,14 +27,13 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include <algorithm>
-
#include "base/message_loop.h"
+#include <algorithm>
+
#include "base/logging.h"
#include "base/string_util.h"
#include "base/thread_local_storage.h"
-#include "base/win_util.h"
// a TLS index to the message loop for the current thread
// Note that if we start doing complex stuff in other static initializers
@@ -43,25 +42,10 @@
//------------------------------------------------------------------------------
-static const wchar_t kWndClass[] = L"Chrome_MessageLoopWindow";
-
-// Windows Message numbers handled by WindowMessageProc.
-
-// Message sent to get an additional time slice for pumping (processing) another
-// task (a series of such messages creates a continuous task pump).
-static const int kMsgPumpATask = WM_USER + 1;
-
-// Message sent by Quit() to cause our main message pump to terminate as soon as
-// all pending task and message queues have been emptied.
-static const int kMsgQuit = WM_USER + 2;
-
// Logical events for Histogram profiling. Run with -message-loop-histogrammer
// to get an accounting of messages and actions taken on each thread.
-static const int kTaskRunEvent = WM_USER + 16; // 0x411
-static const int kSleepingApcEvent = WM_USER + 17; // 0x411
-static const int kPollingSignalEvent = WM_USER + 18; // 0x412
-static const int kSleepingSignalEvent = WM_USER + 19; // 0x413
-static const int kTimerEvent = WM_USER + 20; // 0x414
+static const int kTaskRunEvent = 0x1;
+static const int kTimerEvent = 0x2;
// Provide range of message IDs for use in histogramming and debug display.
static const int kLeastNonZeroMessageId = 1;
@@ -70,26 +54,8 @@ static const int kNumberOfDistinctMessagesDisplayed = 1100;
//------------------------------------------------------------------------------
-#ifndef NDEBUG
-// Force exercise of polling model.
-#define CHROME_MAXIMUM_WAIT_OBJECTS 8
-#else
-#define CHROME_MAXIMUM_WAIT_OBJECTS MAXIMUM_WAIT_OBJECTS
-#endif
-
-//------------------------------------------------------------------------------
-// A strategy of -1 uses the default case. All strategies are selected as
-// positive integers.
-// static
-int MessageLoop::strategy_selector_ = -1;
-
-// static
-void MessageLoop::SetStrategy(int strategy) {
- DCHECK(-1 == strategy_selector_);
- strategy_selector_ = strategy;
-}
+#if defined(OS_WIN)
-//------------------------------------------------------------------------------
// Upon a SEH exception in this thread, it restores the original unhandled
// exception filter.
static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) {
@@ -106,23 +72,22 @@ static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() {
return top_filter;
}
+#endif // defined(OS_WIN)
+
//------------------------------------------------------------------------------
MessageLoop::MessageLoop()
#pragma warning(suppress: 4355) // OK, to use |this| in the initializer list.
: timer_manager_(this),
- message_hwnd_(NULL),
exception_restoration_(false),
nestable_tasks_allowed_(true),
- dispatcher_(NULL),
- quit_received_(false),
- quit_now_(false),
- task_pump_message_pending_(false),
- run_depth_(0) {
+ state_(NULL) {
DCHECK(tls_index_) << "static initializer failed";
DCHECK(!current()) << "should only have one message loop per thread";
ThreadLocalStorage::Set(tls_index_, this);
- InitMessageWnd();
+#if defined(OS_WIN)
+ pump_ = new base::MessagePumpWin();
+#endif
}
MessageLoop::~MessageLoop() {
@@ -135,8 +100,7 @@ MessageLoop::~MessageLoop() {
// OK, now make it so that no one can find us.
ThreadLocalStorage::Set(tls_index_, NULL);
- DCHECK(!dispatcher_);
- DCHECK(!quit_received_ && !quit_now_);
+ DCHECK(!state_);
// Most tasks that have not been Run() are deleted in the |timer_manager_|
// destructor after we remove our tls index. We delete the tasks in our
@@ -147,12 +111,6 @@ MessageLoop::~MessageLoop() {
DeletePendingTasks();
}
-void MessageLoop::SetThreadName(const std::string& thread_name) {
- DCHECK(thread_name_.empty());
- thread_name_ = thread_name;
- StartHistogrammer();
-}
-
void MessageLoop::AddDestructionObserver(DestructionObserver *obs) {
DCHECK(this == current());
destruction_observers_.AddObserver(obs);
@@ -163,26 +121,23 @@ void MessageLoop::RemoveDestructionObserver(DestructionObserver *obs) {
destruction_observers_.RemoveObserver(obs);
}
-void MessageLoop::AddObserver(Observer *obs) {
- DCHECK(this == current());
- observers_.AddObserver(obs);
-}
-
-void MessageLoop::RemoveObserver(Observer *obs) {
- DCHECK(this == current());
- observers_.RemoveObserver(obs);
-}
-
void MessageLoop::Run() {
- RunHandler(NULL, false);
+ AutoRunState save_state(this);
+ RunHandler();
}
-void MessageLoop::Run(Dispatcher* dispatcher) {
- RunHandler(dispatcher, false);
+#if defined(OS_WIN)
+void MessageLoop::Run(base::MessagePumpWin::Dispatcher* dispatcher) {
+ AutoRunState save_state(this);
+ state_->dispatcher = dispatcher;
+ RunHandler();
}
+#endif
void MessageLoop::RunAllPending() {
- RunHandler(NULL, true);
+ AutoRunState save_state(this);
+ state_->quit_received = true; // Means run until we would otherwise block.
+ RunHandler();
}
// Runs the loop in two different SEH modes:
@@ -190,94 +145,43 @@ void MessageLoop::RunAllPending() {
// one that calls SetUnhandledExceptionFilter().
// enable_SEH_restoration_ = true : any unhandled exception goes to the filter
// that was existed before the loop was run.
-void MessageLoop::RunHandler(Dispatcher* dispatcher, bool non_blocking) {
+void MessageLoop::RunHandler() {
+#if defined(OS_WIN)
if (exception_restoration_) {
LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
__try {
- RunInternal(dispatcher, non_blocking);
+ RunInternal();
} __except(SEHFilter(current_filter)) {
}
- } else {
- RunInternal(dispatcher, non_blocking);
+ return;
}
+#endif
+
+ RunInternal();
}
//------------------------------------------------------------------------------
-// IF this was just a simple PeekMessage() loop (servicing all passible 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).
-
-void MessageLoop::RunInternal(Dispatcher* dispatcher, bool non_blocking) {
- // Preserve ability to be called recursively.
- ScopedStateSave save(this); // State is restored on exit.
- dispatcher_ = dispatcher;
- StartHistogrammer();
+void MessageLoop::RunInternal() {
DCHECK(this == current());
- //
- // Process pending messages and signaled objects.
- //
- // Flush these queues before exiting due to a kMsgQuit or else we risk not
- // shutting down properly as some operations may depend on further event
- // processing. (Note: some tests may use quit_now_ to exit more swiftly,
- // and leave messages pending, so don't assert the above fact).
- RunTraditional(non_blocking);
- DCHECK(non_blocking || quit_received_ || quit_now_);
-}
-void MessageLoop::RunTraditional(bool non_blocking) {
- 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 (same thing for ProcessNextObject(), which responds to
- // only one signaled object; etc.). 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 (quit_now_)
- return;
-
- more_work_is_plausible |= ProcessNextDeferredTask();
- more_work_is_plausible |= ProcessNextObject();
- if (more_work_is_plausible)
- continue;
-
- if (quit_received_)
- return;
-
- // Run any timer that is ready to run. It may create messages etc.
- if (ProcessSomeTimers())
- continue;
-
- // We run delayed non nestable tasks only after all nestable tasks have
- // run, to preserve FIFO ordering.
- if (ProcessNextDelayedNonNestableTask())
- continue;
-
- if (non_blocking)
- return;
+ StartHistogrammer();
- // We service APCs in WaitForWork, without returning.
- WaitForWork(); // Wait (sleep) until we have work to do again.
+#if defined(OS_WIN)
+ if (state_->dispatcher) {
+ pump_win()->RunWithDispatcher(this, state_->dispatcher);
+ return;
}
+#endif
+
+ pump_->Run(this);
}
//------------------------------------------------------------------------------
// Wrapper functions for use in above message loop framework.
bool MessageLoop::ProcessNextDelayedNonNestableTask() {
- if (run_depth_ != 1)
+ if (state_->run_depth != 1)
return false;
if (delayed_non_nestable_queue_.Empty())
@@ -287,53 +191,15 @@ bool MessageLoop::ProcessNextDelayedNonNestableTask() {
return true;
}
-bool MessageLoop::ProcessNextDeferredTask() {
- ReloadWorkQueue();
- return QueueOrRunTask(NULL);
-}
-
-bool MessageLoop::ProcessSomeTimers() {
- return timer_manager_.RunSomePendingTimers();
-}
-
//------------------------------------------------------------------------------
void MessageLoop::Quit() {
- EnsureMessageGetsPosted(kMsgQuit);
-}
-
-bool MessageLoop::WatchObject(HANDLE object, Watcher* watcher) {
- DCHECK(this == current());
- DCHECK(object);
- DCHECK_NE(object, INVALID_HANDLE_VALUE);
-
- std::vector<HANDLE>::iterator it = find(objects_.begin(), objects_.end(),
- object);
- if (watcher) {
- if (it == objects_.end()) {
- static size_t warning_multiple = 1;
- if (objects_.size() >= warning_multiple * MAXIMUM_WAIT_OBJECTS / 2) {
- LOG(INFO) << "More than " << warning_multiple * MAXIMUM_WAIT_OBJECTS / 2
- << " objects being watched";
- // This DCHECK() is an artificial limitation, meant to warn us if we
- // start creating too many objects. It can safely be raised to a higher
- // level, and the program is designed to handle much larger values.
- // Before raising this limit, make sure that there is a very good reason
- // (in your debug testing) to be watching this many objects.
- DCHECK(2 <= warning_multiple);
- ++warning_multiple;
- }
- objects_.push_back(object);
- watchers_.push_back(watcher);
- } else {
- watchers_[it - objects_.begin()] = watcher;
- }
- } else if (it != objects_.end()) {
- std::vector<HANDLE>::difference_type index = it - objects_.begin();
- objects_.erase(it);
- watchers_.erase(watchers_.begin() + index);
+ DCHECK(current() == this);
+ if (state_) {
+ state_->quit_received = true;
+ } else {
+ NOTREACHED() << "Must be inside Run to call Quit";
}
- return true;
}
// Possibly called on a background thread!
@@ -352,88 +218,23 @@ void MessageLoop::PostTaskInternal(Task* task) {
// directly, as it could starve handling of foreign threads. Put every task
// into this queue.
- // Local stack variables to use IF we need to process after releasing locks.
- HWND message_hwnd;
+ scoped_refptr<base::MessagePump> pump;
{
- AutoLock lock1(incoming_queue_lock_);
+ AutoLock locked(incoming_queue_lock_);
+
bool was_empty = incoming_queue_.Empty();
incoming_queue_.Push(task);
if (!was_empty)
return; // Someone else should have started the sub-pump.
- // We may have to start the sub-pump.
- AutoLock lock2(task_pump_message_lock_);
- if (task_pump_message_pending_)
- return; // Someone else continued the pumping.
- task_pump_message_pending_ = true; // We'll send one.
- message_hwnd = message_hwnd_;
- } // Release both locks.
- // We may have just posted a kMsgQuit, and so this instance may now destroyed!
- // Do not invoke non-static methods, or members in any way!
-
- // PostMessage may fail, as the hwnd may have vanished due to kMsgQuit.
- PostMessage(message_hwnd, kMsgPumpATask, 0, 0);
-}
-
-void MessageLoop::InitMessageWnd() {
- HINSTANCE hinst = GetModuleHandle(NULL);
-
- WNDCLASSEX wc = {0};
- wc.cbSize = sizeof(wc);
- wc.lpfnWndProc = WndProcThunk;
- wc.hInstance = hinst;
- wc.lpszClassName = kWndClass;
- RegisterClassEx(&wc);
-
- message_hwnd_ = CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0,
- hinst, 0);
- DCHECK(message_hwnd_);
-}
-
-// static
-LRESULT CALLBACK MessageLoop::WndProcThunk(
- HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
- DCHECK(MessageLoop::current());
- return MessageLoop::current()->WndProc(hwnd, message, wparam, lparam);
-}
-
-LRESULT MessageLoop::WndProc(
- HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
- if (hwnd == message_hwnd_) {
- switch (message) {
- case kMsgPumpATask: {
- ProcessPumpReplacementMessage(); // Avoid starving paint and timer.
- if (!nestable_tasks_allowed_)
- return 0;
- PumpATaskDuringWndProc();
- return 0;
- }
-
- case WM_TIMER:
- ProcessSomeTimers(); // Give the TimerManager a tickle.
- DidChangeNextTimerExpiry(); // Maybe generate another WM_TIMER.
- return 0;
-
- case kMsgQuit: {
- // TODO(jar): bug 1300541 The following assert should be used, but
- // currently too much code actually triggers the assert, especially in
- // tests :-(.
- // Discarding a second quit will cause a hang.
- //CHECK(!quit_received_);
- quit_received_ = true;
- return 0;
- }
- }
+ pump = pump_;
}
- return ::DefWindowProc(hwnd, message, wparam, lparam);
-}
+ // Since the incoming_queue_ may contain a task that destroys this message
+ // loop, we cannot exit incoming_queue_lock_ until we are done with |this|.
+ // We use a stack-based reference to the message pump so that we can call
+ // ScheduleWork outside of incoming_queue_lock_.
-void MessageLoop::WillProcessMessage(const MSG& msg) {
- FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg));
-}
-
-void MessageLoop::DidProcessMessage(const MSG& msg) {
- FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg));
+ pump->ScheduleWork();
}
void MessageLoop::SetNestableTasksAllowed(bool allowed) {
@@ -442,7 +243,7 @@ void MessageLoop::SetNestableTasksAllowed(bool allowed) {
if (!nestable_tasks_allowed_)
return;
// Start the native pump if we are not already pumping.
- EnsurePumpATaskWasPosted();
+ pump_->ScheduleWork();
}
}
@@ -450,215 +251,7 @@ bool MessageLoop::NestableTasksAllowed() const {
return nestable_tasks_allowed_;
}
-bool MessageLoop::ProcessNextWindowsMessage() {
- MSG msg;
- if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
- return ProcessMessageHelper(msg);
- }
- return false;
-}
-
-bool MessageLoop::ProcessMessageHelper(const MSG& msg) {
- HistogramEvent(msg.message);
-
- if (WM_QUIT == msg.message) {
- // Repost the QUIT message so that it will be retrieved by the primary
- // GetMessage() loop.
- quit_now_ = true;
- PostQuitMessage(static_cast<int>(msg.wParam));
- return false;
- }
-
- // While running our main message pump, we discard kMsgPumpATask messages.
- if (msg.message == kMsgPumpATask && msg.hwnd == message_hwnd_)
- return ProcessPumpReplacementMessage();
-
- WillProcessMessage(msg);
-
- if (dispatcher_) {
- if (!dispatcher_->Dispatch(msg))
- quit_now_ = true;
- } else {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- DidProcessMessage(msg);
- return true;
-}
-
-bool MessageLoop::ProcessPumpReplacementMessage() {
- MSG msg;
- bool have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
- DCHECK(!have_message || kMsgPumpATask != msg.message
- || msg.hwnd != message_hwnd_);
- {
- // Since we discarded a kMsgPumpATask message, we must update the flag.
- AutoLock lock(task_pump_message_lock_);
- DCHECK(task_pump_message_pending_);
- task_pump_message_pending_ = false;
- }
- return have_message && ProcessMessageHelper(msg);
-}
-
-// Create a mini-message-pump to force immediate processing of only Windows
-// WM_PAINT messages.
-void MessageLoop::PumpOutPendingPaintMessages() {
- // Don't provide an infinite loop, but do enough peeking to get the job done.
- // Actual common max is 4 peeks, but we'll be a little safe here.
- const int kMaxPeekCount = 20;
- int peek_count;
- bool win2k(true);
- if (win_util::GetWinVersion() > win_util::WINVERSION_2000)
- win2k = false;
- for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
- MSG msg;
- if (win2k) {
- if (!PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE))
- break;
- } else {
- if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
- break;
- }
- ProcessMessageHelper(msg);
- if (quit_now_ ) // Handle WM_QUIT.
- break;
- }
- // Histogram what was really being used, to help to adjust kMaxPeekCount.
- DHISTOGRAM_COUNTS(L"Loop.PumpOutPendingPaintMessages Peeks", peek_count);
-}
-
//------------------------------------------------------------------------------
-// If we handle more than the OS limit on the number of objects that can be
-// waited for, we'll need to poll (sequencing through subsets of the objects
-// that can be passed in a single OS wait call). The following is the polling
-// interval used in that (unusual) case. (I don't have a lot of justifcation
-// for the specific value, but it needed to be short enough that it would not
-// add a lot of latency, and long enough that we wouldn't thrash the CPU for no
-// reason... especially considering the silly user probably has a million tabs
-// open, etc.)
-static const int kMultipleWaitPollingInterval = 20;
-
-void MessageLoop::WaitForWork() {
- bool original_can_run = nestable_tasks_allowed_;
- int wait_flags = original_can_run ? MWMO_ALERTABLE | MWMO_INPUTAVAILABLE
- : MWMO_INPUTAVAILABLE;
-
- bool use_polling = false; // Poll if too many objects for one OS Wait call.
- for (;;) {
- // Do initialization here, in case APC modifies object list.
- size_t total_objs = original_can_run ? objects_.size() : 0;
-
- int delay;
- size_t polling_index = 0; // The first unprocessed object index.
- do {
- size_t objs_len =
- (polling_index < total_objs) ? total_objs - polling_index : 0;
- if (objs_len >= CHROME_MAXIMUM_WAIT_OBJECTS) {
- objs_len = CHROME_MAXIMUM_WAIT_OBJECTS - 1;
- use_polling = true;
- }
- HANDLE* objs = objs_len ? polling_index + &objects_.front() : NULL;
-
- // Only wait up to the time needed by the timer manager to fire the next
- // set of timers.
- delay = timer_manager_.GetCurrentDelay();
- if (use_polling && delay > kMultipleWaitPollingInterval)
- delay = kMultipleWaitPollingInterval;
- if (delay < 0) // Negative value means no timers waiting.
- delay = INFINITE;
-
- DWORD result;
- result = MsgWaitForMultipleObjectsEx(static_cast<DWORD>(objs_len), objs,
- delay, QS_ALLINPUT, wait_flags);
-
- if (WAIT_IO_COMPLETION == result) {
- HistogramEvent(kSleepingApcEvent);
- // We'll loop here when we service an APC. At it currently stands,
- // *ONLY* the IO thread uses *any* APCs, so this should have no impact
- // on the UI thread.
- break; // Break to outer loop, and waitforwork() again.
- }
-
- // Use unsigned type to simplify range detection;
- size_t signaled_index = result - WAIT_OBJECT_0;
- if (signaled_index < objs_len) {
- SignalWatcher(polling_index + signaled_index);
- HistogramEvent(kSleepingSignalEvent);
- return; // We serviced a signaled object.
- }
-
- if (objs_len == signaled_index)
- return; // A WM_* message is available.
-
- DCHECK_NE(WAIT_FAILED, result) << GetLastError();
-
- DCHECK(!objs || result == WAIT_TIMEOUT);
- if (!use_polling)
- return;
- polling_index += objs_len;
- } while (polling_index < total_objs);
- // For compatibility, we didn't return sooner. This made us do *some* wait
- // call(s) before returning. This will probably change in next rev.
- if (!delay || !timer_manager_.GetCurrentDelay())
- return; // No work done, but timer is ready to fire.
- }
-}
-
-// Note: MsgWaitMultipleObjects() can't take a nil list, and that is why I had
-// to use SleepEx() to handle APCs when there were no objects.
-bool MessageLoop::ProcessNextObject() {
- if (!nestable_tasks_allowed_)
- return false;
-
- size_t total_objs = objects_.size();
- if (!total_objs) {
- return false;
- }
-
- size_t polling_index = 0; // The first unprocessed object index.
- do {
- DCHECK(polling_index < total_objs);
- size_t objs_len = total_objs - polling_index;
- if (objs_len >= CHROME_MAXIMUM_WAIT_OBJECTS)
- objs_len = CHROME_MAXIMUM_WAIT_OBJECTS - 1;
- HANDLE* objs = polling_index + &objects_.front();
-
- // Identify 1 pending object, or allow an IO APC to be completed.
- DWORD result = WaitForMultipleObjectsEx(static_cast<DWORD>(objs_len), objs,
- FALSE, // 1 signal is sufficient.
- 0, // Wait 0ms.
- false); // Not alertable (no APC).
-
- // Use unsigned type to simplify range detection;
- size_t signaled_index = result - WAIT_OBJECT_0;
- if (signaled_index < objs_len) {
- SignalWatcher(polling_index + signaled_index);
- HistogramEvent(kPollingSignalEvent);
- return true; // We serviced a signaled object.
- }
-
- // If an handle is invalid, it will be WAIT_FAILED.
- DCHECK_EQ(WAIT_TIMEOUT, result) << GetLastError();
- polling_index += objs_len;
- } while (polling_index < total_objs);
- return false; // We serviced nothing.
-}
-
-bool MessageLoop::SignalWatcher(size_t object_index) {
- BeforeTaskRunSetup();
- DCHECK(objects_.size() > object_index);
- // On reception of OnObjectSignaled() to a Watcher object, it may call
- // WatchObject(). watchers_ and objects_ will be modified. This is
- // expected, so don't be afraid if, while tracing a OnObjectSignaled()
- // function, the corresponding watchers_[result] is inexistant.
- watchers_[object_index]->OnObjectSignaled(objects_[object_index]);
- // Signaled objects tend to be removed from the watch list, and then added
- // back (appended). As a result, they move to the end of the objects_ array,
- // and this should make their service "fair" (no HANDLEs should be starved).
- AfterTaskRunRestore();
- return true;
-}
bool MessageLoop::RunTimerTask(Timer* timer) {
HistogramEvent(kTimerEvent);
@@ -705,16 +298,15 @@ bool MessageLoop::QueueOrRunTask(Task* new_task) {
// Execute oldest task.
while (!work_queue_.Empty()) {
Task* task = work_queue_.Pop();
- if (task->nestable() || run_depth_ == 1) {
+ if (task->nestable() || state_->run_depth == 1) {
RunTask(task);
// Show that we ran a task (Note: a new one might arrive as a
// consequence!).
return true;
- } else {
- // We couldn't run the task now because we're in a nested message loop
- // and the task isn't nestable.
- delayed_non_nestable_queue_.Push(task);
}
+ // We couldn't run the task now because we're in a nested message loop
+ // and the task isn't nestable.
+ delayed_non_nestable_queue_.Push(task);
}
// Nothing happened.
@@ -743,54 +335,12 @@ void MessageLoop::AfterTaskRunRestore() {
nestable_tasks_allowed_ = true;
}
-void MessageLoop::PumpATaskDuringWndProc() {
- // TODO(jar): Perchance we should check on signaled objects here??
- // Signals are generally starved during a native message loop. Even if we
- // try to service a signaled object now, we wouldn't automatically get here
- // (i.e., the native pump would not re-start) when the next object was
- // signaled. If we really want to avoid starving signaled objects, we need
- // to translate them into Tasks that can be passed in via PostTask.
- // If these native message loops (and sub-pumping activities) are short
- // lived, then the starvation won't be that long :-/.
-
- if (!ProcessNextDeferredTask())
- return; // Nothing to do, so lets stop the sub-pump.
-
- // We ran a task, so make sure we come back and try to run more tasks.
- EnsurePumpATaskWasPosted();
-}
-
-void MessageLoop::EnsurePumpATaskWasPosted() {
- {
- AutoLock lock(task_pump_message_lock_);
- if (task_pump_message_pending_)
- return; // Someone else continued the pumping.
- task_pump_message_pending_ = true; // We'll send one.
- }
- EnsureMessageGetsPosted(kMsgPumpATask);
-}
-
-void MessageLoop::EnsureMessageGetsPosted(int message) const {
- const int kRetryCount = 30;
- const int kSleepDurationWhenFailing = 100;
- for (int i = 0; i < kRetryCount; ++i) {
- // Posting to our own windows should always succeed. If it doesn't we're in
- // big trouble.
- if (PostMessage(message_hwnd_, message, 0, 0))
- return;
- Sleep(kSleepDurationWhenFailing);
- }
- LOG(FATAL) << "Crash with last error " << GetLastError();
- int* p = NULL;
- *p = 0; // Crash.
-}
-
void MessageLoop::ReloadWorkQueue() {
// We can improve performance of our loading tasks from incoming_queue_ to
- // work_queue_ by wating until the last minute (work_queue_ is empty) to load.
- // That reduces the number of locks-per-task significantly when our queues get
- // large. The optimization is disabled on threads that make use of the
- // priority queue (prioritization requires all our tasks to be in the
+ // work_queue_ by waiting until the last minute (work_queue_ is empty) to
+ // load. That reduces the number of locks-per-task significantly when our
+ // queues get large. The optimization is disabled on threads that make use
+ // of the priority queue (prioritization requires all our tasks to be in the
// work_queue_ ASAP).
if (!work_queue_.Empty() && !work_queue_.use_priority_queue())
return; // Wait till we *really* need to lock and load.
@@ -833,42 +383,65 @@ void MessageLoop::DeletePendingTasks() {
}
void MessageLoop::DidChangeNextTimerExpiry() {
-#if defined(OS_WIN)
- //
- // 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.
- //
int delay = timer_manager_.GetCurrentDelay();
- if (delay == -1) {
- KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
+ if (delay == -1)
+ return;
+
+ // Simulates malfunctioning, early firing timers. Pending tasks should only
+ // be invoked when the delay they specify has elapsed.
+ if (timer_manager_.use_broken_delay())
+ delay = 10;
+
+ pump_->ScheduleDelayedWork(TimeDelta::FromMilliseconds(delay));
+}
+
+bool MessageLoop::DoWork() {
+ ReloadWorkQueue();
+ return QueueOrRunTask(NULL);
+}
+
+bool MessageLoop::DoDelayedWork(TimeDelta* next_delay) {
+ bool did_work = timer_manager_.RunSomePendingTimers();
+
+ // We may not have run any timers, but we may still have future timers to
+ // run, so we need to inform the pump again of pending timers.
+ *next_delay = TimeDelta::FromMilliseconds(timer_manager_.GetCurrentDelay());
+
+ return did_work;
+}
+
+bool MessageLoop::DoIdleWork() {
+ if (ProcessNextDelayedNonNestableTask())
+ return true;
+
+ if (state_->quit_received)
+ pump_->Quit();
+
+ return false;
+}
+
+//------------------------------------------------------------------------------
+// MessageLoop::AutoRunState
+
+MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) {
+ // Make the loop reference us.
+ previous_state_ = loop_->state_;
+ if (previous_state_) {
+ run_depth = previous_state_->run_depth + 1;
} else {
- if (delay < USER_TIMER_MINIMUM)
- delay = USER_TIMER_MINIMUM;
- // Simulates malfunctioning, early firing timers. Pending tasks should only
- // be invoked when the delay they specify has elapsed.
- if (timer_manager_.use_broken_delay())
- delay = 10;
- // 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, NULL);
+ run_depth = 1;
}
-#endif // defined(OS_WIN)
+ loop_->state_ = this;
+
+ // Initialize the other fields:
+ quit_received = false;
+#if defined(OS_WIN)
+ dispatcher = NULL;
+#endif
+}
+
+MessageLoop::AutoRunState::~AutoRunState() {
+ loop_->state_ = previous_state_;
}
//------------------------------------------------------------------------------
@@ -963,6 +536,7 @@ void MessageLoop::EnableHistogrammer(bool enable) {
void MessageLoop::StartHistogrammer() {
if (enable_histogrammer_ && !message_histogram_.get()
&& StatisticsRecorder::WasStarted()) {
+ DCHECK(!thread_name_.empty());
message_histogram_.reset(new LinearHistogram(
ASCIIToWide("MsgLoop:" + thread_name_).c_str(),
kLeastNonZeroMessageId,
@@ -978,11 +552,6 @@ void MessageLoop::HistogramEvent(int event) {
message_histogram_->Add(event);
}
-// Add one undocumented windows message to clean up our display.
-#ifndef WM_SYSTIMER
-#define WM_SYSTIMER 0x118
-#endif
-
// Provide a macro that takes an expression (such as a constant, or macro
// constant) and creates a pair to initalize an array of pairs. In this case,
// our pair consists of the expressions value, and the "stringized" version
@@ -999,33 +568,13 @@ void MessageLoop::HistogramEvent(int event) {
// in the pair (i.e., the quoted string) when printing out a histogram.
#define VALUE_TO_NUMBER_AND_NAME(name) {name, #name},
-
// static
const LinearHistogram::DescriptionPair MessageLoop::event_descriptions_[] = {
- // Only provide an extensive list in debug mode. In release mode, we have to
- // read the octal values.... but we save about 450 strings, each of length
- // 10 from our binary image.
-#ifndef NDEBUG
- // Prepare to include a list of names provided in a special header file4.
-#define A_NAMED_MESSAGE_FROM_WINUSER_H VALUE_TO_NUMBER_AND_NAME
-#include "base/windows_message_list.h"
-#undef A_NAMED_MESSAGE_FROM_WINUSER_H
- // Add an undocumented message that appeared in our list :-/.
- VALUE_TO_NUMBER_AND_NAME(WM_SYSTIMER)
-#endif // NDEBUG
-
// Provide some pretty print capability in our histogram for our internal
// messages.
- // Values we use for WM_USER+n
- VALUE_TO_NUMBER_AND_NAME(kMsgPumpATask)
- VALUE_TO_NUMBER_AND_NAME(kMsgQuit)
-
// A few events we handle (kindred to messages), and used to profile actions.
VALUE_TO_NUMBER_AND_NAME(kTaskRunEvent)
- VALUE_TO_NUMBER_AND_NAME(kSleepingApcEvent)
- VALUE_TO_NUMBER_AND_NAME(kSleepingSignalEvent)
- VALUE_TO_NUMBER_AND_NAME(kPollingSignalEvent)
VALUE_TO_NUMBER_AND_NAME(kTimerEvent)
{-1, NULL} // The list must be null terminated, per API to histogram.