summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordarin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-26 05:53:57 +0000
committerdarin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-26 05:53:57 +0000
commit4d9bdfafcd1393385860bc9fe947e0c07719c0f4 (patch)
tree67c91c3fa4c658352995e22b894535e60b04f282
parent3c17b9c00d5fc28f5523ab60b0df502dd7c2a596 (diff)
downloadchromium_src-4d9bdfafcd1393385860bc9fe947e0c07719c0f4.zip
chromium_src-4d9bdfafcd1393385860bc9fe947e0c07719c0f4.tar.gz
chromium_src-4d9bdfafcd1393385860bc9fe947e0c07719c0f4.tar.bz2
Allow consumers of MessageLoop to specify the type of MessageLoop they want.
This CL introduces a Type enum to MessageLoop, and I also created subclasses of MessageLoop corresponding to the non-default types: MessageLoopForIO and MessageLoopForUI. I moved all of the platform-specific MessageLoop APIs onto either MessageLoopForIO or MessageLoopForUI. MessageLoopForIO gets the Watcher API, and MessageLoopForUI gets the Observer and Dispatcher APIs. Under the hood, both are implemented in terms of MessagePumpWin, but that will change in a future CL. The Thread class is changed to allow the consumer to specify the Type of MessageLoop they want to have setup on the created thread. I re-organized message_loop_unittest.cc and timer_unittest.cc so that I could exercise all (or most) of the tests against each type of MessageLoop. Note: I know that "explicit MessageLoop(Type type = TYPE_DEFAULT);" is in violation to the style-guide's restriction against default arguments. I'm working on finding a decent solution to that problem. Please ignore this issue for now. The corresponding chrome/ changes are coming in a separate CL due to Reitveld data size limitations. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@1362 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/build/base.vcproj8
-rw-r--r--base/idle_timer.cc2
-rw-r--r--base/idletimer_unittest.cc15
-rw-r--r--base/message_loop.cc72
-rw-r--r--base/message_loop.h136
-rw-r--r--base/message_loop_unittest.cc224
-rw-r--r--base/object_watcher_unittest.cc32
-rw-r--r--base/run_all_unittests.cc6
-rw-r--r--base/test_suite.h34
-rw-r--r--base/thread.cc67
-rw-r--r--base/thread.h49
-rw-r--r--base/thread_unittest.cc8
-rw-r--r--base/timer_unittest.cc73
-rw-r--r--net/base/listen_socket_unittest.h4
-rw-r--r--net/base/run_all_unittests.cc58
-rw-r--r--net/build/net_unittests.vcproj2
-rw-r--r--net/proxy/proxy_service.cc2
-rw-r--r--net/proxy/proxy_service.h4
-rw-r--r--net/url_request/url_request.cc7
-rw-r--r--net/url_request/url_request_file_job.cc6
-rw-r--r--net/url_request/url_request_file_job.h2
-rw-r--r--net/url_request/url_request_unittest.h63
-rw-r--r--webkit/default_plugin/plugin_install_job_monitor.h2
-rw-r--r--webkit/tools/test_shell/simple_resource_loader_bridge.cc10
24 files changed, 632 insertions, 254 deletions
diff --git a/base/build/base.vcproj b/base/build/base.vcproj
index 7105b1b..a58c0a2 100644
--- a/base/build/base.vcproj
+++ b/base/build/base.vcproj
@@ -426,6 +426,14 @@
>
</File>
<File
+ RelativePath="..\message_pump_default.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\message_pump_default.h"
+ >
+ </File>
+ <File
RelativePath="..\message_pump_win.cc"
>
</File>
diff --git a/base/idle_timer.cc b/base/idle_timer.cc
index fa5e1cc..39b9bd4 100644
--- a/base/idle_timer.cc
+++ b/base/idle_timer.cc
@@ -13,6 +13,8 @@ IdleTimerTask::IdleTimerTask(TimeDelta idle_time, bool repeat)
: idle_interval_(idle_time),
repeat_(repeat),
get_last_input_info_fn_(GetLastInputInfo) {
+ DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()) <<
+ "Requires a thread that processes Windows UI events";
}
IdleTimerTask::~IdleTimerTask() {
diff --git a/base/idletimer_unittest.cc b/base/idletimer_unittest.cc
index 613adad..ea53854 100644
--- a/base/idletimer_unittest.cc
+++ b/base/idletimer_unittest.cc
@@ -8,6 +8,9 @@
namespace {
class IdleTimerTest : public testing::Test {
+ private:
+ // IdleTimer requires a UI message loop on the current thread.
+ MessageLoopForUI message_loop_;
};
};
@@ -63,7 +66,7 @@ class ResetIdleTask : public Task {
// A non-repeating idle timer will fire once on idle, and
// then will not fire again unless it goes non-idle first.
-TEST(IdleTimerTest, NoRepeatIdle) {
+TEST_F(IdleTimerTest, NoRepeatIdle) {
// Create an IdleTimer, which should fire once after 100ms.
// Create a Quit timer which will fire after 1s.
// Verify that we fired exactly once.
@@ -80,7 +83,7 @@ TEST(IdleTimerTest, NoRepeatIdle) {
delete t;
}
-TEST(IdleTimerTest, NoRepeatFlipIdleOnce) {
+TEST_F(IdleTimerTest, NoRepeatFlipIdleOnce) {
// Create an IdleTimer, which should fire once after 100ms.
// Create a Quit timer which will fire after 1s.
// Create a timer to reset once, idle after 500ms.
@@ -101,7 +104,7 @@ TEST(IdleTimerTest, NoRepeatFlipIdleOnce) {
delete t2;
}
-TEST(IdleTimerTest, NoRepeatNotIdle) {
+TEST_F(IdleTimerTest, NoRepeatNotIdle) {
// Create an IdleTimer, which should fire once after 100ms.
// Create a Quit timer which will fire after 1s.
// Create a timer to reset idle every 50ms.
@@ -129,7 +132,7 @@ TEST(IdleTimerTest, NoRepeatNotIdle) {
// as it has been idle. So, if the machine remains idle, it will continue
// firing over and over.
-TEST(IdleTimerTest, Repeat) {
+TEST_F(IdleTimerTest, Repeat) {
// Create an IdleTimer, which should fire repeatedly after 100ms.
// Create a Quit timer which will fire after 1.05s.
// Verify that we fired 10 times.
@@ -149,7 +152,7 @@ TEST(IdleTimerTest, Repeat) {
delete t;
}
-TEST(IdleTimerTest, RepeatIdleReset) {
+TEST_F(IdleTimerTest, RepeatIdleReset) {
// Create an IdleTimer, which should fire repeatedly after 100ms.
// Create a Quit timer which will fire after 1s.
// Create a reset timer, which fires after 550ms
@@ -173,7 +176,7 @@ TEST(IdleTimerTest, RepeatIdleReset) {
delete t2;
}
-TEST(IdleTimerTest, RepeatNotIdle) {
+TEST_F(IdleTimerTest, RepeatNotIdle) {
// Create an IdleTimer, which should fire repeatedly after 100ms.
// Create a Quit timer which will fire after 1s.
// Create a timer to reset idle every 50ms.
diff --git a/base/message_loop.cc b/base/message_loop.cc
index a84b3b5..4528b93 100644
--- a/base/message_loop.cc
+++ b/base/message_loop.cc
@@ -55,19 +55,30 @@ static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() {
//------------------------------------------------------------------------------
-MessageLoop::MessageLoop()
- : ALLOW_THIS_IN_INITIALIZER_LIST(timer_manager_(this)),
+MessageLoop::MessageLoop(Type type)
+ : type_(type),
+ ALLOW_THIS_IN_INITIALIZER_LIST(timer_manager_(this)),
nestable_tasks_allowed_(true),
exception_restoration_(false),
state_(NULL) {
- DCHECK(!current()) << "should only have one message loop per thread";
+ DCHECK(!tls_index_.Get()) << "should only have one message loop per thread";
tls_index_.Set(this);
- // TODO(darin): Generalize this to support instantiating different pumps.
+
+ switch (type) {
+ case TYPE_DEFAULT:
+ pump_ = new base::MessagePumpDefault();
+ break;
+ case TYPE_UI:
+ case TYPE_IO:
+ // TODO(darin): Use a special pumps for UI and IO, and figure out what to
+ // do for non-Windows platforms.
#if defined(OS_WIN)
- pump_ = new base::MessagePumpWin();
+ pump_ = new base::MessagePumpWin();
#else
- pump_ = new base::MessagePumpDefault();
+ pump_ = new base::MessagePumpDefault();
#endif
+ break;
+ }
}
MessageLoop::~MessageLoop() {
@@ -106,14 +117,6 @@ void MessageLoop::Run() {
RunHandler();
}
-#if defined(OS_WIN)
-void MessageLoop::Run(base::MessagePumpWin::Dispatcher* dispatcher) {
- AutoRunState save_state(this);
- state_->dispatcher = dispatcher;
- RunHandler();
-}
-#endif
-
void MessageLoop::RunAllPending() {
AutoRunState save_state(this);
state_->quit_received = true; // Means run until we would otherwise block.
@@ -560,3 +563,44 @@ const LinearHistogram::DescriptionPair MessageLoop::event_descriptions_[] = {
{-1, NULL} // The list must be null terminated, per API to histogram.
};
+//------------------------------------------------------------------------------
+// MessageLoopForUI
+
+#if defined(OS_WIN)
+
+void MessageLoopForUI::Run(Dispatcher* dispatcher) {
+ AutoRunState save_state(this);
+ state_->dispatcher = dispatcher;
+ RunHandler();
+}
+
+void MessageLoopForUI::AddObserver(Observer* observer) {
+ pump_win()->AddObserver(observer);
+}
+
+void MessageLoopForUI::RemoveObserver(Observer* observer) {
+ pump_win()->RemoveObserver(observer);
+}
+
+void MessageLoopForUI::WillProcessMessage(const MSG& message) {
+ pump_win()->WillProcessMessage(message);
+}
+void MessageLoopForUI::DidProcessMessage(const MSG& message) {
+ pump_win()->DidProcessMessage(message);
+}
+void MessageLoopForUI::PumpOutPendingPaintMessages() {
+ pump_win()->PumpOutPendingPaintMessages();
+}
+
+#endif // defined(OS_WIN)
+
+//------------------------------------------------------------------------------
+// MessageLoopForIO
+
+#if defined(OS_WIN)
+
+void MessageLoopForIO::WatchObject(HANDLE object, Watcher* watcher) {
+ pump_win()->WatchObject(object, watcher);
+}
+
+#endif // defined(OS_WIN)
diff --git a/base/message_loop.h b/base/message_loop.h
index d014bc1..43faf4a 100644
--- a/base/message_loop.h
+++ b/base/message_loop.h
@@ -155,11 +155,34 @@ class MessageLoop : public base::MessagePump::Delegate {
}
};
+ // A MessageLoop has a particular type, which indicates the set of
+ // asynchronous events it may process in addition to tasks and timers.
+ //
+ // TYPE_DEFAULT
+ // This type of ML only supports tasks and timers.
+ //
+ // TYPE_UI
+ // This type of ML also supports native UI events (e.g., Windows messages).
+ // See also MessageLoopForUI.
+ //
+ // TYPE_IO
+ // This type of ML also supports asynchronous IO. See also
+ // MessageLoopForIO.
+ //
+ enum Type {
+ TYPE_DEFAULT,
+ TYPE_UI,
+ TYPE_IO
+ };
+
// Normally, it is not necessary to instantiate a MessageLoop. Instead, it
// is typical to make use of the current thread's MessageLoop instance.
- MessageLoop();
+ explicit MessageLoop(Type type = TYPE_DEFAULT);
~MessageLoop();
+ // Returns the type passed to the constructor.
+ Type type() const { return type_; }
+
// Optional call to connect the thread name with this loop.
void set_thread_name(const std::string& thread_name) {
DCHECK(thread_name_.empty()) << "Should not rename this thread!";
@@ -169,7 +192,9 @@ class MessageLoop : public base::MessagePump::Delegate {
// Returns the MessageLoop object for the current thread, or null if none.
static MessageLoop* current() {
- return static_cast<MessageLoop*>(tls_index_.Get());
+ MessageLoop* loop = static_cast<MessageLoop*>(tls_index_.Get());
+ DCHECK(loop) << "Ouch, did you forget to initialize me?";
+ return loop;
}
// Returns the TimerManager object for the current thread.
@@ -201,39 +226,9 @@ class MessageLoop : public base::MessagePump::Delegate {
exception_restoration_ = restore;
}
- //----------------------------------------------------------------------------
-#if defined(OS_WIN)
- // Backwards-compat for the old Windows-specific MessageLoop API. These APIs
- // are deprecated.
-
- typedef base::MessagePumpWin::Dispatcher Dispatcher;
- typedef base::MessagePumpWin::Observer Observer;
- typedef base::MessagePumpWin::Watcher Watcher;
-
- void Run(Dispatcher* dispatcher);
-
- void WatchObject(HANDLE object, Watcher* watcher) {
- pump_win()->WatchObject(object, watcher);
- }
- void AddObserver(Observer* observer) {
- pump_win()->AddObserver(observer);
- }
- void RemoveObserver(Observer* observer) {
- pump_win()->RemoveObserver(observer);
- }
- void WillProcessMessage(const MSG& message) {
- pump_win()->WillProcessMessage(message);
- }
- void DidProcessMessage(const MSG& message) {
- pump_win()->DidProcessMessage(message);
- }
- void PumpOutPendingPaintMessages() {
- pump_win()->PumpOutPendingPaintMessages();
- }
-#endif // defined(OS_WIN)
//----------------------------------------------------------------------------
- private:
+ protected:
friend class TimerManager; // So it can call DidChangeNextTimerExpiry
struct RunState {
@@ -406,6 +401,8 @@ class MessageLoop : public base::MessagePump::Delegate {
static const LinearHistogram::DescriptionPair event_descriptions_[];
static bool enable_histogrammer_;
+ Type type_;
+
TimerManager timer_manager_;
// A list of tasks that need to be processed by this instance. Note that this
@@ -448,5 +445,76 @@ class MessageLoop : public base::MessagePump::Delegate {
DISALLOW_COPY_AND_ASSIGN(MessageLoop);
};
-#endif // BASE_MESSAGE_LOOP_H_
+//-----------------------------------------------------------------------------
+// MessageLoopForUI extends MessageLoop with methods that are particular to a
+// MessageLoop instantiated with TYPE_UI.
+//
+// This class is typically used like so:
+// MessageLoopForUI::current()->...call some method...
+//
+class MessageLoopForUI : public MessageLoop {
+ public:
+ MessageLoopForUI() : MessageLoop(TYPE_UI) {
+ }
+
+ // Returns the MessageLoopForUI of the current thread.
+ static MessageLoopForUI* current() {
+ MessageLoop* loop = MessageLoop::current();
+ DCHECK_EQ(MessageLoop::TYPE_UI, loop->type());
+ return static_cast<MessageLoopForUI*>(loop);
+ }
+
+#if defined(OS_WIN)
+ typedef base::MessagePumpWin::Dispatcher Dispatcher;
+ typedef base::MessagePumpWin::Observer Observer;
+ // Please see MessagePumpWin for definitions of these methods.
+ void Run(Dispatcher* dispatcher);
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+ void WillProcessMessage(const MSG& message);
+ void DidProcessMessage(const MSG& message);
+ void PumpOutPendingPaintMessages();
+#endif // defined(OS_WIN)
+};
+
+// Do not add any member variables to MessageLoopForUI! This is important b/c
+// MessageLoopForUI is often allocated via MessageLoop(TYPE_UI). Any extra
+// data that you need should be stored on the MessageLoop's pump_ instance.
+COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI),
+ MessageLoopForUI_should_not_have_extra_member_variables);
+
+//-----------------------------------------------------------------------------
+// MessageLoopForIO extends MessageLoop with methods that are particular to a
+// MessageLoop instantiated with TYPE_IO.
+//
+// This class is typically used like so:
+// MessageLoopForIO::current()->...call some method...
+//
+class MessageLoopForIO : public MessageLoop {
+ public:
+ MessageLoopForIO() : MessageLoop(TYPE_IO) {
+ }
+
+ // Returns the MessageLoopForIO of the current thread.
+ static MessageLoopForIO* current() {
+ MessageLoop* loop = MessageLoop::current();
+ DCHECK_EQ(MessageLoop::TYPE_IO, loop->type());
+ return static_cast<MessageLoopForIO*>(loop);
+ }
+
+#if defined(OS_WIN)
+ typedef base::MessagePumpWin::Watcher Watcher;
+
+ // Please see MessagePumpWin for definitions of these methods.
+ void WatchObject(HANDLE object, Watcher* watcher);
+#endif // defined(OS_WIN)
+};
+
+// Do not add any member variables to MessageLoopForIO! This is important b/c
+// MessageLoopForIO is often allocated via MessageLoop(TYPE_IO). Any extra
+// data that you need should be stored on the MessageLoop's pump_ instance.
+COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForIO),
+ MessageLoopForIO_should_not_have_extra_member_variables);
+
+#endif // BASE_MESSAGE_LOOP_H_
diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc
index ca84f60..4fcde2f 100644
--- a/base/message_loop_unittest.cc
+++ b/base/message_loop_unittest.cc
@@ -14,28 +14,14 @@
#include "base/scoped_handle.h"
#endif
-//
-// TODO(darin): This file needs to be re-organized into acceptance tests that
-// apply to a MessageLoop configured to use any MessagePump. Then, those tests
-// need to be run against all MessagePump types supported by a platform.
-//
-// Finally, platform-specific MessageLoop tests should be grouped together to
-// avoid the chopping this file up with so many #ifdefs.
-//
+using base::Thread;
+
+// TODO(darin): Platform-specific MessageLoop tests should be grouped together
+// to avoid chopping this file up with so many #ifdefs.
namespace {
-class MessageLoopTest : public testing::Test {
- public:
- virtual void SetUp() {
- enable_recursive_task_ = MessageLoop::current()->NestableTasksAllowed();
- }
- virtual void TearDown() {
- MessageLoop::current()->SetNestableTasksAllowed(enable_recursive_task_);
- }
- private:
- bool enable_recursive_task_;
-};
+class MessageLoopTest : public testing::Test {};
class Foo : public base::RefCounted<Foo> {
public:
@@ -87,9 +73,9 @@ class QuitMsgLoop : public base::RefCounted<QuitMsgLoop> {
}
};
-} // namespace
+void RunTest_PostTask(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
-TEST(MessageLoopTest, PostTask) {
// Add tests to message loop
scoped_refptr<Foo> foo = new Foo();
std::string a("a"), b("b"), c("c"), d("d");
@@ -118,7 +104,9 @@ TEST(MessageLoopTest, PostTask) {
EXPECT_EQ(foo->result(), "abacad");
}
-TEST(MessageLoopTest, InvokeLater_SEH) {
+void RunTest_PostTask_SEH(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
// Add tests to message loop
scoped_refptr<Foo> foo = new Foo();
std::string a("a"), b("b"), c("c"), d("d");
@@ -149,8 +137,6 @@ TEST(MessageLoopTest, InvokeLater_SEH) {
EXPECT_EQ(foo->result(), "abacad");
}
-namespace {
-
class NestingTest : public Task {
public:
explicit NestingTest(int* depth) : depth_(depth) {
@@ -246,14 +232,9 @@ LONG WINAPI HandleCrasherTaskException(EXCEPTION_POINTERS *ex_info) {
return EXCEPTION_CONTINUE_EXECUTION;
}
-#endif // defined(OS_WIN)
-
-} // namespace
-
+void RunTest_Crasher(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
-#if defined(OS_WIN)
-
-TEST(MessageLoopTest, Crasher) {
if (::IsDebuggerPresent())
return;
@@ -268,7 +249,9 @@ TEST(MessageLoopTest, Crasher) {
::SetUnhandledExceptionFilter(old_SEH_filter);
}
-TEST(MessageLoopTest, CrasherNasty) {
+void RunTest_CrasherNasty(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
if (::IsDebuggerPresent())
return;
@@ -285,16 +268,15 @@ TEST(MessageLoopTest, CrasherNasty) {
#endif // defined(OS_WIN)
+void RunTest_Nesting(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
-TEST(MessageLoopTest, Nesting) {
int depth = 100;
MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(&depth));
MessageLoop::current()->Run();
EXPECT_EQ(depth, 0);
}
-namespace {
-
const wchar_t* const kMessageBoxTitle = L"MessageLoop Unit Test";
enum TaskType {
@@ -542,9 +524,9 @@ class Recursive2Tasks : public Task {
#endif // defined(OS_WIN)
-} // namespace
+void RunTest_RecursiveDenial1(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
-TEST(MessageLoop, RecursiveDenial1) {
EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
TaskList order;
MessageLoop::current()->PostTask(FROM_HERE,
@@ -573,8 +555,9 @@ TEST(MessageLoop, RecursiveDenial1) {
EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false));
}
-
-TEST(MessageLoop, RecursiveSupport1) {
+void RunTest_RecursiveSupport1(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
TaskList order;
MessageLoop::current()->PostTask(FROM_HERE,
new RecursiveTask(2, &order, 1, true));
@@ -608,9 +591,13 @@ TEST(MessageLoop, RecursiveSupport1) {
// message loop functionality.
// A side effect of this test is the generation a beep. Sorry.
-TEST(MessageLoop, RecursiveDenial2) {
+void RunTest_RecursiveDenial2(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
Thread worker("RecursiveDenial2_worker");
- ASSERT_EQ(true, worker.Start());
+ Thread::Options options;
+ options.message_loop_type = message_loop_type;
+ ASSERT_EQ(true, worker.StartWithOptions(options));
TaskList order;
ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
worker.message_loop()->PostTask(FROM_HERE,
@@ -645,10 +632,15 @@ TEST(MessageLoop, RecursiveDenial2) {
EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, false));
}
-// A side effect of this test is the generation a beep. Sorry.
-TEST(MessageLoop, RecursiveSupport2) {
+// A side effect of this test is the generation a beep. Sorry. This test also
+// needs to process windows messages on the current thread.
+void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
Thread worker("RecursiveSupport2_worker");
- ASSERT_EQ(true, worker.Start());
+ Thread::Options options;
+ options.message_loop_type = message_loop_type;
+ ASSERT_EQ(true, worker.StartWithOptions(options));
TaskList order;
ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
worker.message_loop()->PostTask(FROM_HERE,
@@ -704,13 +696,12 @@ class TaskThatPumps : public OrderedTasks {
MessageLoop::current()->SetNestableTasksAllowed(old_state);
RunEnd();
}
-
- private:
};
-
// Tests that non nestable tasks run in FIFO if there are no nested loops.
-TEST(MessageLoop, NonNestableWithNoNesting) {
+void RunTest_NonNestableWithNoNesting(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
TaskList order;
Task* task = new OrderedTasks(&order, 1);
@@ -731,7 +722,9 @@ TEST(MessageLoop, NonNestableWithNoNesting) {
}
// Tests that non nestable tasks don't run when there's code in the call stack.
-TEST(MessageLoop, NonNestableInNestedLoop) {
+void RunTest_NonNestableInNestedLoop(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
TaskList order;
MessageLoop::current()->PostTask(FROM_HERE,
@@ -745,7 +738,6 @@ TEST(MessageLoop, NonNestableInNestedLoop) {
non_nestable_quit->set_nestable(false);
MessageLoop::current()->PostTask(FROM_HERE, non_nestable_quit);
-
MessageLoop::current()->Run();
// FIFO order.
@@ -764,41 +756,36 @@ TEST(MessageLoop, NonNestableInNestedLoop) {
#if defined(OS_WIN)
-namespace {
-
-class AutoresetWatcher : public MessageLoop::Watcher {
+class AutoresetWatcher : public MessageLoopForIO::Watcher {
public:
- AutoresetWatcher(HANDLE signal, MessageLoop* message_loop)
- : signal_(signal),
- message_loop_(message_loop) {
+ AutoresetWatcher(HANDLE signal) : signal_(signal) {
}
virtual void OnObjectSignaled(HANDLE object);
private:
HANDLE signal_;
- MessageLoop* message_loop_;
};
void AutoresetWatcher::OnObjectSignaled(HANDLE object) {
- message_loop_->WatchObject(object, NULL);
+ MessageLoopForIO::current()->WatchObject(object, NULL);
ASSERT_TRUE(SetEvent(signal_));
}
class AutoresetTask : public Task {
public:
- AutoresetTask(HANDLE object, MessageLoop::Watcher* watcher)
+ AutoresetTask(HANDLE object, MessageLoopForIO::Watcher* watcher)
: object_(object), watcher_(watcher) {}
virtual void Run() {
- MessageLoop::current()->WatchObject(object_, watcher_);
+ MessageLoopForIO::current()->WatchObject(object_, watcher_);
}
private:
HANDLE object_;
- MessageLoop::Watcher* watcher_;
+ MessageLoopForIO::Watcher* watcher_;
};
-} // namespace
+void RunTest_AutoresetEvents(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
-TEST(MessageLoop, AutoresetEvents) {
SECURITY_ATTRIBUTES attributes;
attributes.nLength = sizeof(attributes);
attributes.bInheritHandle = false;
@@ -811,14 +798,16 @@ TEST(MessageLoop, AutoresetEvents) {
ASSERT_TRUE(NULL != callback_called);
Thread thread("Autoreset test");
- ASSERT_TRUE(thread.Start());
+ Thread::Options options;
+ options.message_loop_type = message_loop_type;
+ ASSERT_TRUE(thread.StartWithOptions(options));
- MessageLoop* message_loop = thread.message_loop();
- ASSERT_TRUE(NULL != message_loop);
+ MessageLoop* thread_loop = thread.message_loop();
+ ASSERT_TRUE(NULL != thread_loop);
- AutoresetWatcher watcher(callback_called, message_loop);
+ AutoresetWatcher watcher(callback_called);
AutoresetTask* task = new AutoresetTask(autoreset, &watcher);
- message_loop->PostTask(FROM_HERE, task);
+ thread_loop->PostTask(FROM_HERE, task);
Sleep(100); // Make sure the thread runs and sleeps for lack of work.
ASSERT_TRUE(SetEvent(autoreset));
@@ -829,9 +818,7 @@ TEST(MessageLoop, AutoresetEvents) {
thread.Stop();
}
-namespace {
-
-class DispatcherImpl : public MessageLoop::Dispatcher {
+class DispatcherImpl : public MessageLoopForUI::Dispatcher {
public:
DispatcherImpl() : dispatch_count_(0) {}
@@ -844,9 +831,9 @@ class DispatcherImpl : public MessageLoop::Dispatcher {
int dispatch_count_;
};
-} // namespace
+void RunTest_Dispatcher(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
-TEST(MessageLoop, Dispatcher) {
class MyTask : public Task {
public:
virtual void Run() {
@@ -857,9 +844,96 @@ TEST(MessageLoop, Dispatcher) {
Task* task = new MyTask();
MessageLoop::current()->PostDelayedTask(FROM_HERE, task, 100);
DispatcherImpl dispatcher;
- MessageLoop::current()->Run(&dispatcher);
+ MessageLoopForUI::current()->Run(&dispatcher);
ASSERT_EQ(2, dispatcher.dispatch_count_);
}
-#endif
+#endif // defined(OS_WIN)
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+// Each test is run against each type of MessageLoop. That way we are sure
+// that message loops work properly in all configurations. Of course, in some
+// cases, a unit test may only be for a particular type of loop.
+
+TEST(MessageLoopTest, PostTask) {
+ RunTest_PostTask(MessageLoop::TYPE_DEFAULT);
+ RunTest_PostTask(MessageLoop::TYPE_UI);
+ RunTest_PostTask(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, PostTask_SEH) {
+ RunTest_PostTask_SEH(MessageLoop::TYPE_DEFAULT);
+ RunTest_PostTask_SEH(MessageLoop::TYPE_UI);
+ RunTest_PostTask_SEH(MessageLoop::TYPE_IO);
+}
+
+#if defined(OS_WIN)
+TEST(MessageLoopTest, Crasher) {
+ RunTest_Crasher(MessageLoop::TYPE_DEFAULT);
+ RunTest_Crasher(MessageLoop::TYPE_UI);
+ RunTest_Crasher(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, CrasherNasty) {
+ RunTest_CrasherNasty(MessageLoop::TYPE_DEFAULT);
+ RunTest_CrasherNasty(MessageLoop::TYPE_UI);
+ RunTest_CrasherNasty(MessageLoop::TYPE_IO);
+}
+#endif // defined(OS_WIN)
+
+TEST(MessageLoopTest, Nesting) {
+ RunTest_Nesting(MessageLoop::TYPE_DEFAULT);
+ RunTest_Nesting(MessageLoop::TYPE_UI);
+ RunTest_Nesting(MessageLoop::TYPE_IO);
+}
+TEST(MessageLoopTest, RecursiveDenial1) {
+ RunTest_RecursiveDenial1(MessageLoop::TYPE_DEFAULT);
+ RunTest_RecursiveDenial1(MessageLoop::TYPE_UI);
+ RunTest_RecursiveDenial1(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RecursiveSupport1) {
+ RunTest_RecursiveSupport1(MessageLoop::TYPE_DEFAULT);
+ RunTest_RecursiveSupport1(MessageLoop::TYPE_UI);
+ RunTest_RecursiveSupport1(MessageLoop::TYPE_IO);
+}
+
+#if defined(OS_WIN)
+TEST(MessageLoopTest, RecursiveDenial2) {
+ RunTest_RecursiveDenial2(MessageLoop::TYPE_DEFAULT);
+ RunTest_RecursiveDenial2(MessageLoop::TYPE_UI);
+ RunTest_RecursiveDenial2(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RecursiveSupport2) {
+ // This test requires a UI loop
+ RunTest_RecursiveSupport2(MessageLoop::TYPE_UI);
+}
+#endif // defined(OS_WIN)
+
+TEST(MessageLoopTest, NonNestableWithNoNesting) {
+ RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_DEFAULT);
+ RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_UI);
+ RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, NonNestableInNestedLoop) {
+ RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_DEFAULT);
+ RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_UI);
+ RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_IO);
+}
+
+#if defined(OS_WIN)
+TEST(MessageLoopTest, AutoresetEvents) {
+ // This test requires an IO loop
+ RunTest_AutoresetEvents(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, Dispatcher) {
+ // This test requires a UI loop
+ RunTest_Dispatcher(MessageLoop::TYPE_UI);
+}
+#endif // defined(OS_WIN)
diff --git a/base/object_watcher_unittest.cc b/base/object_watcher_unittest.cc
index 1598d76..d830dfc 100644
--- a/base/object_watcher_unittest.cc
+++ b/base/object_watcher_unittest.cc
@@ -9,6 +9,7 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace {
+
typedef testing::Test ObjectWatcherTest;
class QuitDelegate : public base::ObjectWatcher::Delegate {
@@ -29,9 +30,11 @@ class DecrementCountDelegate : public base::ObjectWatcher::Delegate {
int* counter_;
};
-}
+} // namespace
TEST(ObjectWatcherTest, BasicSignal) {
+ MessageLoop message_loop;
+
base::ObjectWatcher watcher;
// A manual-reset event that is not yet signaled.
@@ -49,6 +52,8 @@ TEST(ObjectWatcherTest, BasicSignal) {
}
TEST(ObjectWatcherTest, BasicCancel) {
+ MessageLoop message_loop;
+
base::ObjectWatcher watcher;
// A manual-reset event that is not yet signaled.
@@ -65,6 +70,8 @@ TEST(ObjectWatcherTest, BasicCancel) {
TEST(ObjectWatcherTest, CancelAfterSet) {
+ MessageLoop message_loop;
+
base::ObjectWatcher watcher;
int counter = 1;
@@ -91,10 +98,10 @@ TEST(ObjectWatcherTest, CancelAfterSet) {
CloseHandle(event);
}
-// Used so we can simulate a MessageLoop that dies before an ObjectWatcher.
-// This ordinarily doesn't happen when people use the Thread class, but it can
-// happen when people use the Singleton pattern or atexit.
-static unsigned __stdcall ThreadFunc(void* param) {
+TEST(ObjectWatcherTest, OutlivesMessageLoop) {
+ // Simulate a MessageLoop that dies before an ObjectWatcher. This ordinarily
+ // doesn't happen when people use the Thread class, but it can happen when
+ // people use the Singleton pattern or atexit.
HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL); // not signaled
{
base::ObjectWatcher watcher;
@@ -106,19 +113,4 @@ static unsigned __stdcall ThreadFunc(void* param) {
}
}
CloseHandle(event);
- return 0;
}
-
-TEST(ObjectWatcherTest, OutlivesMessageLoop) {
- unsigned int thread_id;
- HANDLE thread = reinterpret_cast<HANDLE>(
- _beginthreadex(NULL,
- 0,
- ThreadFunc,
- NULL,
- 0,
- &thread_id));
- WaitForSingleObject(thread, INFINITE);
- CloseHandle(thread);
-}
-
diff --git a/base/run_all_unittests.cc b/base/run_all_unittests.cc
index 7b469e2..c0b131f 100644
--- a/base/run_all_unittests.cc
+++ b/base/run_all_unittests.cc
@@ -2,15 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/at_exit.h"
#include "base/test_suite.h"
int main(int argc, char** argv) {
- // Set up an AtExitManager so Singleton objects will be destroyed.
- base::AtExitManager at_exit_manager;
-
- CommandLine::SetArgcArgv(argc, argv);
-
return TestSuite(argc, argv).Run();
}
diff --git a/base/test_suite.h b/base/test_suite.h
index 8a163ec..ae5faf1 100644
--- a/base/test_suite.h
+++ b/base/test_suite.h
@@ -9,13 +9,11 @@
// instantiate this class in your main function and call its Run method to run
// any gtest based tests that are linked into your executable.
-#include "build/build_config.h"
-
+#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/debug_on_start.h"
#include "base/icu_util.h"
#include "base/logging.h"
-#include "base/message_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
@@ -26,23 +24,18 @@
class TestSuite {
public:
TestSuite(int argc, char** argv) {
+ CommandLine::SetArgcArgv(argc, argv);
testing::InitGoogleTest(&argc, argv);
}
- virtual ~TestSuite() {
- // Flush any remaining messages. This ensures that any accumulated Task
- // objects get destroyed before we exit, which avoids noise in purify
- // leak-test results.
- message_loop_.RunAllPending();
- }
+ virtual ~TestSuite() {}
int Run() {
Initialize();
#if defined(OS_WIN)
// Check to see if we are being run as a client process.
- std::wstring client_func =
- parsed_command_line_.GetSwitchValue(kRunClientProcess);
+ std::wstring client_func = CommandLine().GetSwitchValue(kRunClientProcess);
if (!client_func.empty()) {
// Convert our function name to a usable string for GetProcAddress.
std::string func_name(client_func.begin(), client_func.end());
@@ -57,7 +50,11 @@ class TestSuite {
return -1;
}
#endif
- return RUN_ALL_TESTS();
+
+ int result = RUN_ALL_TESTS();
+
+ Shutdown();
+ return result;
}
protected:
@@ -80,11 +77,14 @@ class TestSuite {
}
#endif
+ // Override these for custom initialization and shutdown handling. Use these
+ // instead of putting complex code in your constructor/destructor.
+
virtual void Initialize() {
#if defined(OS_WIN)
// In some cases, we do not want to see standard error dialogs.
if (!IsDebuggerPresent() &&
- !parsed_command_line_.HasSwitch(L"show-error-dialogs")) {
+ !CommandLine().HasSwitch(L"show-error-dialogs")) {
SuppressErrorDialogs();
logging::SetLogAssertHandler(UnitTestAssertHandler);
}
@@ -93,8 +93,12 @@ class TestSuite {
icu_util::Initialize();
}
- CommandLine parsed_command_line_;
- MessageLoop message_loop_;
+ virtual void Shutdown() {
+ }
+
+ // Make sure that we setup an AtExitManager so Singleton objects will be
+ // destroyed.
+ base::AtExitManager at_exit_manager_;
};
#endif // BASE_TEST_SUITE_H_
diff --git a/base/thread.cc b/base/thread.cc
index 0fe023a..d411ae9 100644
--- a/base/thread.cc
+++ b/base/thread.cc
@@ -4,10 +4,11 @@
#include "base/thread.h"
-#include "base/message_loop.h"
#include "base/string_util.h"
#include "base/waitable_event.h"
+namespace base {
+
// This task is used to trigger the message loop to exit.
class ThreadQuitTask : public Task {
public:
@@ -17,11 +18,24 @@ class ThreadQuitTask : public Task {
}
};
+// Used to pass data to ThreadMain. This structure is allocated on the stack
+// from within StartWithOptions.
+struct Thread::StartupData {
+ // We get away with a const reference here because of how we are allocated.
+ const Thread::Options& options;
+
+ // Used to synchronize thread startup.
+ WaitableEvent event;
+
+ StartupData(const Options& opt) : options(opt), event(false, false) {}
+};
+
Thread::Thread(const char *name)
- : message_loop_(NULL),
- startup_event_(NULL),
- name_(name),
- thread_created_(false) {
+ : startup_data_(NULL),
+ thread_(NULL),
+ message_loop_(NULL),
+ thread_id_(0),
+ name_(name) {
}
Thread::~Thread() {
@@ -52,59 +66,62 @@ bool Thread::GetThreadWasQuitProperly() {
}
bool Thread::Start() {
- return StartWithStackSize(0);
+ return StartWithOptions(Options());
}
-bool Thread::StartWithStackSize(size_t stack_size) {
+bool Thread::StartWithOptions(const Options& options) {
DCHECK(!message_loop_);
SetThreadWasQuitProperly(false);
- base::WaitableEvent event(false, false);
- startup_event_ = &event;
+ StartupData startup_data(options);
+ startup_data_ = &startup_data;
- if (!PlatformThread::Create(stack_size, this, &thread_)) {
+ if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
DLOG(ERROR) << "failed to create thread";
+ startup_data_ = NULL; // Record that we failed to start.
return false;
}
// Wait for the thread to start and initialize message_loop_
- startup_event_->Wait();
- startup_event_ = NULL;
+ startup_data.event.Wait();
DCHECK(message_loop_);
return true;
}
void Thread::Stop() {
- if (!thread_created_)
+ if (!thread_was_started())
return;
- DCHECK_NE(thread_id_, PlatformThread::CurrentId()) <<
- "Can't call Stop() on the currently executing thread";
+ // We should only be called on the same thread that started us.
+ DCHECK_NE(thread_id_, PlatformThread::CurrentId());
- // If StopSoon was called, then we won't have a message loop anymore, but
- // more importantly, we won't need to tell the thread to stop.
+ // StopSoon may have already been called.
if (message_loop_)
message_loop_->PostTask(FROM_HERE, new ThreadQuitTask());
- // Wait for the thread to exit. It should already have terminated but make
+ // Wait for the thread to exit. It should already have terminated but make
// sure this assumption is valid.
+ //
+ // TODO(darin): Unfortunately, we need to keep message_loop_ around until
+ // the thread exits. Some consumers are abusing the API. Make them stop.
+ //
PlatformThread::Join(thread_);
// The thread can't receive messages anymore.
message_loop_ = NULL;
// The thread no longer needs to be joined.
- thread_created_ = false;
+ startup_data_ = NULL;
}
void Thread::StopSoon() {
if (!message_loop_)
return;
- DCHECK_NE(thread_id_, PlatformThread::CurrentId()) <<
- "Can't call StopSoon() on the currently executing thread";
+ // We should only be called on the same thread that started us.
+ DCHECK_NE(thread_id_, PlatformThread::CurrentId());
// We had better have a message loop at this point! If we do not, then it
// most likely means that the thread terminated unexpectedly, probably due
@@ -119,17 +136,16 @@ void Thread::StopSoon() {
void Thread::ThreadMain() {
// The message loop for this thread.
- MessageLoop message_loop;
+ MessageLoop message_loop(startup_data_->options.message_loop_type);
// Complete the initialization of our Thread object.
thread_id_ = PlatformThread::CurrentId();
PlatformThread::SetName(name_.c_str());
message_loop.set_thread_name(name_);
message_loop_ = &message_loop;
- thread_created_ = true;
- startup_event_->Signal();
- // startup_event_ can't be touched anymore since the starting thread is now
+ startup_data_->event.Signal();
+ // startup_data_ can't be touched anymore since the starting thread is now
// unlocked.
// Let the thread do extra initialization.
@@ -147,3 +163,4 @@ void Thread::ThreadMain() {
message_loop_ = NULL;
}
+} // namespace base
diff --git a/base/thread.h b/base/thread.h
index 1b3ba14..619d2b7 100644
--- a/base/thread.h
+++ b/base/thread.h
@@ -7,14 +7,11 @@
#include <string>
+#include "base/message_loop.h"
#include "base/platform_thread.h"
#include "base/thread_local_storage.h"
-class MessageLoop;
-
namespace base {
-class WaitableEvent;
-}
// A simple thread abstraction that establishes a MessageLoop on a new thread.
// The consumer uses the MessageLoop of the thread to cause code to execute on
@@ -23,12 +20,27 @@ class WaitableEvent;
// before the thread is terminated.
class Thread : PlatformThread::Delegate {
public:
+ struct Options {
+ // Specifies the type of message loop that will be allocated on the thread.
+ MessageLoop::Type message_loop_type;
+
+ // Specifies the maximum stack size that the thread is allowed to use.
+ // This does not necessarily correspond to the thread's initial stack size.
+ // A value of 0 indicates that the default maximum should be used.
+ size_t stack_size;
+
+ Options() : message_loop_type(MessageLoop::TYPE_DEFAULT), stack_size(0) {}
+ };
+
// Constructor.
// name is a display string to identify the thread.
explicit Thread(const char *name);
// Destroys the thread, stopping it if necessary.
//
+ // NOTE: If you are subclassing from Thread, and you wish for your CleanUp
+ // method to be called, then you need to call Stop() from your destructor.
+ //
virtual ~Thread();
// Starts the thread. Returns true if the thread was successfully started;
@@ -41,13 +53,12 @@ class Thread : PlatformThread::Delegate {
bool Start();
// Starts the thread. Behaves exactly like Start in addition to allow to
- // override the default process stack size. This is not the initial stack size
- // but the maximum stack size that thread is allowed to use.
+ // override the default options.
//
// Note: This function can't be called on Windows with the loader lock held;
// i.e. during a DllMain, global object construction or destruction, atexit()
// callback.
- bool StartWithStackSize(size_t stack_size);
+ bool StartWithOptions(const Options& options);
// Signals the thread to exit and returns once the thread has exited. After
// this method returns, the Thread object is completely reset and may be used
@@ -93,10 +104,10 @@ class Thread : PlatformThread::Delegate {
protected:
// Called just prior to starting the message loop
- virtual void Init() { }
+ virtual void Init() {}
// Called just after the message loop ends
- virtual void CleanUp() { }
+ virtual void CleanUp() {}
static void SetThreadWasQuitProperly(bool flag);
static bool GetThreadWasQuitProperly();
@@ -105,25 +116,27 @@ class Thread : PlatformThread::Delegate {
// PlatformThread::Delegate methods:
virtual void ThreadMain();
+ // We piggy-back on the startup_data_ member to know if we successfully
+ // started the thread. This way we know that we need to call Join.
+ bool thread_was_started() const { return startup_data_ != NULL; }
+
+ // Used to pass data to ThreadMain.
+ struct StartupData;
+ StartupData* startup_data_;
+
// The thread's handle.
PlatformThreadHandle thread_;
- // The thread's ID. Used for debugging purposes.
- int thread_id_;
-
// The thread's message loop. Valid only while the thread is alive. Set
// by the created thread.
MessageLoop* message_loop_;
- // Used to synchronize thread startup.
- base::WaitableEvent* startup_event_;
+ // Our thread's ID. Used for debugging purposes.
+ int thread_id_;
// The name of the thread. Used for debugging purposes.
std::string name_;
- // This flag indicates if we created a thread that needs to be joined.
- bool thread_created_;
-
static TLSSlot tls_index_;
friend class ThreadQuitTask;
@@ -131,5 +144,7 @@ class Thread : PlatformThread::Delegate {
DISALLOW_COPY_AND_ASSIGN(Thread);
};
+} // namespace base
+
#endif // BASE_THREAD_H_
diff --git a/base/thread_unittest.cc b/base/thread_unittest.cc
index c570e91..cad346f 100644
--- a/base/thread_unittest.cc
+++ b/base/thread_unittest.cc
@@ -8,6 +8,8 @@
#include "base/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::Thread;
+
namespace {
class ToggleValue : public Task {
@@ -50,11 +52,13 @@ TEST(ThreadTest, Restart) {
EXPECT_FALSE(a.message_loop());
}
-TEST(ThreadTest, StartWithStackSize) {
+TEST(ThreadTest, StartWithOptions_StackSize) {
Thread a("StartWithStackSize");
// Ensure that the thread can work with only 12 kb and still process a
// message.
- EXPECT_TRUE(a.StartWithStackSize(12*1024));
+ Thread::Options options;
+ options.stack_size = 12*1024;
+ EXPECT_TRUE(a.StartWithOptions(options));
EXPECT_TRUE(a.message_loop());
bool was_invoked = false;
diff --git a/base/timer_unittest.cc b/base/timer_unittest.cc
index 3fa85f5..ab13c08 100644
--- a/base/timer_unittest.cc
+++ b/base/timer_unittest.cc
@@ -8,9 +8,8 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace {
- class TimerTest : public testing::Test {
- };
-};
+
+class TimerTest : public testing::Test {};
// A base class timer task that sanity-checks timer functionality and counts
// the number of times it has run. Handles all message loop and memory
@@ -196,7 +195,12 @@ void RunTimerTest() {
EXPECT_EQ(10, task3.iterations());
}
-TEST(TimerTest, TimerComparison) {
+//-----------------------------------------------------------------------------
+// The timer test cases:
+
+void RunTest_TimerComparison(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
// Make sure TimerComparison sorts correctly.
const TimerTask task1(10, false);
const Timer* timer1 = task1.timer();
@@ -207,11 +211,15 @@ TEST(TimerTest, TimerComparison) {
EXPECT_TRUE(comparison(timer2, timer1));
}
-TEST(TimerTest, TimerCase) {
+void RunTest_BasicTimer(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
RunTimerTest();
}
-TEST(TimerTest, BrokenTimerCase) {
+void RunTest_BrokenTimer(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
// Simulate faulty early-firing timers. The tasks in RunTimerTest should
// nevertheless be invoked after their specified delays, regardless of when
// WM_TIMER fires.
@@ -221,7 +229,9 @@ TEST(TimerTest, BrokenTimerCase) {
manager->set_use_broken_delay(false);
}
-TEST(TimerTest, DeleteFromRun) {
+void RunTest_DeleteFromRun(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
// Make sure TimerManager correctly handles a Task that deletes itself when
// run.
new DeletingTask(50, true);
@@ -231,7 +241,9 @@ TEST(TimerTest, DeleteFromRun) {
EXPECT_EQ(1, timer_task.iterations());
}
-TEST(TimerTest, Reset) {
+void RunTest_Reset(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
// Make sure resetting a timer after it has fired works.
TimerTask timer_task1(250, false);
TimerTask timer_task2(100, true);
@@ -253,7 +265,9 @@ TEST(TimerTest, Reset) {
EXPECT_EQ(0, timer_task4.iterations());
}
-TEST(TimerTest, FifoOrder) {
+void RunTest_FifoOrder(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
// Creating timers with the same timeout should
// always compare to result in FIFO ordering.
@@ -309,3 +323,44 @@ TEST(TimerTest, FifoOrder) {
EXPECT_GT(new_id, last_id);
}
+} // namespace
+
+//-----------------------------------------------------------------------------
+// Each test is run against each type of MessageLoop. That way we are sure
+// that timers work properly in all configurations.
+
+TEST(TimerTest, TimerComparison) {
+ RunTest_TimerComparison(MessageLoop::TYPE_DEFAULT);
+ RunTest_TimerComparison(MessageLoop::TYPE_UI);
+ RunTest_TimerComparison(MessageLoop::TYPE_IO);
+}
+
+TEST(TimerTest, BasicTimer) {
+ RunTest_BasicTimer(MessageLoop::TYPE_DEFAULT);
+ RunTest_BasicTimer(MessageLoop::TYPE_UI);
+ RunTest_BasicTimer(MessageLoop::TYPE_IO);
+}
+
+TEST(TimerTest, BrokenTimer) {
+ RunTest_BrokenTimer(MessageLoop::TYPE_DEFAULT);
+ RunTest_BrokenTimer(MessageLoop::TYPE_UI);
+ RunTest_BrokenTimer(MessageLoop::TYPE_IO);
+}
+
+TEST(TimerTest, DeleteFromRun) {
+ RunTest_DeleteFromRun(MessageLoop::TYPE_DEFAULT);
+ RunTest_DeleteFromRun(MessageLoop::TYPE_UI);
+ RunTest_DeleteFromRun(MessageLoop::TYPE_IO);
+}
+
+TEST(TimerTest, Reset) {
+ RunTest_Reset(MessageLoop::TYPE_DEFAULT);
+ RunTest_Reset(MessageLoop::TYPE_UI);
+ RunTest_Reset(MessageLoop::TYPE_IO);
+}
+
+TEST(TimerTest, FifoOrder) {
+ RunTest_FifoOrder(MessageLoop::TYPE_DEFAULT);
+ RunTest_FifoOrder(MessageLoop::TYPE_UI);
+ RunTest_FifoOrder(MessageLoop::TYPE_IO);
+}
diff --git a/net/base/listen_socket_unittest.h b/net/base/listen_socket_unittest.h
index 8c5cb6d..36af167 100644
--- a/net/base/listen_socket_unittest.h
+++ b/net/base/listen_socket_unittest.h
@@ -77,7 +77,7 @@ class ListenSocketTester :
server_ = NULL;
net::WinsockInit::Init();
- thread_.reset(new Thread("socketio_test"));
+ thread_.reset(new base::Thread("socketio_test"));
thread_->Start();
loop_ = thread_->message_loop();
@@ -251,7 +251,7 @@ class ListenSocketTester :
ASSERT_EQ(buf, HELLO_WORLD);
}
- scoped_ptr<Thread> thread_;
+ scoped_ptr<base::Thread> thread_;
MessageLoop* loop_;
ListenSocket* server_;
ListenSocket* connection_;
diff --git a/net/base/run_all_unittests.cc b/net/base/run_all_unittests.cc
new file mode 100644
index 0000000..a1bae81
--- /dev/null
+++ b/net/base/run_all_unittests.cc
@@ -0,0 +1,58 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (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 "base/message_loop.h"
+#include "base/test_suite.h"
+
+class NetTestSuite : public TestSuite {
+ public:
+ NetTestSuite(int argc, char** argv) : TestSuite(argc, argv) {
+ }
+
+ virtual void Initialize() {
+ TestSuite::Initialize();
+
+ message_loop_.reset(new MessageLoopForIO());
+ }
+
+ virtual void CleanUp() {
+ // We want to destroy this here before the TestSuite continues to tear down
+ // the environment.
+ message_loop_.reset();
+
+ TestSuite::Shutdown();
+ }
+
+ private:
+ scoped_ptr<MessageLoop> message_loop_;
+};
+
+int main(int argc, char** argv) {
+ return NetTestSuite(argc, argv).Run();
+}
diff --git a/net/build/net_unittests.vcproj b/net/build/net_unittests.vcproj
index 1619e5c..d1d720d 100644
--- a/net/build/net_unittests.vcproj
+++ b/net/build/net_unittests.vcproj
@@ -172,7 +172,7 @@
>
</File>
<File
- RelativePath="..\..\base\run_all_unittests.cc"
+ RelativePath="..\base\run_all_unittests.cc"
>
</File>
</Filter>
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc
index 1d48090..ec4fdd6 100644
--- a/net/proxy/proxy_service.cc
+++ b/net/proxy/proxy_service.cc
@@ -325,7 +325,7 @@ int ProxyService::ResolveProxy(const GURL& url, ProxyInfo* result,
if (callback) {
// Create PAC thread for asynchronous mode.
if (!pac_thread_.get()) {
- pac_thread_.reset(new Thread("pac-thread"));
+ pac_thread_.reset(new base::Thread("pac-thread"));
pac_thread_->Start();
}
} else {
diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h
index 94e8235..5ca41e8 100644
--- a/net/proxy/proxy_service.h
+++ b/net/proxy/proxy_service.h
@@ -129,7 +129,7 @@ class ProxyService {
friend class PacRequest;
ProxyResolver* resolver() { return resolver_; }
- Thread* pac_thread() { return pac_thread_.get(); }
+ base::Thread* pac_thread() { return pac_thread_.get(); }
// Identifies the proxy configuration.
ProxyConfig::ID config_id() const { return config_.id(); }
@@ -151,7 +151,7 @@ class ProxyService {
bool ShouldBypassProxyForURL(const GURL& url);
ProxyResolver* resolver_;
- scoped_ptr<Thread> pac_thread_;
+ scoped_ptr<base::Thread> pac_thread_;
// We store the IE proxy config and a counter that is incremented each time
// the config changes.
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 6547e38..8c6d5f3 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -5,6 +5,7 @@
#include "net/url_request/url_request.h"
#include "base/basictypes.h"
+#include "base/message_loop.h"
#include "base/process_util.h"
#include "base/singleton.h"
#include "base/stats_counters.h"
@@ -47,6 +48,12 @@ URLRequest::URLRequest(const GURL& url, Delegate* delegate)
URLREQUEST_COUNT_CTOR();
SIMPLE_STATS_COUNTER(L"URLRequestCount");
origin_pid_ = process_util::GetCurrentProcId();
+
+ // Sanity check out environment.
+ DCHECK(MessageLoop::current()) <<
+ "The current MessageLoop must exist";
+ DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) <<
+ "The current MessageLoop must be TYPE_IO";
}
URLRequest::~URLRequest() {
diff --git a/net/url_request/url_request_file_job.cc b/net/url_request/url_request_file_job.cc
index 2ceaed3..4e23d78 100644
--- a/net/url_request/url_request_file_job.cc
+++ b/net/url_request/url_request_file_job.cc
@@ -140,7 +140,7 @@ void URLRequestFileJob::Start() {
void URLRequestFileJob::Kill() {
// If we are killed while waiting for an overlapped result...
if (is_waiting_) {
- MessageLoop::current()->WatchObject(overlapped_.hEvent, NULL);
+ MessageLoopForIO::current()->WatchObject(overlapped_.hEvent, NULL);
is_waiting_ = false;
Release();
}
@@ -178,7 +178,7 @@ bool URLRequestFileJob::ReadRawData(char* dest, int dest_size,
DWORD err = GetLastError();
if (err == ERROR_IO_PENDING) {
// OK, wait for the object to become signaled
- MessageLoop::current()->WatchObject(overlapped_.hEvent, this);
+ MessageLoopForIO::current()->WatchObject(overlapped_.hEvent, this);
is_waiting_ = true;
SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
AddRef();
@@ -261,7 +261,7 @@ void URLRequestFileJob::OnObjectSignaled(HANDLE object) {
// We'll resume watching this handle if need be when we do
// another IO.
- MessageLoop::current()->WatchObject(object, NULL);
+ MessageLoopForIO::current()->WatchObject(object, NULL);
is_waiting_ = false;
DWORD bytes_read = 0;
diff --git a/net/url_request/url_request_file_job.h b/net/url_request/url_request_file_job.h
index f5999d3..638f4ff 100644
--- a/net/url_request/url_request_file_job.h
+++ b/net/url_request/url_request_file_job.h
@@ -14,7 +14,7 @@
// A request job that handles reading file URLs
class URLRequestFileJob : public URLRequestJob,
- protected MessageLoop::Watcher {
+ protected MessageLoopForIO::Watcher {
public:
URLRequestFileJob(URLRequest* request);
virtual ~URLRequestFileJob();
diff --git a/net/url_request/url_request_unittest.h b/net/url_request/url_request_unittest.h
index ea9b68e..f17e7fc 100644
--- a/net/url_request/url_request_unittest.h
+++ b/net/url_request/url_request_unittest.h
@@ -13,6 +13,8 @@
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/waitable_event.h"
#include "net/base/net_errors.h"
#include "net/http/http_network_layer.h"
#include "net/url_request/url_request.h"
@@ -113,7 +115,7 @@ class TestDelegate : public URLRequest::Delegate {
request->Cancel();
}
- void OnResponseCompleted(URLRequest* request) {
+ virtual void OnResponseCompleted(URLRequest* request) {
if (quit_on_complete_)
MessageLoop::current()->Quit();
}
@@ -183,8 +185,7 @@ class TestDelegate : public URLRequest::Delegate {
class TestServer : public process_util::ProcessFilter {
public:
TestServer(const std::wstring& document_root)
- : context_(new TestURLRequestContext),
- process_handle_(NULL),
+ : process_handle_(NULL),
is_shutdown_(true) {
Init(kDefaultHostName, kDefaultPort, document_root, std::wstring());
}
@@ -213,16 +214,23 @@ class TestServer : public process_util::ProcessFilter {
// A subclass may wish to send the request in a different manner
virtual bool MakeGETRequest(const std::string& page_name) {
- TestDelegate d;
- URLRequest r(TestServerPage(page_name), &d);
- r.set_context(context_);
- r.set_method("GET");
- r.Start();
- EXPECT_TRUE(r.is_pending());
-
- MessageLoop::current()->Run();
-
- return r.status().is_success();
+ const GURL& url = TestServerPage(page_name);
+
+ // Spin up a background thread for this request so that we have access to
+ // an IO message loop, and in cases where this thread already has an IO
+ // message loop, we also want to avoid spinning a nested message loop.
+
+ SyncTestDelegate d;
+ {
+ base::Thread io_thread("MakeGETRequest");
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ io_thread.StartWithOptions(options);
+ io_thread.message_loop()->PostTask(FROM_HERE, NewRunnableFunction(
+ &TestServer::StartGETRequest, url, &d));
+ d.Wait();
+ }
+ return d.did_succeed();
}
protected:
@@ -232,8 +240,7 @@ class TestServer : public process_util::ProcessFilter {
// constructed. The subclass should call Init once it is ready (usually in
// its constructor).
TestServer(ManualInit)
- : context_(new TestURLRequestContext),
- process_handle_(NULL),
+ : process_handle_(NULL),
is_shutdown_(true) {
}
@@ -334,7 +341,31 @@ class TestServer : public process_util::ProcessFilter {
}
private:
- scoped_refptr<TestURLRequestContext> context_;
+ // Used by MakeGETRequest to implement sync load behavior.
+ class SyncTestDelegate : public TestDelegate {
+ public:
+ SyncTestDelegate() : event_(false, false), success_(false) {
+ }
+ virtual void OnResponseCompleted(URLRequest* request) {
+ MessageLoop::current()->DeleteSoon(FROM_HERE, request);
+ success_ = request->status().is_success();
+ event_.Signal();
+ }
+ void Wait() { event_.Wait(); }
+ bool did_succeed() const { return success_; }
+ private:
+ base::WaitableEvent event_;
+ bool success_;
+ DISALLOW_COPY_AND_ASSIGN(SyncTestDelegate);
+ };
+ static void StartGETRequest(const GURL& url, URLRequest::Delegate* delegate) {
+ URLRequest* request = new URLRequest(url, delegate);
+ request->set_context(new TestURLRequestContext());
+ request->set_method("GET");
+ request->Start();
+ EXPECT_TRUE(request->is_pending());
+ }
+
std::string base_address_;
std::wstring python_runtime_;
HANDLE process_handle_;
diff --git a/webkit/default_plugin/plugin_install_job_monitor.h b/webkit/default_plugin/plugin_install_job_monitor.h
index 97b5c79..825c2e2 100644
--- a/webkit/default_plugin/plugin_install_job_monitor.h
+++ b/webkit/default_plugin/plugin_install_job_monitor.h
@@ -14,7 +14,7 @@
// The PluginInstallationJobMonitorThread class represents a background
// thread which monitors the install job completion port which is associated
// with the job when an instance of this class is initialized.
-class PluginInstallationJobMonitorThread : public Thread {
+class PluginInstallationJobMonitorThread : public base::Thread {
public:
PluginInstallationJobMonitorThread();
virtual ~PluginInstallationJobMonitorThread();
diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.cc b/webkit/tools/test_shell/simple_resource_loader_bridge.cc
index 2e69ce8..4c58b34 100644
--- a/webkit/tools/test_shell/simple_resource_loader_bridge.cc
+++ b/webkit/tools/test_shell/simple_resource_loader_bridge.cc
@@ -51,11 +51,11 @@ namespace {
//-----------------------------------------------------------------------------
URLRequestContext* request_context = NULL;
-Thread* io_thread = NULL;
+base::Thread* io_thread = NULL;
-class IOThread : public Thread {
+class IOThread : public base::Thread {
public:
- IOThread() : Thread("IOThread") {
+ IOThread() : base::Thread("IOThread") {
}
~IOThread() {
@@ -80,7 +80,9 @@ bool EnsureIOThread() {
SimpleResourceLoaderBridge::Init(NULL);
io_thread = new IOThread();
- return io_thread->Start();
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ return io_thread->StartWithOptions(options);
}
//-----------------------------------------------------------------------------