Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(338)

Unified Diff: base/threading/simple_thread.cc

Issue 2204333003: Add joinable option to SimpleThread::Options as was just done for Thread. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@b1_nonjoinable_thread
Patch Set: nits:thestig Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: base/threading/simple_thread.cc
diff --git a/base/threading/simple_thread.cc b/base/threading/simple_thread.cc
index 6c64a17d6ab2fea1ec792715ad4697adcc21bd94..41b577b403771a12b3e7f5543bf63d418b181634 100644
--- a/base/threading/simple_thread.cc
+++ b/base/threading/simple_thread.cc
@@ -4,7 +4,6 @@
#include "base/threading/simple_thread.h"
-#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_restrictions.h"
@@ -12,53 +11,46 @@
namespace base {
SimpleThread::SimpleThread(const std::string& name_prefix)
- : name_prefix_(name_prefix),
- name_(name_prefix),
- thread_(),
- event_(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED),
- tid_(0),
- joined_(false) {}
+ : SimpleThread(name_prefix, Options()) {}
SimpleThread::SimpleThread(const std::string& name_prefix,
const Options& options)
: name_prefix_(name_prefix),
name_(name_prefix),
options_(options),
- thread_(),
event_(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED),
- tid_(0),
- joined_(false) {}
+ WaitableEvent::InitialState::NOT_SIGNALED) {}
SimpleThread::~SimpleThread() {
DCHECK(HasBeenStarted()) << "SimpleThread was never started.";
- DCHECK(HasBeenJoined()) << "SimpleThread destroyed without being Join()ed.";
+ DCHECK(!options_.joinable || HasBeenJoined())
+ << "Joinable SimpleThread destroyed without being Join()ed.";
}
void SimpleThread::Start() {
DCHECK(!HasBeenStarted()) << "Tried to Start a thread multiple times.";
- bool success;
- if (options_.priority() == ThreadPriority::NORMAL) {
- success = PlatformThread::Create(options_.stack_size(), this, &thread_);
- } else {
- success = PlatformThread::CreateWithPriority(options_.stack_size(), this,
- &thread_, options_.priority());
- }
+ bool success =
+ options_.joinable
+ ? PlatformThread::CreateWithPriority(options_.stack_size, this,
+ &thread_, options_.priority)
+ : PlatformThread::CreateNonJoinableWithPriority(
+ options_.stack_size, this, options_.priority);
DCHECK(success);
- base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ ThreadRestrictions::ScopedAllowWait allow_wait;
event_.Wait(); // Wait for the thread to complete initialization.
}
void SimpleThread::Join() {
+ DCHECK(options_.joinable) << "A non-joinable thread can't be joined.";
DCHECK(HasBeenStarted()) << "Tried to Join a never-started thread.";
DCHECK(!HasBeenJoined()) << "Tried to Join a thread multiple times.";
PlatformThread::Join(thread_);
+ thread_ = PlatformThreadHandle();
joined_ = true;
}
bool SimpleThread::HasBeenStarted() {
- base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ ThreadRestrictions::ScopedAllowWait allow_wait;
return event_.IsSignaled();
}
@@ -75,26 +67,75 @@ void SimpleThread::ThreadMain() {
Run();
}
+DelegateSimpleThread::Delegate::Delegate()
+#if DCHECK_IS_ON()
+ : active_runners_decremented_(WaitableEvent::ResetPolicy::AUTOMATIC,
+ WaitableEvent::InitialState::NOT_SIGNALED)
+#endif
+{
+}
+
+DelegateSimpleThread::Delegate::~Delegate() {
+#if DCHECK_IS_ON()
+ // A Delegate is allowed to be destroyed after returning from Run(). This
+ // means that destruction can legally occur between the end of Run() and
+ // |active_runners_| being decremented in RunHelper(). Allow a minimal
+ // reasonable delay for this race to unwind.
+ constexpr TimeDelta kMaxCompletionWait = TimeDelta::FromMilliseconds(200);
+ TimeDelta remaining_time = kMaxCompletionWait;
+ const TimeTicks max_completion_time = TimeTicks::Now() + kMaxCompletionWait;
+ do {
+ // At least a single Wait() call is required (even when |active_runners_| is
+ // already zero) in order to synchronize with the matching Signal() call;
+ // otherwise there is a race between it and |active_runners_decremented_|'s
+ // destructor (as highlighted by TSAN); hence the do-while.
+ ThreadRestrictions::ScopedAllowWait allow_wait;
+ active_runners_decremented_.TimedWait(remaining_time);
+ remaining_time = max_completion_time - TimeTicks::Now();
+ } while (!AtomicRefCountIsZero(&active_runners_) &&
+ remaining_time > TimeDelta());
+
+ DCHECK(AtomicRefCountIsZero(&active_runners_))
danakj 2016/08/12 20:49:10 Hm, I guess this is better than nothing? Idk, it's
gab 2016/08/12 21:23:17 Right... I can't think of a way around this. My th
+ << "Delegate cannot be deleted with Run() in progress.";
+#endif // DCHECK_IS_ON()
+}
+
+void DelegateSimpleThread::Delegate::RunHelper() {
+#if DCHECK_IS_ON()
+ AtomicRefCountInc(&active_runners_);
+#endif // DCHECK_IS_ON()
+
+ Run();
+
danakj 2016/08/12 20:49:09 would it help to DCHECK here that ~Delegate hasn't
gab 2016/08/12 21:23:17 It's correct for a Delegate to say "DeleteSoon(thi
+#if DCHECK_IS_ON()
+ AtomicRefCountDec(&active_runners_);
+ active_runners_decremented_.Signal();
+#endif // DCHECK_IS_ON()
+}
+
DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate,
const std::string& name_prefix)
- : SimpleThread(name_prefix),
- delegate_(delegate) {
-}
+ : DelegateSimpleThread(delegate, name_prefix, Options()) {}
DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate,
const std::string& name_prefix,
const Options& options)
: SimpleThread(name_prefix, options),
delegate_(delegate) {
+ DCHECK(delegate_);
}
-DelegateSimpleThread::~DelegateSimpleThread() {
-}
+DelegateSimpleThread::~DelegateSimpleThread() = default;
void DelegateSimpleThread::Run() {
- DCHECK(delegate_) << "Tried to call Run without a delegate (called twice?)";
- delegate_->Run();
- delegate_ = NULL;
+#if DCHECK_IS_ON()
+ // |ran_| must be verified and updated before calling RunHelper() as non-
+ // joinable DelegateSimpleThread can be deleted before it returns.
+ DCHECK(!ran_) << "Ran twice?";
+ ran_ = true;
+#endif // DCHECK_IS_ON()
+
+ delegate_->RunHelper();
}
DelegateSimpleThreadPool::DelegateSimpleThreadPool(
@@ -167,7 +208,7 @@ void DelegateSimpleThreadPool::Run() {
if (!work)
break;
- work->Run();
+ work->RunHelper();
}
}

Powered by Google App Engine
This is Rietveld 408576698