blob: a4798e0cfc20ad69c3a87650470e7d1a1fe5e8c0 [file] [log] [blame]
license.botf003cfe2008-08-24 09:55:55 +09001// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit3f4a7322008-07-27 06:49:38 +09004
maruel@google.comf6e3db82008-08-13 03:37:35 +09005#include "base/thread.h"
6
initial.commit3f4a7322008-07-27 06:49:38 +09007#include "base/message_loop.h"
initial.commit3f4a7322008-07-27 06:49:38 +09008#include "base/string_util.h"
darin@google.comc62bf352008-08-11 23:35:15 +09009#include "base/waitable_event.h"
maruel@google.comf6e3db82008-08-13 03:37:35 +090010
initial.commit3f4a7322008-07-27 06:49:38 +090011// This task is used to trigger the message loop to exit.
12class ThreadQuitTask : public Task {
13 public:
14 virtual void Run() {
15 MessageLoop::current()->Quit();
16 Thread::SetThreadWasQuitProperly(true);
17 }
18};
19
initial.commit3f4a7322008-07-27 06:49:38 +090020Thread::Thread(const char *name)
darin@google.comc18d7ae2008-08-21 18:46:32 +090021 : message_loop_(NULL),
22 startup_event_(NULL),
23 name_(name),
24 thread_created_(false) {
initial.commit3f4a7322008-07-27 06:49:38 +090025}
26
27Thread::~Thread() {
28 Stop();
29}
30
31// We use this thread-local variable to record whether or not a thread exited
32// because its Stop method was called. This allows us to catch cases where
33// MessageLoop::Quit() is called directly, which is unexpected when using a
34// Thread to setup and run a MessageLoop.
35// Note that if we start doing complex stuff in other static initializers
36// this could cause problems.
evanm@google.comf26fd3a2008-08-21 07:54:52 +090037// TODO(evanm): this shouldn't rely on static initialization.
38TLSSlot Thread::tls_index_;
initial.commit3f4a7322008-07-27 06:49:38 +090039
40void Thread::SetThreadWasQuitProperly(bool flag) {
41#ifndef NDEBUG
evanm@google.comf26fd3a2008-08-21 07:54:52 +090042 tls_index_.Set(reinterpret_cast<void*>(flag));
initial.commit3f4a7322008-07-27 06:49:38 +090043#endif
44}
45
46bool Thread::GetThreadWasQuitProperly() {
47 bool quit_properly = true;
48#ifndef NDEBUG
evanm@google.comf26fd3a2008-08-21 07:54:52 +090049 quit_properly = (tls_index_.Get() != 0);
initial.commit3f4a7322008-07-27 06:49:38 +090050#endif
51 return quit_properly;
52}
53
initial.commit3f4a7322008-07-27 06:49:38 +090054bool Thread::Start() {
55 return StartWithStackSize(0);
56}
57
58bool Thread::StartWithStackSize(size_t stack_size) {
darin@google.comc18d7ae2008-08-21 18:46:32 +090059 DCHECK(!message_loop_);
initial.commit3f4a7322008-07-27 06:49:38 +090060
darin@google.comc18d7ae2008-08-21 18:46:32 +090061 SetThreadWasQuitProperly(false);
62
63 base::WaitableEvent event(false, false);
64 startup_event_ = &event;
65
66 if (!PlatformThread::Create(stack_size, this, &thread_)) {
initial.commit3f4a7322008-07-27 06:49:38 +090067 DLOG(ERROR) << "failed to create thread";
68 return false;
69 }
70
71 // Wait for the thread to start and initialize message_loop_
darin@google.comc18d7ae2008-08-21 18:46:32 +090072 startup_event_->Wait();
73 startup_event_ = NULL;
74
75 DCHECK(message_loop_);
initial.commit3f4a7322008-07-27 06:49:38 +090076 return true;
77}
78
79void Thread::Stop() {
darin@google.comc18d7ae2008-08-21 18:46:32 +090080 if (!thread_created_)
initial.commit3f4a7322008-07-27 06:49:38 +090081 return;
82
darin@google.comc18d7ae2008-08-21 18:46:32 +090083 DCHECK_NE(thread_id_, PlatformThread::CurrentId()) <<
84 "Can't call Stop() on the currently executing thread";
initial.commit3f4a7322008-07-27 06:49:38 +090085
darin@google.comc18d7ae2008-08-21 18:46:32 +090086 // If StopSoon was called, then we won't have a message loop anymore, but
87 // more importantly, we won't need to tell the thread to stop.
88 if (message_loop_)
maruel@google.comf6e3db82008-08-13 03:37:35 +090089 message_loop_->PostTask(FROM_HERE, new ThreadQuitTask());
90
91 // Wait for the thread to exit. It should already have terminated but make
92 // sure this assumption is valid.
darin@google.comc18d7ae2008-08-21 18:46:32 +090093 PlatformThread::Join(thread_);
maruel@google.comf6e3db82008-08-13 03:37:35 +090094
darin@google.comc18d7ae2008-08-21 18:46:32 +090095 // The thread can't receive messages anymore.
96 message_loop_ = NULL;
97
98 // The thread no longer needs to be joined.
99 thread_created_ = false;
maruel@google.comf6e3db82008-08-13 03:37:35 +0900100}
101
102void Thread::StopSoon() {
darin@google.comc18d7ae2008-08-21 18:46:32 +0900103 if (!message_loop_)
maruel@google.comf6e3db82008-08-13 03:37:35 +0900104 return;
105
darin@google.comc18d7ae2008-08-21 18:46:32 +0900106 DCHECK_NE(thread_id_, PlatformThread::CurrentId()) <<
107 "Can't call StopSoon() on the currently executing thread";
maruel@google.comf6e3db82008-08-13 03:37:35 +0900108
initial.commit3f4a7322008-07-27 06:49:38 +0900109 // We had better have a message loop at this point! If we do not, then it
110 // most likely means that the thread terminated unexpectedly, probably due
111 // to someone calling Quit() on our message loop directly.
112 DCHECK(message_loop_);
113
114 message_loop_->PostTask(FROM_HERE, new ThreadQuitTask());
115
maruel@google.comf6e3db82008-08-13 03:37:35 +0900116 // The thread can't receive messages anymore.
darin@google.comc18d7ae2008-08-21 18:46:32 +0900117 message_loop_ = NULL;
initial.commit3f4a7322008-07-27 06:49:38 +0900118}
119
darin@google.comc18d7ae2008-08-21 18:46:32 +0900120void Thread::ThreadMain() {
initial.commit3f4a7322008-07-27 06:49:38 +0900121 // The message loop for this thread.
122 MessageLoop message_loop;
123
maruel@google.comf6e3db82008-08-13 03:37:35 +0900124 // Complete the initialization of our Thread object.
darin@google.comc18d7ae2008-08-21 18:46:32 +0900125 thread_id_ = PlatformThread::CurrentId();
126 PlatformThread::SetName(thread_id_, name_.c_str());
127 message_loop.set_thread_name(name_);
128 message_loop_ = &message_loop;
129 thread_created_ = true;
130
131 startup_event_->Signal();
132 // startup_event_ can't be touched anymore since the starting thread is now
133 // unlocked.
initial.commit3f4a7322008-07-27 06:49:38 +0900134
135 // Let the thread do extra initialization.
darin@google.comc18d7ae2008-08-21 18:46:32 +0900136 Init();
initial.commit3f4a7322008-07-27 06:49:38 +0900137
138 message_loop.Run();
139
140 // Let the thread do extra cleanup.
darin@google.comc18d7ae2008-08-21 18:46:32 +0900141 CleanUp();
initial.commit3f4a7322008-07-27 06:49:38 +0900142
143 // Assert that MessageLoop::Quit was called by ThreadQuitTask.
darin@google.comc18d7ae2008-08-21 18:46:32 +0900144 DCHECK(GetThreadWasQuitProperly());
initial.commit3f4a7322008-07-27 06:49:38 +0900145
maruel@google.comf6e3db82008-08-13 03:37:35 +0900146 // We can't receive messages anymore.
darin@google.comc18d7ae2008-08-21 18:46:32 +0900147 message_loop_ = NULL;
initial.commit3f4a7322008-07-27 06:49:38 +0900148}
license.botf003cfe2008-08-24 09:55:55 +0900149