license.bot | f003cfe | 2008-08-24 09:55:55 +0900 | [diff] [blame^] | 1 | // 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.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 4 | |
maruel@google.com | f6e3db8 | 2008-08-13 03:37:35 +0900 | [diff] [blame] | 5 | #include "base/thread.h" |
| 6 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 7 | #include "base/message_loop.h" |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 8 | #include "base/string_util.h" |
darin@google.com | c62bf35 | 2008-08-11 23:35:15 +0900 | [diff] [blame] | 9 | #include "base/waitable_event.h" |
maruel@google.com | f6e3db8 | 2008-08-13 03:37:35 +0900 | [diff] [blame] | 10 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 11 | // This task is used to trigger the message loop to exit. |
| 12 | class ThreadQuitTask : public Task { |
| 13 | public: |
| 14 | virtual void Run() { |
| 15 | MessageLoop::current()->Quit(); |
| 16 | Thread::SetThreadWasQuitProperly(true); |
| 17 | } |
| 18 | }; |
| 19 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 20 | Thread::Thread(const char *name) |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 21 | : message_loop_(NULL), |
| 22 | startup_event_(NULL), |
| 23 | name_(name), |
| 24 | thread_created_(false) { |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 25 | } |
| 26 | |
| 27 | Thread::~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.com | f26fd3a | 2008-08-21 07:54:52 +0900 | [diff] [blame] | 37 | // TODO(evanm): this shouldn't rely on static initialization. |
| 38 | TLSSlot Thread::tls_index_; |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 39 | |
| 40 | void Thread::SetThreadWasQuitProperly(bool flag) { |
| 41 | #ifndef NDEBUG |
evanm@google.com | f26fd3a | 2008-08-21 07:54:52 +0900 | [diff] [blame] | 42 | tls_index_.Set(reinterpret_cast<void*>(flag)); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 43 | #endif |
| 44 | } |
| 45 | |
| 46 | bool Thread::GetThreadWasQuitProperly() { |
| 47 | bool quit_properly = true; |
| 48 | #ifndef NDEBUG |
evanm@google.com | f26fd3a | 2008-08-21 07:54:52 +0900 | [diff] [blame] | 49 | quit_properly = (tls_index_.Get() != 0); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 50 | #endif |
| 51 | return quit_properly; |
| 52 | } |
| 53 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 54 | bool Thread::Start() { |
| 55 | return StartWithStackSize(0); |
| 56 | } |
| 57 | |
| 58 | bool Thread::StartWithStackSize(size_t stack_size) { |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 59 | DCHECK(!message_loop_); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 60 | |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 61 | SetThreadWasQuitProperly(false); |
| 62 | |
| 63 | base::WaitableEvent event(false, false); |
| 64 | startup_event_ = &event; |
| 65 | |
| 66 | if (!PlatformThread::Create(stack_size, this, &thread_)) { |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 67 | DLOG(ERROR) << "failed to create thread"; |
| 68 | return false; |
| 69 | } |
| 70 | |
| 71 | // Wait for the thread to start and initialize message_loop_ |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 72 | startup_event_->Wait(); |
| 73 | startup_event_ = NULL; |
| 74 | |
| 75 | DCHECK(message_loop_); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 76 | return true; |
| 77 | } |
| 78 | |
| 79 | void Thread::Stop() { |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 80 | if (!thread_created_) |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 81 | return; |
| 82 | |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 83 | DCHECK_NE(thread_id_, PlatformThread::CurrentId()) << |
| 84 | "Can't call Stop() on the currently executing thread"; |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 85 | |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 86 | // 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.com | f6e3db8 | 2008-08-13 03:37:35 +0900 | [diff] [blame] | 89 | 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.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 93 | PlatformThread::Join(thread_); |
maruel@google.com | f6e3db8 | 2008-08-13 03:37:35 +0900 | [diff] [blame] | 94 | |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 95 | // 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.com | f6e3db8 | 2008-08-13 03:37:35 +0900 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | void Thread::StopSoon() { |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 103 | if (!message_loop_) |
maruel@google.com | f6e3db8 | 2008-08-13 03:37:35 +0900 | [diff] [blame] | 104 | return; |
| 105 | |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 106 | DCHECK_NE(thread_id_, PlatformThread::CurrentId()) << |
| 107 | "Can't call StopSoon() on the currently executing thread"; |
maruel@google.com | f6e3db8 | 2008-08-13 03:37:35 +0900 | [diff] [blame] | 108 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 109 | // 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.com | f6e3db8 | 2008-08-13 03:37:35 +0900 | [diff] [blame] | 116 | // The thread can't receive messages anymore. |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 117 | message_loop_ = NULL; |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 118 | } |
| 119 | |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 120 | void Thread::ThreadMain() { |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 121 | // The message loop for this thread. |
| 122 | MessageLoop message_loop; |
| 123 | |
maruel@google.com | f6e3db8 | 2008-08-13 03:37:35 +0900 | [diff] [blame] | 124 | // Complete the initialization of our Thread object. |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 125 | 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.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 134 | |
| 135 | // Let the thread do extra initialization. |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 136 | Init(); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 137 | |
| 138 | message_loop.Run(); |
| 139 | |
| 140 | // Let the thread do extra cleanup. |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 141 | CleanUp(); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 142 | |
| 143 | // Assert that MessageLoop::Quit was called by ThreadQuitTask. |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 144 | DCHECK(GetThreadWasQuitProperly()); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 145 | |
maruel@google.com | f6e3db8 | 2008-08-13 03:37:35 +0900 | [diff] [blame] | 146 | // We can't receive messages anymore. |
darin@google.com | c18d7ae | 2008-08-21 18:46:32 +0900 | [diff] [blame] | 147 | message_loop_ = NULL; |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 148 | } |
license.bot | f003cfe | 2008-08-24 09:55:55 +0900 | [diff] [blame^] | 149 | |