Index: sandbox/linux/services/thread_helpers.cc |
diff --git a/sandbox/linux/services/thread_helpers.cc b/sandbox/linux/services/thread_helpers.cc |
index 80766a9bc5dc970666efbb5c3e7ff21ad646c4ff..c1ba04a248c37c75346690dfdfaa5297dbd5c713 100644 |
--- a/sandbox/linux/services/thread_helpers.cc |
+++ b/sandbox/linux/services/thread_helpers.cc |
@@ -29,6 +29,10 @@ namespace { |
const char kAssertSingleThreadedError[] = |
"Current process is not mono-threaded!"; |
+const char kAssertThreadDoesNotAppearInProcFS[] = |
+ "Started thread does not appear in /proc"; |
+const char kAssertThreadDoesNotDisappearInProcFS[] = |
+ "Stopped thread does not disappear in /proc"; |
bool IsSingleThreadedImpl(int proc_fd) { |
CHECK_LE(0, proc_fd); |
@@ -56,13 +60,18 @@ bool IsThreadPresentInProcFS(int proc_fd, |
return true; |
} |
+bool IsNotThreadPresentInProcFS(int proc_fd, |
+ const std::string& thread_id_dir_str) { |
+ return !IsThreadPresentInProcFS(proc_fd, thread_id_dir_str); |
+} |
+ |
// Run |cb| in a loop until it returns false. Every time |cb| runs, sleep |
// for an exponentially increasing amount of time. |cb| is expected to return |
// false very quickly and this will crash if it doesn't happen within ~64ms on |
// Debug builds (2s on Release builds). |
// This is guaranteed to not sleep more than twice as much as the bare minimum |
// amount of time. |
-void RunWhileTrue(const base::Callback<bool(void)>& cb) { |
+void RunWhileTrue(const base::Callback<bool(void)>& cb, const char* message) { |
#if defined(NDEBUG) |
// In Release mode, crash after 30 iterations, which means having spent |
// roughly 2s in |
@@ -91,8 +100,7 @@ void RunWhileTrue(const base::Callback<bool(void)>& cb) { |
PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts))); |
} |
- LOG(FATAL) << kAssertSingleThreadedError << " (iterations: " << kMaxIterations |
- << ")"; |
+ LOG(FATAL) << message << " (iterations: " << kMaxIterations << ")"; |
NOTREACHED(); |
} |
@@ -101,6 +109,51 @@ bool IsMultiThreaded(int proc_fd) { |
return !ThreadHelpers::IsSingleThreaded(proc_fd); |
} |
+enum class ThreadAction { Start, Stop }; |
+ |
+bool ChangeThreadStateAndWatchProcFS( |
+ int proc_fd, base::Thread* thread, ThreadAction action) { |
+ DCHECK_LE(0, proc_fd); |
+ DCHECK(thread); |
+ DCHECK(action == ThreadAction::Start || action == ThreadAction::Stop); |
+ |
+ base::Callback<bool(void)> cb; |
+ const char* message; |
+ |
+ if (action == ThreadAction::Start) { |
+ // Should start the thread before calling thread_id(). |
+ if (!thread->Start()) |
+ return false; |
+ } |
+ |
+ const base::PlatformThreadId thread_id = thread->thread_id(); |
+ const std::string thread_id_dir_str = |
+ "self/task/" + base::IntToString(thread_id) + "/"; |
+ |
+ if (action == ThreadAction::Stop) { |
+ // The target thread should exist in /proc. |
+ DCHECK(IsThreadPresentInProcFS(proc_fd, thread_id_dir_str)); |
+ thread->Stop(); |
+ } |
+ |
+ // The kernel is at liberty to wake the thread id futex before updating |
+ // /proc. Start() above or following Stop(), the thread is started or joined, |
+ // but entries in /proc may not have been updated. |
+ if (action == ThreadAction::Start) { |
+ cb = base::Bind(&IsNotThreadPresentInProcFS, proc_fd, thread_id_dir_str); |
+ message = kAssertThreadDoesNotAppearInProcFS; |
+ } else { |
+ cb = base::Bind(&IsThreadPresentInProcFS, proc_fd, thread_id_dir_str); |
+ message = kAssertThreadDoesNotDisappearInProcFS; |
+ } |
+ RunWhileTrue(cb, message); |
+ |
+ DCHECK_EQ(action == ThreadAction::Start, |
+ IsThreadPresentInProcFS(proc_fd, thread_id_dir_str)); |
+ |
+ return true; |
+} |
+ |
} // namespace |
// static |
@@ -119,7 +172,7 @@ bool ThreadHelpers::IsSingleThreaded() { |
void ThreadHelpers::AssertSingleThreaded(int proc_fd) { |
DCHECK_LE(0, proc_fd); |
const base::Callback<bool(void)> cb = base::Bind(&IsMultiThreaded, proc_fd); |
- RunWhileTrue(cb); |
+ RunWhileTrue(cb, kAssertSingleThreadedError); |
} |
void ThreadHelpers::AssertSingleThreaded() { |
@@ -128,25 +181,15 @@ void ThreadHelpers::AssertSingleThreaded() { |
} |
// static |
+bool ThreadHelpers::StartThreadAndWatchProcFS(int proc_fd, |
+ base::Thread* thread) { |
+ return ChangeThreadStateAndWatchProcFS(proc_fd, thread, ThreadAction::Start); |
+} |
+ |
+// static |
bool ThreadHelpers::StopThreadAndWatchProcFS(int proc_fd, |
base::Thread* thread) { |
- DCHECK_LE(0, proc_fd); |
- DCHECK(thread); |
- const base::PlatformThreadId thread_id = thread->thread_id(); |
- const std::string thread_id_dir_str = |
- "self/task/" + base::IntToString(thread_id) + "/"; |
- |
- // The kernel is at liberty to wake the thread id futex before updating |
- // /proc. Following Stop(), the thread is joined, but entries in /proc may |
- // not have been updated. |
- thread->Stop(); |
- |
- const base::Callback<bool(void)> cb = |
- base::Bind(&IsThreadPresentInProcFS, proc_fd, thread_id_dir_str); |
- |
- RunWhileTrue(cb); |
- |
- return true; |
+ return ChangeThreadStateAndWatchProcFS(proc_fd, thread, ThreadAction::Stop); |
} |
// static |